16

I have been incorporating subprocess calls in my program. I have had no issues with subprocess calls for other commands, but I am having trouble getting the command line input

ffmpeg -r 10 -i frame%03d.png -r ntsc movie.mpg

To work inside a subprocess.call()

I tried the following with no success:

subprocess.call('ffmpeg -r 10 -i %s frame%03.d.png - r ntsc movie.mpg')

Any thoughts? Do I separate out different commands, do I specify string, integer etc. with %s, %d?

Burhan Khalid
  • 169,990
  • 18
  • 245
  • 284
user3295674
  • 893
  • 5
  • 19
  • 42
  • 3
    Run your command, but with each argument split out in a list. If you do it as a single string then you will have to specify `shell=True` which you likely don't want to do anyway. So, assuming your command is built up as variable `cmd`, run `subprocess.call(cmd.split())` and wait for the magic. – sberry Sep 21 '14 at 02:55
  • What is the error? shell=True may solve it. – tdelaney Sep 21 '14 at 02:58
  • Thanks, yes, shell=True lets me run the commands as one big string, but I was also curious to see the syntax for it if I replace 'movie.mpg' with a variable name. – user3295674 Sep 21 '14 at 03:57
  • The command you want to run has `frame%03d.png`. The command you're running with `subprocess` has `frame%03.d.png `. It also has an extra `%s` that doesn't correspond to anything in your command. So… what exactly are you trying to do? What's the actual command line you want to execute? – abarnert Sep 21 '14 at 04:45
  • 1
    Unrelated, but you should use `-framerate` instead of `-r` when using the [image2 demuxer](https://ffmpeg.org/ffmpeg-formats.html#image2-1) since, IIRC, it is less picky if the inputs vary by frame size or pixel format. – llogan Sep 21 '14 at 05:33

4 Answers4

26

When you use subprocess, your command must either be a string that looks exactly like what you would type on the command line (and you set shell=True), or a list where each command is an item in the list (and you take the default shell=False). In either case, you have to deal with the variable part of the string. For instance, the operating system has no idea what "%03d" is, you have to fill it in.

I can't tell from your question exactly what the parameters are, but lets assume you want to convert frame 3, it would look something like this in a string:

my_frame = 3
subprocess.call(
    'ffmpeg -r 10 -i frame%03d.png -r ntsc movie%03d.mpg' % (my_frame, my_frame),
    shell=True)

Its kinda subtle in this example, but that's risky. Suppose these things were in a directory whose name name had spaces (e.g., ./My Movies/Scary Movie). The shell would be confused by those spaces.

So, you can put it into a list and avoid the problem

my_frame = 3
subprocess.call([
    'ffmpeg',
    '-r', '10',
    '-i', 'frame%03d.png' % my_frame,
    '-r', 'ntsc',
    'movie%03d.mpg' % my_frame,
])

More typing, but safer.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
tdelaney
  • 73,364
  • 6
  • 83
  • 116
  • Thank you. I have a question, for the 'frame' part I want it to take _all_ the files in the directory in the format frame###.png to stitch them together, not just one frame. Should I create a variable for a string called 'frame*.png' or something of the sort? – user3295674 Sep 21 '14 at 18:21
  • 1
    Note that [ffmpeg](https://www.ffmpeg.org/ffmpeg-formats.html#Examples) itself can interpret `%03d` syntax in filename specs. – PM 2Ring Sep 21 '14 at 18:22
  • @PM2Ring that's what I thought, but it tells me 'no such file as frame%03d.png' Thoughts? – user3295674 Sep 21 '14 at 18:35
  • 1
    @user3295674 : That's weird. I haven't used ffmpeg (or avconv) for a few months, so I might be a bit rusty... Does putting the filename in quotes help? Try `"` double-quotes first and then single quotes `'`. I'd do some experiments myself, but it's getting late & I _really_ should go. If you're sending this from Python, then if the whole command string is quoted with `'` then you'll either need to escape any `'` in the command string with a backslash, or use triple-quoting (like Python docstrings use) around the whole command string. – PM 2Ring Sep 21 '14 at 18:53
  • I don't know the ffmpeg command line but if you can get it to select the frames, especially if you want many frames to go to one movie, is the best. You can get a list of files from os.listdir and search them for patterns you like, or use the glob module to do shell-like listings such as frame*.pgn. – tdelaney Sep 22 '14 at 01:19
  • Splitting out arguments in a list is the way to go (when shell=False.) This helped me fix `Error splitting the argument list: Option not found` issues. – Fabien Snauwaert Sep 01 '20 at 12:14
8

I found this alternative, simple, answer to also work.

subprocess.call('ffmpeg -r 10 -i frame%03d.png -r ntsc '+str(out_movie), shell=True)
user3295674
  • 893
  • 5
  • 19
  • 42
2
import shlex
import pipes
from subprocess import check_call

command = 'ffmpeg -r 10 -i frame%03d.png -r ntsc ' + pipes.quote(out_movie)
check_call(shlex.split(command))
jfs
  • 399,953
  • 195
  • 994
  • 1,670
0

'ffmpeg -r 10 -i frame%03d.png -r ntsc movie.mpg' should be fine. OTOH, If you don't need the power of frame%03d.png, frame*.png is a bit simpler.

If you want to "see the syntax for it if I replace 'movie.mpg' with a variable name", it looks something like this:

cmd = 'ffmpeg -r 10 -i "frame%%03d.png" -r ntsc "%s"' % moviename

We need to escape the % with an extra % to hide it from Python's % substitution machinery. I've also added double quotes " , to cope with the issues that tdelaney mentioned.

PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
  • There can't be any whitespace issues with `frame%03d.png`. Using quotes is useless here. Use `pipes.quote(moviename)` instead of `"%s"`. – jfs Sep 22 '14 at 07:32
  • @J.F.Sebastian : True, there's no whitespace issue here, I just put those quotes in as a general example for future reference. OTOH, I agree that the stuff I said about quoting in my last comment to tdelaney's post were not helpful; I should know better than to post at 5AM. :) But I didn't know about `pipes.quote`, so thanks for that (although I see in the docs that it's been deprecated in Python 3). – PM 2Ring Sep 22 '14 at 08:01
  • 1
    there is no [tag:python-3.x] tag, therefore I've used `pipes.quote` instead of `shlex.quote` name (it is the same function). – jfs Sep 22 '14 at 08:08