AAC格式分析

简介

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

通常会将AAC与ADTS等容器格式混淆,但它们其实是不同的概念。AAC是编码规范,ADTS/ADIF是它的容器格式。

  1. ADIF (Audio Data Interchange Format):

    • ADIF是一种AAC音频的文件封装格式,它将AAC编码后的数据直接存储在文件中。
    • ADIF文件中不包含任何帧头信息,整个文件就是纯AAC编码数据。
    • ADIF格式不适合流媒体传输,因为无法实现随机访问。
  2. ADTS (Audio Data Transport Stream):

    • ADTS是AAC音频的一种容器格式,常用于流媒体传输。
    • ADTS在AAC编码数据前添加了帧头信息,包括一些同步字节、采样率、声道数等。
    • ADTS格式允许随机访问,非常适合用于流式传输。
    • ADTS通常用于MP4、TS等容器格式中的AAC音频部分。

简单说,ADTS可以在任意帧解码,也就是说它每⼀帧都有头信息。ADIF只有⼀ 个统⼀的头,所以必须得到所有的数据后解码。 

且这两种的header的格式也是不同的,⽬前⼀般编码后的和抽取出的都是 ADTS格式的⾳频流。两者具体的组织结构如下所示:

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

ADIF 分析

这个格式比较少见,简单认识一下即可,ADIF 是一种用于存储 MP3 编码音频数据的容器格式。下图是ADIF格式的序列,由adif_header、byte_alignment、raw_data_stream三部分组成。byte_alignment是用来做字节对齐的,也就是说,ADIF格式由一个ADIF头信息(adif_header) 和 原始数据流(raw_data_stream) 构成。

 

ADTS分析

ADTS格式每⼀帧由ADTS Header和AAC Audio Data组成。结构体如 下:

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

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

adts_fixed_header();
adts_variable_header();

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

 adts_fixed_header();

  • 同步字头(Sync word, 12 位)

    • 值为 0xFFF,用于标识 ADTS 帧的开始位置。
  • ID (1 位)

    • 0 表示 MPEG-4 音频,1 表示 MPEG-2 音频。
  • 层(Layer, 2 位)

    • 0 表示不使用任何层1、2、3 分别表示使用第 1、2、3 层
  • 无保护(Protection absent, 1 位) 决定7/9B

    • 0 表示有 CRC 校验   1 表示没有 CRC 校验
  • 编码 Profile (Profile object type, 2 位)

    • 0 表示保留
    • 1 表示 AAC main
    • 2 表示 AAC LC (Low Complexity)
    • 3 表示 AAC SSR (Scalable Sampling Rate)
    • 4 表示 AAC LTP (Long Term Prediction)

  • 采样率索引(Sampling frequency index, 4 位)

    • 指示音频的采样率

  • 私有位(Private bit, 1 位)

    • 可由应用自由使用 占用 1 bit,编码时设置为0,解码时忽略;
  • 声道配置(Channel configuration, 3 位)

    • 指示音频的声道数配置

  • 原始/拷贝(Original/copy, 1 位)

    • 占用 1 bit,编码时设置为0,解码时忽略;  0 表示拷贝1 表示原始
  • 家庭(Home, 1 位)

    • 通常设为 0

采样率 编码格式 声道数

下图可知,id字段决定了mpeg-2还是mpeg-4,

这里画错了,不是absent字段。

profile字段依赖id字段,给出了多种aac版本,官方文档定义的是要在给定值-1操作,才能正确取出对应值。

ffmpeg则自定义了宏,无需+1-1操作。

声道位 

下面给出预定义好的声道和采样率 方便以后使用
sampling_frequencies表示使⽤的采样率下标,通过这个下标在数组中查找得知采样率的值
const int sampling_frequencies[] = {
    96000,  // 0x0
    88200,  // 0x1
    64000,  // 0x2
    48000,  // 0x3
    44100,  // 0x4
    32000,  // 0x5
    24000,  // 0x6
    22050,  // 0x7
    16000,  // 0x8
    12000,  // 0x9
    11025,  // 0xa
    8000,   // 0xb
    7350    // 0xc
    // 0xd e f是保留的
};

sampling_frequencies 表示声道数,⽐如2表示⽴体声双声道
FFmpeg 提供了一些常用的声道宏定
AV_CH_FRONT_LEFT:前左声道
AV_CH_FRONT_RIGHT:前右声道
AV_CH_FRONT_CENTER:前中声道
AV_CH_LOW_FREQUENCY:低频声道
AV_CH_BACK_LEFT:后左声道
AV_CH_BACK_RIGHT:后右声道
AV_CH_FRONT_LEFT_OF_CENTER:前左中声道
AV_CH_FRONT_RIGHT_OF_CENTER:前右中声道
AV_CH_BACK_CENTER:后中声道
AV_CH_SIDE_LEFT:左侧声道
AV_CH_SIDE_RIGHT:右侧声道
AV_CH_TOP_CENTER:顶中声道
AV_CH_TOP_FRONT_LEFT:前左顶声道
AV_CH_TOP_FRONT_CENTER:前中顶声道
AV_CH_TOP_FRONT_RIGHT:前右顶声道
AV_CH_TOP_BACK_LEFT:后左顶声道
AV_CH_TOP_BACK_CENTER:后中顶声道
AV_CH_TOP_BACK_RIGHT:后右顶声道

adts_variable_header(); 

ADTS 可变头部包含的字段:

  1. 版权标识位(Copyright Identification Bit):

这是 1 位的标志位

表示当前 ADTS 帧是否有版权保护

值为 0 表示没有版权保护

值为 1 表示有版权保护

  1. 版权标识起始(Copyright Identification Start):

这也是 1 位的标志位

表示当前 ADTS 帧是否为版权数据的起始

值为 0 表示当前帧不是版权数据的起始

值为 1 表示当前帧是版权数据的起始 

AAC 帧长度 (AAC Frame Length): 13 位表示整个 ADTS 帧的长度,包括头部和负载数据,单位为字节。包括 ADTS 头部长度和 AAC 原始数据长度protection_absent 为 0 时,头部长度为 9 字节  aac_frame_length = (protection_absent == 1 ? 7 : 9) + size(AACFrame)

ADTS 缓冲区满溢 (ADTS Buffer Fullness): 11 位表示当前帧的缓冲区填充程度。用于码率控制和帧间同步。 默认0x7FF 说明是码率可变的码流。0x000 表示固定码率的码流;

原始数据块数 (Number of Raw Data Blocks in Frame): 2 位,表示当前帧包含的原始数据块数。一般为 0(ADTS 帧中只有 1 个 AAC 数据块)。表示 ADTS 帧中包含的 AAC 原始数据块数,当 number_of_raw_data_blocks_in_frame == 0 时,表示 ADTS 帧中只有 1 个 AAC 数据块

总结作用:
- 版权标识: 表示当前帧是否受版权保护。
- 帧长度: 解码器需要知道帧的长度以正确解析数据。
- 缓冲区满溢: 用于码率控制和帧间同步。
- 原始数据块数: 用于支持一个 ADTS 帧包含多个原始数据块的情况。

 

adts与aac文件存储排布分析

 

头部 28bit固定 28bit可变
0xFF 0xF1 0x4C 0x40 0x20 0xFF 0xFC

0xFF 0xF1:
0xfff 同步字头
0001 
0 	ID:MPEG-4 音频
00	Layer:不使用任何层
1	保护位:没有 CRC 校验

0x4c: 0100 1100
01  	编码Profile:AAC main
00 11 音频的采样率(Sampling frequency index)指示  48000	
0	暂定位 Private:可由应用自由使用

0x40: 0100 0000
0 01 	声道配置(Channel configuration):中前扬声器
0  	原始/拷贝(Original/copy):拷贝
0 	home:位 
以上是adts_fixed_header 

接下来是 adts_variable_header
0  版权标识位(Copyright Identification):没有版权保护
0  版权标识起始(Copyright Identification Start):不是版权数据的起始

0x20 ->0010 0000
0xFF  -> 1111 1111 
00  0010 0000  111   	AAC 帧长度 (AAC Frame Length):  转换成⼗进制为263 

0xFC -> 1111 1100
1 1111  1111 11 		
ADTS 缓冲区满溢 (ADTS Buffer Fullness):默认0x7FF 说明是码率可变的码流。

00-> 原始数据块数:表示 ADTS 帧中包含的 AAC 原始数据块数
当 number_of_raw_data_blocks_in_frame == 0 时,表示 ADTS 帧中只有 1 个 AAC 数据块

 

解析AAC文件的C语言代码

代码来源于:视音频数据处理入门:AAC音频码流解析

/**
 * 最简单的视音频数据处理示例
 * Simplest MediaData Test
 *
 * 雷霄骅 Lei Xiaohua
 * leixiaohua1020@126.com
 * 中国传媒大学/数字电视技术
 * Communication University of China / Digital TV Technology
 * http://blog.youkuaiyun.com/leixiaohua1020
 *
 * 本项目包含如下几种视音频测试示例:
 *  (1)像素数据处理程序。包含RGB和YUV像素格式处理的函数。
 *  (2)音频采样数据处理程序。包含PCM音频采样格式处理的函数。
 *  (3)H.264码流分析程序。可以分离并解析NALU。
 *  (4)AAC码流分析程序。可以分离并解析ADTS帧。
 *  (5)FLV封装格式分析程序。可以将FLV中的MP3音频码流分离出来。
 *  (6)UDP-RTP协议分析程序。可以将分析UDP/RTP/MPEG-TS数据包。
 *
 * This project contains following samples to handling multimedia data:
 *  (1) Video pixel data handling program. It contains several examples to handle RGB and YUV data.
 *  (2) Audio sample data handling program. It contains several examples to handle PCM data.
 *  (3) H.264 stream analysis program. It can parse H.264 bitstream and analysis NALU of stream.
 *  (4) AAC stream analysis program. It can parse AAC bitstream and analysis ADTS frame of stream.
 *  (5) FLV format analysis program. It can analysis FLV file and extract MP3 audio stream.
 *  (6) UDP-RTP protocol analysis program. It can analysis UDP/RTP/MPEG-TS Packet.
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int getADTSframe(unsigned char* buffer, int buf_size, unsigned char* data ,int* data_size){
	int size = 0;

	if(!buffer || !data || !data_size ){
		return -1;
	}

	while(1){
		if(buf_size  < 7 ){
			return -1;
		}
		//Sync words
		if((buffer[0] == 0xff) && ((buffer[1] & 0xf0) == 0xf0) ){
			size |= ((buffer[3] & 0x03) <<11);     //high 2 bit
			size |= buffer[4]<<3;                //middle 8 bit
			size |= ((buffer[5] & 0xe0)>>5);        //low 3bit
			break;
		}
		--buf_size;
		++buffer;
	}

	if(buf_size < size){
		return 1;
	}

	memcpy(data, buffer, size);
	*data_size = size;

	return 0;
}

int simplest_aac_parser(char *url)
{
	int data_size = 0;
	int size = 0;
	int cnt=0;
	int offset=0;

	//FILE *myout=fopen("output_log.txt","wb+");
	FILE *myout=stdout;

	unsigned char *aacframe=(unsigned char *)malloc(1024*5);
	unsigned char *aacbuffer=(unsigned char *)malloc(1024*1024);

	FILE *ifile = fopen(url, "rb");
	if(!ifile){
		printf("Open file error\n");
		return -1;
	}

	printf("-----+- ADTS Frame Table -+------+\n");
	printf(" NUM | Profile | Frequency| Size |\n");
	printf("-----+---------+----------+------+\n");

	while(!feof(ifile)){
		data_size = fread(aacbuffer+offset, 1, 1024*1024-offset, ifile);
		unsigned char* input_data = aacbuffer;

		while(1)
		{
			int ret=getADTSframe(input_data, data_size, aacframe, &size);
			if(ret==-1){
				break;
			}else if(ret==1){
				memcpy(aacbuffer,input_data,data_size);
				offset=data_size;
				break;
			}

			char profile_str[10]={0};
			char frequence_str[10]={0};

			unsigned char profile=aacframe[2]&0xC0;
			profile=profile>>6;
			switch(profile){
			case 0: sprintf(profile_str,"Main");break;
			case 1: sprintf(profile_str,"LC");break;
			case 2: sprintf(profile_str,"SSR");break;
			default:sprintf(profile_str,"unknown");break;
			}

			unsigned char sampling_frequency_index=aacframe[2]&0x3C;
			sampling_frequency_index=sampling_frequency_index>>2;
			switch(sampling_frequency_index){
			case 0: sprintf(frequence_str,"96000Hz");break;
			case 1: sprintf(frequence_str,"88200Hz");break;
			case 2: sprintf(frequence_str,"64000Hz");break;
			case 3: sprintf(frequence_str,"48000Hz");break;
			case 4: sprintf(frequence_str,"44100Hz");break;
			case 5: sprintf(frequence_str,"32000Hz");break;
			case 6: sprintf(frequence_str,"24000Hz");break;
			case 7: sprintf(frequence_str,"22050Hz");break;
			case 8: sprintf(frequence_str,"16000Hz");break;
			case 9: sprintf(frequence_str,"12000Hz");break;
			case 10: sprintf(frequence_str,"11025Hz");break;
			case 11: sprintf(frequence_str,"8000Hz");break;
			default:sprintf(frequence_str,"unknown");break;
			}


			fprintf(myout,"%5d| %8s|  %8s| %5d|\n",cnt,profile_str ,frequence_str,size);
			data_size -= size;
			input_data += size;
			cnt++;
		}   

	}
	fclose(ifile);
	free(aacbuffer);
	free(aacframe);

	return 0;
}

int main()
{
	simplest_aac_parser((char*)"./audio.aac");
	return 0;
}

总结

AAC是编码格式,ADTS是一种容器格式,在处理AAC音频时需要注意两者的区别和联系。

         ADTS格式在每个AAC音频帧的开头添加了一些头部信息,如采样率、声道数等,用于指示这个音频帧的属性。这样可以方便接收方识别和解析音频数据。

参考资料

【音视频 | AAC】AAC格式音频文件解析_aac 格式 详解-优快云博客

学习资料分享

0voice · GitHub

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值