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等协议来完成。

java使用OpenCV处理rtsp流 rtsp opencv_java使用OpenCV处理rtsp流

基于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)

主函数

主函数的逻辑如下:

  1. 生成两个通信量,包括保存堆栈和帧校验索引。
  2. 开启3(多)个独立线程,用来缓冲帧数据。
  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)