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
作為推流的目標(biāo)地址。 - 構(gòu)造
ffmpeg
的命令列表command
,該列表包含了一系列的參數(shù),如-y
表示覆蓋輸出文件、-f rawvideo
表示輸入格式為原始視頻等。 - 使用
sp.Popen
創(chuàng)建一個子進程,將ffmpeg
命令作為子進程運行,并且將其輸入管道stdin
連接到我們的程序。
循環(huán)讀取和推流:
- 在一個
while
循環(huán)中,不斷讀取攝像頭的幀。 - 若讀取失敗,打印錯誤信息并繼續(xù)。
- 使用
cv2.imshow
顯示當(dāng)前幀,同時監(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è)置為守護線程。
四、代碼解釋和注意事項
單線程推流:
- 這種方式相對簡單,適合初學(xué)者理解。但由于是單線程操作,在處理復(fù)雜任務(wù)時可能會導(dǎo)致性能瓶頸,特別是在同時進行視頻顯示、讀取和推流的情況下,可能會出現(xiàn)卡頓現(xiàn)象。
多線程推流:
- 利用多線程可以將不同的任務(wù)分配給不同的線程,提高性能。
frame_queue
是一個線程安全的隊列,用于在兩個線程之間傳遞幀數(shù)據(jù),避免了數(shù)據(jù)競爭問題。setDaemon(True)
使得線程在主線程結(jié)束時自動終止,防止程序無法正常退出。
五、總結(jié)
通過上述代碼和解釋,我們可以看到如何使用 Python 進行單線程和多線程的視頻推流操作。單線程代碼簡單明了,但性能可能受限;多線程代碼可以更好地處理高負載,但也需要注意線程安全和資源管理等問題。在實際應(yīng)用中,我們可以根據(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字典如何獲取最大和最小value對應(yīng)的key
這篇文章主要介紹了python字典如何獲取最大和最小value對應(yīng)的key問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-11-11python 執(zhí)行文件時額外參數(shù)獲取的實例
今天小編就為大家分享一篇python 執(zhí)行文件時額外參數(shù)獲取的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-12-12Python實現(xiàn)csv文件(點表和線表)轉(zhuǎn)換為shapefile文件的方法
這篇文章主要介紹了Python實現(xiàn)csv文件(點表和線表)轉(zhuǎn)換為shapefile文件的方法,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-10-10使用Python Flask實現(xiàn)簡易文件上傳功能
在平時工作中,文件上傳是一項常見的需求,例如將應(yīng)用異常時通過腳本生成的dump文件收集起來進行分析,但實現(xiàn)起來卻可能相當(dāng)復(fù)雜,在本文中,我們將探討如何使用Flask實現(xiàn)文件上傳功能,編寫Dockerfile將應(yīng)用程序通過docker部署,需要的朋友可以參考下2024-05-05Pandas庫之DataFrame使用的學(xué)習(xí)筆記
這篇文章主要介紹了Pandas庫之DataFrame使用的學(xué)習(xí)筆記,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06Python利用keras接口實現(xiàn)深度神經(jīng)網(wǎng)絡(luò)回歸
這篇文章主要為大家詳細介紹了基于Python語言中TensorFlow的Keras接口,實現(xiàn)深度神經(jīng)網(wǎng)絡(luò)回歸的方法。文中的示例代碼講解詳細,感興趣的可以了解一下2023-02-02給Django Admin添加驗證碼和多次登錄嘗試限制的實現(xiàn)
這篇文章主要介紹了給Django Admin添加驗證碼和多次登錄嘗試限制的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07通過Python實現(xiàn)電腦定時關(guān)機的兩種方法
這篇文章主要介紹了分別利用PyQT5和Tkinter實現(xiàn)電腦的定時關(guān)機小程序,文中的示例代碼講解詳細,對我們學(xué)習(xí)Python有一定的幫助,快跟隨小編一起學(xué)習(xí)一下吧2021-12-12Python實現(xiàn)快速提取Word表格并轉(zhuǎn)Markdown
這篇文章主要為大家詳細介紹了一套Python零基礎(chǔ)可操作的代碼方案,幫助測試工程師3分鐘內(nèi)完成表格提取與轉(zhuǎn)換,直接對接自動化測試或大模型,需要的小伙伴可以參考下2025-04-04