Python結(jié)合ffmpeg 實(shí)現(xiàn)單線程和多線程推流
一、引言
在本文中,我們將詳細(xì)介紹如何使用 Python 進(jìn)行視頻的推流操作。我們將通過兩個(gè)不同的實(shí)現(xiàn)方式,即單線程推流和多線程推流,來展示如何利用 cv2(OpenCV)和 subprocess 等庫將視頻幀推送到指定的 RTMP 地址。這兩種方式都涉及到從攝像頭讀取視頻幀,以及使用 ffmpeg 命令行工具將視頻幀進(jìn)行編碼和推流的過程。
二、單線程推流
以下是單線程推流的代碼:
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)建、管理子進(jìn)程
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()
在這個(gè)單線程的實(shí)現(xiàn)中,我們執(zhí)行以下步驟:
初始化視頻讀取對象:
- 使用
cv2.VideoCapture(0)來打開默認(rèn)的攝像頭設(shè)備。 - 獲取攝像頭的幀率
fps、寬度w和高度h等參數(shù)。
設(shè)置推流地址和參數(shù):
- 定義
rtmpUrl作為推流的目標(biāo)地址。 - 構(gòu)造
ffmpeg的命令列表command,該列表包含了一系列的參數(shù),如-y表示覆蓋輸出文件、-f rawvideo表示輸入格式為原始視頻等。 - 使用
sp.Popen創(chuàng)建一個(gè)子進(jìn)程,將ffmpeg命令作為子進(jìn)程運(yùn)行,并且將其輸入管道stdin連接到我們的程序。
循環(huán)讀取和推流:
- 在一個(gè)
while循環(huán)中,不斷讀取攝像頭的幀。 - 若讀取失敗,打印錯(cuò)誤信息并繼續(xù)。
- 使用
cv2.imshow顯示當(dāng)前幀,同時(shí)監(jiān)聽q鍵,按下q鍵時(shí)退出程序。 - 將讀取到的幀通過管道發(fā)送給
ffmpeg進(jìn)行推流。
三、多線程推流
以下是多線程推流的代碼:
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):
# 防止多線程時(shí) 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]
在這個(gè)多線程的實(shí)現(xiàn)中,我們使用了 threading 和 queue 庫:
類的初始化:
- 創(chuàng)建一個(gè)
Live類,在__init__方法中初始化幀隊(duì)列frame_queue、command、rtmpUrl和camera_path等變量。
讀取幀的線程方法:
read_frame方法中,使用cv2.VideoCapture(self.camera_path)打開攝像頭。- 獲取攝像頭的參數(shù),并構(gòu)造
ffmpeg命令。 - 不斷從攝像頭讀取幀,并將幀放入隊(duì)列
frame_queue中。
推流的線程方法:
push_frame方法中,等待command被設(shè)置,然后使用sp.Popen啟動(dòng)ffmpeg子進(jìn)程。- 從幀隊(duì)列中取出幀,并將其寫入
ffmpeg的輸入管道進(jìn)行推流。
啟動(dòng)線程:
run方法創(chuàng)建并啟動(dòng)兩個(gè)線程,一個(gè)用于讀取幀,一個(gè)用于推流,并且將它們設(shè)置為守護(hù)線程。
四、代碼解釋和注意事項(xiàng)
單線程推流:
- 這種方式相對簡單,適合初學(xué)者理解。但由于是單線程操作,在處理復(fù)雜任務(wù)時(shí)可能會(huì)導(dǎo)致性能瓶頸,特別是在同時(shí)進(jìn)行視頻顯示、讀取和推流的情況下,可能會(huì)出現(xiàn)卡頓現(xiàn)象。
多線程推流:
- 利用多線程可以將不同的任務(wù)分配給不同的線程,提高性能。
frame_queue是一個(gè)線程安全的隊(duì)列,用于在兩個(gè)線程之間傳遞幀數(shù)據(jù),避免了數(shù)據(jù)競爭問題。setDaemon(True)使得線程在主線程結(jié)束時(shí)自動(dòng)終止,防止程序無法正常退出。
五、總結(jié)
通過上述代碼和解釋,我們可以看到如何使用 Python 進(jìn)行單線程和多線程的視頻推流操作。單線程代碼簡單明了,但性能可能受限;多線程代碼可以更好地處理高負(fù)載,但也需要注意線程安全和資源管理等問題。在實(shí)際應(yīng)用中,我們可以根據(jù)具體的需求和硬件性能來選擇合適的推流方式。同時(shí),我們可以進(jìn)一步優(yōu)化代碼,例如添加異常處理、優(yōu)化幀處理邏輯等,以提高程序的穩(wěn)定性和性能。
到此這篇關(guān)于Python結(jié)合ffmpeg 實(shí)現(xiàn)單線程和多線程推流的文章就介紹到這了,更多相關(guān)Python ffmpeg 單線程和多線程推流內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
零基礎(chǔ)也能輕松上手的Python自動(dòng)化辦公實(shí)戰(zhàn)教學(xué)(新手必看)
這篇文章為Python零基礎(chǔ)用戶提供了3個(gè)實(shí)用的自動(dòng)化辦公項(xiàng)目,幫助解決重復(fù)性工作問題,特別適合被重復(fù)性辦公工作困擾的職場人士和想提升效率的零基礎(chǔ)用戶,希望對大家有所幫助2026-06-06
Python鍵盤輸入轉(zhuǎn)換為列表的實(shí)例
今天小編就為大家分享一篇Python鍵盤輸入轉(zhuǎn)換為列表的實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-06-06
Python爬蟲使用瀏覽器cookies:browsercookie過程解析
這篇文章主要介紹了Python爬蟲使用瀏覽器cookies:browsercookie,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10
python數(shù)據(jù)挖掘需要學(xué)的內(nèi)容
在本篇文章中我們給大家整理了關(guān)于python數(shù)據(jù)挖掘需要學(xué)什么的知識點(diǎn)指南,有興趣的朋友們跟著參考下。2019-06-06
Python實(shí)現(xiàn)遍歷目錄的方法【測試可用】
這篇文章主要介紹了Python實(shí)現(xiàn)遍歷目錄的方法,涉及Python針對目錄與文件的遍歷、判斷、讀取相關(guān)操作技巧,需要的朋友可以參考下2017-03-03
Python如何import文件夾下的文件(實(shí)現(xiàn)方法)
下面小編就為大家?guī)硪黄狿ython如何import文件夾下的文件(實(shí)現(xiàn)方法)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-01-01
Python實(shí)現(xiàn)數(shù)值積分方式
今天小編就為大家分享一篇Python實(shí)現(xiàn)數(shù)值積分方式,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-11-11

