按关键词阅读:
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 , 最后就到了真正的数据 。
【详解RTP封包和拆包AAC实战分析(2)】(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(rtp_payload_check(helper,// save payloadptr = (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-sizeau_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]// 获取au的size,对应函数rtp_mpeg4_generic_pack_input(xxx) , 前面13位表示au sizesize = 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,ptr += au_size;// 跳过au header sizepau += size;// 跳过au sizeif (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) , 这样才能够正确打开 。 源码如下:
稿源:(未知)
【傻大方】网址:http://www.shadafang.com/c/111J310M2020.html
标题:详解RTP封包和拆包AAC实战分析(2)( 四 )