赞
踩
流媒体协议,英文学名Streaming Protocol,用一句人话来解释:流媒体协议是一种用于通过 Web 传递多媒体的协议。传统视频流协议:RTMP和RTSP,其中 RTMP 是基于 TCP 开发的,那么 RTSP 使用到了 UDP 。
RTMP 的最大优点是可以在服务器和客户端服务器之间保持稳定的连接,无论用户的互联网连接质量如何,它都可以无缝低延迟进行流媒体传输。这个技术主要通过将数据流分成相等的小部分(音频数据默认为 64 字节,视频数据默认为 128 字节)并将它们顺序传输到接收设备,然后将它们重新组合成视频流来实现的。
RTMP 工作原理 ⭐
那么这个环节中RTMP就起到了非常重要的作用,在视频从摄像头到服务器的过程中,RTMP将大量数据分割成小块并跨多个虚拟通道传输,在视频源和 RTMP 服务器之间提供了稳定和流畅的视频流。
RTSP 工作原理 ⭐
由于 RTSP 依赖于专用服务器,并且依赖于 RTP,因此该协议不支持加密视频内容或重传丢失的数据包。
推流
,指的是把采集阶段封包好的内容传输到服务器的过程。
拉流
,这个指的是用户端从服务器拉取语音视频流到客户端播放。
更详细内容见:手撕RTSP协议系列
FFmpeg 是一款用于多媒体处理的自由软件工具,基于 GPL 许可证发布。FFmpeg 提供的最核心的命令行工具是 “ffmpeg”,“ffmpeg” 命令行工具的主要特征是输出过程快、输出品质高、输出文件小。“FFmpeg” 中 “FF” 表示 “Fast Forward”,“mpeg” 表示 “Moving Pictures Experts Group”。
FFmpeg 提供如下四个命令行工具:
命令行实现:(和VLC工具等效)
ffmpeg推流rtsp
ffmpeg -re -i 1.mp4 -vcodec copy -codec copy -f rtsp rtsp://127.0.0.1:554/stream
推摄像头视频
ffmpeg -f dshow -i video="Integrated Camera" -vcodec libx264 -preset:v ultrafast -tune:v zerolatency -rtsp_transport udp -f rtsp rtsp://127.0.0.1/stream
ffplay拉流播放
ffplay rtsp://127.0.0.1:554/stream
测试使用的几个rtmp:
美国1: rtmp://ns8.indexforce.com/home/mystream
美国2: rtmp://media3.scctv.net/live/scctv_800
韩国GoodTV: rtmp://mobliestream.c3tv.com:554/live/goodtv.sdp
调用FFmpeg建立了子进程(管道一个放共享文件的地方),并将RTSP流转化为RTMP流。然后我们对每一帧图像进行处理(使用AI算法)。最后推流代码将处理后的视频帧传递给FFmpeg子进程,从而实现推流。
# coding=gbk
# 本地摄像头推流
import cv2
import mediapipe as mp
import numpy as np
import subprocess as sp
mp_drawing = mp.solutions.drawing_utils
mp_drawing_styles = mp.solutions.drawing_styles
mp_holistic = mp.solutions.holistic
mp_pose = mp.solutions.pose
# 调用ai算法进行帧处理并返回(这里使用Mediapipe骨骼点检测算法,读者可自行更改)
def frame_handler(image):
with mp_holistic.Holistic(
min_detection_confidence=0.5,
min_tracking_confidence=0.5) as holistic:
image.flags.writeable = False
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
results = holistic.process(image_rgb)
if results.pose_world_landmarks is not None:
# 判断是否捕获到人体
coords = np.array(results.pose_world_landmarks.landmark)
# 汇总所有点的XYZ坐标
def get_x(each):
return each.x
def get_y(each):
return each.y
def get_z(each):
return each.z
# 分别获取关键点XYZ坐标
points_x = np.array(list(map(get_x, coords)))
points_y = np.array(list(map(get_y, coords)))
points_z = np.array(list(map(get_z, coords)))
# 将三个方向坐标合并
points = np.vstack((points_x, points_y, points_z)).T
# 画图
image.flags.writeable = True
# 在关节点渲染
mp_drawing.draw_landmarks(
image,
results.pose_landmarks,
mp_holistic.POSE_CONNECTIONS,
landmark_drawing_spec=mp_drawing_styles.get_default_pose_landmarks_style())
image = cv2.flip(image, 1)
return image
# 请读者自行修改url
# RTSP源地址src, 指定从哪里拉取视频流,测试时可以用VLC工具进行rtsp推流
src = "rtsp://:8554/"
# RTMP推流地址dst, 指定 用opencv把各种处理后的流(视频帧)推到哪里
dst = "rtmp://192.168.0.1:1935/live/test"
# 打开RTSP流,也可以用0,调用本地视频流,并取出视频流的帧率、帧宽、帧高
cap = cv2.VideoCapture(src)
fps = int(cap.get(cv2.CAP_PROP_FPS))
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# ffmpeg command 保存进程参数
command = ['ffmpeg',
'-y',
'rawvideo',
'-vcodec', 'rawvideo',
'-pix_fmt', 'bgr24',
'-s', "{}x{}".format(width, height),
'-r', str(fps),
'-c:v', 'libx264',
'-pix_fmt', 'yuv420p',
'-preset', 'ultrafast',
'-f', 'flv',
'-g', '5',
dst]
# 获取视频流的基本信息
fps = int(cap.get(cv2.CAP_PROP_FPS))
size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),
int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
# 建立子进程(配置管道)
pipe = sp.Popen(command, stdin=sp.PIPE)
# 循环读取视频流
while True:
ret, frame = cap.read() # 从视频流中获取一帧
if not ret:
raise IOError("could't open webcamera or video")
# 处理代码(使用AI算法)
frame_handler(frame)
cv2.imshow('Video', frame) # 显示处理结果
# 推流代码
# pipe.stdin.write(frame.tostring())
# 按下q键退出
if cv2.waitKey(1) == ord('q'):
break
# 释放视频流
cap.release()
# 关闭窗口
cv2.destroyAllWindows()
# 关闭进程
pipe.stdin.close()
pipe.wait()
VLC推流 Python拉流处理后 显示效果:
细节解析:
command
这个参数列表是用于在Python脚本中控制FFmpeg进程的一些参数。具体来说,这里我们使用FFmpeg进程来将视频流从本地相机或者RTSP流中读取出来并推流到RTMP服务器,这些参数的作用可以分为以下几个部分:
音频和视频格式
-y 表示覆盖输出文件。在这里,我们没有使用输出文件,所以这个参数的作用是防止在执行过程中出现询问是否覆盖的问题。
rawvideo 表示输出文件格式为raw video,即无压缩的视频数据。
-vcodec 表示视频编码器,这里我们使用的是raw video编码器。
-pix_fmt 表示视频像素格式,这里我们使用bgr24格式,即每个像素由三个字节组成,分别表示蓝、绿、红三个颜色通道的亮度值。
视频尺寸、帧率和编码
-s 表示视频尺寸,这里使用字符串格式化将 width 和 height 代入到尺寸参数中。
-r 表示视频帧率,即每秒钟显示的帧数。
-c:v 表示视频编码器类型,这里我们使用的是libx264编码器。
-pix_fmt 表示视频像素格式。由于使用了libx264编码器,这里我们使用yuv420p格式,即420 Planar格式,每个像素需要包含YUV三个颜色通道的信息。
-preset 表示编码速度的快慢,这里我们使用的是ultrafast模式。ultrafast模式下编码速度最快,但是视频质量也最差;如果需要更高的视频质量,则可以使用更高级别的preset模式,但是编码速度也会相应降低。
推流协议和参数
-f 表示输出文件格式,这里我们使用的是flv格式。
-g 表示I帧间隔数,即每隔多少帧产生一个I帧。这里我们使用的是5,表示每隔5个帧产生一个I帧,可以提高视频帧率和压缩比。
RTMP服务器地址
最后一个参数 dst 是需要推送到的RTMP服务器地址。
这些参数的原理和作用可以互相配合着理解,主要是控制FFmpeg进程进行视频录制或推流操作的各种参数。通过设置不同的参数,可以实现不同的录制或推流效果,同时也可以控制视频质量、编码速度、带宽占用率等方面的性能指标,以便满足不同应用场景的需求。
pipe = sp.Popen(command, stdin=sp.PIPE)
使用了Python的subprocess模块中的Popen函数来创建一个子进程,并建立了一个管道。具体来说,它执行了以下操作:
通过调用subprocess.Popen()函数创建了一个子进程,该函数的第一个参数是一个命令字符串或一个包含命令及其参数的列表,表示要在子进程中执行的命令。(本文传入的是包含ffmpeg命令的参数列表)
通过指定stdin参数为sp.PIPE,将子进程的标准输入(stdin)与管道连接起来,这样就可以向子进程发送数据。
返回一个Popen对象,该对象代表了创建的子进程,并可以用它来与子进程进行交互。
总的来说,这段代码的作用是创建一个子进程,并建立了一个管道,使得主进程可以使用管道的stdin向子进程发送数据。这在很多场景下都是非常有用的,比如在父子进程之间进行通信、在主进程中启动一个后台进程等。
pipe.stdin.write(frame.tostring())
是通过管道向子进程输入视频帧数据的方法。具体来说,该操作会将当前视频帧的二进制数据转换为字符串,然后通过管道将该字符串写入到FFmpeg子进程的标准输入(stdin)中。把处理后的图片放入管道, 让ffmpeg读取处理后的图像帧并进行rtmp推流即可。
在FFmpeg子进程启动后,会通过管道从标准输入(stdin)中读取数据并进行处理。在这个过程中,FFmpeg会根据指定的参数对视频进行编码压缩,最终生成一个压缩后的视频流;然后将该视频流通过网络协议推送到指定的RTMP服务器上。
因此,pipe.stdin.write(frame.tostring()) 的原理就是通过管道将视频帧数据传递给FFmpeg进程,并将其转化为RTMP格式进行推流。这是一种常见的视频流推流技术,可以用于实现视频直播等应用场景。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。