【协议开发】RTSP协议开发-2 【AAC封包RTP】

本节内容主要讲解:

RTP之AAC封包和解包2-原理和源码分析

1.源码编译及结果运行

最后会生成文件sdp文件;
运行方式1:装有vlc的环境,直接双击test.sdp文件使用vlc播放;
运行方式2:使用ffmplay运行;运行的 时候要声明白名单;

 2.AAC的sdp类型

有两种aac的sdp类型:一种是mpeg4-generic,另外一种是MP4A-LATM;

3.怎么在原有的结构添加支持mpeg4-generic(AAC)?代码解析

文件1:rtp-payload-internal.h

在头文件中增加封装接口:与h264的封装的包返回的结构体的格式是一致的;

 文件2:封装接口实现文件

 

 函数1:

关键的两个函数:

【关键函数1】rtp_mpeg4_generic_pack_input--封包;

【关键函数2】rtp_decode_mpeg4_generic--解包;

下面的profile不是固定的,但是需要提前约定好;

 

 文件3:封装aac源码 aac-util.c 和aac-util.h

 1 #ifndef _aac_util_h_
 2 #define _aac_util_h_
 3 #include <stdio.h>
 4 #include <stdint.h>
 5 
 6 typedef struct{
 7     unsigned int syncword;  //12 bit 同步字 '1111 1111 1111',说明一个ADTS帧的开始
 8     unsigned int id;        //1 bit MPEG 标示符, 0 for MPEG-4,1 for MPEG-2
 9     unsigned int layer;     //2 bit 总是'00'
10     unsigned int protection_absent;  //1 bit 1表示没有crc,0表示有crc
11     unsigned int profile;           //2 bit 表示使用哪个级别的AAC
12     unsigned int sampling_frequency_index; //4 bit 表示使用的采样频率
13     unsigned int private_bit;        //1 bit
14     unsigned int channel_configuration; //3 bit 表示声道数
15     unsigned int original_copy;      //1 bit
16     unsigned int home;              //1 bit
17 
18     /*下面的为改变的参数即每一帧都不同*/
19     unsigned int copyright_id;   //1 bit
20     unsigned int copyright_id_start; //1 bit
21     unsigned int aac_frame_length;               //13 bit 一个ADTS帧的长度包括ADTS头和AAC原始流
22     unsigned int adts_buffer_fullness;           //11 bit 0x7FF adts buffer fullness
23 
24     /* number_of_raw_data_blocks_in_frame
25      * 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧
26      * 所以说number_of_raw_data_blocks_in_frame == 0
27      * 表示说ADTS帧中有一个AAC数据块并不是说没有。(一个AAC原始帧包含一段时间内1024个采样及相关数据)
28      */
29     unsigned int num_raw_data_blocks; //2 bit
30 }aac_header_t;
31 
32 typedef struct
33 {
34     aac_header_t header;        // 存储到是adts头部解析后的参数
35     uint8_t adts_buf[9];        // adts header最多9字节,从文件读取出来的数据先放到adts_buf里面
36     int     adts_len;           // adts 头部长度
37     uint8_t frame_buf[8192];    // 包括adts header 13bit最多8192字节
38     int     frame_len;          // 包括adts header + data length
39 }aac_frame_t;
40 
41 extern int aac_freq[];
42 
43 // 打开文件
44 FILE *aac_open_bitstream_file (char *filename);
45 // 根据传入的adts header buffer 解析出来对应的参数, show=1的时候打印解析结果,=0就不打印了
46 int aac_parse_header(uint8_t* in, aac_header_t* res, uint8_t show);
47 // 读取一帧完整的AAC帧(adts header + data),不会去查找sync word
48 int aac_get_one_frame (aac_frame_t *aac_frame, FILE *bits);
49 void aac_rtp_create_sdp(uint8_t *file, uint8_t *ip, uint16_t port,
50                         uint16_t profile, uint16_t chn,
51                         uint16_t freq, uint16_t type);
52 #endif // _aac_util_h_

aac-util.c

读取一帧数据:

 1 /**
 2  * 取一帧数据;
 3  * 数据组成的形式:header+data;
 4  * 1.先取7个字节,然后解析;
 5  * 3.根据解析结果查看有没有校验,如果有校验,则adts_header是9个字节;
 6  */
 7 //这里没有去检测sync word,如果中间有数据丢失该函数将不起作用
 8 int aac_get_one_frame (aac_frame_t *aac_frame, FILE *bits)
 9 {
10     // 1. 先读取7个字节,放到adts_buf
11     if (7 != fread (aac_frame->adts_buf, 1, 7, bits))//从码流中读3个字节
12     {
13         printf("read adts_buf failed\n");
14         return -1;
15     }
16     // 2.解析adts header
17     if(aac_parse_header(aac_frame->adts_buf, &aac_frame->header, 0) < 0)
18     {
19         return -1;
20     }
21     // 3.根据解析结果查看有没有校验
22     if(0 == aac_frame->header.protection_absent)
23     {
24         aac_frame->adts_len = 9;    // 变成adts header长度就是9个字节
25         // 0表示有CRC校验
26         if (2 != fread (&aac_frame->adts_buf[7], 1, 2, bits))//从码流中读2个字节
27         {
28             return -1;
29         }
30     } else {
31         aac_frame->adts_len = 7;
32     }
33 
34     //4. 拷贝header部分到buff中;
35     aac_frame->frame_len =  aac_frame->header.aac_frame_length; // 整帧长度
36     memcpy(aac_frame->frame_buf, aac_frame->adts_buf, aac_frame->adts_len); // 拷贝adts header
37 
38     //5.读取dadta部分到buff;因为5中已经读取header;
39     if ((aac_frame->frame_len - aac_frame->adts_len)        // 从文件读取data部分
40         != fread (&aac_frame->frame_buf[aac_frame->adts_len], 1,
41                   aac_frame->frame_len - aac_frame->adts_len, bits))
42     {
43         printf("read aac frame data failed\n");
44         return -1;
45     }
46 
47     return 0;
48 }

 3.3 main逻辑

aac编码和aac解码;

 

3.4 验证封装的数据是否正确

对比验证

封装前数据 in.aac VS 封装好的RTP数据包然后解封装写入到out.aac 进行对比;

修改代码:

使用200k的数据:

 

 

 3.5 如何保证RTP数据是实时播放的?

在合适的时间内发送适量的数据,如果数据过多则多少发送一些数据;

 4.对AAC进行封包的步骤

【1】只有一个AU_HEADER_LENGTH,但是可以有多个AU_HEADER;

【2】有多少个AU_HEADER,就有多少个AU;

 

 4.2 AU的关键字段

在编写sdp信息的时候需要将AU-size和AU_lenght,还有AU_LENGHET_DELTA都要加上,否则ffplay无法正常播放;

下面是ffplay的源码,需要使用到这三个字段进行计算。

 SDP信息中描述的关键字段信息:

 

 

 

 【特别说明】关于这些计算关系有点乱;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2021-07-31 09:46  OzTaking  阅读(1343)  评论(0)    收藏  举报