当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. 多元线性回归模型的特征压缩:岭回归和Lasso回归

    多元线性回归模型中,如果所有特征一起上,容易造成过拟合使测试数据误差方差过大:因此减少不必要的特征,简化模型是减小方差的一个重要步骤.除了直接对特征筛选,来也可以进行特征压缩,减少某些不重要的特征系数 ...

  2. “玲珑杯”ACM比赛 Round #18--最后你还是AK了(搜索+思维)

    题目链接   DESCRIPTION INPUT OUTPUT SAMPLE INPUT 1 4 2 1 2 5 2 3 5 3 4 5 5 5 SAMPLE OUTPUT 35 HINT 对于样例, ...

  3. Iterator、for..of,for...in和自定义遍历器**

    Iterator.for..of,for...in和自定义遍历器 Iterator: var arr = [1,2,3,4,5]; var ite = arr[Symbol.iterator](); ...

  4. 关于"模块计算机类型与目标计算机类型冲突"的解决

    问题描述:我的64位工程包含32位静态库之后报错(模块计算机类型"x86"与目标计算机类型"x64"冲突),将工程修改为32位之后,又报错(若干个无法解析的外部 ...

  5. oracle高级查询(实例基于scott用户四张表)

    oracle高级查询(实例基于scott用户四张表) 分组查询 多表查询 子查询 综合实例 ====================================================== ...

  6. (转).tar.gz文件和.rpm文件的区别

    场景:在Linux环境下安装软件时候总是会遇到安装软件格式的选择,以及安装. 1 软件的二进制分发 Linux软件的二进制分发是指事先已经编译好二进制形式的软件包的发布形式, 其优点是安装使用容易,缺 ...

  7. HDU1124 Factorial

    Problem Description The most important part of a GSM network is so called Base Transceiver Station ( ...

  8. opnet的simple_source模块学习 分类: opnet 2014-05-18 09:50 170人阅读 评论(0) 收藏

    simple_source模块可以在外部设置的属性 有四个局部统计量,分别为产生的bit速率.包速率.包大小,包间隔 状态机为三个非强制对象,在头文件里定义了自中断和转移条件. /*Include f ...

  9. Java之线程,常用方法,线程同步,死锁

    1, 线程的概念 进程与线程 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程.(进程是资源分配的最小单位) 线程:同一类线程共享代码和数据 ...

  10. Tornado-StaticFileHandler参考

    StaticFileHandler ====== tornado.web.StaticFileHandler 源代码中的解释 class StaticFileHandler(RequestHandle ...