ffmpeg 视频在 'Invalid buffer size, packet size < expected frame_size' 错误后垂直滑动(vsync 屏幕撕裂故障)?

ffmpeg video slides vertically after 'Invalid buffer size, packet size < expected frame_size' error (vsync screen tearing glitch)?

我有一个视频,我想使用 opencv 和 ffmpeg 进行剪切和裁剪。

我希望输出为 H265,因此我正在使用 ffmpeg 子进程(将帧字节写入标准输入),如 所述。这是导致错误的代码的最低版本:

import os, shlex, subprocess, cv2, imutils

VIDEO_DIR = '../SoExample' # should contain a file 'in.mpg'
TIMESPAN = (3827, 3927)  # cut to this timespan (frame numbers)
CROP = dict(min_x=560, max_x=731, min_y=232, max_y=418)  # crop to this area

#  calculate output video size
size = (CROP['max_x']-CROP['min_x'], CROP['max_y']-CROP['min_y'])  # (w,h)
# ffmpeg throws an error when having odd dimensions that are not dividable by 2,
# so i just add a pixel to the size and stretch the original image by 1 pixel later.
size_rounded = (size[0]+1 if size[0] % 2 != 0 else size[0],
                size[1]+1 if size[1] % 2 != 0 else size[1])

# read input video
vid_path_in = os.path.join(VIDEO_DIR, 'in.mpg')
cap = cv2.VideoCapture(vid_path_in)
fps = int(cap.get(cv2.CAP_PROP_FPS))

# generate and run ffmpeg command
ffmpeg_cmd = (f'/usr/bin/ffmpeg -y -s {size_rounded[0]}x{size_rounded[1]} -pixel_format'
              + f' bgr24 -f rawvideo -r {fps} -re -i pipe: -vcodec libx265 -pix_fmt yuv420p'
              + f' -crf 24 -x265-params "ctu=64" "{os.path.join(VIDEO_DIR, "out.mp4")}"')
print("using cmd", ffmpeg_cmd)
process = subprocess.Popen(shlex.split(ffmpeg_cmd), stdin=subprocess.PIPE)

# seek to the beginning of the cutting timespan and loop through frames of input video
cap.set(cv2.CAP_PROP_POS_FRAMES, TIMESPAN[0])
frame_returned = True
while cap.isOpened() and frame_returned:
    frame_returned, frame = cap.read()
    frame_number = cap.get(cv2.CAP_PROP_POS_FRAMES) - 1

    # check if timespan end is not reached yet
    if frame_number < TIMESPAN[1]:

        # crop to relevant image area
        frame_cropped = frame[CROP['min_y']:CROP['max_y'],
                              CROP['min_x']:CROP['max_x']]

        # resize to even frame size if needed:
        if size != size_rounded:
            frame_cropped = imutils.resize(frame_cropped, width=size_rounded[0],
                                           height=size_rounded[1])

        # Show processed image using opencv: I see no errors here.
        cv2.imshow('Frame', frame_cropped)

        # Write cropped video frame to input stream of ffmpeg sub-process.
        process.stdin.write(frame_cropped.tobytes())
    else:
        break

    # Press Q on keyboard to exit earlier
    if cv2.waitKey(25) & 0xFF == ord('q'):
        break

process.stdin.close()  # Close and flush stdin
process.wait()         # Wait for sub-process to finish
process.terminate()    # Terminate the sub-process

print("Done!")

不幸的是,我的输出是这样的:

输出不应包含此垂直滑动故障。有谁知道如何修复它?

上述脚本的控制台输出显示:

using cmd /usr/bin/ffmpeg -y -s 172x186 -pixel_format bgr24 -f rawvideo -r 23 -i pipe: -vcodec libx265 -pix_fmt yuv420p -crf 24 -x265-params "ctu=64" "../SoExample/out.mp4"
ffmpeg version 3.4.6-0ubuntu0.18.04.1 Copyright (c) 2000-2019 the FFmpeg developers
  built with gcc 7 (Ubuntu 7.3.0-16ubuntu3)
  configuration: --prefix=/usr --extra-version=0ubuntu0.18.04.1 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --enable-gpl --disable-stripping --enable-avresample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librubberband --enable-librsvg --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-omx --enable-openal --enable-opengl --enable-sdl2 --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libopencv --enable-libx264 --enable-shared
  WARNING: library configuration mismatch
  avcodec     configuration: --prefix=/usr --extra-version=0ubuntu0.18.04.1 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --enable-gpl --disable-stripping --enable-avresample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librubberband --enable-librsvg --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-omx --enable-openal --enable-opengl --enable-sdl2 --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libopencv --enable-libx264 --enable-shared --enable-version3 --disable-doc --disable-programs --enable-libopencore_amrnb --enable-libopencore_amrwb --enable-libtesseract --enable-libvo_amrwbenc
  libavutil      55. 78.100 / 55. 78.100
  libavcodec     57.107.100 / 57.107.100
  libavformat    57. 83.100 / 57. 83.100
  libavdevice    57. 10.100 / 57. 10.100
  libavfilter     6.107.100 /  6.107.100
  libavresample   3.  7.  0 /  3.  7.  0
  libswscale      4.  8.100 /  4.  8.100
  libswresample   2.  9.100 /  2.  9.100
  libpostproc    54.  7.100 / 54.  7.100
Input #0, rawvideo, from 'pipe:':
  Duration: N/A, start: 0.000000, bitrate: 17659 kb/s
    Stream #0:0: Video: rawvideo (BGR[24] / 0x18524742), bgr24, 172x186, 17659 kb/s, 23 tbr, 23 tbn, 23 tbc
Stream mapping:
  Stream #0:0 -> #0:0 (rawvideo (native) -> hevc (libx265))
x265 [info]: HEVC encoder version 2.6
x265 [info]: build info [Linux][GCC 7.2.0][64 bit] 8bit+10bit+12bit
x265 [info]: using cpu capabilities: MMX2 SSE2Fast LZCNT SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2
x265 [info]: Main profile, Level-2 (Main tier)
x265 [info]: Thread pool created using 4 threads
x265 [info]: Slices                              : 1
x265 [info]: frame threads / pool features       : 2 / wpp(3 rows)
x265 [warning]: Source height < 720p; disabling lookahead-slices
x265 [info]: Coding QT: max CU size, min CU size : 64 / 8
x265 [info]: Residual QT: max TU size, max depth : 32 / 1 inter / 1 intra
x265 [info]: ME / range / subpel / merge         : hex / 57 / 2 / 2
x265 [info]: Keyframe min / max / scenecut / bias: 23 / 250 / 40 / 5.00
x265 [info]: Lookahead / bframes / badapt        : 20 / 4 / 2
x265 [info]: b-pyramid / weightp / weightb       : 1 / 1 / 0
x265 [info]: References / ref-limit  cu / depth  : 3 / on / on
x265 [info]: AQ: mode / str / qg-size / cu-tree  : 1 / 1.0 / 32 / 1
x265 [info]: Rate Control / qCompress            : CRF-24.0 / 0.60
x265 [info]: tools: rd=3 psy-rd=2.00 rskip signhide tmvp strong-intra-smoothing
x265 [info]: tools: deblock sao
Output #0, mp4, to '../SoExample/out.mp4':
  Metadata:
    encoder         : Lavf57.83.100
    Stream #0:0: Video: hevc (libx265) (hev1 / 0x31766568), yuv420p, 172x186, q=2-31, 23 fps, 11776 tbn, 23 tbc
    Metadata:
      encoder         : Lavc57.107.100 libx265
[rawvideo @ 0x564ebd221aa0] Invalid buffer size, packet size 51600 < expected frame_size 95976    
Error while decoding stream #0:0: Invalid argument
frame=  100 fps= 30 q=-0.0 Lsize=      36kB time=00:00:04.21 bitrate=  69.1kbits/s speed=1.25x    
video:32kB audio:0kB subtitle:0kB other streams:0kB global headers:2kB muxing overhead: 12.141185%
x265 [info]: frame I:      1, Avg QP:22.44  kb/s: 179.77  
x265 [info]: frame P:     29, Avg QP:24.20  kb/s: 130.12  
x265 [info]: frame B:     70, Avg QP:29.99  kb/s: 27.82   
x265 [info]: Weighted P-Frames: Y:0.0% UV:0.0%
x265 [info]: consecutive B-frames: 20.0% 3.3% 16.7% 43.3% 16.7% 

encoded 100 frames in 3.35s (29.83 fps), 59.01 kb/s, Avg QP:28.23
Done!

如您所见,出现错误:Invalid buffer size, packet size 51600 < expected frame_size 95976 Error while decoding stream #0:0: Invalid argument,您认为这可能是所显示问题的原因吗?我不确定,因为最后它告诉我所有 100 帧都会被编码。

如果您想在完全相同的视频上重现此内容,您可以在 UCF Aerial Action Dataset 中找到 actions1.mpg

非常感谢任何帮助,因为我真的被这个错误困住了。

修复错误比预期的要容易。

imutils.resize() 仅尝试评估宽度或高度参数,而不是两者,并尝试保留图像的原始纵横比,而 cv2.resize() 为您提供自由选择(w/o 任何约束) [Source Code, Explanation]

就我而言,这会导致意外的调整大小行为。我告诉 imutils 将我的 (171, 186) 图像调整为 (172, 186),但它所做的是调整为 (172, 187),因为它决定宁愿保持纵横比而不是想要的高度,所以它丢弃了我提交的高度。

简单的解决方法是替换

imutils.resize(frame_cropped, width=size_rounded[0], height=size_rounded[1])

来自

cv2.resize(frame_cropped, (size_rounded[0], size_rounded[1]))

修复调整大小有助于防止缓冲区大小错误,因此不会导致垂直同步错误。