Add the ability to specify ffmpeg path & use Matplotlib's ffmpeg path#39
Add the ability to specify ffmpeg path & use Matplotlib's ffmpeg path#39calw20 wants to merge 12 commits intoali-ramadhan:mainfrom
Conversation
ali-ramadhan
left a comment
There was a problem hiding this comment.
Hi @calw20 and thank you for contributing by opening this PR! I didn't know that matplotlib came with an ffmpeg executable and I think using the matplotlib ffmpeg is a great improvement, especially for users on systems with no ffmpeg (or a very old ffmpeg version).
Out of curiosity, do you know if ffmpeg versions are unique to matplotlib versions? E.g. if you have matplotlib version x.y.z then you're guaranteed to have ffmpeg version a.b.c?
I just left a couple of comments/questions to address before we merge. And thanks for tagging v0.9.2!
| def _check_ffmpeg_availability(): | ||
| """Check if ffmpeg is available on the system.""" |
There was a problem hiding this comment.
Should the user-supplied ffmpeg_path be an input here? I guess it can't be since this is __init__.py. I guess in the instance that ffmpeg is not available on the system but the user-supplied path works this will still produce a warning, but maybe this is fine?
There was a problem hiding this comment.
Yeah that was my thought as well but then I saw it was passed to __init__.py I couldn't figure out a way to make it stick. The hope was that by using matplotlib's rcPrams if a user updated the rcPrams before importing Loom it would pull the updated value.
This works but only in the context of the main thread, as soon as you parallelise Loom it re-imports matplotlib & its rcPrams seems to be reset to the file-based defaults. I'm yet to test it but in theory if you add an rcPrams file where matplotlib expects it those settings should be maintained in the mutli-threaded/process context.
|
|
||
| DEFAULT_FFMPEG_PATH = plt.rcParams['animation.ffmpeg_path'] | ||
|
|
||
| ACCEPTABLE_EXTENSIONS = ("mp4", "gif") |
There was a problem hiding this comment.
Hmmm, I could be wrong but I thought Loom should still work if you specify another extension as long as ffmpeg supports it? So I was keeping it open and up to ffmpeg whether it supports the output extension. But do you think we need to limit them using ACCEPTABLE_EXTENSIONS?
There was a problem hiding this comment.
I added the check as a bit of a bodge; it was something I was hopeing to clean up a bit later a work & wanted to seek comments on - I added it as I got an erorr from Loom when trying to create an .avi movie file. From memory the command list wasn't created as the conditional logic block didn't have a default else case that functioned.
There was a problem hiding this comment.
Now that's a format I haven't seen in a while haha.
It's true that we don't really test for any output format besides GIF and MP4. I would say if AVI output does not work, let's open an issue about it and hopefully it'll be an easy fix.
|
As far as I know matplotlib also defaults to trying the |
|
I can show some examples later next week once I'm back at work - if it helps as well. I'm super chuffed how easy it was to use |
ali-ramadhan
left a comment
There was a problem hiding this comment.
Hey @calw20 sorry I've been meaning to revisit this PR. I left a few more comments. Just let me know if I can help with this PR.
I might make some changes to the rest of the code (nothing too drastic) but if any conflicts show up in this PR I can help resolve them.
| self.ffmpeg_path: str = DEFAULT_FFMPEG_PATH | ||
| if ffmpeg_path is not None: | ||
| _ffmpeg_path = Path(ffmpeg_path) | ||
| if _ffmpeg_path.exists(): | ||
| self.ffmpeg_path = str(_ffmpeg_path.absolute()) |
There was a problem hiding this comment.
This is great but if the user provides an ffmpeg_path that doesn't exist, the code will silently fall back to the default. To alert the user and avoid confusing behavior, I think we should either produce an error and exit, or warn the user then switch to the default.
|
|
||
| DEFAULT_FFMPEG_PATH = plt.rcParams['animation.ffmpeg_path'] | ||
|
|
||
| ACCEPTABLE_EXTENSIONS = ("mp4", "gif") |
There was a problem hiding this comment.
Now that's a format I haven't seen in a while haha.
It's true that we don't really test for any output format besides GIF and MP4. I would say if AVI output does not work, let's open an issue about it and hopefully it'll be an easy fix.
Co-authored-by: Ali Ramadhan <ali.hh.ramadhan@gmail.com>
…path provided by imageio_ffmpeg
|
Hey @ali-ramadhan, I also have to apologise, I also have been meaning to continue looking at this PR but got caught up in end-of-year work shenanigans. I've made a few additional commits, the crux of which are:
Hope this helps! |
ali-ramadhan
left a comment
There was a problem hiding this comment.
@calw20 Thanks for revisiting this PR! Nice work adding the enable_ffmpeg_path_fallback option and the example.
Took another look and I have a few more thoughts:
-
Good idea to add an example of using a custom ffmpeg path! But I think we should not add it to the
parallel_sine_wave.pyexample. I think each example should be as simple and self-contained as possible. So I would actually suggest a completely new example:custom_ffmpeg_sine_wave.py? Then we can test it in CI and include it in the README and the docs. -
I think we should get rid of
ACCEPTABLE_EXTENSIONSand aim to support any extension by default (that ffmpeg also supports). If a particular extension causes an issue, we can open a GitHub issue and work on fixing it. I feel like ffmpeg supports a huge range of output file formats, and many of them would probably work out of the box for us here. -
I'm a little confused by what
_LOOM_DEFAULT_ENVIRON_VARdoes and the use case for it. -
Can we add some tests for this new functionality? Should be simple to test the logic of
ffmpeg_pathandenable_ffmpeg_path_fallback. Testing the new example would also be super useful. I'll try to think of others too. -
Would also be great if we can add a section to the docs documenting the default path and the different ways you can use a custom ffmpeg path.
Apologies for the many asks! Very happy to work on some of them!
| # In theory this should never fail. | ||
| assert isinstance(self.ffmpeg_path, str), "ffmpeg path is not a string?" |
There was a problem hiding this comment.
Hmmm, I'm thinking we will probably want self.ffmpeg_path to always be a Path object?
| elif self.odd_dimension_handling == "pad": | ||
| return "pad='if(mod(iw,2),iw+1,iw)':'if(mod(ih,2),ih+1,ih)':0:0:color=white" | ||
| else: | ||
| raise ValueError("Scale Settings Incorrect!") |
There was a problem hiding this comment.
Might be good to print the value of self.odd_dimension_handling and the possible values so the user can know what is wrong.
|
|
||
| # Either the environ var OR matplotlib rcPram *needs* to be set BEFORE | ||
| # importing matplotloom | ||
| import imageio_ffmpeg # python library that provides ffmpeg binary |
There was a problem hiding this comment.
Nice, didn't know about this package!
Changed default ffmpeg command to use matplotlib.pyplot.rcParams['animation.ffmpeg_path'] and added "ffmpeg_path" as a new argument to the Loom initialiser to allow for each Loom instance to have its own ffmpeg command path (defaults to matplotlib's).
Sorry for the double pull-request - I managed to mangle my git history.