Search code examples
pythonopencvffmpeg

Piping OpenCV raw video frames into FFMpeg results in corrupted output


I have written a python script to open my webcam in OpenCV and convert captured frames to video streams. I have followed some tutorials that basically do these things:

  1. Creating a pipe input to FFMpeg
  2. Writing OpenCV video frames into the pipe. FFMpeg should then encode and output the video.

In my case, FFMpeg runs without errors, and using imshow() displays captured frames correctly, but the FFMpeg video output is corrupted.

imshow() video output

code:

import cv2 as cv
import subprocess as sp

FPS = 24
WIDTH = 720
HEIGHT = 360

cap = cv.VideoCapture(0)
cap.set(cv.CAP_PROP_FPS, FPS)
cap.set(cv.CAP_PROP_FRAME_WIDTH, WIDTH)
cap.set(cv.CAP_PROP_FRAME_HEIGHT, HEIGHT)

# ffmpeg command
ffmpeg_cmd = [
    "./libs/ffmpeg/bin/ffmpeg.exe",
    '-hwaccel', 'auto', '-y',
    '-f', 'rawvideo',
    '-vcodec','rawvideo',
    '-pix_fmt', 'bgr24',
    '-s', f"{WIDTH}x{HEIGHT}",
    '-r', str(FPS),
    '-i', '-',
    '-c:v', 'libx264',
    '-pix_fmt', 'yuv420p',
    '-preset', 'ultrafast',
    '-f', 'flv',
    'out.flv'
]
ffmpeg_sp = sp.Popen(ffmpeg_cmd, stdin=sp.PIPE)

while True:
    ret,frame = cap.read()
    # video capture test: OK
    cv.imshow('video', frame)
    if cv.waitKey(50)&0xFF == ord('q'):
        break
    #frame = cv.cvtColor(frame, cv.COLOR_BGR2YUV)   # It doesn't work
    # write to ffmpeg pipe
    ffmpeg_sp.stdin.write(frame.tobytes())

ffmpeg_sp.stdin.close()
cap.release()
cv.destroyAllWindows()

I'm running Python 3.11.5, OpenCV 4.8.0.76 on Windows 11. The ffmpeg -version output is given below.

ffmpeg version N-112841-g2d9ed64859-20231125 Copyright (c) 2000-2023 the FFmpeg developers
built with gcc 13.2.0 (crosstool-NG 1.25.0.232_c175b21)
configuration: --prefix=/ffbuild/prefix --pkg-config-flags=--static --pkg-config=pkg-config --cross-prefix=x86_64-w64-mingw32- --arch=x86_64 --target-os=mingw32 --enable-gpl --enable-version3 --disable-debug --disable-w32threads --enable-pthreads --enable-iconv --enable-libxml2 --enable-zlib --enable-libfreetype --enable-libfribidi --enable-gmp --enable-lzma --enable-fontconfig --enable-libharfbuzz --enable-libvorbis --enable-opencl --disable-libpulse --enable-libvmaf --disable-libxcb --disable-xlib --enable-amf --enable-libaom --enable-libaribb24 --enable-avisynth --enable-chromaprint --enable-libdav1d --enable-libdavs2 --disable-libfdk-aac --enable-ffnvcodec --enable-cuda-llvm --enable-frei0r --enable-libgme --enable-libkvazaar --enable-libaribcaption --enable-libass --enable-libbluray --enable-libjxl --enable-libmp3lame --enable-libopus --enable-librist --enable-libssh --enable-libtheora --enable-libvpx --enable-libwebp --enable-lv2 --enable-libvpl --enable-openal --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenh264 --enable-libopenjpeg --enable-libopenmpt --enable-librav1e --enable-librubberband --enable-schannel --enable-sdl2 --enable-libsoxr --enable-libsrt --enable-libsvtav1 --enable-libtwolame --enable-libuavs3d --disable-libdrm --enable-vaapi --enable-libvidstab --enable-vulkan --enable-libshaderc --enable-libplacebo --enable-libx264 --enable-libx265 --enable-libxavs2 --enable-libxvid --enable-libzimg --enable-libzvbi --extra-cflags=-DLIBTWOLAME_STATIC --extra-cxxflags= --extra-ldflags=-pthread --extra-ldexeflags= --extra-libs=-lgomp --extra-version=20231125
libavutil      58. 32.100 / 58. 32.100
libavcodec     60. 34.100 / 60. 34.100
libavformat    60. 17.100 / 60. 17.100
libavdevice    60.  4.100 / 60.  4.100
libavfilter     9. 13.100 /  9. 13.100
libswscale      7.  6.100 /  7.  6.100
libswresample   4. 13.100 /  4. 13.100
libpostproc    57.  4.100 / 57.  4.100

I have been playing around with FFMpeg arguments for a while and none of them works. I have also transcoded video files using FFMpeg successfully. It somehow just doesn't work with raw video frames from OpenCV. Any help/advice is appreciated.


Solution

  • I figured out piping raw video frames causes the problem, so I followed guides from Pipe raw OpenCV images to FFmpeg to encode frames into images using imencode() before piping into FFMpeg, and it works!