最近在做一個校園安防的項目,其中涉及到前端要實時展示監控攝像機的畫面,其中畫面要求是經過神經網絡處理過的畫面。
如果前端只要求展示原始畫面,只需要在接入攝像機的時候,把視頻流推送到一個服務器地址上,前端可根據地址獲取視頻流,
本次開發,前端藉助的是一個視頻流插件video.js,可拉取rtmp格式的視頻流。
如果接入多路的攝像頭,可以藉助服務器Nginx + ffmpeg,具體的安裝配置可參考:這篇博客
在這邊主要講解代碼上的推流實現,也是借鑑別人的方法,結合自己的實際項目修改,其中有些地方需要注意:
import cv2
import queue
import os
import numpy as np
from threading import Thread
import datetime,_thread
import subprocess as sp
import time
# 使用線程鎖,防止線程死鎖
mutex = _thread.allocate_lock()
# 存圖片的隊列
frame_queue = queue.Queue()
# 推流的地址,前端通過這個地址拉流,主機的IP,2019是ffmpeg在nginx中設置的端口號
rtmpUrl="rtmp://192.168.40.145:2019/live/1"
# 用於推流的配置,參數比較多,可網上查詢理解
command=['ffmpeg',
'-y',
'-f', 'rawvideo',
'-vcodec','rawvideo',
'-pix_fmt', 'bgr24',
'-s', "{}x{}".format(640, 480),# 圖片分辨率
'-r', str(25.0),# 視頻幀率
'-i', '-',
'-c:v', 'libx264',
'-pix_fmt', 'yuv420p',
'-preset', 'ultrafast',
'-f', 'flv',
rtmpUrl]
def Video():
# 調用相機拍圖的函數
vid = cv2.VideoCapture(0)
if not vid.isOpened():
raise IOError("Couldn't open webcam or video")
while (vid.isOpened()):
return_value, frame = vid.read()
# 原始圖片推入隊列中
frame_queue.put(frame)
def push_frame():
# 推流函數
accum_time = 0
curr_fps = 0
fps = "FPS: ??"
prev_time = time()
# 防止多線程時 command 未被設置
while True:
if len(command) > 0:
# 管道配置,其中用到管道
p = sp.Popen(command, stdin=sp.PIPE)
break
while True:
if frame_queue.empty() != True:
#從隊列中取出圖片
frame = frame_queue.get()
#curr_time = timer()
#exec_time = curr_time - prev_time
#prev_time = curr_time
#accum_time = accum_time + exec_time
#curr_fps = curr_fps + 1
# process frame
# 你處理圖片的代碼
# 將圖片從隊列中取出來做處理,然後再通過管道推送到服務器上
# 增加畫面幀率
#if accum_time > 1:
#accum_time = accum_time - 1
#fps = "FPS: " + str(curr_fps)
#curr_fps = 0
# write to pipe
# 將處理後的圖片通過管道推送到服務器上,image是處理後的圖片
p.stdin.write(image.tostring())
def run():
#使用兩個線程處理
thread1 = Thread(target=Video,)
thread1.start()
thread2 = Thread(target=push_frame,)
thread2.start()
if __name__ == '__main__':
run()
確保自己已經安裝了ffmpeg ,而且ffmpeg已經和nginx配置好。
在處理圖像的時候,最好是將原圖存到隊列中,再從隊列中取出來做處理,之前試過將處理後的圖片存到隊列中,然後直接推送,發現推送的進程佔用了所有的資源,導致處理圖像的進程無法執行。所以順序不對,很容易產生資源佔用的情況。
怎樣查看推流是否成功,可藉助vlc軟件,具體可上網查一下,很多教程。
業餘時候和幾個朋友討論過推流的問題,如果一幀一幀往前端推送,方法比較傻,前端小夥伴估計也不願意,這裏就考慮到代理服務器,服務器類似於一個容器,將圖片以流的形式放到容器中,容器可以做到均衡負載,高訪問量。當然與服務器的通信協議
要以UDP的形式,不容易丟包,ffmpeg內部就封裝好了UDP協議,不需要自己額外的實現。
以上的代碼實現參考:這篇博客
以上的內容如有錯誤,歡迎指正,不吝賜教。也歡迎各位共同討論交流深度學習。