思路:
opencv讀取視頻 —> 將視頻分割爲幀 —> 將每一幀進行需求加工後 —> 將此幀寫入pipe管道 —> 利用ffmpeg進行推流直播
pipe管道:
啥是pipe管道? 粗略的理解就是一個放共享文件的地方(理解不是很深刻。。。)
利用這個特點, 把處理後的圖片放入管道, 讓ffmpeg讀取處理後的圖像幀並進行rtmp推流即可
代碼
import subprocess as sp
rtmpUrl = ""
camera_path = ""
cap = cv.VideoCapture(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
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',
rtmpUrl]
# 管道配置
p = sp.Popen(command, stdin=sp.PIPE)
# read webcamera
while(cap.isOpened()):
ret, frame = cap.read()
if not ret:
print("Opening camera is failed")
break
# process frame
# your code
# process frame
# write to pipe
p.stdin.write(frame.tostring())
代碼說明:
- rtmpUrl就是要接收視頻的服務器了, 我做實驗時是在自己機子上配置了一個nginx服務器接收視頻流(ubuntu 不要通過apt安裝哦, 請從源碼安裝, 因爲apt安裝的版本沒有rtmp協議, 需要下載nginx源碼然後配合nginx-rtmp-module這個東西安裝 推薦一篇nginx安裝教程)
- camera_path就是要進行直播的視頻地址了
- 重點的代碼其實就這幾句:
import subprocess as sp
# ffmpeg command
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',
rtmpUrl]
# 管道配置
p = sp.Popen(command, stdin=sp.PIPE)
# write to pipe
p.stdin.write(frame.tostring())
- 我讀取的視頻是rtsp網絡攝像頭的視頻流, 但是一旦運行沒多久就會出現 pipe broke 的報錯(1080p視頻), 不知道是什麼原因, 若有大神還請指點指點
WriteN, RTMP send error 104 (129 bytes)
更新: 我改1080p爲720p 暫時沒出現這個問題
- 我在讀取視頻時候還遇到這個報錯
error while decoding xxx
經過一方百度, 是“FFMPEG Lib對在rtsp協議中的H264 vidos不支持”的問題, 解決方法: 程序開啓兩個線程, 一個線程讀取攝像頭的幀, 另一個線程處理這幀圖片, 這裏還推薦一個大佬用隊列處理視頻的方法 ,大家可以套用一下啊 飛機票 多線程處理的版本:
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 = ""
# 自行設置
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應該替換爲:
# cap = cv.VideoCapture(self.camera_path)
# 因爲我這倆天遇到的項目裏出現斷流的毛病
# 特別是拉取rtmp流的時候!!!!
break
# put frame into queue
self.frame_queue.put(frame)
def push_frame(self):
# 防止多線程時 command 未被設置
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]
博主忙着做項目ing 能力也不是很高 歡迎和大家一起討論 但是有的是真不會呀 還請見諒