Tik0
Tik0

Reputation: 2699

Encoding HEVC video using OpenCV and ffmpeg backend

I try to encode my webcam using OpenCV with ffmpeg backend and Python3 to an HEVC video. It works fine with other codecs like mjpg. Here is my example script which uses the corresponding fourcc (also tried hevc, h265, x265, etc.):

#!/bin/python3

import cv2
import time
import subprocess

def video(seconds, frameRate):
    cap = cv2.VideoCapture(0)
    if(not cap.isOpened()):
        return "error"

    # Define the codec and create VideoWriter object
    fourcc = cv2.VideoWriter_fourcc(*'HEVC')
    name = "/tmp/" + time.strftime("%d-%m-%Y_%X")+".hevc"
    out = cv2.VideoWriter(name, fourcc, frameRate, (640,480))
    program_starts = time.time()
    result = subprocess.Popen(["ffprobe", name], stdout = subprocess.PIPE, stderr = subprocess.STDOUT, shell=True)
    nFrames=0
    while(nFrames<seconds*frameRate):
        ret, frame = cap.read()
        if ret==True:
            out.write(frame)
            nFrames += 1
        else:
            break
    cap.release()
    return name 
# Store a video to /tmp for 2 seconds
print(video(2,15))

Returning error:

OpenCV: FFMPEG: tag 0x43564548/'HEVC' is not found (format 'hevc / raw HEVC video')'

My Ubuntu system configuration is as follows:

bash> cat /etc/issue
Ubuntu 16.04.6 LTS \n \l
bash> python3 -m pip list | grep opencv
opencv-contrib-python-nonfree 4.1.1.1               
opencv-python-nonfree         4.1.1.1
bash> ffmpeg -encoders
ffmpeg version 2.8.15-0ubuntu0.16.04.1 Copyright (c) 2000-2018 the FFmpeg developers
  built with gcc 5.4.0 (Ubuntu 5.4.0-6ubuntu1~16.04.10) 20160609
  configuration: --prefix=/usr --extra-version=0ubuntu0.16.04.1 --build-suffix=-ffmpeg --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --cc=cc --cxx=g++ --enable-gpl --enable-shared --disable-stripping --disable-decoder=libopenjpeg --disable-decoder=libschroedinger --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-libmodplug --enable-libmp3lame --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-librtmp --enable-libschroedinger --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-libxvid --enable-libzvbi --enable-openal --enable-opengl --enable-x11grab --enable-libdc1394 --enable-libiec61883 --enable-libzmq --enable-frei0r --enable-libx264 --enable-libopencv
  libavutil      54. 31.100 / 54. 31.100
  libavcodec     56. 60.100 / 56. 60.100
  libavformat    56. 40.101 / 56. 40.101
  libavdevice    56.  4.100 / 56.  4.100
  libavfilter     5. 40.101 /  5. 40.101
  libavresample   2.  1.  0 /  2.  1.  0
  libswscale      3.  1.101 /  3.  1.101
  libswresample   1.  2.101 /  1.  2.101
  libpostproc    53.  3.100 / 53.  3.100
Encoders:
...
 V..... libx265              libx265 H.265 / HEVC (codec hevc)
...

Upvotes: 5

Views: 18048

Answers (1)

Tik0
Tik0

Reputation: 2699

It turns out that the Python3 opencv-*-nonfree packages are shipped with its own FFmpeg. However, I've prepared a little minimal script that, given that all dev libraries are installed, builds OpenCV which links against the locally installed FFmpeg with HEVC (and others): INSTALLSCRIPT.

After installation (bash> make install), I can run the mentioned script via

bash> ln -s /opt/opencv/ffmpeg/lib/python3.5/dist-packages/cv2/python-3.5/cv2.cpython-35m-x86_64-linux-gnu.so /opt/opencv/ffmpeg/lib/python3.5/dist-packages/cv2/cv2.so
bash> export PYTHONPATH=/opt/opencv/ffmpeg/lib/python3.5/dist-packages/
bash> export LD_LIBRARY_PATH=/opt/opencv/ffmpeg/lib
bash> python3 /tmp/test_encoding.py
x265 [info]: HEVC encoder version 1.9
x265 [info]: build info [Linux][GCC 5.3.1][64 bit] 8bit+10bit+12bit
x265 [info]: using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX AVX2 FMA3 LZCNT BMI2
x265 [info]: Main profile, Level-3.1 (Main tier)
x265 [info]: Thread pool created using 4 threads
x265 [info]: frame threads / pool features       : 1 / wpp(8 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       : 15 / 250 / 40
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 / 1 / 1
x265 [info]: AQ: mode / str / qg-size / cu-tree  : 1 / 1.0 / 32 / 1
x265 [info]: Rate Control / qCompress            : ABR-9216 kbps / 0.60
x265 [info]: tools: rd=3 psy-rd=2.00 signhide tmvp strong-intra-smoothing
x265 [info]: tools: deblock sao
x265 [info]: frame I:      1, Avg QP:1.89  kb/s: 22236.48
x265 [info]: frame P:      8, Avg QP:6.27  kb/s: 14486.10
x265 [info]: frame B:     21, Avg QP:8.79  kb/s: 12282.91
x265 [info]: Weighted P-Frames: Y:75.0% UV:75.0%
x265 [info]: consecutive B-frames: 11.1% 11.1% 33.3% 22.2% 22.2% 

encoded 30 frames in 5.27s (5.69 fps), 13202.21 kb/s, Avg QP:7.89
/tmp/25-11-2019_23:13:04.mov

Furthermore, it is super important that the fourcc (in my case hvc1 for HEVC) is correct and that the container supports the codec (in my case mov).

content of final /tmp/test_encoding.py

#!/bin/python3

import cv2
import time
import subprocess

def video(seconds, frameRate):
    cap = cv2.VideoCapture(0)
    if(not cap.isOpened()):
        return "error"

    # Define the codec and create VideoWriter object
    fourcc = cv2.VideoWriter_fourcc(*'hvc1')
    name = "/tmp/" + time.strftime("%d-%m-%Y_%X")+".mov"
    out = cv2.VideoWriter(name, fourcc, frameRate, (640,480))
    program_starts = time.time()
    result = subprocess.Popen(["ffprobe", name], stdout = subprocess.PIPE, stderr = subprocess.STDOUT, shell=True)
    nFrames=0
    while(nFrames<seconds*frameRate):
        ret, frame = cap.read()
        if ret==True:
            out.write(frame)
            nFrames += 1
        else:
            break
    cap.release()
    return name 
# Store a video to /tmp for 2 seconds
print(video(2,15))

Upvotes: 3

Related Questions