当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的最大值。

if(it->second.MediaType == "video") return GetVideoData(&(it->second), buf, size, max_size);
if(it->second.MediaType == "audio") return GetAudioData(&(it->second), buf, size, max_size);

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

 uint8_t * RtspClient::GetMediaData(string media_type, uint8_t * buf, size_t * size, size_t max_size)
{
MyRegex Regex;
map<string, MediaSession>::iterator it;
bool IgnoreCase = true;
if(!buf) return NULL;
if(!size) return NULL; *size = ; for(it = MediaSessionMap->begin(); it != MediaSessionMap->end(); it++) {
if(Regex.Regex(it->first.c_str(), media_type.c_str(), IgnoreCase)) break;
} if(it == MediaSessionMap->end()) {
fprintf(stderr, "%s: No such media session\n", __func__);
return NULL;
} if(it->second.MediaType == "video") return GetVideoData(&(it->second), buf, size, max_size);
if(it->second.MediaType == "audio") return GetAudioData(&(it->second), buf, size, max_size);
return NULL;
}

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

if(true == get_vps_sps_pps_periodly) {
if(GetVideoDataCount >= GetSPS_PPS_Period) {
GetVideoDataCount = 0;

......

}

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

if(media_session->EncodeType == "H264") {
NALUTypeBaseTmp = &NaluBaseType_H264Obj;
} else if (media_session->EncodeType == "H265") {
NALUTypeBaseTmp = &NaluBaseType_H265Obj;
} else {
// Unknown Nalu type
printf("Unsupported codec type: %s\n", media_session->EncodeType.c_str());
return NULL;
}

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

do {
EndFlag = true;

......

EndFlag = NALUType->GetEndFlag();
} while(!EndFlag);

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

 uint8_t * RtspClient::GetVideoData(MediaSession * media_session, uint8_t * buf, size_t * size, size_t max_size, bool get_vps_sps_pps_periodly)
{
if(!media_session || !buf || !size) return NULL; *size = ; const size_t GetSPS_PPS_Period = GET_SPS_PPS_PERIOD; // 30 times
if(true == get_vps_sps_pps_periodly) {
if(GetVideoDataCount >= GetSPS_PPS_Period) {
GetVideoDataCount = ; const size_t NALU_StartCodeSize = ;
size_t SizeTmp = ;
if(!GetVPSNalu(buf + (*size), &SizeTmp) || SizeTmp <= NALU_StartCodeSize) {
// fprintf(stderr, "\033[31mWARNING: No H264 VPS\033[0m\n");
} else {
*size += SizeTmp;
}
if(!GetSPSNalu(buf + (*size), &SizeTmp) || SizeTmp <= NALU_StartCodeSize) {
fprintf(stderr, "\033[31mWARNING: No SPS\033[0m\n");
} else {
*size += SizeTmp;
}
if(!GetPPSNalu(buf + (*size), &SizeTmp) || SizeTmp <= NALU_StartCodeSize) {
fprintf(stderr, "\033[31mWARNING: No PPS\033[0m\n");
} else {
*size += SizeTmp;
}
return buf;
} else {
GetVideoDataCount++;
}
} size_t SizeTmp = ;
bool EndFlag = false;
NALUTypeBase * NALUTypeBaseTmp = NULL;
NALUTypeBase * NALUType; int PM = media_session->Packetization;
if(!IS_PACKET_MODE_VALID(PM)) {
cerr << "WARNING:Invalid Packetization Mode" << endl;
return NULL;
}
if(media_session->EncodeType == "H264") {
NALUTypeBaseTmp = &NaluBaseType_H264Obj;
} else if (media_session->EncodeType == "H265") {
NALUTypeBaseTmp = &NaluBaseType_H265Obj;
} else {
// Unknown Nalu type
printf("Unsupported codec type: %s\n", media_session->EncodeType.c_str());
return NULL;
} do {
EndFlag = true;
if(!media_session->GetMediaData(VideoBuffer.Buf, &SizeTmp)) return NULL;
if( == SizeTmp) {
cerr << "No RTP data" << endl;
return NULL;
}
int NT;
NT = NALUTypeBaseTmp->ParseNALUHeader_Type(VideoBuffer.Buf);
NALUType = NALUTypeBaseTmp->GetNaluRtpType(PM, NT);
if(NULL == NALUType) {
printf("Unknown NALU Type: %s\n", media_session->EncodeType.c_str());
return NULL;
} if(SizeTmp > VideoBuffer.Size) {
cerr << "Error: RTP Packet too large(" << SizeTmp << " bytes > " << VideoBuffer.Size << "bytes)" << endl;
return NULL;
} if(*size + SizeTmp > max_size) {
fprintf(stderr, "\033[31mWARNING: NALU truncated because larger than buffer: %u(NALU size) > %u(Buffer size)\033[0m\n", *size + SizeTmp, max_size);
return buf;
} SizeTmp = NALUType->CopyData(buf + (*size), VideoBuffer.Buf, SizeTmp);
*size += SizeTmp;
EndFlag = NALUType->GetEndFlag();
} while(!EndFlag); return buf;
}

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

该函数和GetVideoData的思路一样:

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

 uint8_t * RtspClient::GetAudioData(MediaSession * media_session, uint8_t * buf, size_t * size, size_t max_size)
{
if(!media_session || !buf || !size) return NULL; *size = ; size_t SizeTmp = ;
MPEGTypeBase * MPEGType; if(!media_session->GetMediaData(AudioBuffer.Buf, &SizeTmp)) return NULL;
if( == SizeTmp) {
cerr << "No RTP data" << endl;
return NULL;
} MPEGType = &MPEG_AudioObj; if(SizeTmp > AudioBuffer.Size) {
cerr << "Error: RTP Packet too large(" << SizeTmp << " bytes > " << AudioBuffer.Size << "bytes)" << endl;
return NULL;
} if(*size + SizeTmp > max_size) {
fprintf(stderr, "\033[31mWARNING: NALU truncated because larger than buffer: %u(NALU size) > %u(Buffer size)\033[0m\n", *size + SizeTmp, max_size);
return buf;
} SizeTmp = MPEGType->CopyData(buf + (*size), AudioBuffer.Buf, SizeTmp);
*size += SizeTmp; return buf;
}

上一篇                       回目录                    下一篇

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

  1. 一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient)——实现篇:(四)用户接口层之处理SDP报文

    当RTSP客户端向RTSP服务端发送DESCRIBE命令时,服务端理应当回复一条SDP报文. 该SDP报文中包含RTSP服务端的基本信息.所能提供的音视频媒体类型以及相应的负载能力,以下是一段SDP示 ...

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

    myRTSPClient主要可以分成3个部分: 1. RTSPClient用户接口层: 2. RTP 音视频传输解析层: 3. RTP传输层. "RTSPClient用户接口层": ...

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

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

  4. 一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient)——实现篇:(十)使用JRTPLIB传输RTP数据

    myRtspClient通过简单修改JRTPLIB的官方例程作为其RTP传输层实现.因为JRTPLIB使用的是CMAKE编译工具,这就是为什么编译myRtspClient时需要预装CMAKE. 该部分 ...

  5. 一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient)——解码篇:(一)用ffmpeg解码视频

    一.概述 myRTSPClient(RTSPClient)获取音视频数据之后,接下来的工作便是将音视频数据交给解码器去解码(ffmpeg),ffmpeg解码之后于是便有了呈现在终端用户(USER)面前 ...

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

    RtspClient类是myRTSPClient函数库所有特性集中实现的地方. 主要为用户提供: 1. RTSP协议通信接口函数,如DoOPTIONS(): 2. RTSP账号.密码设置函数,如Set ...

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

    截至版本1.2.3,myRtspClient函数库共支持以下6个RTSP命令: (1)OPTIONS (2)DESCRIBE (3)SETUP (4)PLAY (5)PAUSE (6)TEARDOWN ...

  8. 一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient)——解码篇:(三)一个简单的rtsp播放器

    该篇内容简单的将前两篇内容组合在一起,创建了2个线程,分别播放音频和视频. int main(int argc, char * argv[]) { RtspClient Client; pthread ...

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

    一.MPEG RTP音频传输 相较H264的RTP传输格式,MPEGE音频传输格式则简单许多. 每一包MPEG音频RTP包都前缀一个4字节的Header,如下图(RFC2550) “MBZ”必须为0( ...

随机推荐

  1. 集合用法笔记-Map用法

    一.Map遍历 Map<String, String> map = new HashMap<String, String>(); map.put("1", ...

  2. spring boot controller路由 url 扫描不到问题

    spring boot项目出现controller的路由没被注册,原因:启动类application跟controller不在一个包中,扫描不到controller, 如启动类在com.oyx.a,c ...

  3. nodejs 全局变量和全局对象

    1.全局对象 所有模块都可以调用 1)global:表示Node所在的全局环境,类似于浏览器中的window对象. 2)process:指向Node内置的process模块,允许开发者与当前进程互动. ...

  4. 个人作业2--英语学习APP案例分析

    1.下载APP并使用,上手体验 个人很喜欢这种风格,画面简洁,排版精细,尤其是联想词的界面,很惊喜.但是很多链接比如精选文章点进去之后的UI设计并不理想,感觉只是一个网页而已.并且我不能够保存或者收藏 ...

  5. linux执行sh报错:$’\r’: 未找到命令的解决

    背景 执行.sh脚本时出现$'\r': 未找到命令, 原因 是因为命令直接从windows 复制过来导致的 解决 yum install dos2unix dos2unix **.sh 进行转换 再次 ...

  6. c# 多线程 创建对象实例

    本次的标题是我在写单例模式的博客时遇到的问题,所以今天专门写了的demo让自己记住怎么简单的使用多线程. 一直纠结的是怎么在for循环中多次实例化对象,好复现单例模式在没有加锁的情况下出现多个实例对象 ...

  7. Spring源码情操陶冶-AbstractApplicationContext#finishRefresh

    承接前文Spring源码情操陶冶-AbstractApplicationContext#finishBeanFactoryInitialization 约定web.xml配置的contextClass ...

  8. 设计模式-策略模式Strategy以及消灭if else

    概述 如果在开发过程中,出现大量的if else或者switch case 语句,如果这些语句块中的代码并不是包含业务逻辑,只是单纯的分流方法,那么,每一个语句块中都是一个算法或者叫策略. 背景 比如 ...

  9. Mybatis Dynamic Query 框架整合

    项目地址:https://github.com/wz2cool/mybatis-dynamic-query 文档地址:https://wz2cool.gitbooks.io/mybatis-dynam ...

  10. 如何让Oracle释放undo表空间

    如何让Oracle释放undo表空间   最佳答案   在日常的数据库维护和数据库编程中经常会遇到犹豫对大数据量做DML操作后是得ORACLE的undo表空间扩展到十几个G或者几十个G 但是这些表空间 ...