当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. str-字符串功能介绍

    叨逼叨:字符串的各个功能修改不是本身,本身不变,会产生新的值,需要赋值给新的变量来接收 以下 "举例" 是解释每个功能的实例   "举例"下一行是pycharm ...

  2. 在Docker容器中运行.Net Core web Api项目

    安装Docker环境 参考本人这篇<CentOS 7 下Docker的安装>文章进行安装以及环境配置,这里不做赘述. 通过.NetCore开发WebApi项目 1. 创建.Net Core ...

  3. Spring+SpringMVC+MyBatis深入学习及搭建(十三)——SpringMVC入门程序(二)

    1.非注解的处理器映射器和适配器 1.1非注解的处理器映射器 前面我们配置的org.springframework.web.servlet.handler.BeanNameUrlHandlerMapp ...

  4. Linux工具之bc计算器进制的转换

    bc是Linux下的命令行式的计算器. 题目虽然叫任意进制,但是因为bc的限制,输入进制是2~16范围:输出进制是2~999范围.这与常见计算器的进制范围是一致的,比如windows计算器最高也只能处 ...

  5. C++学习(五)入门篇——基本类型

    面向对象编程的本质是设计并扩展自己的数据类型,让类型和数据匹配. 内置C++分成两种类型:基本类型和复合类型 1.简单变量 程序需要存储信息时,必须记录三个基本属性 (1)信息将存储在哪 (2)要存储 ...

  6. ASP.NET底层封装HttpModule实例---FormsAuthentication类的分析

    HttpModule是用来注册HttpApplication事件的,实现IHttpModule接口的托管代码模块可以访问该请求管道的所有事件.那么对于我们最常用的ASP.NET Forms身份验证模块 ...

  7. Groovy - 介绍

    Groovy 是 用于Java虚拟机的一种敏捷的动态语言,它是一种成熟的面向对象编程语言,既可以用于面向对象编程,又可以用作纯粹的脚本语言.使用该种语言不必编写过多的代码,同时又具有闭包和动态语言中的 ...

  8. Samba远程代码执行-分析(CVE-2017-7494)

    经历了前一阵windows的EternalBlue之后,某天看见了360的 samba高危预警,这个号称linux端的EternalBlue(EternalRed),于是便研究了一波 概述(抄) Sa ...

  9. 初学Python(十)——列表生成式

    初学Python(十)--列表生成式 初学Python,主要整理一些学习到的知识点,这次是列表生成式. # -*- coding:utf-8 -*- ''''' 列表生成式 ''' #一行代码表达所有 ...

  10. Redola.Rpc 集成 Consul 服务发现

    Redola.Rpc 解决了什么问题? Redola.Rpc 是一个使用 C# 开发的 RPC 框架,代码开源在 GitHub 上.目前版本仅支持 .NET Framework 4.6 以上版本,未来 ...