当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. Jenkins-FQA

    1.svn url不能设置成文件路径,而应该是"文件夹"路径,也就是说必须是目录. 示例: Checking out https://svn.gw.com.cn:10000/svn ...

  2. SpringMvc支持跨域访问,Spring跨域访问,SpringMvc @CrossOrigin 跨域

    SpringMvc支持跨域访问,Spring跨域访问,SpringMvc @CrossOrigin 跨域 >>>>>>>>>>>> ...

  3. Oracle系统表实用操作笔记

    1.取得指定用户的所有表名: SQL1: SELECT OWNER AS "对象所有者", OBJECT_NAME AS "表名", OBJECT_ID AS ...

  4. 自定义程序异常Exception

    /** * 用于循环时的异常定位抛出异常 * 如第i行记录,抛出异常 * @author zhengwei * */ public class MyExcetion extends RuntimeEx ...

  5. JavaScript一个cookie存储的类

    所有输出都在浏览器的控制台中 <script type="text/javascript"> /** * cookieStorage.js * 本类实现像localSt ...

  6. 解决win10系统以太网适配器的驱动程序可能出现问题

    插上网线显示未连接-连接可用,连上无线显示未连接-连接不可用,右下角显示感叹号 ,以太网和无线属性显示ipv4未连接详细信息为空,在设备管理器里卸载网卡驱动重装上仍然没有,通过windoes自带的网络 ...

  7. DOM0级事件处理、DOM2级事件处理

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  8. Android事件分发理解

    Android事件分发机制是个难点和重点,结合下各家,写点自己的理解.. 首先抛出一个小问题,写一个button的点击事件 button.setOnClickListener(new OnClickL ...

  9. Java之StringBuffer,StringBuilder,Math,Date,SimpleDateFormat,UUID,File

    java.lang 类 StringBuffer java.lang.Object java.lang.StringBuffer 所有已实现的接口: Serializable, Appendable, ...

  10. linux自动化创建补丁

    BUS #!/bin/sh #判断H5_BD_${BD_data}目录是否存在 BD_data=$(date +"%F") #当日打包多次的情况下,初始化补丁目录 if [ -d ...