1. 程式人生 > 程式設計 >Python-OpenCV錄製H264編碼的MP4視訊

Python-OpenCV錄製H264編碼的MP4視訊

前言

因最近專案需求涉及計算機視覺相關內容,需要實現在Python錄製視訊,並且錄製完成後可在瀏覽器前端中進行視訊回放的功能;特寫下此篇文章以記錄整體實現過程。

2019-08-02 更新

之前一直在忙別的事,沒有繼續深入探究,這篇文章也暫時擱置了;但是最近發現之前的實現方式(錄製avi視訊後由Java呼叫FFmpeg轉換為mp4)會影響到系統的效能,原因為呼叫FFmpeg轉換視訊時CPU佔用較高QAQ,於是在此前的基礎上繼續尋找解決方式。

降低FFmpeg的CPU佔用

既然FFmpeg的CPU佔用較高,那麼我們首先嚐試如何降低對CPU的佔用,搜尋發現可以在FFmpeg命令中新增-threads引數來指定CPU的使用

FFmpeg轉換測試

此次測試均使用相同avi視訊檔案,大小為113

1. 原始轉換命令
ffmpeg -i test.avi -vcodec libx264 -f mp4 test.mp4
# 轉換用時 30s~31s
# CPU佔用 950%~1000%
複製程式碼
2. 新增-threads 6引數
ffmpeg -i test.avi -threads 6 -vcodec libx264 -f mp4 test.mp4
# 轉換用時 45s~46s
# CPU 佔用490%~550%
複製程式碼
3. 新增-threads 2引數
ffmpeg -i test.avi -threads 2 -vcodec libx264 -f
mp4 test.mp4 # 轉換用時 87s~88s # CPU佔用 205%~230% 複製程式碼

可以看出,新增-threads引數後CPU的佔用確實少了,但相應的視訊轉換耗時也增加了,顯然這不是我們想要的效果;所以還是逃避不了錄製H264視訊的問題

編譯安裝OpenCV錄製視訊

之前一直無法錄製H264編碼的MP4視訊是因為使用的為pip安裝的opencv-python,這個庫中自帶FFmpeg,所以不論我們如何折騰系統的FFmpeg都不會有任何作用;如果我們想要呼叫系統的FFmpeg則需要手動編譯安裝OpenCV。具體原因可以參考下圖:

如何編譯安裝OpenCV就不過多敘述了,這也不是此篇文章的重點,但還是給懶癌患者放個連結吧!
Ubuntu16.04 install OpenCV with ffmpeg

編譯安裝後import cv2正常引入即可,程式碼就不放了,原文和網上都有,只是改個fourcc。

至此在Python中呼叫OpenCV錄製H264編碼的MP4視訊已經可以實現,沒有特殊需求的同學看到這裡就可以了~撒花!

使用vidgear庫錄製視訊

因為專案原因我們還不能使用手動編譯的OpenCV(WTF!!!),所以不得不繼續尋找解決方案QAQ

vidgear-github官方連結,這個方案已經脫離主題,只是由於專案原因而採用,在此就不過多敘述了,感興趣的同學可以看一下。

以下內容為原文


Python-OpenCV錄製視訊

環境

python 3.7.1
opencv-python 3.4.4.19

引入庫支援

import cv2
複製程式碼

呼叫攝像頭

入參傳入“0”、“1”、“2”等數字為攝像頭索引,0為自帶攝像頭,可按順序呼叫攝像頭,也可傳入視訊檔案路徑

cap = cv2.VideoCapture(0)
複製程式碼

獲取攝像頭寬高

width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
複製程式碼

使用攝像頭幀率錄製視訊後播放存在快進情況,暫時寫死在VideoWriter中 不知道是否與攝像頭有關,此處未進行深入瞭解 fps = cap.get(cv2. CV_CAP_PROP_FPS)

指定視訊編解碼

需要傳入fourcc(four character code)四字元編解碼程式碼: fourcc參考

encode = cv2.VideoWriter_fourcc(*'mp4v')
複製程式碼

初始化VideoWriter

入參參考:官方檔案

out = cv2.VideoWriter( './test.mp4',encode,10,(width,height),True)
複製程式碼

獲取影象幀並寫入視訊檔案

  • 迴圈從攝像頭/視訊中獲取單幀影象
  • 新開一個視窗展示影象幀,每隔25毫秒播放下一幀,鍵入“q”跳出迴圈
  • 將影象幀寫入視訊檔案
while True:
    if cv2.waitKey(25) & 0xFF == ord('q'):
        break
    ret,frame = cap.read()
    cv2.imshow('test',frame)
    out.write(frame)
複製程式碼

釋放資源

  • 釋放VideoWriter
  • 釋放攝像頭
  • 關閉視窗
out.release()
cap.release()
cv2.destroyAllWindows()
複製程式碼

完整程式碼

此處程式碼為演示demo,僅供參考

#! /usr/bin/env python3
# -*- coding: utf-8 -*-

import cv2

# 呼叫攝像頭
cap = cv2.VideoCapture(0)
# 獲取攝像頭寬高
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# 獲取攝像頭幀率
#fps = cap.get(cv2.CAP_PROP_FPS)
# 指定fourcc編解碼
encode = cv2.VideoWriter_fourcc(*'mp4v')
# 初始化VideoWriter
out = cv2.VideoWriter('./test.mp4',True)
while True:
    # 每隔25毫秒播放下一幀,若鍵入“q”跳出迴圈
    if cv2.waitKey(25) & 0xFF == ord('q'):
        break
    # 從攝像頭獲取下一幀
    ret,frame = cap.read()
    # 新開視窗展示影象
    cv2.imshow('test',frame)
    # 將當前幀寫入視訊檔案
    out.write(frame)
# 釋放VideoWriter
out.release()
# 釋放攝像頭
cap.release()
# 關閉視窗
cv2.destroyAllWindows()
複製程式碼

瀏覽器中播放視訊

環境

macOS Mojave 10.14.3
Ubuntu 16.04
vue 2.9.6
nginx 1.15.5
前端為vue專案,打包後部署在nginx,配置server塊/location塊提供圖片/視訊等靜態資源訪問

h5中video無法播放視訊問題

問題排查
  1. 程式碼錯誤
    • python錄製視訊是否成功
    • 前端中video的src是否正確
  2. 網路請求
    • 瀏覽器控制檯是否報錯
    • nginx服務是否啟動
    • 請求路徑是否正確
    • 是否跨域問題
  3. 瀏覽器支援
格式 IE Firefox Opera Chrome Safari
Ogg - 3.5+ 10.5+ 5.0+ -
MPEG 4 9.0+ - - 5.0+ 3.0+
WebM - 4.0+ 10.6+ 6.0+ -
  1. 視訊編解碼
格式 視訊編碼 音訊編碼
Ogg Theora Vorbis
MPEG 4 H.264 AAC
WebM VP8 Vorbis

問題定位

排除程式碼及網路請求問題後,可以將問題定位在瀏覽器,我使用的瀏覽器為Chrome,排除版本問題,因此可以確定是視訊編解碼問題,在python中錄製視訊時未使用H.264編解碼:

encode = cv2.VideoWriter_fourcc(*'mp4v')
複製程式碼

檢視視訊簡介可以發現該視訊也確實非H.264編解碼,因此造成該視訊可以在視訊播放軟體中正常播放卻無法在h5的video中播放,見下圖:

視訊編解碼-MP4V.jpg

嘗試更改fourcc重新錄製視訊

encode = cv2.VideoWriter_fourcc(*'X264')
複製程式碼

報錯資訊.jpg
貌似不支援這個編解碼QAQ,好像需要FFmpeg的庫,Ubuntu下在終端輸入:

$sudo apt-get install ffmpeg x264 libx264-dev
複製程式碼

安裝完成後Ubuntu上無法錄製(視訊檔案都無法生成),但是在我自己的電腦不影響錄製:

視訊編解碼-X264.jpg

Java使用FFmpeg轉換視訊

因暫時未能實現錄製H.264編解碼的MP4視訊,所以採用迂迴戰術:在python中錄製.avi格式視訊後,前端請求後臺,在java中使用FFmpeg將.avi格式視訊轉換為.mp4格式視訊

首先安裝FFmpeg (Ubuntu下我沒有安裝,好像是自帶的?) macOS安裝FFmpeg Ubuntu安裝FFmpeg java這邊就不再詳述了,直接上程式碼~(同樣為演示demo,僅供參考)

    // FFmpeg轉換命令
    String transferCommand = "ffmpeg -i filePath/fileName.avi -vcodec libx264 -f mp4 filePath/fileName.mp4";
    Process process = Runtime.getRuntime().exec("/bin/bash");
    printWriter = new PrintWriter(new BufferedWriter(new 
    OutputStreamWriter(process.getOutputStream())),true);
    printWriter.println(transferCommand);
    // 這個命令必須執行,否則in流不結束。
    printWriter.println("exit");
    printWriter.close();
    process.waitFor();
複製程式碼

轉換過程需要些許時間,採取方案為啟一條執行緒完成視訊轉換,不影響當前介面響應時間,在使用者無感知的情況下完成視訊轉換。

總結

以上內容為本次實現過程記錄,程式碼均為演示demo,非實際應用程式碼,如有需要可根據實際需求加以調整。因為時間原因未能在錄製H.264視訊上投入過多精力,可能未來會繼續嘗試~

如有疑問或遇到類似需求可以留言或私信我~
如能實現錄製.264視訊或有更優解決方案也請不吝賜教~

參考連結

video不能播放mp4的問題

什麼是python OpenCV中mp4視訊的編解碼器

FFmpeg限制CPU的使用率

opencv-python-issues

vidgear-github