Afternoon!
So, from the README:
Special option names:
Arguments with special names such as -qscale:v (variable bitrate), -b:v (constant bitrate), etc. can be specified as a keyword-args dictionary as follows:(
ffmpeg
.input('in.mp4')
.output('out.mp4', **{'qscale:v': 3})
.run()
)
However, if you're running multiple renditions, such as in the example below, how could this be achieved? The kwargs dictionary would have to contain duplicate keys, which obviously isn't possible:
ffmpeg -hide_banner -y -i beach.mkv \
-vf scale=w=640:h=360:force_original_aspect_ratio=decrease -c:a aac -ar 48000 -c:v h264 -profile:v main -crf 20 -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod -b:v 800k -maxrate 856k -bufsize 1200k -b:a 96k -hls_segment_filename beach/360p_%03d.ts beach/360p.m3u8 \
-vf scale=w=842:h=480:force_original_aspect_ratio=decrease -c:a aac -ar 48000 -c:v h264 -profile:v main -crf 20 -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod -b:v 1400k -maxrate 1498k -bufsize 2100k -b:a 128k -hls_segment_filename beach/480p_%03d.ts beach/480p.m3u8 \
-vf scale=w=1280:h=720:force_original_aspect_ratio=decrease -c:a aac -ar 48000 -c:v h264 -profile:v main -crf 20 -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod -b:v 2800k -maxrate 2996k -bufsize 4200k -b:a 128k -hls_segment_filename beach/720p_%03d.ts beach/720p.m3u8 \
-vf scale=w=1920:h=1080:force_original_aspect_ratio=decrease -c:a aac -ar 48000 -c:v h264 -profile:v main -crf 20 -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod -b:v 5000k -maxrate 5350k -bufsize 7500k -b:a 192k -hls_segment_filename beach/1080p_%03d.ts beach/1080p.m3u8
What I have so far is as follows, but obviously can't be expanded in to kwargs, so pretty useless:
renditions = [
{'resolution': '426x240', 'bitrate': '400k', 'audiorate': '64k'},
{'resolution': '640x360', 'bitrate': '800k', 'audiorate': '96k'},
{'resolution': '842x480', 'bitrate': '1400k', 'audiorate': '128k'},
{'resolution': '1280x720', 'bitrate': '2800k', 'audiorate': '128k'},
{'resolution': '1920x1080', 'bitrate': '5000k', 'audiorate': '192k'}
]
ffmpeg_params = {'renditions': []}
for rendition in renditions:
ffmpeg_params['renditions'].append(
{
'vf': "scale=w={}:h={}:force_original_aspect_ratio=decrease".format(
rendition['resolution'].split('x')[0], rendition['resolution'].split('x')[1]),
'c:a': 'aac',
'ar': '48000',
'c:v': 'h264',
'profile:v': 'main',
'crf': '20',
'sc_threshold': '0',
'g': '48',
'keyint_min': '48',
'hls_time': '4',
'hls_playlist_type': 'vod',
'b:v': f"{rendition['bitrate']}",
'maxrate': '856k',
'bufsize': '1200k',
'b:a': f"{rendition['audiorate']}",
'hls_segment_filename': f"{rendition['resolution'].split('x')[1]}p_{segment_format}"
}
)
I can see a few references to multiple inputs and concat in the README, but I'm not grasping how to make use of these, if they're even relevant to my issue.
Thanks in advance.
In that particular case, it looks like you have multiple outputs and as such, multiple calls to .output can be made, each with its own kwargs. The multi-output filter graph can be combined and executed using .merge_outputs (see examples).
Outstanding support, thank you @kkroening
Here's what I've done taking your support on-board:
import ffmpeg
input_file = 'input.mp4'
segment_format = '%03d.ts' # Will be prepended with "<resolution>_"
renditions = [
{'name': '240p', 'resolution': '426x240', 'bitrate': '400k', 'audiorate': '64k'},
{'name': '360p', 'resolution': '640x360', 'bitrate': '700k', 'audiorate': '96k'},
{'name': '480p', 'resolution': '854x480', 'bitrate': '1250k', 'audiorate': '128k'},
{'name': 'HD 720p', 'resolution': '1280x720', 'bitrate': '2500k', 'audiorate': '128k'},
{'name': 'HD 720p 60fps', 'resolution': '1280x720', 'bitrate': '3500k', 'audiorate': '128k'},
{'name': 'Full HD 1080p', 'resolution': '1920x1080', 'bitrate': '4500k', 'audiorate': '192k'},
{'name': 'Full HD 1080p 60fps', 'resolution': '1920x1080', 'bitrate': '5800k', 'audiorate': '192k'},
{'name': '4k', 'resolution': '3840x2160', 'bitrate': '14000k', 'audiorate': '192k'},
{'name': '4k 60fps', 'resolution': '3840x2160', 'bitrate': '23000k', 'audiorate': '192k'}
]
ffmpeg_input_stream = ffmpeg.input(input_file)
ffmpeg_output_streams = []
for rendition in renditions:
ffmpeg_params = {
'vf': "scale=w={}:h={}:force_original_aspect_ratio=decrease".format(
rendition['resolution'].split('x')[0], rendition['resolution'].split('x')[1]),
'c:a': 'aac',
'ar': '48000',
'c:v': 'h264',
'profile:v': 'main',
'crf': '20',
'sc_threshold': '0',
'g': '48',
'keyint_min': '48',
'hls_time': '4',
'hls_playlist_type': 'vod',
'b:v': f"{rendition['bitrate']}",
'maxrate': '856k',
'bufsize': '1200k',
'b:a': f"{rendition['audiorate']}",
'hls_segment_filename': f"{rendition['resolution'].split('x')[1]}p_{segment_format}"
}
ffmpeg_output_streams.append(
ffmpeg.output(
ffmpeg_input_stream,
f"{rendition['resolution'].split('x')[1]}p.m3u8",
**ffmpeg_params
)
)
output_streams = ffmpeg.merge_outputs(*ffmpeg_output_streams)
ffmpeg.run(output_streams)
Thanks for sharing, @monokal
Most helpful comment
Outstanding support, thank you @kkroening
Here's what I've done taking your support on-board: