最近在做一个校园安防的项目,其中涉及到前端要实时展示监控摄像机的画面,其中画面要求是经过神经网络处理过的画面。
如果前端只要求展示原始画面,只需要在接入摄像机的时候,把视频流推送到一个服务器地址上,前端可根据地址获取视频流,
本次开发,前端借助的是一个视频流插件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协议,不需要自己额外的实现。
以上的代码实现参考:这篇博客
以上的内容如有错误,欢迎指正,不吝赐教。也欢迎各位共同讨论交流深度学习。