pythonffmpegffmpeg-python

I have trouble converted ffmpeg command to ffmpeg-python


I am trying convert from HDR to SDR by using the following ffmpeg command, which works

ffmpeg -i input.mov -vf zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p -c:v libx264 -crf 0 -preset ultrafast -tune fastdecode output.mp4

However, I have trouble converting this in ffmpeg-python format. Could you give me guidance on how to deal with semicolons, equals and weird = = cases?

ffmpeg.input("input.mov", r=fps).filter('zscale', t='linear:npl').filter('format', 'gbrpf32le') ...


Solution

  • The conversion is quite straightforward:

    ffmpeg.input('input.mov')\
        .video.filter('zscale', t='linear', npl=100)\
        .filter('format', pix_fmts='gbrpf32le')\
        .filter('zscale', p='bt709')\
        .filter('tonemap', tonemap='hable', desat=0)\
        .filter('zscale', t='bt709', m='bt709', r='tv')\
        .filter('format', pix_fmts='yuv420p')\
        .output('output.mp4', vcodec='libx264', crf=0, preset='ultrafast', tune='fastdecode')\
        .run()
    

    The only caveat is the format filter.
    The parameter name of format filter is pix_fmts (we usually skip it).
    One way for getting the parameter name is using FFmpeg help (in command line):

    ffmpeg -h filter=format

    Output:

    Filter format
      Convert the input video to one of the specified pixel formats.
        Inputs:
           #0: default (video)
        Outputs:
           #0: default (video)
    (no)format AVOptions:
       pix_fmts          <string>     ..FV....... A '|'-separated list of pixel formats
    

    Here we can see that the parameter name is pix_fmts.


    When using ffmpeg-python, it is recommended to add .overwrite_output() (equivalent to -y argument).

    For testing it is recommended to add .global_args('-report') for creating a log file (remove .global_args('-report') after finish testing).

    ffmpeg.input('input.mov')\
        .video.filter('zscale', t='linear', npl=100)\
        .filter('format', pix_fmts='gbrpf32le')\
        .filter('zscale', p='bt709')\
        .filter('tonemap', tonemap='hable', desat=0)\
        .filter('zscale', t='bt709', m='bt709', r='tv')\
        .filter('format', pix_fmts='yuv420p')\
        .output('output.mp4', vcodec='libx264', crf=0, preset='ultrafast', tune='fastdecode')\
        .global_args('-report').overwrite_output().run()
    

    The log shows us the actual FFmpeg command:

    ffmpeg -i input.mov -filter_complex "[0:v]zscale=npl=100:t=linear[s0];[s0]format=pix_fmts=gbrpf32le[s1];[s1]zscale=p=bt709[s2];[s2]tonemap=desat=0:tonemap=hable[s3];[s3]zscale=m=bt709:r=tv:t=bt709[s4];[s4]format=pix_fmts=yuv420p[s5]" -map "[s5]" -crf 0 -preset ultrafast -tune fastdecode -vcodec libx264 output.mp4 -report -y

    As you can see, ffmpeg-python uses -filter_complex instead of -vf, and uses temporary naming as [s0], [s1], [s2]...
    The result is equivalent to your original command.