rtkcoas模式操作步骤(详解RTP封包和拆包AAC实战分析)
rtkcoas模式操作步骤(详解RTP封包和拆包AAC实战分析)HLS实战之Wireshark抓包分析RTP协议详解RTP协议之H264封包细节(1)详细解析RTSP框架和数据包分析(1)手把手搭建RTSP流媒体服务器
0.引言
为了更好理解本篇文章,可以先阅读前面几篇文章,文章列表如下:
详解RTP打包AAC实战分析(1)
详解RTP协议之H264封包和解包实战
详解RTP协议之H264封包细节(1)
详细解析RTSP框架和数据包分析(1)
手把手搭建RTSP流媒体服务器
RTP协议
HLS实战之Wireshark抓包分析
HTTP实战之Wireshark抓包分析
建议:阅读本文前,一定要阅读前面的文章,只有理解了原理,才能够真正读懂代码。
1.Rtp封包AAC重要步骤
把AAC数据封装成RTP包,有以下一些重要步骤,如下:
(1)读入AAC文件时,将AAC的前7个字节(或9个字节)的ADTS header去掉,跳过adts header。
(2)添加RTP Header。
(3)添加2字节的AU_HEADER_LENGTH,一般是2个字节,也有可能大于2个字节,根据实际情况,灵活改变。
(4)添加2字节的AU_HEADER,一般是2个字节,也有可能大于2个字节,根据实际情况,灵活改变。
(5)从第17字节开始就是payload(去掉ADTS Header的aac数据)封装数据了。
1.1 Rtp封包AAC格式
RTP承载aac的格式,由如下组成:
(1)2个字节的AU-headers-length。
(2)n个AU-header,每个2字节。
(3)n个AU,是aac去掉adts的payload。
存储格式如下:
注意:前面的RTP header AU_HEADER_LENGTH一般是固定结构,AU_HEADER的个数和AU是一一对应。AU_HEADER_LENGTH是描述所有AU_HEADER的总bit数量,一般是被16整除,这个大小在SDP文件中会有描述,一般一个AU_HEADER也有16bit。
发送数据时,推流协议可以自己控制,可以不支持那么复杂的协议,rtp aac每次只发送一个AU,也就只有一个AU_HEADER。
一个packet,一般只含1个AU。也有包含多个AU的情况,如下图。但实际工程项目中,打包的情况很少,根据实际情况来设计。
1.2 重要数据结构说明
(1)AU_HEADER_LENGTH
AU_HEADER_LENGTH表示所有au-header的长度,单位是Bit,一般是2个字节。一个au-header的长度是2个字节,因为可以有多个au-header,所以AU-Headers-Length的值是16的倍数。一般音频都是单个音频数据流的发送,所以AU_HEADER_LENGTH的值是16bit,2个字节。
//AU_HEADER_LENGTH
bytes[12] = 0x00; //⾼位
bytes[13] = 0x10; //低位 只有⼀个AU_HEADER
因为单位是bit 除以8就是auHeader的字节⻓度;⼜因为单个auheader字节⻓度2字节,再除以2就是auheader的个数,所以只有1个。
(2)AU-header
au-header的⾼13个bits就是⼀个au 的字节⻓度,如下所示:
//AU_HEADER
bytes[14] = (byte)((len & 0x1fe0) >> 5); //⾼位
bytes[15] = (byte)((len & 0x1f) << 3); //低位
AU-Header数据段的格式如下图:
1.3 实际的音频数据用AU表示
(1)当RTSP的⾳频使⽤AAC格式时 SDP文件的内容如下:
v=0
o=- 16128587303007558182 16128587303007558182 IN IP4 WINDOWS-75ID U9Q
s=Unnamed
i=N/A 5 c=IN IP4 0.0.0.0
t=0 0
a=tool:vlc 3.0.5
a=recvonly
a=type:broadcast
a=charset:UTF-8
a=control:rtsp://192.168.129.15:8554/
m=audio 0 RTP/AVP 96
b=AS:128
b=RR:0
a=rtpmap:96 mpeg4-generic/22050
a=fmtp:96 streamtype=5; profile-level-id=15; mode=AAC-hbr; config =138856e500; sizeLength=13; indexLength=3; indexDeltaLength=3; Pr ofile=1;
a=control:rtsp://192.168.2.195:8554/trackID=4
m=video 0 RTP/AVP 96
b=AS:800
b=RR:0
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1;profile-level-id=42c01e;sprop-para meter-sets=Z0LAHtoCQKeX/8CgAJ/EAAADAZAAAF2qPFi6gA== aM43IA==;
a=control:rtsp://192.168.129.15:8554/trackID=5
(2)上面这些参数是符合RFC规范,具体链接如下:
https://tools.ietf.org/html/rfc3640
具体界面如下:
(3)根据上面参数,重点说明config SizeLength IndexLength IndexDeltaLength的作⽤。
前两个字节的为ios-14996-3中定义的AudioSpecificConfig 前13个bits的格式如下表:
samplingFrequencyIndex的取值,根据不同的索引,匹配不同的采样值。如下图:
根据上面的图给出的格式,这里就以1388举例说明,1388 转换成2进制为 0001 0011 1000 1000。
audioObjectType为 00010 即 2,samplingFrequencyIndex为 0111 即 7 对应的采样频率为 22050,channelConfiguration为 0001 表示channel数量为1。
上面的sizeLength=13; indexLength=3; indexDeltaLength=3涉及到⾳频的AU Header.AU Header解决了⼀个RTP包容纳多个⾳频包的问题。
每个AU-Header包含以下信息:
(4)描述了当前的RTP包含了多少个⾳频包,也就是AU的个数。
(5)每个⾳频包(AU)的⼤⼩是多少。
(6)时间戳是多少。
AU-size、AU-Index(描述一个RTP包的AU序号)、AU-Index-delta具体含义描述如下图:
注意:其它的值都是可选的 如果sdp中没有出现相关的参数(或者为0) 则表示它们不会出现。以最简单的情况举例 假设aac数据⻓度为200字节 只有⼀个au-header。200 的⼆进制为 0000011001000 (补⾜为13 bits)。AU-headers-length 值为16 因为只有⼀个au-header au-header中只有AU-size和AU-Index 共占⽤16bits。整个au-header数据段的内容如下:
通常情况下 ⼀个rtp中只有⼀个aac包 不需要加再AU-Header 那么sdp中的aac参数可以简化为如下图:
2.源码解析
2.1 SDP文件和封装细节说明
(1)修改了indexLength的长度(相当于去掉了indexLength=3)。源码修改成如下:
创建sdp时,一定要按照格式来,字符千万不能写错了,比如写错audio,否则就有可能写错。
/*可以参考ffmpeg sdp.c
SDP中几个参数含义:config,就是AudioSpecificConfig的十六进制表示
*/
void aac_rtp_create_sdp(uint8_t *file uint8_t *ip uint16_t port
uint16_t profile
uint16_t chn
uint16_t freq uint16_t type)
{
char buff[1024] = {0};
char typeName[64] = {0};
char demo[] =
"m=audio 9832 RTP/AVP 97\n"
"a=rtpmap:97 mpeg4-generic/44100/2\n"
"a=fmtp:97 sizeLength=13;mode=AAC-hbr;config=1210;\n"
"c=IN IP4 127.0.0.1";
char demo[] =
"m=audio %d RTP/AVP %d\n"
"a=rtpmap:%d %s/%d/%d\n"
"a=fmtp:%d streamtype=5;profile-level-id=1;sizeLength=13;IndexLength=3;indexDeltaLength=3;mode=AAC-hbr;config=%d;\n"
"c=IN IP4 %s";
/*char demo[] =
"m=audio %d RTP/AVP %d\n"
"a=rtpmap:%d %s/%d/%d\n"
"a=fmtp:%d streamtype=5;profile-level-id=1;sizeLength=13;indexDeltaLength=3;mode=AAC-hbr;config=%d;\n"
"c=IN IP4 %s";*/
uint16_t config = 1410 _freq = 8;
int fd;
........
// 从aac adts header读取出来的profile是被减1的
//这些参数就是很关键的,adts header虽然很跳过了,但是信息还是很关键,关键信息要保存
config = profile 1;
config <<= 5; // 5 aac的profile 通常情况是1 或者2
config |= _freq;
config <<= 4; //aac的采样频率的索引
config |= chn;
config <<= 3;
//转成16进制
config = ((config>>12)&0xF)*1000 ((config>>8)&0xF)*100 ((config>>4)&0xF)*10 ((config>>0)&0xF);
snprintf(buff sizeof(buff) demo port type type typeName freq chn type config ip);
remove(file);
if((fd = fopen(file "wt")) > 0)
{
//把buff的数据写到创建的aac.sdp文件里面去
fwrite(buff strlen(buff) 1 fd);
fclose(fd);
}
(2)这个时候再去拉取流播放,就会报错。
(3)在ffmpeg的源码文件rtpdec_mpeg4.c,对应源码如下:
下面推荐的这两个源码文件,是非常值得学习,就是rtpdec_mpeg4.c和rtpenc_aac.c。
static int rtp_parse_mp4_au(PayloadContext *data const uint8_t *buf int len)
{
int au_headers_length au_header_size i;
GetBitContext getbitcontext;
if (len < 2)
return AVERROR_INVALIDDATA;
/* decode the first 2 bytes where the AUHeader sections are stored
length in bits */
au_headers_length = AV_RB16(buf);
if (au_headers_length > RTP_MAX_PACKET_LENGTH)
return -1;
data->au_headers_length_bytes = (au_headers_length 7) / 8;
/* skip AU headers length section (2 bytes) */
buf = 2;
len -= 2;
if (len < data->au_headers_length_bytes)
return AVERROR_INVALIDDATA;
init_get_bits(&getbitcontext buf data->au_headers_length_bytes * 8);
/* XXX: Wrong if optional additional sections are present (cts dts etc...) */
//把AU-size和AU-Index/AU-Index-delta加起来,如果不这样做,就会报上述问题
au_header_size = data->sizelength data->indexlength;
if (au_header_size <= 0 || (au_headers_length % au_header_size != 0))
return -1;
data->nb_au_headers = au_headers_length / au_header_size;
if (!data->au_headers || data->au_headers_allocated < data->nb_au_headers) {
av_free(data->au_headers);
data->au_headers = av_malloc(sizeof(struct AUHeaders) * data->nb_au_headers);
if (!data->au_headers)
return AVERROR(ENOMEM);
data->au_headers_allocated = data->nb_au_headers;
}
for (i = 0; i < data->nb_au_headers; i) {
data->au_headers[i].size = get_bits_long(&getbitcontext data->sizelength);
data->au_headers[i].index = get_bits_long(&getbitcontext data->indexlength);
}
return 0;
}
rtpenc_aac.c的函数ff_rtp_send_aac(xxx)的源码如下:
void ff_rtp_send_aac(AVFormatContext *s1 const uint8_t *buff int size)
{
RTPMuxContext *s = s1->priv_data;
AVStream *st = s1->streams[0];
const int max_au_headers_size = 2 2 * s->max_frames_per_packet;
int len max_packet_size = s->max_payload_size - max_au_headers_size;
uint8_t *p;
/* skip ADTS header if present */
if ((s1->streams[0]->codecpar->extradata_size) == 0) {
size -= 7;
buff = 7;
}
/* test if the packet must be sent */
len = (s->buf_ptr - s->buf);
if (s->num_frames &&
(s->num_frames == s->max_frames_per_packet ||
(len size) > s->max_payload_size ||
av_compare_ts(s->cur_timestamp - s->timestamp st->time_base
s1->max_delay AV_TIME_BASE_Q) >= 0)) {
int au_size = s->num_frames * 2;
p = s->buf max_au_headers_size - au_size - 2;
if (p != s->buf) {
memmove(p 2 s->buf 2 au_size);
}
/* Write the AU header size */
AV_WB16(p au_size * 8);
ff_rtp_send_data(s1 p s->buf_ptr - p 1);
s->num_frames = 0;
}
if (s->num_frames == 0) {
s->buf_ptr = s->buf max_au_headers_size;
s->timestamp = s->cur_timestamp;
}
if (size <= max_packet_size) {
p = s->buf s->num_frames * 2 2;
AV_WB16(p size * 8);
memcpy(s->buf_ptr buff size);
s->buf_ptr = size;
} else {
int au_size = size;
max_packet_size = s->max_payload_size - 4;
p = s->buf;
AV_WB16(p 2 * 8);
while (size > 0) {
len = FFMIN(size max_packet_size);
AV_WB16(&p[2] au_size * 8);
memcpy(p 4 buff len);
ff_rtp_send_data(s1 p len 4 len == size);
size -= len;
buff = len;
}
}
}
调用这个函数rtp_parse_mp4_au(xxx),出现了异常。源码如下:
static int rtp_parse_mp4_au(PayloadContext *data const uint8_t *buf int len)
{
int au_headers_length au_header_size i;
GetBitContext getbitcontext;
if (len < 2)
return AVERROR_INVALIDDATA;
/* decode the first 2 bytes where the AUHeader sections are stored
length in bits */
au_headers_length = AV_RB16(buf);
if (au_headers_length > RTP_MAX_PACKET_LENGTH)
return -1;
data->au_headers_length_bytes = (au_headers_length 7) / 8;
/* skip AU headers length section (2 bytes) */
buf = 2;
len -= 2;
if (len < data->au_headers_length_bytes)
return AVERROR_INVALIDDATA;
init_get_bits(&getbitcontext buf data->au_headers_length_bytes * 8);
/* XXX: Wrong if optional additional sections are present (cts dts etc...) */
//把AU-size和AU-Index/AU-Index-delta加起来,如果不这样做,就会报上述问题
au_header_size = data->sizelength data->indexlength;
if (au_header_size <= 0 || (au_headers_length % au_header_size != 0))
return -1;
data->nb_au_headers = au_headers_length / au_header_size;
if (!data->au_headers || data->au_headers_allocated < data->nb_au_headers) {
av_free(data->au_headers);
data->au_headers = av_malloc(sizeof(struct AUHeaders) * data->nb_au_headers);
if (!data->au_headers)
return AVERROR(ENOMEM);
data->au_headers_allocated = data->nb_au_headers;
}
for (i = 0; i < data->nb_au_headers; i) {
data->au_headers[i].size = get_bits_long(&getbitcontext data->sizelength);
data->au_headers[i].index = get_bits_long(&getbitcontext data->indexlength);
}
return 0;
}
(4)AU_HEADER_LENGTH描述所有AU_HEADER的总bit数,一般是需要16整除,如果不够整除是可以用padding值来去补充。AU_HEADER的大小是需要SDP去描述。一般一个AU_HEADER大小都是16bit。
注意:目前源码中cts、dts暂时是不支持。
只有一个AU_HEADER才写成10。AU_HEADER再到AU。
AAC帧可能要拆分成多个包,如果AAC帧的size超过了RTP打包的size,那就需要分包。一般情况下AAC帧是不会超过RTP打包的size,即不用分包。
(5)我这里就是一个包对应一个AU的情况,暂时不考虑一个包对应多个AU。发送数据时,推流协议是可以自己控制,可以不支持那么复杂的协议。rtp aac可以每次只发送一个AU,AU_HEADER用AU-headers-length占2个字节,高13bit是 AU size。如果一次发送完,代码下的bytes就会等于0。打包把RTP_FIXED_HEADER N_AU_HEADER packer->pkt.payloadlen会全部打包起来序列化(就是把数据按照RTP协议的格式封装到RTP header里面去),然后发送。如果是最后一个packet,这里的AU mark的标记就会设置为1。N_AU_HEADER为什么是4?就是AU_HEADER_LENGTH AU_HEADER的大小,就表示是4个字节。
前面已经把AU header序列化到RTP header里面去了,接下来就是封装payload了,封装的大小n N_AU_HEADER() packer->pkt.payloadlen,其中n表示RTP头长度,N_AU_HEADER表示整个AU header长度,即AU_HEADER_LENGTH AU_HEADER的大小,packer->pkt.payloadlen表示payload长度。
前面已经都封装好了,这样就回调用户的回调函数packer->handler.packet(xxx),进行rtp包的数据发送。源码如下:
/**
* @brief rtp_mpeg4_generic_pack_input
* @param pack
* @param data 包括adts header
* @param bytes 包括adts header的长度
* @param timestamp
* @return <0:失败
*/
static int rtp_mpeg4_generic_pack_input(void* pack const void* data int bytes uint32_t timestamp)
{
int r;
int n size;
uint8_t *rtp;
uint8_t header[4];
const uint8_t *ptr;
struct rtp_encode_mpeg4_generic_t *packer;
packer = (struct rtp_encode_mpeg4_generic_t *)pack;
packer->pkt.rtp.timestamp = timestamp; //(uint32_t)(time * KHz);
r = 0;
ptr = (const uint8_t *)data;
if (0xFF == ptr[0] && 0xF0 == (ptr[1] & 0xF0) && bytes > 7)
{
// skip ADTS header 跳过adts头部
assert(bytes == (((ptr[3] & 0x03) << 11) | (ptr[4] << 3) | ((ptr[5] >> 5) & 0x07)));
ptr = 7; // 这里直接判断为7个字节
bytes -= 7;
}
// 担心AAC帧可能要拆分多个RTP包
for (size = bytes; 0 == r && bytes > 0; packer->pkt.rtp.seq)
{
// 3.3.6. High Bit-rate AAC
// SDP fmtp: sizeLength=13; indexLength=3; indexDeltaLength = 3;
// au-header的高13个bits就是一个au 的字节长度:
header[0] = 0x00; // 高位
header[1] = 0x10; // 低位 16-bits AU headers-lenght
// AU headers 用2个字节,高13bit是 AU size
header[2] = 0; header[3] = 0;
header[2] = (uint8_t)(size >> 5); // 高位
header[3] = (uint8_t)(size & 0x1f) << 3; // 低位 这里一个包只发送一个AU
packer->pkt.payload = ptr;
// N_AU_HEADER 占据了4个字节,AU-headers-length占2个字节,AU-header占2个字节
packer->pkt.payloadlen = (bytes N_AU_HEADER RTP_FIXED_HEADER) <= packer->size ? // 判断是否要划分多个AU
bytes : (packer->size - N_AU_HEADER - RTP_FIXED_HEADER);
ptr = packer->pkt.payloadlen;
bytes -= packer->pkt.payloadlen; // 剩余的字节数
n = RTP_FIXED_HEADER N_AU_HEADER packer->pkt.payloadlen;
rtp = (uint8_t*)packer->handler.alloc(packer->cbparam n); // 分配缓存,以备用来保存序列化数据
if (!rtp) return -ENOMEM;
// Marker (M) bit: The M bit is set to 1 to indicate that the RTP packet
// payload contains either the final fragment of a fragmented Access
// Unit or one or more complete Access Units
packer->pkt.rtp.m = (0 == bytes) ? 1 : 0; // 最后一个AU mark设置为1 否则为0
n = rtp_packet_serialize_header(&packer->pkt rtp n);
if (n != RTP_FIXED_HEADER)
{
assert(0);
return -1;
}
memcpy(rtp n header N_AU_HEADER); // N_AU_HEADER为什么是4, AU_HEADER_LENGTH AU_HEADER
memcpy(rtp n N_AU_HEADER packer->pkt.payload packer->pkt.payloadlen); // 封装payload
r = packer->handler.packet(packer->cbparam rtp n N_AU_HEADER packer->pkt.payloadlen packer->pkt.rtp.timestamp 0);
packer->handler.free(packer->cbparam rtp);
}
return r;
}
2.2 解封装源码细节
(1)首先通过反序列化,把RTP头部解析出来。
(2)然后,再解析AU。
(3)解析出RTP头部后,就可以知道payload的起始位置。注意,这里的payload是包括了AU_header_length au_header占用的字节了。就可以求出 au_header的大小(单位字节数),但是暂时还不包括AU-header-length的大小。一般AU-header-length是固定占用2个字节。
注意:大部分情况下,SDP都是这类参数,SDP fmtp: sizeLength=13; indexLength=3; indexDeltaLength=3;一般au header占用2字节,其他格式不支持,如CTS和DTS,如果加上au header就会超过2个字节,这里暂时不支持。
(4)有了AU-header-length就可以算出au 的个数。那ptr就会跳过au header(大小就是AU-header-length=2)的起始位置,pau指向au的起始位置。注意这里的au_header_length就是所有AU_Header的总长度。如下图所示:
(5)由于前面已经获取了AU size,即找到了AU的位置,这样就可以去解析rtp_payload部分了。
(6)缓存完各个au,ptr就可以跳过au_header的size,pau就跳过au的size,最后就到了真正的数据。
(7)收到真正完整的数据,就可以返回给应用层去处理了。
上面所述步骤的源码如下:
static int rtp_decode_mpeg4_generic(void* p const void* packet int bytes)
{
int i size;
int au_size;
int au_numbers;
int au_header_length;
const uint8_t *ptr *pau *pend;
struct rtp_packet_t pkt;
struct rtp_payload_helper_t *helper;
helper = (struct rtp_payload_helper_t *)p;
//1. 反序列化,把RTP 头解析出来
if (!helper || 0 != rtp_packet_deserialize(&pkt packet bytes) || pkt.payloadlen < 4)
return -EINVAL;
rtp_payload_check(helper &pkt);
// save payload
ptr = (const uint8_t *)pkt.payload; // 这里还是包括了AU_header_length au_header占用的字节的
pend = ptr pkt.payloadlen;
// AU-headers-length AU-headers-length是固定占用2个字节
au_header_length = (ptr[0] << 8) ptr[1]; // 读取长度
au_header_length = (au_header_length 7) / 8; // bit -> byte 计算出来au_heade占用的字节情况
if (ptr au_header_length /*AU-size*/ > pend || au_header_length < 2) //如果au_header_length至少都是2个字节 如果小于2个字节 就不对了.还有一种情况就是越界了
{
assert(0);
//helper->size = 0;
helper->lost = 1;
//helper->flags |= RTP_PAYLOAD_FLAG_PACKET_LOST;
return -1; // invalid packet
}
// 3.3.6. High Bit-rate AAC
// SDP fmtp: sizeLength=13; indexLength=3; indexDeltaLength=3; 一般au header占用2字节,其他格式不支持
au_size = 2; // only AU-size
au_numbers = au_header_length / au_size; // 计算au个数
assert(0 == au_header_length % au_size);
ptr = 2; // skip AU headers length section 2-bytes ; 此时ptr指向au header的起始位置
pau = ptr au_header_length; // point to Access Unit 跳过AU header才是真正的数据; 此时pau指向的是au的起始位置
for (i = 0; i < au_numbers; i )
{
size = (ptr[0] << 8) | (ptr[1] & 0xF8); // 获取au的size 对应函数rtp_mpeg4_generic_pack_input(xxx),前面13位表示au size
size = size >> 3; // 右移3bit 转成字节
if (pau size > pend)
{
assert(0);
//helper->size = 0;
helper->lost = 1;
//helper->flags |= RTP_PAYLOAD_FLAG_PACKET_LOST;
return -1; // invalid packet
}
// TODO: add ADTS/ASC ???
pkt.payload = pau;
pkt.payloadlen = size;
rtp_payload_write(helper &pkt);
ptr = au_size; // 跳过au header size
pau = size; // 跳过au size
if (au_numbers > 1 || pkt.rtp.m) // 收到完整数据再发送给应用层
{
rtp_payload_onframe(helper);
}
}
return helper->lost ? 0 : 1; // packet handled
}
(8)rtp_payload_helper_t* helper就是作为缓存,就是把多个AU 组成一帧数据。这里记录了缓存的字节数,并进行累加,只要不超过缓存的容量。如果超过了超过缓存的容量,就需要重新分配内存,如果没超过,就直接拷贝到helper指向的缓存里面去,即helper->ptr helper->size指向的位置。源码如下:
/**
* @brief rtp_payload_write
* @param helper 缓存每个au解析出来的数据
* @param pkt
* @return
*/
int rtp_payload_write(struct rtp_payload_helper_t* helper const struct rtp_packet_t* pkt)
{
int size;
size = helper->size pkt->payloadlen;//记录缓存字节数
if (size > helper->maxsize || size < 0)
return -EINVAL;
if (size > helper->capacity)//最大缓存
{
void *ptr;
size = size / 4 > 16000 ? size / 4 : 16000;
ptr = realloc(helper->ptr size);
if (!ptr)
{
//helper->flags |= RTP_PAYLOAD_FLAG_PACKET_LOST;
helper->lost = 1;
//helper->size = 0;
return -ENOMEM;
}
helper->ptr = (uint8_t*)ptr;
helper->capacity = size;
}
assert(helper->capacity >= helper->size pkt->payloadlen);
memcpy(helper->ptr helper->size pkt->payload pkt->payloadlen);
helper->size = pkt->payloadlen;
return 0;
}
(9)前面rtp_payload_helper_t* helper就是作为缓存一帧帧完整的数据,然后调用用户设置的回调函数helper->handler.packet(helper->cbparam helper->ptr helper->size helper->timestamp helper->__flags | (helper->lost ? RTP_PAYLOAD_FLAG_PACKET_CORRUPT : 0)),可以看出都是从helper缓存里取的,用户的回调函数,设置的工作是进行写文件(写文件是要注意一定要注意加上adts header),这样才能够正确打开。源码如下:
int rtp_payload_onframe(struct rtp_payload_helper_t *helper)
{
int r;
r = 0;
if (helper->size > 0
#if !defined(RTP_ENABLE_COURRUPT_PACKET)
&& 0 == helper->lost
#endif
)
{
// previous packet done
r = helper->handler.packet(helper->cbparam helper->ptr helper->size helper->timestamp helper->__flags | (helper->lost ? RTP_PAYLOAD_FLAG_PACKET_CORRUPT : 0));
// RTP_PAYLOAD_FLAG_PACKET_LOST: miss
helper->__flags &= ~RTP_PAYLOAD_FLAG_PACKET_LOST; // clear packet lost flag
}
// set packet lost flag on next frame
if(helper->lost)
helper->__flags |= RTP_PAYLOAD_FLAG_PACKET_LOST;
// new frame start
helper->lost = 0;
helper->size = 0;
return r;
}
3.总结
本篇文章主要讲解了rtp封包的更细节部分,封包格式详细说明,还结合源码更详细的解释真个打包和拆包的过程。希望能够帮助到大家。欢迎关注,收藏,转发,分享。
后期关于项目知识,也会更新在微信公众号“记录世界 from antonio”,欢迎关注