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

当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\n", __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 }

二、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, "\033[31mWARNING: No H264 VPS\033[0m\n");
16             } else {
17                 *size += SizeTmp;
18             }
19             if(!GetSPSNalu(buf + (*size), &SizeTmp) || SizeTmp <= NALU_StartCodeSize) {
20                 fprintf(stderr, "\033[31mWARNING: No SPS\033[0m\n");
21             } else {
22                 *size += SizeTmp;
23             }
24             if(!GetPPSNalu(buf + (*size), &SizeTmp) || SizeTmp <= NALU_StartCodeSize) {
25                 fprintf(stderr, "\033[31mWARNING: No PPS\033[0m\n");
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\n", 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\n", 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, "\033[31mWARNING: NALU truncated because larger than buffer: %u(NALU size) > %u(Buffer size)\033[0m\n", *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 }

三、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, "\033[31mWARNING: NALU truncated because larger than buffer: %u(NALU size) > %u(Buffer size)\033[0m\n", *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 }

时间: 2024-10-26 10:42:01

一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient)——实现篇:(五)用户接口层之提取媒体流数据的相关文章

一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient)——实现篇:(一)概览

myRTSPClient主要可以分成3个部分: 1. RTSPClient用户接口层: 2. RTP 音视频传输解析层: 3. RTP传输层. "RTSPClient用户接口层": 包含所有myRTSPClient的外显特性,包括与RTSP服务器交互接口和音视频数据提取接口,直接供用户使用. "RTP音视频传输解析层": 用于区分不同音视频格式(H264,H265,MPA等等)RTP数据包,分离其中的音视频数据和格式数据."RTSPClient用户接口层&

一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient)——实现篇:(六)RTP音视频传输解析层之音视频数据传输格式

一.差异 本地音视频数据格式和用来传输的音视频数据格式存在些许差异,由于音视频数据流到达客户端时,需要考虑数据流的数据边界.分包.组包顺序等问题,所以传输中的音视频数据往往会多一些字节. 举个例子,有时候一个媒体分包数据量很大(比如H264的一个分包常常会有2-4K),而大多数网络的MTU(最大传输单元)基本都是1500字节. 如果频繁收发这么大的数据包,会额外增添路由器的负担,甚至会导致网络阻塞,不利于网络的稳定. 于是服务器就自行对H264进行了分包以适应MTU,每个分包的开始处往往会多出一

一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient)——实现篇:(二)用户接口层之RtspClient类及其构造函数

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

一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient)——实现篇:(三)用户接口层之RTSP命令

截至版本1.2.3,myRtspClient函数库共支持以下6个RTSP命令: (1)OPTIONS (2)DESCRIBE (3)SETUP (4)PLAY (5)PAUSE (6)TEARDOWN 对应的接口函数都以"Do"开头,如"DoOPTIONS".各个接口函数写法相似,大同小异,差异部分会在后续章节做说明,现以DoOPTIONS()和DoPLAY()举例. 一.ErrorType RtspClient::DoOPTIONS(string uri) 1 E

一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient)——实现篇:(九)以g711-mulaw为例添加新的编码格式解析支持

一.myRtspClient音频解析架构 AudioTypeBase是处理解析各种编码的音频数据的接口类.处理MPA数据的MPEG_Audio类和处理g711-mulaw的PCMU_Audio类均从AudioTypeBase继承而来.AudioTypeBase最重要的接口为CopyData,它的作用就是将RTP接收到的实时数据(data)存放到用户的缓存区(buf),每当接收到1包新的RTP数据时,该函数就会得到调用. 二.myRtspClient视频解析架构 NALUTypeBase是处理解析

一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient):(二)示例

一.搭建RTSP服务器 要想测试RTSP客户端,没有服务端怎么行呢?然而,有时候条件有限,手头并没有独立的RTSP服务器拿来用,那么我们不妨自己撘一个. 以下有2种方便的做法可供选择: 第一种:使用vlc播放器(推荐做法) 这种方法最方便,而且任何视频格式的文件都可以拿来测试,具体做法网上有很多,以下提供一个链接以供参考. http://blog.csdn.net/beitiandijun/article/details/9232405 这种方法只有一个地方需要注意一下: 如果你想让你的vlc

一个基于Android系统的新闻客户端(一)

一.整体概述 在服务器端,通过对凤凰网的抓取存入数据库,客户端通过向服务器发送请求得到新闻. 服务端用WCF,宿主为window服务,客户端为Java写的安卓程序. 二.客户端 我在eclipse里新建了个Android项目,命名为MyNewClient,eclipse自动生成二个xml布局文件,如图: 其中,fragment_main.xml是新版的布局文件,暂时不会用,把它删掉. 新建xml文件,命名为activity_foot.xml,在这里我们要做app的底部,先上代码: <?xml v

RestExpress 一个基于Netty的轻量级Rest服务开发框架

RestExpress 原文的定义是这么说的: RestExpress is the easiest way to create RESTful web services in Java. An extremely Lightweight, Fast, REST Engine and API for Java. Supports JSON and XML serialization automagically as well as ISO 8601 date formats. A thin wr

基于XMPP的IOS聊天客户端程序(XMPP服务器架构)

最近看了关于XMPP的框架,以文本聊天为例,需要发送的消息为: <message type="chat" from="[email protected]" to="[email protected]">          <body>helloWord</body>      </message> 基中from是从哪个用户发送的消息,to是发给谁的消息,XMPP的用户都是以邮箱形式.body就是我们