Python結(jié)合ffmpeg 實現(xiàn)單線程和多線程推流
一、引言
在本文中,我們將詳細介紹如何使用 Python 進行視頻的推流操作。我們將通過兩個不同的實現(xiàn)方式,即單線程推流和多線程推流,來展示如何利用 cv2(OpenCV)和 subprocess 等庫將視頻幀推送到指定的 RTMP 地址。這兩種方式都涉及到從攝像頭讀取視頻幀,以及使用 ffmpeg 命令行工具將視頻幀進行編碼和推流的過程。
二、單線程推流
以下是單線程推流的代碼:
import cv2 as cv
import subprocess as sp
def push_stream():
# 視頻讀取對象
cap = cv.VideoCapture(0)
fps = int(cap.get(cv.CAP_PROP_FPS))
w = int(cap.get(cv.CAP_PROP_FRAME_WIDTH))
h = int(cap.get(cv.CAP_PROP_FRAME_HEIGHT))
ret, frame = cap.read()
# 推流地址
rtmpUrl = "rtmp://192.168.3.33:1935/live/"
# 推流參數(shù)
command = ['ffmpeg',
'-y',
'-f', 'rawvideo',
'-vcodec','rawvideo',
'-pix_fmt', 'bgr24',
'-s', "{}x{}".format(w, h),
'-r', str(fps),
'-i', '-',
'-c:v', 'libx264',
'-pix_fmt', 'yuv420p',
'-preset', 'ultrafast',
'-f', 'flv',
rtmpUrl]
# 創(chuàng)建、管理子進程
pipe = sp.Popen(command, stdin=sp.PIPE, bufsize=10 ** 8)
# 循環(huán)讀取
while cap.isOpened():
# 讀取一幀
ret, frame = cap.read()
if frame is None:
print('read frame err!')
continue
# 顯示一幀
cv.imshow("frame", frame)
# 按鍵退出
if cv.waitKey(1) & 0xFF == ord('q'):
break
# 讀取尺寸、推流
# img=cv.resize(frame,size)
pipe.stdin.write(frame)
# 關(guān)閉窗口
cv.destroyAllWindows()
# 停止讀取
cap.release()
在這個單線程的實現(xiàn)中,我們執(zhí)行以下步驟:
初始化視頻讀取對象:
- 使用
cv2.VideoCapture(0)來打開默認的攝像頭設(shè)備。 - 獲取攝像頭的幀率
fps、寬度w和高度h等參數(shù)。
設(shè)置推流地址和參數(shù):
- 定義
rtmpUrl作為推流的目標地址。 - 構(gòu)造
ffmpeg的命令列表command,該列表包含了一系列的參數(shù),如-y表示覆蓋輸出文件、-f rawvideo表示輸入格式為原始視頻等。 - 使用
sp.Popen創(chuàng)建一個子進程,將ffmpeg命令作為子進程運行,并且將其輸入管道stdin連接到我們的程序。
循環(huán)讀取和推流:
- 在一個
while循環(huán)中,不斷讀取攝像頭的幀。 - 若讀取失敗,打印錯誤信息并繼續(xù)。
- 使用
cv2.imshow顯示當前幀,同時監(jiān)聽q鍵,按下q鍵時退出程序。 - 將讀取到的幀通過管道發(fā)送給
ffmpeg進行推流。
三、多線程推流
以下是多線程推流的代碼:
import queue
import threading
import cv2 as cv
import subprocess as sp
class Live(object):
def __init__(self):
self.frame_queue = queue.Queue()
self.command = ""
# 自行設(shè)置
self.rtmpUrl = ""
self.camera_path = ""
def read_frame(self):
print("開啟推流")
cap = cv.VideoCapture(self.camera_path)
# Get video information
fps = int(cap.get(cv.CAP_PROP_FPS))
width = int(cap.get(cv.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv.CAP_PROP_FRAME_HEIGHT))
# ffmpeg command
self.command = ['ffmpeg',
'-y',
'-f', 'rawvideo',
'-vcodec','rawvideo',
'-pix_fmt', 'bgr24',
'-s', "{}x{}".format(width, height),
'-r', str(fps),
'-i', '-',
'-c:v', 'libx264',
'-pix_fmt', 'yuv420p',
'-preset', 'ultrafast',
'-f', 'flv',
self.rtmpUrl]
# read webcamera
while(cap.isOpened()):
ret, frame = cap.read()
if not ret:
print("Opening camera is failed")
break
# put frame into queue
self.frame_queue.put(frame)
def push_frame(self):
# 防止多線程時 command 未被設(shè)置
while True:
if len(self.command) > 0:
# 管道配置
p = sp.Popen(self.command, stdin=sp.PIPE)
break
while True:
if self.frame_queue.empty()!= True:
frame = self.frame_queue.get()
# process frame
# 你處理圖片的代碼
# write to pipe
p.stdin.write(frame.tostring())
def run(self):
threads = [
threading.Thread(target=Live.read_frame, args=(self,)),
threading.Thread(target=Live.push_frame, args=(self,))
]
[thread.setDaemon(True) for thread in threads]
[thread.start() for thread in threads]
在這個多線程的實現(xiàn)中,我們使用了 threading 和 queue 庫:
類的初始化:
- 創(chuàng)建一個
Live類,在__init__方法中初始化幀隊列frame_queue、command、rtmpUrl和camera_path等變量。
讀取幀的線程方法:
read_frame方法中,使用cv2.VideoCapture(self.camera_path)打開攝像頭。- 獲取攝像頭的參數(shù),并構(gòu)造
ffmpeg命令。 - 不斷從攝像頭讀取幀,并將幀放入隊列
frame_queue中。
推流的線程方法:
push_frame方法中,等待command被設(shè)置,然后使用sp.Popen啟動ffmpeg子進程。- 從幀隊列中取出幀,并將其寫入
ffmpeg的輸入管道進行推流。
啟動線程:
run方法創(chuàng)建并啟動兩個線程,一個用于讀取幀,一個用于推流,并且將它們設(shè)置為守護線程。
四、代碼解釋和注意事項
單線程推流:
- 這種方式相對簡單,適合初學者理解。但由于是單線程操作,在處理復雜任務時可能會導致性能瓶頸,特別是在同時進行視頻顯示、讀取和推流的情況下,可能會出現(xiàn)卡頓現(xiàn)象。
多線程推流:
- 利用多線程可以將不同的任務分配給不同的線程,提高性能。
frame_queue是一個線程安全的隊列,用于在兩個線程之間傳遞幀數(shù)據(jù),避免了數(shù)據(jù)競爭問題。setDaemon(True)使得線程在主線程結(jié)束時自動終止,防止程序無法正常退出。
五、總結(jié)
通過上述代碼和解釋,我們可以看到如何使用 Python 進行單線程和多線程的視頻推流操作。單線程代碼簡單明了,但性能可能受限;多線程代碼可以更好地處理高負載,但也需要注意線程安全和資源管理等問題。在實際應用中,我們可以根據(jù)具體的需求和硬件性能來選擇合適的推流方式。同時,我們可以進一步優(yōu)化代碼,例如添加異常處理、優(yōu)化幀處理邏輯等,以提高程序的穩(wěn)定性和性能。
到此這篇關(guān)于Python結(jié)合ffmpeg 實現(xiàn)單線程和多線程推流的文章就介紹到這了,更多相關(guān)Python ffmpeg 單線程和多線程推流內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python 執(zhí)行文件時額外參數(shù)獲取的實例
今天小編就為大家分享一篇python 執(zhí)行文件時額外參數(shù)獲取的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-12-12
Python實現(xiàn)csv文件(點表和線表)轉(zhuǎn)換為shapefile文件的方法
這篇文章主要介紹了Python實現(xiàn)csv文件(點表和線表)轉(zhuǎn)換為shapefile文件的方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-10-10
使用Python Flask實現(xiàn)簡易文件上傳功能
在平時工作中,文件上傳是一項常見的需求,例如將應用異常時通過腳本生成的dump文件收集起來進行分析,但實現(xiàn)起來卻可能相當復雜,在本文中,我們將探討如何使用Flask實現(xiàn)文件上傳功能,編寫Dockerfile將應用程序通過docker部署,需要的朋友可以參考下2024-05-05
Python利用keras接口實現(xiàn)深度神經(jīng)網(wǎng)絡回歸
這篇文章主要為大家詳細介紹了基于Python語言中TensorFlow的Keras接口,實現(xiàn)深度神經(jīng)網(wǎng)絡回歸的方法。文中的示例代碼講解詳細,感興趣的可以了解一下2023-02-02
給Django Admin添加驗證碼和多次登錄嘗試限制的實現(xiàn)
這篇文章主要介紹了給Django Admin添加驗證碼和多次登錄嘗試限制的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-07-07
通過Python實現(xiàn)電腦定時關(guān)機的兩種方法
這篇文章主要介紹了分別利用PyQT5和Tkinter實現(xiàn)電腦的定時關(guān)機小程序,文中的示例代碼講解詳細,對我們學習Python有一定的幫助,快跟隨小編一起學習一下吧2021-12-12
Python實現(xiàn)快速提取Word表格并轉(zhuǎn)Markdown
這篇文章主要為大家詳細介紹了一套Python零基礎(chǔ)可操作的代碼方案,幫助測試工程師3分鐘內(nèi)完成表格提取與轉(zhuǎn)換,直接對接自動化測試或大模型,需要的小伙伴可以參考下2025-04-04

