1

I want to use ffmpeg with Python in a Jupyter notebook to create a video from 2000 PNG image files generated by matplotlib. I'm on a Windows 7 machine.

The frames are very narrow and high, so I'd like to cut them vertically into seven equal-sized "tiles" using crop and stacked those "tiles" horizontally using hstack, but I'm getting only getting cryptic error messages:

ffmpeg_path = notebook_directory    # Make sure ffmpeg.exe is in notebook directory!

frames_path = 'C:\\Users\\Username\\Desktop'
frames_renamed_path = os.path.join(frames_path, 'renamed')
os.chdir(frames_renamed_path)    # Temporarily change current working directory

check_call(
    [
        os.path.join(ffmpeg_path, 'ffmpeg'),
        '-y',    # Overwrite output files without asking
        '-report',    # Write logfile to current working directory
        '-hwaccel', 'cuda',    # Use GPU acceleration
        '-framerate', '60',    # Input frame rate
        '-i', os.path.join(frames_renamed_path, 'frame%05d.png'),    # Path to input frames
        #'-r', '60',    # Output frame rate
        '-vf', 'pad=ceil(iw/2)*2:ceil(ih/2)*2',    # Pad frames to even pixel numbers (required by many codecs)
        #'-pix_fmt', 'yuv420p',
        '-pix_fmt', 'rgb32',    # Use RGB32 pixel format matching MatPlotLib image output
        '-filter_complex',    # Cut frames into tiles and rearrange (here: 7 horizontal tiles)
            'crop=in_w:in_h/7:0:0[tile_1],'    # 1st tile from top
            + 'crop=in_w:in_h/7:0:in_h*1/7[tile_2],'    # 2nd tile from top
            + 'crop=in_w:in_h/7:0:in_h*2/7[tile_3],'    # 3rd tile from top
            + 'crop=in_w:in_h/7:0:in_h*3/7[tile_4],'    # 4th tile from top
            + 'crop=in_w:in_h/7:0:in_h*4/7[tile_5],'    # 5th tile from top
            + 'crop=in_w:in_h/7:0:in_h*5/7[tile_6],'    # 6th tile from top
            + 'crop=in_w:in_h/7:0:in_h*6/7[tile_7],'    # 7th tile from top
            + '[tile_1][tile_2][tile_3][tile_4][tile_5][tile_6][tile_7]hstack=7',    # Stack tiles horizontally
        os.path.join(frames_renamed_path, 'video.mp4')    # Path to store output video
    ]
)

os.chdir(notebook_directory)    # Reset current working directory to notebook directory

---------------------------------------------------------------------------
CalledProcessError                        Traceback (most recent call last)
Input In [10], in <cell line: 9>()
      6 frames_renamed_path = os.path.join(frames_path, 'renamed')
      7 os.chdir(frames_renamed_path)    # Temporarily change current working directory
----> 9 check_call(
     10     [
     11         os.path.join(ffmpeg_path, 'ffmpeg'),
     12         '-y',    # Overwrite output files without asking
     13         '-report',    # Write logfile to current working directory
     14         '-hwaccel', 'cuda',    # Use GPU acceleration
     15         '-framerate', '60',    # Input frame rate
     16         '-i', os.path.join(frames_renamed_path, 'frame%05d.png'),    # Path to input frames
     17         #'-pattern_type', 'glob', '-i', os.path.join(frames_renamed_path, '*.png'),    # glob not available in Windows by default
     18         #'-r', '60',    # Output frame rate
     19         '-vf', 'pad=ceil(iw/2)*2:ceil(ih/2)*2',    # Pad frames to even pixel numbers (required by many codecs)
     20         #'-pix_fmt', 'yuv420p',
     21         '-pix_fmt', 'rgb32',    # Use RGB32 pixel format matching MatPlotLib image output
     22         '-filter_complex',    # Cut frames into tiles and rearrange (here: 7 horizontal tiles)
     23             'crop=in_w:in_h/7:0:0[tile_1],'    # 1st tile from top
     24             + 'crop=in_w:in_h/7:0:in_h*1/7[tile_2],'    # 2nd tile from top
     25             + 'crop=in_w:in_h/7:0:in_h*2/7[tile_3],'    # 3rd tile from top
     26             + 'crop=in_w:in_h/7:0:in_h*3/7[tile_4],'    # 4th tile from top
     27             + 'crop=in_w:in_h/7:0:in_h*4/7[tile_5],'    # 5th tile from top
     28             + 'crop=in_w:in_h/7:0:in_h*5/7[tile_6],'    # 6th tile from top
     29             + 'crop=in_w:in_h/7:0:in_h*6/7[tile_7],'    # 7th tile from top
     30             + '[tile_1][tile_2][tile_3][tile_4][tile_5][tile_6][tile_7]hstack=7',    # Stack tiles horizontally
     31         os.path.join(frames_renamed_path, 'video.mp4')    # Path to store output video
     32     ]
     33 )
     35 os.chdir(notebook_directory)

File C:\Program Files\Python38\lib\subprocess.py:364, in check_call(*popenargs, **kwargs)
    362     if cmd is None:
    363         cmd = popenargs[0]
--> 364     raise CalledProcessError(retcode, cmd)
    365 return 0

CalledProcessError: Command '['C:\\Users\\Username\\Desktop\\ffmpeg\\ffmpeg', '-y', '-report', '-hwaccel', 'cuda', '-framerate', '60', '-i', 'C:\\Users\\Username\\Desktop\\renamed\\frame%05d.png', '-vf', 'pad=ceil(iw/2)*2:ceil(ih/2)*2', '-pix_fmt', 'rgb32', '-filter_complex', 'crop=in_w:in_h/7:0:0[tile_1],crop=in_w:in_h/7:0:in_h*1/7[tile_2],crop=in_w:in_h/7:0:in_h*2/7[tile_3],crop=in_w:in_h/7:0:in_h*3/7[tile_4],crop=in_w:in_h/7:0:in_h*4/7[tile_5],crop=in_w:in_h/7:0:in_h*5/7[tile_6],crop=in_w:in_h/7:0:in_h*6/7[tile_7],[tile_1][tile_2][tile_3][tile_4][tile_5][tile_6][tile_7]hstack=7', 'C:\\Users\\Username\\Desktop\\renamed\\video.mp4']' returned non-zero exit status 1.

This is the ffmpeg log output (cropped):

ffmpeg started on 2022-08-04 at 01:17:19
Report written to "ffmpeg-20220804-011719.log"
Log level: 48
Command line:
"C:\\Users\\Username\\Desktop\\ffmpeg\\ffmpeg" -y -report -hwaccel cuda -framerate 60 -i "C:\\Users\\Username\\Desktop\\renamed\\frame%05d.png" -vf "pad=ceil(iw/2)*2:ceil(ih/2)*2" -pix_fmt rgb32 -filter_complex "crop=in_w:in_h/7:0:0[tile_1],crop=in_w:in_h/7:0:in_h*1/7[tile_2],crop=in_w:in_h/7:0:in_h*2/7[tile_3],crop=in_w:in_h/7:0:in_h*3/7[tile_4],crop=in_w:in_h/7:0:in_h*4/7[tile_5],crop=in_w:in_h/7:0:in_h*5/7[tile_6],crop=in_w:in_h/7:0:in_h*6/7[tile_7],[tile_1][tile_2][tile_3][tile_4][tile_5][tile_6][tile_7]hstack=7" "C:\\Users\\Username\\Desktop\\renamed\\video.mp4"
ffmpeg version 2022-07-14-git-882aac99d2-full_build-www.gyan.dev Copyright (c) 2000-2022 the FFmpeg developers
  built with gcc 12.1.0 (Rev2, Built by MSYS2 project)
  configuration: --enable-gpl --enable-version3 --enable-static --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-bzlib --enable-lzma --enable-libsnappy --enable-zlib --enable-librist --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-libbluray --enable-libcaca --enable-sdl2 --enable-libdav1d --enable-libdavs2 --enable-libuavs3d --enable-libzvbi --enable-librav1e --enable-libsvtav1 --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxavs2 --enable-libxvid --enable-libaom --enable-libjxl --enable-libopenjpeg --enable-libvpx --enable-mediafoundation --enable-libass --enable-frei0r --enable-libfreetype --enable-libfribidi --enable-liblensfun --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-ffnvcodec --enable-nvdec --enable-nvenc --enable-d3d11va --enable-dxva2 --enable-libmfx --enable-libshaderc --enable-vulkan --enable-libplacebo --ena  libavutil      57. 29.100 / 57. 29.100
  libavcodec     59. 38.100 / 59. 38.100
  libavformat    59. 28.100 / 59. 28.100
  libavdevice    59.  8.100 / 59.  8.100
  libavfilter     8. 45.100 /  8. 45.100
  libswscale      6.  8.100 /  6.  8.100
  libswresample   4.  8.100 /  4.  8.100
  libpostproc    56.  7.100 / 56.  7.100
Splitting the commandline.
Reading option '-y' ... matched as option 'y' (overwrite output files) with argument '1'.
Reading option '-report' ... matched as option 'report' (generate a report) with argument '1'.
Reading option '-hwaccel' ... matched as option 'hwaccel' (use HW accelerated decoding) with argument 'cuda'.
Reading option '-framerate' ... matched as AVOption 'framerate' with argument '60'.
Reading option '-i' ... matched as input url with argument 'C:\Users\Username\Desktop\renamed\frame%05d.png'.
Reading option '-vf' ... matched as option 'vf' (set video filters) with argument 'pad=ceil(iw/2)*2:ceil(ih/2)*2'.
Reading option '-pix_fmt' ... matched as option 'pix_fmt' (set pixel format) with argument 'rgb32'.
Reading option '-filter_complex' ... matched as option 'filter_complex' (create a complex filtergraph) with argument 'crop=in_w:in_h/7:0:0[tile_1],crop=in_w:in_h/7:0:in_h*1/7[tile_2],crop=in_w:in_h/7:0:in_h*2/7[tile_3],crop=in_w:in_h/7:0:in_h*3/7[tile_4],crop=in_w:in_h/7:0:in_h*4/7[tile_5],crop=in_w:in_h/7:0:in_h*5/7[tile_6],crop=in_w:in_h/7:0:in_h*6/7[tile_7],[tile_1][tile_2][tile_3][tile_4][tile_5][tile_6][tile_7]hstack=7'.
Reading option 'C:\Users\Username\Desktop\renamed\video.mp4' ... matched as output url.
Finished splitting the commandline.
Parsing a group of options: global .
Applying option y (overwrite output files) with argument 1.
Applying option report (generate a report) with argument 1.
Applying option filter_complex (create a complex filtergraph) with argument crop=in_w:in_h/7:0:0[tile_1],crop=in_w:in_h/7:0:in_h*1/7[tile_2],crop=in_w:in_h/7:0:in_h*2/7[tile_3],crop=in_w:in_h/7:0:in_h*3/7[tile_4],crop=in_w:in_h/7:0:in_h*4/7[tile_5],crop=in_w:in_h/7:0:in_h*5/7[tile_6],crop=in_w:in_h/7:0:in_h*6/7[tile_7],[tile_1][tile_2][tile_3][tile_4][tile_5][tile_6][tile_7]hstack=7.
Successfully parsed a group of options.
Parsing a group of options: input url C:\Users\Username\Desktop\renamed\frame%05d.png.
Applying option hwaccel (use HW accelerated decoding) with argument cuda.
Successfully parsed a group of options.
Opening an input file: C:\Users\Username\Desktop\renamed\frame%05d.png.
[image2 @ 00000000005db0c0] Opening 'C:\Users\Username\Desktop\renamed\frame00000.png' for reading
[file @ 00000000005f6c00] Setting default whitelist 'file,crypto,data'
[AVIOContext @ 00000000005fed40] Statistics: 28860 bytes read, 0 seeks

<...>

[AVIOContext @ 00000000005fda00] Statistics: 67659 bytes read, 0 seeks
[image2 @ 00000000005db0c0] Probe buffer size limit of 5000000 bytes reached
Input #0, image2, from 'C:\Users\Username\Desktop\renamed\frame%05d.png':
  Duration: 00:00:25.00, start: 0.000000, bitrate: N/A
  Stream #0:0, 80, 1/60: Video: png, rgba(pc), 1200x1857 [SAR 3937:3937 DAR 400:619], 60 fps, 60 tbr, 60 tbn
Successfully opened the file.
[Parsed_crop_0 @ 00000000005fdc40] Setting 'out_w' to value 'in_w'
[Parsed_crop_0 @ 00000000005fdc40] Setting 'out_h' to value 'in_h/7'
[Parsed_crop_0 @ 00000000005fdc40] Setting 'x' to value '0'
[Parsed_crop_0 @ 00000000005fdc40] Setting 'y' to value '0'
[Parsed_crop_1 @ 0000000000607500] Setting 'out_w' to value 'in_w'
[Parsed_crop_1 @ 0000000000607500] Setting 'out_h' to value 'in_h/7'
[Parsed_crop_1 @ 0000000000607500] Setting 'x' to value '0'
[Parsed_crop_1 @ 0000000000607500] Setting 'y' to value 'in_h*1/7'
[Parsed_crop_2 @ 0000000000607880] Setting 'out_w' to value 'in_w'
[Parsed_crop_2 @ 0000000000607880] Setting 'out_h' to value 'in_h/7'
[Parsed_crop_2 @ 0000000000607880] Setting 'x' to value '0'
[Parsed_crop_2 @ 0000000000607880] Setting 'y' to value 'in_h*2/7'
[Parsed_crop_3 @ 0000000000607c00] Setting 'out_w' to value 'in_w'
[Parsed_crop_3 @ 0000000000607c00] Setting 'out_h' to value 'in_h/7'
[Parsed_crop_3 @ 0000000000607c00] Setting 'x' to value '0'
[Parsed_crop_3 @ 0000000000607c00] Setting 'y' to value 'in_h*3/7'
[Parsed_crop_4 @ 00000000005fda00] Setting 'out_w' to value 'in_w'
[Parsed_crop_4 @ 00000000005fda00] Setting 'out_h' to value 'in_h/7'
[Parsed_crop_4 @ 00000000005fda00] Setting 'x' to value '0'
[Parsed_crop_4 @ 00000000005fda00] Setting 'y' to value 'in_h*4/7'
[Parsed_crop_5 @ 0000000002b8f800] Setting 'out_w' to value 'in_w'
[Parsed_crop_5 @ 0000000002b8f800] Setting 'out_h' to value 'in_h/7'
[Parsed_crop_5 @ 0000000002b8f800] Setting 'x' to value '0'
[Parsed_crop_5 @ 0000000002b8f800] Setting 'y' to value 'in_h*5/7'
[Parsed_crop_6 @ 0000000000607d00] Setting 'out_w' to value 'in_w'
[Parsed_crop_6 @ 0000000000607d00] Setting 'out_h' to value 'in_h/7'
[Parsed_crop_6 @ 0000000000607d00] Setting 'x' to value '0'
[Parsed_crop_6 @ 0000000000607d00] Setting 'y' to value 'in_h*6/7'
[Parsed_hstack_7 @ 0000000002b8fb80] Setting 'inputs' to value '7'
Cannot find a matching stream for unlabeled input pad 0 on filter Parsed_crop_1

What is the problem here? Also, how does one interpret this cryptic error message at the end of the log output:

Cannot find a matching stream for unlabeled input pad 0 on filter Parsed_crop_1

E.g. what is input pad 0, why is it unlabeled, and why can't it find a matching stream?

(PS.: Yes, I do have a habit of excessively documenting my code. ^^)

1
  • Mmmh...could it be that dividing the input height in_h by 7 is resulting in unequal "tile" sizes, causing hstack to fail? I.e. is it necessary to apply padding to the individual "tiles" after cropping them into pieces? Commented Aug 4, 2022 at 0:01

1 Answer 1

1

Both -vf and -filter_complex can't be specified for the same input stream. In any case, you would have to carry out the pad at the end of the stacking, so remove the -vf and use the following.

    '-filter_complex',    # Cut frames into tiles and rearrange (here: 7 horizontal tiles)
        'split=7[i1][i2][i3][i4][i5][i6][i7];'          # 7 clones, 1 for each crop
        + '[i1]crop=in_w:in_h/7:0:0[tile_1];'           # 1st tile from top
        + '[i2]crop=in_w:in_h/7:0:in_h*1/7[tile_2];'    # 2nd tile from top
        + '[i3]crop=in_w:in_h/7:0:in_h*2/7[tile_3];'    # 3rd tile from top
        + '[i4]crop=in_w:in_h/7:0:in_h*3/7[tile_4];'    # 4th tile from top
        + '[i5]crop=in_w:in_h/7:0:in_h*4/7[tile_5];'    # 5th tile from top
        + '[i6]crop=in_w:in_h/7:0:in_h*5/7[tile_6];'    # 6th tile from top
        + '[i7]crop=in_w:in_h/7:0:in_h*6/7[tile_7];'    # 7th tile from top
        + '[tile_1][tile_2][tile_3][tile_4][tile_5][tile_6][tile_7]hstack=7,'
        + 'pad=ceil(iw/2)*2:ceil(ih/2)*2'
    '-pix_fmt', 'rgb32',    # Use RGB32 pixel format matching MatPlotLib image output
7
  • Hi Gyan, many thanks. I'll try your suggestion ASAP. By the way, how did you find out that one can only specify either -vf or -filter_complex, but not both? Is this mentioned in the ffmpeg docs? Also, is padding the frame to even pixels actually required in this case (since I'm not using any codec/compression, e.g. yuv420p)? Commented Aug 4, 2022 at 16:53
  • Sorry, but your solution doesn't work. Same error message even after completely removing both the -vf and pad Commented Aug 4, 2022 at 18:05
  • ...also, using split isn't required here and is, in some cases, known to cause memory saturation Commented Aug 5, 2022 at 15:49
  • Furthermore, rgb32 isn't a supported option. Commented Aug 5, 2022 at 15:49
  • rgb32 is what you specified. With libx264, it will get resampled to yuv444p
    – Gyan
    Commented Aug 5, 2022 at 18:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.