Ffmpeg-python: Add a way to pass arbitrary arguments to ffmpeg.run()

Created on 20 Sep 2017  路  14Comments  路  Source: kkroening/ffmpeg-python

Great work!

I'd like to pass -loglevel quiet to ffmpeg.run.
I've tried this but it fails:

>>> stream = ffmpeg.nodes.GlobalNode(stream, 'loglevel', 'quiet')
>>> ffmpeg.run(stream)
AssertionError: Unsupported global node: loglevel(quiet)

I've used ffmpeg.run(stream, cmd=['ffmpeg', '-loglevel', 'quiet']) as a workaround, but it looks like GlobalNode is very restricted. Also multiple GlobalNode can't be chained.

enhancement

Most helpful comment

For anybody that needs an example (like I did):

(
    ffmpeg
    .input(in_filename, ss=time)
    .output(out_filename, vframes=1)
    .global_args('-loglevel', 'error')
    .global_args('-y')
    .run()
)

All 14 comments

Good idea.

I have a couple of ideas of how we could do this:

  • Add .extra_args(...) operator:
(ffmpeg
    .input('in.mp4')
    .output('out.mp4')
    .extra_args('loglevel', 'quiet')
    .run()
)
  • Add extra_args parameter to .run():
(ffmpeg
    .input('in.mp4')
    .output('out.mp4')
    .run(extra_args(=['loglevel', 'quiet'])
)

Which of these looks better to you? Do you have any other ideas besides these?

I think both solutions should be implemented, but with a difference.

  • output.extra_args should add arguments to that specific output
  • run(extra_args=...) should add global arguments

What do you think?

output.extra_args should add arguments to that specific output

.output() already does that through kwargs, so I'd rather stick with that than have another way to do the same thing.

Actually, the same could be said about .run(extra_args=...) vs .extra_args(...).run(). Maybe we should just stick to one or the other, but I'm not sure which one is better.

I do like the idea of keeping .run as simple as possible though so I'm thinking we should _not_ have an extra_args param for that. I just added a .compile() function that does the same thing as run() but just builds the complete command-line (including ffmpeg command at the beginning), and since these both .run and .compile have basically the same signature I like the idea of keeping these as simple as possible.

And actually .run might get some more args added to it eventually that get passed to subprocess such as capture_stdout/capture_stderr/etc. so even more reason to keep it as simple as possible now.

So basically what I'm thinking is that we just add a .extra_args operator and call it a day.

Submitted PR. I decided to go with global_args instead of extra_args to make it clear that the args are meant to be applied to the entire command-line, as compared to arguments that apply to a particular input, output, or filter.

Great, thanks!

This is broken in a couple of ways.

First, in ffmpeg/_ffmpeg.py 'global_args' needs to be added to the __all__ list or else it can't be used.
Second, when it's in the list, it adds the arguments after the output file/pipe and thus is ignored by ffmpeg.

I added loglevel to the ouput and it put it before the output file/pipe and it worked correctly.

I don't know how to fix the second issue so I'm not going to create a pull request for the first issue.

For anybody that needs an example (like I did):

(
    ffmpeg
    .input(in_filename, ss=time)
    .output(out_filename, vframes=1)
    .global_args('-loglevel', 'error')
    .global_args('-y')
    .run()
)

What does your ffmpeg command line look like with that? When I did that, it put the global_args at the end. I had to do this:

vinput = ffmpeg.input(vdev, f='v4l2', video_size=cvsize,
                      framerate=cvfrate, input_format=cvmode,
                      thread_queue_size=tqueue)['v']
ainput = ffmpeg.input(adev, f='alsa', thread_queue_size=tqueue)['a']
stream = ffmpeg.output(vinput, ainput, "-", f='matroska', pix_fmt='yuv420p',
                       acodec=oaformat, vcodec=ovformat, loglevel='error')
# XXX global_args has bugs, one puts the args after the output file/pipe
#     causing ffmpeg to ignore it. Worked around by adding it to the
#     output for now.
#stream = ffmpeg.global_args(stream, "-loglevel error")
logging.debug("ffmpeg command: %s", " ".join(ffmpeg.compile(stream)))
self._stream = ffmpeg.run_async(stream, pipe_stdout=True,
                                pipe_stderr=True)

generates

2018-12-02 23:24:58,171 DEBUG - mythuc.py:143 - __init__() ffmpeg command: ffmpeg -f v4l2 -video_size 720x480 -framerate 60.0 -input_format yuyv422 -thread_queue_size 2048 -i /dev/video0 -f alsa -thread_queue_size 2048 -i hw:1 -map 0:v -map 1:a -f matr
oska -acodec mp3 -loglevel error -pix_fmt yuv420p -vcodec libx264 -
2018-12-02 23:24:58,176 DEBUG - mythuc.py:146 - __init__() Starting FfmpegReceive threads...
2018-12-02 23:24:58,179 DEBUG - mythuc.py:247 - fr_run() Main ffmpeg thread running...
2018-12-02 23:24:58,182 DEBUG - mythuc.py:211 - loggy() Loggy thread running...
2018-12-02 23:24:58,182 DEBUG - mythuc.py:155 - __init__() Wating for ffmpeg to warm up.
2018-12-02 23:24:59,183 DEBUG - mythuc.py:158 - __init__() ffmpeg warmed up with state: INIT
2018-12-02 23:24:59,188 DEBUG - mythuc.py:68 - ms_run() MythtvSend main thread starting...
2018-12-02 23:24:59,188 INFO - mythuc.py:381 - capture() Starting loop.

Where STDERR is quiet.

Whereas

vinput = ffmpeg.input(vdev, f='v4l2', video_size=cvsize,
                      framerate=cvfrate, input_format=cvmode,
                      thread_queue_size=tqueue)['v']
ainput = ffmpeg.input(adev, f='alsa', thread_queue_size=tqueue)['a']
stream = ffmpeg.output(vinput, ainput, "-", f='matroska', pix_fmt='yuv420p',
                       acodec=oaformat, vcodec=ovformat)
# XXX global_args has bugs, one puts the args after the output file/pipe
#     causing ffmpeg to ignore it. Worked around by adding it to the
#     output for now.
stream = ffmpeg.global_args(stream, "-loglevel", "error")
logging.debug("ffmpeg command: %s", " ".join(ffmpeg.compile(stream)))
self._stream = ffmpeg.run_async(stream, pipe_stdout=True,
                                pipe_stderr=True)

generates

2018-12-03 09:06:59,044 DEBUG - mythuc.py:143 - __init__() ffmpeg command: ffmpeg -f v4l2 -video_size 720x480 -framerate 60.0 -input_format yuyv422 -thread_queue_size 2048 -i /dev/video0 -f alsa -thread_queue_size 2048 -i hw:1 -map 0:v -map 1:a -f matroska -acodec mp3 -pix_fmt yuv420p -vcodec libx264 - -loglevel error
2018-12-03 09:06:59,051 DEBUG - mythuc.py:146 - __init__() Starting FfmpegReceive threads...
2018-12-03 09:06:59,054 DEBUG - mythuc.py:247 - fr_run() Main ffmpeg thread running...
2018-12-03 09:06:59,057 DEBUG - mythuc.py:211 - loggy() Loggy thread running...
2018-12-03 09:06:59,057 DEBUG - mythuc.py:155 - __init__() Wating for ffmpeg to warm up.
2018-12-03 09:06:59,162 DEBUG - mythuc.py:218 - loggy() Ffmpeg: b'ffmpeg version 3.3.9 Copyright (c) 2000-2018 the FFmpeg developers\n'
2018-12-03 09:06:59,162 DEBUG - mythuc.py:218 - loggy() Ffmpeg: b'  built with gcc 6.3.0 (Debian 6.3.0-18+deb9u1) 20170516\n'
2018-12-03 09:06:59,162 DEBUG - mythuc.py:218 - loggy() Ffmpeg: b'  configuration: --disable-decoder=amrnb --disable-decoder=libopenjpeg --disable-mips32r2 --disable-mips32r6 --disable-mips64r6 --disable-mipsdsp --disable-mipsdspr2 --disable-mipsfpu --disable-msa --disable-libopencv --disable-podpages --disable-stripping --enable-avfilter --enable-avresample --enable-gcrypt --enable-gnutls --enable-gpl --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libfdk-aac --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libilbc --enable-libkvazaar --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenh264 --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvo-amrwbenc --enable-libvorbis --enable-libvpx --enable-libx265 --enable-libxvid --enable-libzvbi --enable-nonfree --enable-opengl --enable-openssl --enable-postproc --enable-pthreads --enable-shared --enable-version3 --incdir=/usr/include/x86_64-linux-gnu --libdir=/usr/lib/x86_64-linux-gnu --prefix=/usr --toolchain=hardened --enable-frei0r --enable-chromaprint --enable-libx264 --enable-libiec61883 --enable-libdc1394 --enable-vaapi --disable-opencl --enable-libmfx --disable-altivec --shlibdir=/usr/lib/x86_64-linux-gnu\n'
2018-12-03 09:06:59,163 DEBUG - mythuc.py:218 - loggy() Ffmpeg: b'  libavutil      55. 58.100 / 55. 58.100\n'
2018-12-03 09:06:59,163 DEBUG - mythuc.py:218 - loggy() Ffmpeg: b'  libavcodec     57. 89.100 / 57. 89.100\n'
2018-12-03 09:06:59,163 DEBUG - mythuc.py:218 - loggy() Ffmpeg: b'  libavformat    57. 71.100 / 57. 71.100\n'
2018-12-03 09:06:59,163 DEBUG - mythuc.py:218 - loggy() Ffmpeg: b'  libavdevice    57.  6.100 / 57.  6.100\n'
2018-12-03 09:06:59,163 DEBUG - mythuc.py:218 - loggy() Ffmpeg: b'  libavfilter     6. 82.100 /  6. 82.100\n'
2018-12-03 09:06:59,163 DEBUG - mythuc.py:218 - loggy() Ffmpeg: b'  libavresample   3.  5.  0 /  3.  5.  0\n'
2018-12-03 09:06:59,163 DEBUG - mythuc.py:218 - loggy() Ffmpeg: b'  libswscale      4.  6.100 /  4.  6.100\n'
2018-12-03 09:06:59,163 DEBUG - mythuc.py:218 - loggy() Ffmpeg: b'  libswresample   2.  7.100 /  2.  7.100\n'
2018-12-03 09:06:59,163 DEBUG - mythuc.py:218 - loggy() Ffmpeg: b'  libpostproc    54.  5.100 / 54.  5.100\n'
2018-12-03 09:06:59,163 DEBUG - mythuc.py:218 - loggy() Ffmpeg: b"Unrecognized option 'loglevel error'.\n"
2018-12-03 09:06:59,164 DEBUG - mythuc.py:218 - loggy() Ffmpeg: b'Error splitting the argument list: Option not found\n'
2018-12-03 09:06:59,572 INFO - mythuc.py:244 - adjust_chunksize() Stream read block size changed from 54400 to 59220
2018-12-03 09:06:59,773 INFO - mythuc.py:244 - adjust_chunksize() Stream read block size changed from 59220 to 64484
2018-12-03 09:06:59,980 INFO - mythuc.py:244 - adjust_chunksize() Stream read block size changed from 64484 to 128968
2018-12-03 09:07:00,058 DEBUG - mythuc.py:158 - __init__() ffmpeg warmed up with state: INIT
2018-12-03 09:07:00,059 DEBUG - mythuc.py:68 - ms_run() MythtvSend main thread starting...
2018-12-03 09:07:00,072 INFO - mythuc.py:381 - capture() Starting loop..

which is not quiet.

This is Python 3.5 with ffmpeg-python pip installed and manually adding global_args to the __all__ list.

Please reopen the issue. It's not clear how to use global_args in not fluent mode.
Like this it doesn't work for example

video = ffmpeg.global_args(video, "-loglevel", "error")
video = ffmpeg.global_args(video, "-y")

@kkroening please reopen the issue

Please reopen the issue. It's not clear how to use global_args in not fluent mode.
Like this it doesn't work for example

video = ffmpeg.global_args(video, "-loglevel", "error")
video = ffmpeg.global_args(video, "-y")

Use
video = video.global_args( '-loglevel', 'error')
This works for me.

I'm likewise running into the issue that I can't use this when I need an argument to be before all else, e.g. -hwaccel.

Was this page helpful?
0 / 5 - 0 ratings