dev lopment
dev lopment

Reputation: 21

Execute ffmpeg command from python with subprocess

The following command works fine from the command line:

ffmpeg -y -threads 4 -i /dev/video0 -filter_complex "[v:0]scale=-2:720:force_original_aspect_ratio=decrease[vout001]" -c:v libx264 -b:v 2800k -maxrate:v 2996k -bufsize:v 4200k -c:a aac -b:a 128k -ac 2 -ar 48000 -preset veryfast -x264opts keyint=25:min-keyint=25:no-scenecut -sc_threshold 0 -r 25 -pix_fmt yuv420p -segment_list_flags +live -map [vout001] -f tee -var_stream_map 'v:0' "[f=hls:hls_time=1:hls_playlist_type=event:strftime=1:hls_flags=independent_segments+program_date_time+second_level_segment_index:hls_segment_filename='segment_%%06d_%Y%m%d%H%M%S.ts']playlist.m3u8|[f=hls:hls_time=1:hls_playlist_type=event:strftime=1:hls_flags=independent_segments+program_date_time+second_level_segment_index:hls_segment_filename=\'http://X.X.X.X:pppp/ABCD/segment_%%06d_%Y%m%d%H%M%S.ts\':method=PUT]http://X.X.X.X:pppp/ABCD/playlist.m3u8"

However, it throws error when I execute it from python code via subprocess with the following command:

cmd_ffmpeg = ['ffmpeg', '-y', '-threads', '4', '-i', '/dev/video0', '-filter_complex', '[v:0]scale=-2:720:force_original_aspect_ratio=decrease[vout001]', '-c:v', 'libx264', '-b:v', '2800k', '-maxrate:v', '2996k', '-bufsize:v', '4200k', '-c:a', 'aac', '-b:a', '128k', '-ac', '2', '-ar', '48000', '-preset', 'veryfast', '-x264opts', 'keyint=25:min-keyint=25:no-scenecut', '-sc_threshold', '0', '-r', '25', '-pix_fmt', 'yuv420p', '-segment_list_flags', '+live', '-map', '[vout001]', '-f', 'tee', '"[f=hls:hls_time=1:hls_playlist_type=event:strftime=1:hls_flags=independent_segments+program_date_time+second_level_segment_index:hls_segment_filename=\'segment_%%06d_%Y%m%d%H%M%S.ts\']playlist.m3u8|[f=hls:hls_time=1:hls_playlist_type=event:strftime=1:hls_flags=independent_segments+program_date_time+second_level_segment_index:hls_segment_filename=\'http://X.X.X.X:pppp/ABCD/segment_%%06d_%Y%m%d%H%M%S.ts\':method=PUT]http://X.X.X.X:pppp/ABCD/playlist.m3u8"']

The error is the following:

No option found near "//X.X.X.X:pppp/ABCD/segment_%%06d_%Y%m%d%H%M%S.ts":method=PUT]http://X.X.X.X:pppp/ABCD/playlist.m3u8"

It considers the ':' after the 'http' as option separator when it is executed from python code (the escape doesn't work) while when executed directly from the shell the escape works fine.

How can I resolve this problem?

Upvotes: 0

Views: 3145

Answers (4)

hcf1425
hcf1425

Reputation: 719

If you want to run ffmpeg command in python code, you can try this:

import os
cmd_ffmpeg = """ffmpeg -y -threads 4 -i /dev/video0 -filter_complex \"[v:0]scale=-2:720:force_original_aspect_ratio=decrease[vout001]\" -c:v libx264 -b:v 2800k -maxrate:v 2996k -bufsize:v 4200k -c:a aac -b:a 128k -ac 2 -ar 48000 -preset veryfast -x264opts keyint=25:min-keyint=25:no-scenecut -sc_threshold 0 -r 25 -pix_fmt yuv420p -segment_list_flags +live -map [vout001] -f tee -var_stream_map \'v:0\' \"[f=hls:hls_time=1:hls_playlist_type=event:strftime=1:hls_flags=independent_segments+program_date_time+second_level_segment_index:hls_segment_filename=\'segment_%%06d_%Y%m%d%H%M%S.ts\']playlist.m3u8|[f=hls:hls_time=1:hls_playlist_type=event:strftime=1:hls_flags=independent_segments+program_date_time+second_level_segment_index:hls_segment_filename=\'http://X.X.X.X:pppp/ABCD/segment_%%06d_%Y%m%d%H%M%S.ts\':method=PUT]http://X.X.X.X:pppp/ABCD/playlist.m3u8\""""
print(cmd_ffmpeg)
os.system(cmd_ffmpeg)

Upvotes: 0

dev lopment
dev lopment

Reputation: 21

Thank you guys for your suggestions and help. I have tried the triple quotes as suggested, but it generates another problem consisting of video segments/playlist with the following file names:

"f=hls:hls_time=1:hls_playlist_type=event:strftime=1:hls_flags=independent_segments+program_date_time+second_level_segment_index:hls_segment_filename=segment_%%06d_%Y%m%d%H%M%S.ts]playlist.m3u8

"f=hls:hls_time=1:hls_playlist_type=event:strftime=1:hls_flags=independent_segments+program_date_time+second_level_segment_index:hls_segment_filename=segment_%%06d_%Y%m0%H%M%S.ts]playlist0.ts

"f=hls:hls_time=1:hls_playlist_type=event:strftime=1:hls_flags=independent_segments+program_date_time+second_level_segment_index:hls_segment_filename=segment_%%06d_%Y%m11%H%M%S.ts]playlist11.ts

....

So, it considers the list of parameters as the segment/playlist file name. Also, it generates one output instead of two outputs (one locally and the other to the remote server).

In fact, the solution is to keep the same command and only add '\' before the ':' in the remote server url. So, the final command that works fine from the python code is:

cmd_ffmpeg = ['ffmpeg', '-y', '-threads', '4', '-i', '/dev/video0', '-filter_complex', '[v:0]scale=-2:720:force_original_aspect_ratio=decrease[vout001]', '-c:v', 'libx264', '-b:v', '2800k', '-maxrate:v', '2996k', '-bufsize:v', '4200k', '-c:a', 'aac', '-b:a', '128k', '-ac', '2', '-ar', '48000', '-preset', 'veryfast', '-x264opts', 'keyint=25:min-keyint=25:no-scenecut', '-sc_threshold', '0', '-r', '25', '-pix_fmt', 'yuv420p', '-segment_list_flags', '+live', '-map', '[vout001]', '-f', 'tee', '-var_stream_map', 'v:0', '[f=hls:hls_time=1:hls_playlist_type=event:strftime=1:hls_flags=independent_segments+program_date_time+second_level_segment_index:hls_segment_filename=\'segment_%%06d_%Y%m%d%H%M%S.ts\']playlist.m3u8|[f=hls:hls_time=1:hls_playlist_type=event:strftime=1:hls_flags=independent_segments+program_date_time+second_level_segment_index:hls_segment_filename=\'http\\://X.X.X.X:pppp/ABCD/segment_%%06d_%Y%m%d%H%M%S.ts\']http://X.X.X.X:pppp/ABCD/playlist.m3u8']

Regarding the option/value: '-var_stream_map' and 'v:0', I just missed it. It could be ignored since we have one input.

Thanks.

Upvotes: 1

GordonAitchJay
GordonAitchJay

Reputation: 4880

How did you generate cmd_ffmpeg? Manually? It's different to what shlex.split returns. For example, cmd_ffmpeg has double quotes after filter_complex whereas what shlex.split returns doesn't.

import shlex
shell_ffmpeg_cmd = r'''ffmpeg -y -threads 4 -i /dev/video0 -filter_complex "[v:0]scale=-2:720:force_original_aspect_ratio=decrease[vout001]" -c:v libx264 -b:v 2800k -maxrate:v 2996k -bufsize:v 4200k -c:a aac -b:a 128k -ac 2 -ar 48000 -preset veryfast -x264opts keyint=25:min-keyint=25:no-scenecut -sc_threshold 0 -r 25 -pix_fmt yuv420p -segment_list_flags +live -map [vout001] -f tee -var_stream_map 'v:0' "[f=hls:hls_time=1:hls_playlist_type=event:strftime=1:hls_flags=independent_segments+program_date_time+second_level_segment_index:hls_segment_filename='segment_%%06d_%Y%m%d%H%M%S.ts']playlist.m3u8|[f=hls:hls_time=1:hls_playlist_type=event:strftime=1:hls_flags=independent_segments+program_date_time+second_level_segment_index:hls_segment_filename=\'http://X.X.X.X:pppp/ABCD/segment_%%06d_%Y%m%d%H%M%S.ts\':method=PUT]http://X.X.X.X:pppp/ABCD/playlist.m3u8"'''
popen_args = shlex.split(shell_ffmpeg_cmd)
print(" ".join(popen_args))

Also, for what it's worth, these two switches are missing from cmd_ffmpeg, compared with your shell invocation: -var_stream_map and 'v:0'.

Nevertheless, your problem seems to be with the last element in the list. As eatmeimadanish suggested, try triple quoting it. The following is your cmd_ffmpeg with the last element corrected and triple single quoted. It prints ffmpeg's stderr, which from my experience, comes in handy:

from subprocess import Popen, PIPE

cmd_ffmpeg = ['ffmpeg', '-y', '-threads', '4', '-i', '/dev/video0', '-filter_complex', '[v:0]scale=-2:720:force_original_aspect_ratio=decrease[vout001]', '-c:v', 'libx264', '-b:v', '2800k', '-maxrate:v', '2996k', '-bufsize:v', '4200k', '-c:a', 'aac', '-b:a', '128k', '-ac', '2', '-ar', '48000', '-preset', 'veryfast', '-x264opts', 'keyint=25:min-keyint=25:no-scenecut', '-sc_threshold', '0', '-r', '25', '-pix_fmt', 'yuv420p', '-segment_list_flags', '+live', '-map', '[vout001]', '-f', 'tee', '''"[f=hls:hls_time=1:hls_playlist_type=event:strftime=1:hls_flags=independent_segments+program_date_time+second_level_segment_index:hls_segment_filename='segment_%%06d_%Y%m%d%H%M%S.ts']playlist.m3u8|[f=hls:hls_time=1:hls_playlist_type=event:strftime=1:hls_flags=independent_segments+program_date_time+second_level_segment_index:hls_segment_filename='http://X.X.X.X:pppp/ABCD/segment_%%06d_%Y%m%d%H%M%S.ts':method=PUT]http://X.X.X.X:pppp/ABCD/playlist.m3u8"''']
with Popen(cmd_ffmpeg, text=True, stdout=PIPE, stderr=PIPE) as p:
    for line in p.stderr:
        print(line, end="") # stderr already includes a newline

Upvotes: 0

tripleee
tripleee

Reputation: 189789

The double quotes around the long string are discarded by the shell before ffmpeg sees the command line. You can simply replace them with single quotes in Python. Having literal double quotes inside the single quotes is what throws off the ffmpeg option parser.

Upvotes: 1

Related Questions