RTSP协议简介
RTSP全称实时流协议(Real Time Streaming Protocol),它是一个网络控制协议,设计用于娱乐、会议系统中控制流媒体服务器。
RTSP(Real-Time Stream Protocol)是一种基于文本的应用层协议,在语法及一些消息参数等方面,RTSP协议与HTTP协议类似。是TCP/IP协议体系中的一个应用层协议, 由哥伦比亚大学, 网景和RealNetworks公司提交的IETF RFC标准.
该协议定义了一对多应用程序如何有效地通过IP网络传送多媒体数据. RTSP在体系结构上位于RTP和RTCP之上, 它使用TCP或RTP完成数据传输. RTSP被用于建立的控制媒体流的传输,它为多媒体服务扮演“网络远程控制”的角色。尽管有时可以把RTSP控制信息和媒体数据流交织在一起传送,但一般情况RTSP本身并不用于转送媒体流数据。媒体数据的传送可通过RTP/RTCP等协议来完成。
基于Opencv的RTSP实时流处理
多线程保存数据流
import cv2
import os
from multiprocessing import Process, Manager
from multiprocessing.pool import ThreadPool
# Writing data to a shared buffer stack:
def write(stack, cam, frame_id) -> None:
"""
:param stack: Manager.list object
:param cam: Camera parameter
:param frame_id: Shared id index
:return: None
"""
pid = os.getpid()
print('Process to write: %s' % pid)
cap = cv2.VideoCapture(cam)
while True:
flag, img = cap.read()
frame_id_now = cap.get(cv2.CAP_PROP_POS_FRAMES)
# add frame check, avoid reverse the frame order
if flag and frame_id_now > frame_id.value:
frame_id.value = frame_id_now
stack.append(img)
top = 5
# Reading frame data and handle them
def read(stack) -> None:
"""
:param stack: Manager.list object
:return: None
"""
print('Process to read: %s' % os.getpid())
# Dead loops
while True:
# Start frame consumption
if len(stack) != 0:
img = stack.pop(0)
''' Process below '''
######### Example ##########
cv2.imshow('frame', img)
cv2.waitKey(1)
############################
''' End process'''
# if stack length exceeds upper bound
if len(stack) >= top:
print(" !!!!!!Delete!!!!!! ")
del stack[:len(stack) - top]
gc.collect()
if __name__ == '__main__':
hostIP = '192.168.xxx.xxx'
stack = Manager().list()
frame_id = Manager().Value(ctypes.c_int, 0)
# start buffering
for i in range(3):
pw = Process(target=write, args=(stack, "rtsp://%s/chn" % hostIP, frame_id))
pw.start()
time.sleep(0.5)
# read and process here
read(stack)
主函数
主函数的逻辑如下:
- 生成两个通信量,包括保存堆栈和帧校验索引。
- 开启3(多)个独立线程,用来缓冲帧数据。
- 开启1个主线程,用来处理堆栈内数据。
帧保存write
write函数主要被独立线程调用,保存rtsp推上来的视频流数据至内存中,以便read函数消耗。
这里用多少个线程进行保存取决于视频流大小。
函数内增加了一个通信量帧校验,用来判断是否发生写入帧数据颠倒,避免图像闪烁卡顿。
帧消耗read
帧消耗函数直接由主进程进入,设置死循环一直读取帧数据并进行处理,可以在Process部分里面添加代码。
循环最后的是栈内存检测,避免栈内数据过大溢出,一旦栈长度超过设定值则删去未消耗部分最前面的数据。
这里需要提到的一点是,如果需要read内执行阻塞性质的代码时,可以单独开线程进行执行,避免打断read的执行,例如matplotlib绘图,可以这么写:
# process
draw_results = Manager().list()
pw = Process(target=draw_fig, args=(filename, draw_results))
pw.start()
if len(draw_results) > 0:
fig_res = draw_results[-1]
del draw_results[:]
然后draw_fig
函数这么写,如果需要直接显示可以在函数里输出,也可以转换成numpy存入draw_results由主线程调用:
def draw_fig(pt, filename, draw_results, width=12.8, height=7.2, dpi = 100):
fig = plt.figure(figsize=(width, height))
y = pt[2]
y_GT = pt[1]
x = pt[0]
plt.plot(x, y, label="Reality Result")
plt.scatter(x, y)
plt.plot(x, y_GT, label="Ground Truth")
plt.legend()
plt.xlim(-20, 256)
plt.ylim(-300, 200)
plt.xticks(np.arange(-20, 275, 10))
plt.yticks(np.arange(-300, 200, 20))
plt.xlabel("Zoom-table position")
plt.ylabel("Focus position")
plt.grid(True)
plt.tight_layout()
if True:
# show directly
plt.show()
else:
# convert figure as numpy
fig.canvas.draw()
fig_str = fig.canvas.tostring_rgb()
data = np.frombuffer(fig_str, dtype=np.uint8).reshape((int(height * dpi), -1, 3))
data = data[:, :, ::-1]
draw_results.append(data)
plt.savefig(filename +'.png')
plt.close(fig)