Marshall Davis
Marshall Davis

Reputation: 3650

Do not quote arguments to command with subprocess.check_output()

I'm trying to pass a glob pattern to ffmpeg via subprocess.check_output(). This appears to be having an issue as the output is what I would expect if the pattern was passed within quotes.

Example:

subprocess.check_output([ffmpeg_path, '-pattern_type', 'glob', '-i', '/tmp/*?(jpg|jpeg)'...])

This results in:

/tmp/*?(jpg|jpeg): No such file or directory.

Which is different than the "no matches" error I receive if run this command via the shell. What appears to be happening is that the command is being composed as

ffmpeg -pattern_type 'glob' -i '/tmp/*?(jpg|jpeg)'

and what I'd want is (note lack of quotations)

ffmpeg -pattern_type glob -i /tmp/*?(jpg|jpeg)

Something of note is that I am passing in the directory so directory + '/*?(jpg|jpeg)' is what is in the list being passed to check_output(). This may be part of the issue.

How would I prevent this, or how would I pass the raw arguments desired?

ffmpeg version when running on development machine:

ffmpeg version 4.4.1 Copyright (c) 2000-2021 the FFmpeg developers
  built with Apple clang version 13.0.0 (clang-1300.0.29.3)
  configuration: --prefix=/usr/local/Cellar/ffmpeg/4.4.1_5 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libbluray --enable-libdav1d --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librist --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvmaf --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-avresample --enable-videotoolbox

and using the ffmpeg amd64 static build when deployed.

Upvotes: 1

Views: 176

Answers (2)

Rotem
Rotem

Reputation: 32094

The quotes is not the issue, the FFmpeg glob syntax is a little different than the one you are using.

Here is an example for a correct syntax:

subprocess.check_output([ffmpeg_path, '-vcodec', 'mjpeg', '-f', 'image2', '-pattern_type', 'glob', '-i', '/tmp/*?{jpg,jpeg}', 'test.mp4'])

  • Specify the video codec: '-vcodec', 'mjpeg'.
    I suppose FFmpeg fails to identify the codec automatically due to the pattern.
  • Specify the muxer format: '-f', 'image2'.
    I suppose FFmpeg fails to identify the format automatically due to the pattern.
  • Use the following syntax: '/tmp/*?{jpg,jpeg}' (instead of /tmp/*?(jpg|jpeg)).
    The pattern matches the rules of Bash Gglobing.

The command line includes the quotes, and it's working with the them.

ffmpeg -vcodec mjpeg -f image2 -pattern_type glob -i "/tmp/*?{jpg,jpeg}" -vcodec libx264 test.mp4

Testing:

I tested the Python code under Ubuntu 18.04 (using VirtualBox).

Using command line (console):

Changing directory to /tmp (in Ubuntu it supposed to be existed):
cd /tmp

Checking FFmpeg version:
ffmpeg -version

Output:
ffmpeg version 3.1.3-static http://johnvansickle.com/ffmpeg/ Copyright (c) 2000-2016 the FFmpeg developers built with gcc 5.4.1 (Debian 5.4.1-1) 20160803
(I think it's the default version of Ubuntu 18.04).

Creating 10 (synthetic pattern) files with jpg extension and 7 files with jpeg extension:
ffmpeg -f lavfi -i testsrc=size=192x108:rate=1:duration=10 %d.jpg
ffmpeg -f lavfi -i testsrc=size=192x108:rate=1:duration=7 %d.jpeg

List jpg and jpeg files:
ls *?{jpg,jpeg}

Output:
10.jpg 1.jpeg 1.jpg 2.jpeg 2.jpg 3.jpeg 3.jpg 4.jpeg 4.jpg 5.jpeg 5.jpg 6.jpeg 6.jpg 7.jpeg 7.jpg 8.jpg 9.jpg

Encoding the files (in command line):
ffmpeg -y -vcodec mjpeg -f image2 -pattern_type glob -i "/tmp/*?{jpg,jpeg}" -vcodec libx264 test.mp4

Verifying there are 17 video frames using FFprobe:
ffprobe -v error -select_streams v:0 -count_frames -show_entries stream=nb_read_frames -print_format csv test.mp4

Output:
stream,17

delete test.mp4:
rm test.mp4


Python test (still in folder /tmp, and console):

Checking Python version:
python3 --version

Output:
Python 3.7.4

Copy test.py into /tmp folder.
Content of test.py (the file is in the same folder: /tmp):

import subprocess

ffmpeg_path = 'ffmpeg'

subprocess.check_output([ffmpeg_path, '-vcodec', 'mjpeg', '-f', 'image2', '-pattern_type', 'glob', '-i', '/tmp/*?{jpg,jpeg}', 'test.mp4'])

Executing the Python script form shell:
python3 test.py

Output:
ffmpeg version 3.1.3-static http://johnvansickle.com/ffmpeg/ Copyright (c) 2000-2016 the FFmpeg developers
...
frame= 17

Upvotes: 1

Barmar
Barmar

Reputation: 781004

Your wildcard pattern uses a bash extension, which ffmpeg probably doesn't support.

Instead, use multiple -i options, with standard wildcards.

subprocess.check_output([ffmpeg_path, '-pattern_type', 'glob', '-i', '/tmp/*.jpg', '-i', '/tmp/*.jpeg', ...])

There are no actual quotes being added. When you use shell=False, the list elements are passed as is directly to the program, without going through a shell, so no quoting is needed. The documentation doesn't explain it accurately -- what they mean is that this acts as if you were quoting all the arguments.

Upvotes: 2

Related Questions