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

前言

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

2019-08-02 更新

以前一直在忙別的事,沒有繼續深刻探究,這篇文章也暫時擱置了;可是最近發現以前的實現方式(錄製avi視頻後由Java調用FFmpeg轉換爲mp4)會影響到系統的性能,緣由爲調用FFmpeg轉換視頻時CPU佔用較高QAQ,因而在此前的基礎上繼續尋找解決方式。html

下降FFmpeg的CPU佔用

既然FFmpeg的CPU佔用較高,那麼咱們首先嚐試如何下降對CPU的佔用,搜索發現能夠在FFmpeg命令中添加-threads參數來指定CPU的使用前端

FFmpeg轉換測試

這次測試均使用相同avi視頻文件,大小爲113vue

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視頻的問題java

編譯安裝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爲自帶攝像頭,可按順序調用攝像頭,也可傳入視頻文件路徑python

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)nginx

指定視頻編解碼

須要傳入fourcc(four character code)四字符編解碼代碼: fourcc參考git

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

初始化VideoWriter

入參參考:官方文檔github

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', encode, 10, (width, height), 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

相關文章
相關標籤/搜索