Ffmpeg-python: Duplicate option names via kwargs?

Created on 29 Jun 2019  路  3Comments  路  Source: kkroening/ffmpeg-python

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.

answered question

Most helpful comment

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)

All 3 comments

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

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cbitterfield picture cbitterfield  路  4Comments

oveddan picture oveddan  路  3Comments

DinRigtigeFar picture DinRigtigeFar  路  5Comments

reformstudios picture reformstudios  路  3Comments

Matttman picture Matttman  路  6Comments