• 音视频开发13 FFmpeg 音频 相关格式分析 -- AAC ADTS格式分析


    这一节,我们学习常用的音频的格式 AAC,重点是掌握 AAC的传输格式 ADTS 头部的信息,目的是 : 当音频数据有问题的时候,如果是AAC的编码,在分析 头部信息的时候能够根据头部信息 判断问题是否出现在 头部。

    AAC ADTS格式分析

    AAC⾳频格式:Advanced Audio Coding(⾼级⾳频解码),是⼀种由MPEG-4标准定义的有损⾳频压缩格式,由Fraunhofer发展,Dolby, Sony和AT&T是主要的贡献者。

    AAC 的封装格式有两种 ADIF 和 ADTS

    ADIF:已经基本弃用-ADIF只有⼀个统⼀的头,所以必须得到所有的数据后解码。

    Audio Data Interchange Format ⾳频数据交换格式。这种格式的特征是可以确定的找到这个⾳频数据的开始,不需进⾏在⾳频数据流中间开始的解码,即它的解码必须在明确定义的开始处进⾏。故这种格式常⽤在磁盘⽂件中。

    ADTS ADTS可以在任意帧解码,也就是说它每⼀帧都有头信息,这个是重点

    Audio Data Transport Stream。是AAC⾳频的传输流格式。AAC⾳频格式在MPEG-2(ISO-13318-7 2003)中有定义。AAC后来⼜被采⽤到MPEG-4标准中。这种格式的特征是它是⼀个有同步字的⽐特流,解码可以在这个流中任何位置开始。它的特征类似于mp3数据流格式。

    可能遇见的问题1:

    有的时候当你编码AAC裸流的时候,会遇到写出来的AAC⽂件并不能在PC和⼿机上播放,很⼤的可能就是AAC⽂件的每⼀帧⾥缺少了ADTS头信息⽂件的包装拼接。

    只需要加⼊头⽂件ADTS即可。⼀个AAC原始数据块⻓度是可变的,对原始帧加:上ADTS头进⾏ADTS的封装,就形成了ADTS帧。

    ADTS 格式下 AAC⾳频⽂件格式

    每⼀帧由ADTS Header和AAC Audio Data(在图中,是 AAC ES)组成。结构体如下:

    也就是说,,一个AAC 帧,包含了一个ADTS header 和 一堆具体的数据。另外AAC的一帧一般包含了1024个采样点。

    头文件 ADTS Header 的组成

    是由 固定头信息  可变头信息 。组成
    固定头信息中的数据每⼀帧都相同,⽽可变头信息则在帧与帧之间可变

    每⼀帧的ADTS的头⽂件都包含了⾳频的采样率,声道,帧⻓度等信息,这样解码器才能解析读取。

    ⼀般情况下ADTS的头信息都是7个字节,分为2部分:

    adts_fixed_header();

    adts_variable_header();

    其⼀为固定头信息,紧接着是可变头信息。固定头信息中的数据每⼀帧都相同,⽽可变头信息则在帧与帧之间可变。

    头文件固定部分:adts_fixed_header()

    syncword :同步头 总是0xFFF, all bits must be 1,代表着⼀个ADTS帧的开始 12bits

    ID: MPEG标识符,0标识MPEG-4,1标识MPEG-2 1bits

    Layer: always: '00' 2 bits

    protection_absent:表示是否误码校验。1代表 header 有 7个字节,0代表 header有9个字节,一般情况下都是7个字节。Warning, set to 1 if there is no CRC and 0 if there is CRC 1bits

    profile:表示使⽤哪个级别的AAC,如01 Low Complexity(LC)--- AAC LC。有些芯⽚只⽀持AAC LC 。 2bits。。 通过前面的ID,我们可以设置是 MPEG-4, 还是 MPEG-2

    在MPEG-2 中,有明确的指出 profile这个值是多少。且只有三种 参见下表

    在MPEG-4中,profile的计算要通过  MPEG-4 Audio Object Type - 1

    profile = MPEG-4 Audio Object Type - 1

    如下的MPEG-4中关于 aac audio Object Type的说明

    对应的profile的值

    sampling_frequency_index:表示使⽤的采样率下标,通过这个下标在 Sampling Frequencies[ ]数组中查找得知采样率的值。4bits

    channel_configuration: 表示声道数,⽐如2表示⽴体声双声道 3bits,

    MPEG-4 中规定的值如下:

    0: Defined in AOT Specifc Config

    1: 1 channel: front-center

    2: 2 channels: front-left, front-right

    3: 3 channels: front-center, front-left, front-right

    4: 4 channels: front-center, front-left, front-right, back-center

    5: 5 channels: front-center, front-left, front-right, back-left, back-right

    6: 6 channels: front-center, front-left, front-right, back-left, back-right, LFE-channel

    7: 8 channels: front-center, front-left, front-right, side-left, side-right,back-left, back-right, LFE-channel

    8-15: Reserved

    还有3个没有介绍:都占1bite,

    private_bits:

    original:

    home:

    头文件adts_variable_header()

    copyright_identification_bits: 未知, 占1bits

    copyright_identification_start: 未知,占1bits

    aac_frame_length : ⼀个ADTS帧的⻓度 包括ADTS头和AAC原始流. 单位是bytes

    aac_frame_length = (protection_absent == 1 ? 7 : 9) + size(AACFrame) 13 bits

    protection_absent=0时, header length=9bytes

    protection_absent=1时, header length=7bytes

    adts_buffer_fullness:0x7FF 说明是码率可变的码流。一般都是写的0x7FF这个值 11bits

    number_of_raw_data_blocks_in_frame
    表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧。
    如果number_of_raw_data_blocks_in_frame 的值是0, 表示说ADTS帧中有⼀个AAC原始帧。
    如果 number_of_raw_data_blocks_in_frame 的值是1,表明ADTS帧中有2个原始帧。
    例子:

    下⾯是ADTS的AAC⽂件部分:⾼字节开始算

    第⼀帧的帧头7个字节为:0xFF 0xF1 0x4C 0x40 0x20 0xFF 0xFC

    我们将这7个字节拿出来,转化成2进制

    0xFF         0xF1          0x4C          0x40             0x20            0xFF          0xFC

    11111111   11110001   01001100   0100 0000   0010 0000   1111 1111   1111 1100

    分析各个关键数值:

    0xFF 0xF1 表示如下的部分

    111111111111 syncword :同步头 总是0xFFF, all bits must be 1,代表着?个ADTS帧的开始 12bits

    0 ID: MPEG标识符,0标识MPEG-4,1标识MPEG-2 1bits

    00 Layer: always: '00' 2 bits

    1 protection_absent:表示是否误码校验。1代表 header 有 7个字节,0代表 header有9个字节,一般情况下都是7个字节。Warning, set to 1 if there is no CRC and 0 if there is CRC 1bits


    0x4C 全部, 0x40中的4 表示部分如下

    01 profile:表示使?哪个级别的AAC,如01 Low Complexity(LC)--- AAC LC。有些芯片只支持AAC LC 。 2bits

    0011 ,sampling_frequency_index : 通过这个下标在 Sampling Frequencies[ ]数组中找采样率的值 4bits

    0 private_bits: 1 bits

    001 channel_configuration: 表示声道数,比如2表示立体声双声道 3bits

    0 original: 1bits

    0 home: 1bits


    0x40中的4 ,,, 0x20 0xFF 0xFC 全部表示如下

    0 copyright_identification_bits: 未知, 占1bits

    0 copyright_identification_start: 未知,占1bits

    0000100000111(帧⻓度) aac_frame_length 占 13 bits

    11111111111 adts_buffer_fullness:0x7FF 说明是码率可变的码流。 11bits

    00 number_of_raw_data_blocks_in_frame

    表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧。 占2bits

    一般一个

    计算帧⻓度:将⼆进制 0000100000111 转换成⼗进制为263。观察第⼀帧的⻓度确实为263个字节。红色部分的为帧头部的固定部分 0xFF 0xF1 0x4C 0x40 0x20 0xFF 0xFC , 该帧长度所属位置为蓝色部分,其中 第一个0的后两个0, 0000

    得到帧长度的计算⽅法:(帧⻓度为13位,使⽤unsigned int来存储帧⻓数值)

    1. unsigned int getFrameLength(unsigned char* str)
    2. {
    3. if ( !str )
    4. {
    5. return 0;
    6. }
    7. unsigned int len = 0;
    8. int f_bit = str[3];
    9. int m_bit = str[4];
    10. int b_bit = str[5];
    11. len += (b_bit>>5);
    12. len += (m_bit<<3);
    13. len += ((f_bit&3)<<11);
    14. return len;
    15. }

    核心编码:注意的在生成 ADTS Header 的代码
     
     
    1. // 读取媒体文件,并把aac数据帧写入到本地文件,注意,从mp4文件中读取到的aac就只有 aac data 的部分,没有头的部分,头的部分我们需要自己添加,使用的方法为自定义的adts_header方法
    2. // av_read_frame方法的一些说明
    3. // 对于音频,如果每个帧具有已知的固定大小(例如PCM或ADPCM数据),则它包含整数个帧。
    4. // 如果音频帧具有可变大小(例如MPEG音频),则它包含一个帧。
    5. //当前走到这里,读取的一定是AAC数据,那么av_read_frame读取到pkt中的一定是一帧的大小,因此adts_header方法中,传递的第二个参数就是一帧的大小。
    6. int ret1 =0;
    7. while((ret1 = av_read_frame(ifmt_ctx, &pkt)) >=0 )
    8. {
    9. if(pkt.stream_index == audio_index)
    10. {
    11. char adts_header_buf[7] = {0};//这里我们自己写的时候,头部占7bytes,意味着 校验位的值是1,表示不用校验
    12. adts_header(adts_header_buf, pkt.size,
    13. ifmt_ctx->streams[audio_index]->codecpar->profile,
    14. ifmt_ctx->streams[audio_index]->codecpar->sample_rate,
    15. ifmt_ctx->streams[audio_index]->codecpar->channels);
    16. fwrite(adts_header_buf, 1, 7, aac_fd); // 写adts header , ts流不适用,ts流分离出来的packet带了adts header
    17. len = fwrite( pkt.data, 1, pkt.size, aac_fd); // 写adts data
    18. if(len != pkt.size)
    19. {
    20. av_log(NULL, AV_LOG_DEBUG, "warning, length of writed data isn't equal pkt.size(%d, %d)\n",
    21. len,
    22. pkt.size);
    23. }
    24. }
    25. av_packet_unref(&pkt);
    26. }

    1. int adts_header(char * const p_adts_header, const int data_length,
    2. const int profile, const int samplerate,
    3. const int channels)
    4. {
    5. int sampling_frequency_index = 3; // 默认使用48000hz
    6. int adtsLen = data_length + 7;
    7. int frequencies_size = sizeof(sampling_frequencies) / sizeof(sampling_frequencies[0]);
    8. int i = 0;
    9. for(i = 0; i < frequencies_size; i++)
    10. {
    11. if(sampling_frequencies[i] == samplerate)
    12. {
    13. sampling_frequency_index = i;
    14. break;
    15. }
    16. }
    17. if(i >= frequencies_size)
    18. {
    19. printf("unsupport samplerate:%d\n", samplerate);
    20. return -1;
    21. }
    22. p_adts_header[0] = 0xff; //syncword:0xfff 高8bits
    23. p_adts_header[1] = 0xf0; //syncword:0xfff 低4bits
    24. p_adts_header[1] |= (0 << 3); //MPEG Version:0 for MPEG-4,1 for MPEG-2 1bit
    25. p_adts_header[1] |= (0 << 1); //Layer:0 2bits
    26. p_adts_header[1] |= 1; //protection absent:1 1bit
    27. p_adts_header[2] = (profile)<<6; //profile:profile 2bits
    28. p_adts_header[2] |= (sampling_frequency_index & 0x0f)<<2; //sampling frequency index:sampling_frequency_index 4bits
    29. p_adts_header[2] |= (0 << 1); //private bit:0 1bit
    30. p_adts_header[2] |= (channels & 0x04)>>2; //channel configuration:channels 高1bit
    31. p_adts_header[3] = (channels & 0x03)<<6; //channel configuration:channels 低2bits
    32. p_adts_header[3] |= (0 << 5); //original:0 1bit
    33. p_adts_header[3] |= (0 << 4); //home:0 1bit
    34. p_adts_header[3] |= (0 << 3); //copyright id bit0 1bit
    35. p_adts_header[3] |= (0 << 2); //copyright id start0 1bit
    36. p_adts_header[3] |= ((adtsLen & 0x1800) >> 11); //frame length:value 高2bits
    37. p_adts_header[4] = (uint8_t)((adtsLen & 0x7f8) >> 3); //frame length:value 中间8bits
    38. p_adts_header[5] = (uint8_t)((adtsLen & 0x7) << 5); //frame length:value3bits
    39. p_adts_header[5] |= 0x1f; //buffer fullness:0x7ff 高5bits
    40. p_adts_header[6] = 0xfc; //11111100//buffer fullness:0x7ff 低6bits
    41. // number_of_raw_data_blocks_in_frame:
    42. // 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧。
    43. return 0;
    44. }

    全部代码:

    1. #include <stdio.h>
    2. #include <libavutil/log.h>
    3. #include <libavformat/avio.h>
    4. #include <libavformat/avformat.h>
    5. #include <libavcodec/avcodec.h>
    6. #define ADTS_HEADER_LEN 7;
    7. const int sampling_frequencies[] = {
    8. 96000, // 0x0
    9. 88200, // 0x1
    10. 64000, // 0x2
    11. 48000, // 0x3
    12. 44100, // 0x4
    13. 32000, // 0x5
    14. 24000, // 0x6
    15. 22050, // 0x7
    16. 16000, // 0x8
    17. 12000, // 0x9
    18. 11025, // 0xa
    19. 8000 // 0xb
    20. // 0xc d e f是保留的
    21. };
    22. int adts_header(char * const p_adts_header, const int data_length,
    23. const int profile, const int samplerate,
    24. const int channels)
    25. {
    26. int sampling_frequency_index = 3; // 默认使用48000hz
    27. int adtsLen = data_length + 7;
    28. int frequencies_size = sizeof(sampling_frequencies) / sizeof(sampling_frequencies[0]);
    29. int i = 0;
    30. for(i = 0; i < frequencies_size; i++)
    31. {
    32. if(sampling_frequencies[i] == samplerate)
    33. {
    34. sampling_frequency_index = i;
    35. break;
    36. }
    37. }
    38. if(i >= frequencies_size)
    39. {
    40. printf("unsupport samplerate:%d\n", samplerate);
    41. return -1;
    42. }
    43. p_adts_header[0] = 0xff; //syncword:0xfff 高8bits
    44. p_adts_header[1] = 0xf0; //syncword:0xfff 低4bits
    45. p_adts_header[1] |= (0 << 3); //MPEG Version:0 for MPEG-4,1 for MPEG-2 1bit
    46. p_adts_header[1] |= (0 << 1); //Layer:0 2bits
    47. p_adts_header[1] |= 1; //protection absent:1 1bit
    48. p_adts_header[2] = (profile)<<6; //profile:profile 2bits
    49. p_adts_header[2] |= (sampling_frequency_index & 0x0f)<<2; //sampling frequency index:sampling_frequency_index 4bits
    50. p_adts_header[2] |= (0 << 1); //private bit:0 1bit
    51. p_adts_header[2] |= (channels & 0x04)>>2; //channel configuration:channels 高1bit
    52. p_adts_header[3] = (channels & 0x03)<<6; //channel configuration:channels 低2bits
    53. p_adts_header[3] |= (0 << 5); //original:0 1bit
    54. p_adts_header[3] |= (0 << 4); //home:0 1bit
    55. p_adts_header[3] |= (0 << 3); //copyright id bit0 1bit
    56. p_adts_header[3] |= (0 << 2); //copyright id start0 1bit
    57. p_adts_header[3] |= ((adtsLen & 0x1800) >> 11); //frame length:value 高2bits
    58. p_adts_header[4] = (uint8_t)((adtsLen & 0x7f8) >> 3); //frame length:value 中间8bits
    59. p_adts_header[5] = (uint8_t)((adtsLen & 0x7) << 5); //frame length:value3bits
    60. p_adts_header[5] |= 0x1f; //buffer fullness:0x7ff 高5bits
    61. p_adts_header[6] = 0xfc; //11111100//buffer fullness:0x7ff 低6bits
    62. // number_of_raw_data_blocks_in_frame:
    63. // 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧。
    64. return 0;
    65. }
    66. int main(int argc, char *argv[])
    67. {
    68. int ret = -1;
    69. char errors[1024];
    70. char *in_filename = NULL;
    71. char *aac_filename = NULL;
    72. FILE *aac_fd = NULL;
    73. int audio_index = -1;
    74. int len = 0;
    75. AVFormatContext *ifmt_ctx = NULL;
    76. AVPacket pkt;
    77. // 设置打印级别
    78. av_log_set_level(AV_LOG_DEBUG);
    79. if(argc < 3)
    80. {
    81. av_log(NULL, AV_LOG_DEBUG, "the count of parameters should be more than three!\n");
    82. return -1;
    83. }
    84. in_filename = argv[1]; // 输入文件
    85. aac_filename = argv[2]; // 输出文件
    86. if(in_filename == NULL || aac_filename == NULL)
    87. {
    88. av_log(NULL, AV_LOG_DEBUG, "src or dts file is null, plz check them!\n");
    89. return -1;
    90. }
    91. aac_fd = fopen(aac_filename, "wb");
    92. if (!aac_fd)
    93. {
    94. av_log(NULL, AV_LOG_DEBUG, "Could not open destination file %s\n", aac_filename);
    95. return -1;
    96. }
    97. // 打开输入文件
    98. if((ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL)) < 0)
    99. {
    100. av_strerror(ret, errors, 1024);
    101. av_log(NULL, AV_LOG_DEBUG, "Could not open source file: %s, %d(%s)\n",
    102. in_filename,
    103. ret,
    104. errors);
    105. return -1;
    106. }
    107. // 获取解码器信息
    108. if((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0)
    109. {
    110. av_strerror(ret, errors, 1024);
    111. av_log(NULL, AV_LOG_DEBUG, "failed to find stream information: %s, %d(%s)\n",
    112. in_filename,
    113. ret,
    114. errors);
    115. return -1;
    116. }
    117. // dump媒体信息
    118. // av_dump_format(ifmt_ctx, 0, in_filename, 0);
    119. // 初始化packet
    120. av_init_packet(&pkt);
    121. // 查找audio对应的steam index
    122. audio_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    123. if(audio_index < 0)
    124. {
    125. av_log(NULL, AV_LOG_DEBUG, "Could not find %s stream in input file %s\n",
    126. av_get_media_type_string(AVMEDIA_TYPE_AUDIO),
    127. in_filename);
    128. return AVERROR(EINVAL);
    129. }
    130. // 打印AAC级别
    131. printf("audio profile:%d, FF_PROFILE_AAC_LOW:%d\n",
    132. ifmt_ctx->streams[audio_index]->codecpar->profile,
    133. FF_PROFILE_AAC_LOW);
    134. if(ifmt_ctx->streams[audio_index]->codecpar->codec_id != AV_CODEC_ID_AAC)
    135. {
    136. printf("the media file no contain AAC stream, it's codec_id is %d\n",
    137. ifmt_ctx->streams[audio_index]->codecpar->codec_id);
    138. goto failed;
    139. }
    140. // 读取媒体文件,并把aac数据帧写入到本地文件
    141. while(av_read_frame(ifmt_ctx, &pkt) >=0 )
    142. {
    143. if(pkt.stream_index == audio_index)
    144. {
    145. char adts_header_buf[7] = {0};
    146. adts_header(adts_header_buf, pkt.size,
    147. ifmt_ctx->streams[audio_index]->codecpar->profile,
    148. ifmt_ctx->streams[audio_index]->codecpar->sample_rate,
    149. ifmt_ctx->streams[audio_index]->codecpar->ch_layout.nb_channels);
    150. fwrite(adts_header_buf, 1, 7, aac_fd); // 写adts header , ts流不适用,ts流分离出来的packet带了adts header
    151. len = fwrite( pkt.data, 1, pkt.size, aac_fd); // 写adts data
    152. if(len != pkt.size)
    153. {
    154. av_log(NULL, AV_LOG_DEBUG, "warning, length of writed data isn't equal pkt.size(%d, %d)\n",
    155. len,
    156. pkt.size);
    157. }
    158. }
    159. av_packet_unref(&pkt);
    160. }
    161. failed:
    162. // 关闭输入文件
    163. if(ifmt_ctx)
    164. {
    165. avformat_close_input(&ifmt_ctx);
    166. }
    167. if(aac_fd)
    168. {
    169. fclose(aac_fd);
    170. }
    171. return 0;
    172. }

  • 相关阅读:
    论文阅读笔记(十二)——Augmenting large language models with chemistry tools
    自制操作系统日志——第十一天
    【owt】vs2022 + v141 : 查看WINDOWSSDKDIR
    IO流练习 二
    csp非零段划分
    如果不富有,那就像有钱人一样去行动吧_《有钱人和你想的不一样》读书笔记
    单陷门置换
    微服务[Nacos]
    STS中打开Ibatis的xml文件提示错误
    深度卷积神经网络的整体运行流程(以alexnet为例)
  • 原文地址:https://blog.csdn.net/hunandede/article/details/139311863