思路:
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 能力也不是很高 欢迎和大家一起讨论 但是有的是真不会呀 还请见谅