一个基于jrtplib的轻量级rtsp客户端(myrtspclient)——实现篇:用户接口层之提取媒体流数据

author author     2022-09-16     148

关键词:

当RTSP客户端向RTSP服务端发送完PLAY命令后,RTSP服务端就会另外开启UDP端口(SDP协商定义的端口)发送RTP媒体流数据包。这些数据包之间会间隔一段时间(毫秒级)陆续被发送到RTSP客户端,此时RTSP客户端可以调用GetMediaData等接口获取媒体流数据。

 

一、uint8_t * RtspClient::GetMediaData(string media_type, uint8_t * buf, size_t * size, size_t max_size)

该函数的作用即获取媒体流数据,并将数据放入参数buf中,数据大小放入size中,media_type可以为字符串“audio”或“video”,max_size为buf的最大值。

该函数首先在MediaSessionMap中查询匹配media_type的媒体会话,然后选择调用GetVideoData或GetAudioData。

 

技术分享
 1 uint8_t * RtspClient::GetMediaData(string media_type, uint8_t * buf, size_t * size, size_t max_size) 
 2 {
 3         MyRegex Regex;
 4         map<string, MediaSession>::iterator it;
 5         bool IgnoreCase = true;
 6         if(!buf) return NULL;
 7         if(!size) return NULL;
 8 
 9         *size = 0; 
10 
11         for(it = MediaSessionMap->begin(); it != MediaSessionMap->end(); it++) {
12                 if(Regex.Regex(it->first.c_str(), media_type.c_str(), IgnoreCase)) break;
13         }
14 
15         if(it == MediaSessionMap->end()) {
16                 fprintf(stderr, "%s: No such media session
", __func__);
17                 return NULL;
18         }
19 
20         if(it->second.MediaType == "video") return GetVideoData(&(it->second), buf, size, max_size);
21         if(it->second.MediaType == "audio") return GetAudioData(&(it->second), buf, size, max_size);
22         return NULL;
23 }
View Code

 

 

二、uint8_t * RtspClient::GetVideoData(MediaSession * media_session, uint8_t * buf, size_t * size, size_t max_size, bool get_vps_sps_pps_periodly)

参数get_vps_sps_pps_periodly用来指示是否要周期性写入VPS、SPS和PPS(h264/h265中参数),默认为true。周期写入的目的是为了防止视频传输一开始将这些关键参数丢失,虽然会多耗费一点带宽,但是相对视频数据本身可谓九牛一毛,重要的是这样可以用来防止由于这些参数丢失所导致的诸如花屏之类的问题。

然后根据media_session中的编码类型,获取RTP音视频传输解析层中的特定类对象(NaluBaseType_H264Obj、NaluBaseType_H265Obj)来处理视频数据。

接着是循环获取视频帧的NALU单元,由于一个NALU单元常常会超过一个MTU(最大传输单元),所以NALU单元就会被分包,最后一个分包会带一个结束标志,如果获得了NALU单元的最后一个分包,则跳出该循环并从函数中返回。

技术分享
 1 uint8_t * RtspClient::GetVideoData(MediaSession * media_session, uint8_t * buf, size_t * size, size_t max_size, bool get_vps_sps_pps_periodly) 
 2 {
 3     if(!media_session || !buf || !size) return NULL;
 4 
 5     *size = 0;
 6 
 7     const size_t GetSPS_PPS_Period = GET_SPS_PPS_PERIOD; // 30 times
 8     if(true == get_vps_sps_pps_periodly) {
 9         if(GetVideoDataCount >= GetSPS_PPS_Period) {
10             GetVideoDataCount = 0;
11 
12             const size_t NALU_StartCodeSize = 4;
13             size_t SizeTmp = 0;
14             if(!GetVPSNalu(buf + (*size), &SizeTmp) || SizeTmp <= NALU_StartCodeSize) {
15                 // fprintf(stderr, "33[31mWARNING: No H264 VPS33[0m
");
16             } else {
17                 *size += SizeTmp;
18             }
19             if(!GetSPSNalu(buf + (*size), &SizeTmp) || SizeTmp <= NALU_StartCodeSize) {
20                 fprintf(stderr, "33[31mWARNING: No SPS33[0m
");
21             } else {
22                 *size += SizeTmp;
23             }
24             if(!GetPPSNalu(buf + (*size), &SizeTmp) || SizeTmp <= NALU_StartCodeSize) {
25                 fprintf(stderr, "33[31mWARNING: No PPS33[0m
");
26             } else {
27                 *size += SizeTmp;
28             }
29             return buf;
30         } else {
31             GetVideoDataCount++;
32         }
33     }
34 
35     size_t SizeTmp = 0;
36     bool EndFlag = false;
37     NALUTypeBase * NALUTypeBaseTmp = NULL;
38     NALUTypeBase * NALUType;
39 
40     int PM = media_session->Packetization;
41     if(!IS_PACKET_MODE_VALID(PM)) {
42         cerr << "WARNING:Invalid Packetization Mode" << endl;
43         return NULL;
44     }
45     if(media_session->EncodeType == "H264") {
46         NALUTypeBaseTmp = &NaluBaseType_H264Obj;
47     } else if (media_session->EncodeType == "H265") {
48         NALUTypeBaseTmp = &NaluBaseType_H265Obj;
49     } else {
50         // Unknown Nalu type
51         printf("Unsupported codec type: %s
", media_session->EncodeType.c_str());
52         return NULL;
53     }
54 
55     do {
56         EndFlag = true;
57         if(!media_session->GetMediaData(VideoBuffer.Buf, &SizeTmp)) return NULL;
58         if(0 == SizeTmp) {
59             cerr << "No RTP data" << endl;
60             return NULL;
61         }
62         int NT; 
63         NT = NALUTypeBaseTmp->ParseNALUHeader_Type(VideoBuffer.Buf);
64         NALUType = NALUTypeBaseTmp->GetNaluRtpType(PM, NT);
65         if(NULL == NALUType) {
66             printf("Unknown NALU Type: %s
", media_session->EncodeType.c_str());
67             return NULL;
68         }
69 
70         if(SizeTmp > VideoBuffer.Size) {
71             cerr << "Error: RTP Packet too large(" << SizeTmp << " bytes > " << VideoBuffer.Size << "bytes)" << endl;
72             return NULL;
73         }
74 
75         if(*size + SizeTmp > max_size) {
76             fprintf(stderr, "33[31mWARNING: NALU truncated because larger than buffer: %u(NALU size) > %u(Buffer size)33[0m
", *size + SizeTmp, max_size);
77             return buf;
78         }
79 
80         SizeTmp = NALUType->CopyData(buf + (*size), VideoBuffer.Buf, SizeTmp);
81         *size += SizeTmp;
82         EndFlag = NALUType->GetEndFlag();
83     } while(!EndFlag);
84 
85     return buf;
86 }
View Code

 

三、uint8_t * RtspClient::GetAudioData(MediaSession * media_session, uint8_t * buf, size_t * size, size_t max_size)

该函数和GetVideoData的思路一样:

先是获取RTP音视频传输解析层中的特定类对象(MPEG_AudioObj),然后再去获取音频数据流。

技术分享
 1 uint8_t * RtspClient::GetAudioData(MediaSession * media_session, uint8_t * buf, size_t * size, size_t max_size)
 2 {
 3         if(!media_session || !buf || !size) return NULL;
 4 
 5         *size = 0;
 6 
 7         size_t SizeTmp = 0;
 8         MPEGTypeBase * MPEGType;
 9 
10         if(!media_session->GetMediaData(AudioBuffer.Buf, &SizeTmp)) return NULL;
11         if(0 == SizeTmp) {
12                 cerr << "No RTP data" << endl;
13                 return NULL;
14         }
15 
16         MPEGType = &MPEG_AudioObj;
17 
18         if(SizeTmp > AudioBuffer.Size) {
19                 cerr << "Error: RTP Packet too large(" << SizeTmp << " bytes > " << AudioBuffer.Size << "bytes)" << endl;
20                 return NULL;
21         }
22 
23         if(*size + SizeTmp > max_size) {
24                 fprintf(stderr, "33[31mWARNING: NALU truncated because larger than buffer: %u(NALU size) > %u(Buffer size)33[0m
", *size + SizeTmp, max_size);
25                 return buf;
26         }
27 
28         SizeTmp = MPEGType->CopyData(buf + (*size), AudioBuffer.Buf, SizeTmp);
29         *size += SizeTmp;
30 
31         return buf;
32 }
View Code

 

一个基于jrtplib的轻量级rtsp客户端(myrtspclient)——实现篇:概览

  myRTSPClient主要可以分成3个部分:1.RTSPClient用户接口层;2.RTP音视频传输解析层;3.RTP传输层。 “RTSPClient用户接口层”:包含所有myRTSPClient的外显特性,包括与RTSP服务器交互接口和音视频数据提取接口,直接供用户... 查看详情

一个基于jrtplib的轻量级rtsp客户端(myrtspclient)——实现篇:用户接口层之提取媒体流数据

当RTSP客户端向RTSP服务端发送完PLAY命令后,RTSP服务端就会另外开启UDP端口(SDP协商定义的端口)发送RTP媒体流数据包。这些数据包之间会间隔一段时间(毫秒级)陆续被发送到RTSP客户端,此时RTSP客户端可以调用GetMediaData等接口... 查看详情

一个基于jrtplib的轻量级rtsp客户端(myrtspclient)——实现篇:用户接口层之rtspclient类及其构造函数

RtspClient类是myRTSPClient函数库所有特性集中实现的地方。主要为用户提供:1. RTSP协议通信接口函数,如DoOPTIONS();2.RTSP账号、密码设置函数,如SetUsername();3.音视频码流接收函数,如GetVideoData()。本篇主要介绍RtspClient的构造函... 查看详情

一个基于jrtplib的轻量级rtsp客户端(myrtspclient)——实现篇:rtp音视频传输解析层之h264传输格式

一、H264传输封包格式的2个概念(1)组包模式(PacketizationModes)RFC3984中定义了3种组包模式:单NALU模式(SingleNalUnitMode)、非交错模式(Non-interleavedMode)和交错模式(InterleavedMode)。“单NALU模式”:NALU封包在传输过程中必须是... 查看详情

一个基于jrtplib的轻量级rtsp客户端(myrtspclient)——实现篇:rtp音视频传输解析层之音视频数据传输格式

 一、差异本地音视频数据格式和用来传输的音视频数据格式存在些许差异,由于音视频数据流到达客户端时,需要考虑数据流的数据边界、分包、组包顺序等问题,所以传输中的音视频数据往往会多一些字节。举个例子,有... 查看详情

一个基于jrtplib的轻量级rtsp客户端(myrtspclient)——实现篇:以g711-mulaw为例添加新的编码格式解析支持(代码片段)

一、myRtspClient音频解析架构AudioTypeBase是处理解析各种编码的音频数据的接口类。处理MPA数据的MPEG_Audio类和处理g711-mulaw的PCMU_Audio类均从AudioTypeBase继承而来。AudioTypeBase最重要的接口为CopyData,它的作用就是将RTP接收到的实时数据... 查看详情

流媒体协议之rtsp客户端的实现20171014

RtspClient是基于jrtplib实现的,目前仅支持h264格式,后续将不断迭代优化,加入对其他格式的支持,并且将实现RTSP的服务端。RtspClient的功能是接收服务端过来流,然后写入到当前目录的MediaReceived.h264文件中,可直接用播放器播放... 查看详情

如何拉取公网rtsp/rtmp流在内网多客户端播放

...关模块。内网RTSP网关模块功能内网RTSP网关模块,系内置轻量级RTSP服务模块扩展,完成外部RTSP/RTMP数据拉取并注入到轻量级RTSP服务模块工作,多个内网客户端直接访问内网轻量级RTSP服务获取公网数据,无需部署单独的服务器,... 查看详情

windows平台unitycamera场景实现轻量级rtsp服务和rtmp推送

...ux、Android、iOS平台。本文主要介绍Windows平台Unity环境下的轻量级RTSP服务。通过对外提供RTSP拉流URL的形式,供内网其他终端调用。RTMP的技术方案,我们之前有探讨过,这里先说轻量级RTSP服务,轻量级RTSP服务,我们的设计是,可... 查看详情

如何基于阿里云搭建适合初创企业的轻量级架构?

前言在项目的初期往往存在很多变数,业务逻辑时刻在变,而且还要保证快速及时,所以,一个灵活多变、快速部署、持续集成并可以适应多种情况的架构便显得尤为重要。本文主要介绍基于阿里云搭建适合项目初期的后端架构... 查看详情

实现一个轻量级高可复用的rabbitmq客户端(代码片段)

前言????本示例通过对服务订阅的封装、隐藏细节实现、统一配置、自动重连、异常处理等各个方面来打造一个简单易用的RabbitMQ工厂;本文适合适合有一定RabbitMQ使用经验的读者阅读,如果你还没有实际使用过RabbitMQ,也没有关... 查看详情

技术分享无纸化会议|智慧教室同屏走rtsp组播还是rtmp?

...智慧教室同屏)技术方案的时候,遇到个问题:到底使用轻量级RTSP服务实现组播,还是基于RTMP的解决方案?先说为什么大家喜欢组播吧:组播技术方案,大家第一件事想到的就是服务器部署和带宽问题,直接教师端或者主讲人... 查看详情

开源轻量级im框架mobileimsdk的uniapp客户端库已发布!

...MSDK-Uniapp端是一套基于Uniapp跨端框架的即时通讯库:1)超轻量级、无任何第3方库依赖(开箱即用);2)纯JS编写、ES6语法、高度提炼,简单易用;3)基于Uniapp标准WebSocket API,简洁优雅;4)理论上可运行于任何支持Uniapp跨端... 查看详情

rtsp协议详解

...迟。    HTTP与RTSP相比,HTTP传送HTML,HTTP请求由客户端发出,服务器做出响应;RTSP传送的是多媒体数据,使用RTSP时,客户端和服务器都可以发出 查看详情

android平台实现mp4文件实时推送rtmp|轻量级rtsp服务|gb28181平台

...MP4文件,以实时流数据的形式,推送到RTMP服务器,注入轻量级RTSP服务,或者对接到GB28181平台,这块前几年我们就有对接。本次以MediaExtractor为例,先利用MediaExtractor,把mp4文件的音视频数据分离,然后调用我们publisher模块,实... 查看详情

rtsp,java实现简单的rtsp报文交换(代码片段)

...va实现简单的RTSP报文交换了解RTSP协议使用Java程序编写RTSP客户端访问RTSP服务端,实现拉流RTSP协议是什么RTSP是一种基于文本的协议,用CRLF(回车换行)作为每一行的结束符,其好处是,在使用过程中可以方便地增加... 查看详情

guacamole搭建,一个基于html5的远程桌面(代码片段)

...:VNC,RDP,Telnet,SSH)官网:http://guac-dev.org/其由许多部件组成的轻量级web应用程序,大部分的功能依靠Guacamole的底层组件>来完成。用户通过浏览器连接到Guacamole的服务端。Guacamole的客户端是用JavaScript编写的,Guacamoles 查看详情

如何同时启动android平台gb28181设备接入模块和轻量级rtsp服务模块?

技术背景在介绍GB28181设备接入模块和轻量级RTSP服务之前,我们需要先搞清楚,二者的使用场景和技术设计的差别:首先是GB28181设备接入模块:为什么要设计GB28181设备接入模块?GB28181接入SDK,实现不具备国标音视频能力的Android... 查看详情