Python 通過ffmpeg實現實時推流(ubuntu16+ffmpeg+nginx)

最近在做一個校園安防的項目,其中涉及到前端要實時展示監控攝像機的畫面,其中畫面要求是經過神經網絡處理過的畫面。

如果前端只要求展示原始畫面,只需要在接入攝像機的時候,把視頻流推送到一個服務器地址上,前端可根據地址獲取視頻流,

本次開發,前端藉助的是一個視頻流插件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協議,不需要自己額外的實現。

以上的代碼實現參考:這篇博客

以上的內容如有錯誤,歡迎指正,不吝賜教。也歡迎各位共同討論交流深度學習。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章