AAC音频格式详解
2016-02-27 19:59
309 查看
关于AAC音频格式基本情况,可参考维基百科http://en.wikipedia.org/wiki/Advanced_Audio_Coding
AAC音频格式分析
AAC音频格式有ADIF和ADTS:
ADIF:AudioDataInterchangeFormat音频数据交换格式。这种格式的特征是可以确定的找到这个音频数据的开始,不需进行在音频数据流中间开始的解码,即它的解码必须在明确定义的开始处进行。故这种格式常用在磁盘文件中。
ADTS:AudioDataTransportStream音频数据传输流。这种格式的特征是它是一个有同步字的比特流,解码可以在这个流中任何位置开始。它的特征类似于mp3数据流格式。
简单说,ADTS可以在任意帧解码,也就是说它每一帧都有头信息。ADIF只有一个统一的头,所以必须得到所有的数据后解码。且这两种的header的格式也是不同的,目前一般编码后的和抽取出的都是ADTS格式的音频流。
语音系统对实时性要求较高,基本是这样一个流程,采集音频数据,本地编码,数据上传,服务器处理,数据下发,本地解码
ADTS是帧序列,本身具备流特征,在音频流的传输与处理方面更加合适。
ADTS帧结构:
header
body
ADTS帧首部结构:
序号 域 长度(bits) 说明 1 Syncword 12 allbitsmustbe1 2 MPEGversion 1 0forMPEG-4,1forMPEG-2 3 Layer 2 always0 4 ProtectionAbsent 1 etto1ifthereisnoCRCand0ifthereisCRC 5 Profile 2 theMPEG-4AudioObjectTypeminus1 6 MPEG-4SamplingFrequencyIndex 4 MPEG-4SamplingFrequencyIndex(15isforbidden) 7 PrivateStream 1 setto0whenencoding,ignorewhendecoding 8 MPEG-4ChannelConfiguration 3 MPEG-4ChannelConfiguration(inthecaseof0,thechannelconfigurationissentviaaninband
PCE) 9 Originality 1 setto0whenencoding,ignorewhendecoding 10 Home 1 setto0whenencoding,ignorewhendecoding 11 CopyrightedStream 1 setto0whenencoding,ignorewhendecoding 12 CopyrightedStart 1 setto0whenencoding,ignorewhendecoding 13 FrameLength 13 thisvaluemustinclude7or9bytesofheaderlength:FrameLength=(ProtectionAbsent==1?7:9)+size(AACFrame) 14 BufferFullness 11 bufferfullness 15 NumberofAACFrames 2 numberofAACframes(RDBs)inADTSframeminus1,formaximumcompatibilityalwaysuse1AACframeperADTSframe 16 CRC 16 CRCifprotectionabsentis0
AAC解码
在解码方面,使用了开源的FAAD,http://www.audiocoding.com/faad2.html
sdk解压缩后,docs目录有详细的api说明文档,主要用到的有以下几个:
NeAACDecHandleNEAACAPINeAACDecOpen(void);
创建解码环境并返回一个句柄
voidNEAACAPINeAACDecClose(NeAACDecHandlehDecoder);
关闭解码环境
NeAACDecConfigurationPtrNEAACAPINeAACDecGetCurrentConfiguration(NeAACDecHandlehDecoder);
获取当前解码器库的配置
unsignedcharNEAACAPINeAACDecSetConfiguration(NeAACDecHandlehDecoder,NeAACDecConfigurationPtrconfig);
为解码器库设置一个配置结构
longNEAACAPINeAACDecInit(NeAACDecHandlehDecoder,unsignedchar*buffer,unsignedlongbuffer_size,unsignedlong*samplerate,unsignedchar*channels);
初始化解码器库
void*NEAACAPINeAACDecDecode(NeAACDecHandlehDecoder,NeAACDecFrameInfo*hInfo,unsignedchar*buffer,unsignedlongbuffer_size);
解码AAC数据
对以上api做了简单封装,写了一个解码类,涵盖了FAAD库的基本用法,感兴趣的朋友可以看看
MyAACDecoder.h:
/**
*
*filename:MyAACDecoder.h
*summary:convertaactowave
*author:caosiyang
*email:csy3228@gmail.com
*
*/
#ifndef__MYAACDECODER_H__
#define__MYAACDECODER_H__
#include"Buffer.h"
#include"mytools.h"
#include"WaveFormat.h"
#include"faad.h"
#include<iostream>
usingnamespacestd;
classMyAACDecoder{
public:
MyAACDecoder();
~MyAACDecoder();
int32_tDecode(char*aacbuf,uint32_taacbuflen);
constchar*WavBodyData()const{
return_mybuffer.Data();
}
uint32_tWavBodyLength()const{
return_mybuffer.Length();
}
constchar*WavHeaderData()const{
return_wave_format.getHeaderData();
}
uint32_tWavHeaderLength()const{
return_wave_format.getHeaderLength();
}
private:
MyAACDecoder(constMyAACDecoder&dec);
MyAACDecoder&operator=(constMyAACDecoder&rhs);
//initAACdecoder
int32_t_init_aac_decoder(char*aacbuf,int32_taacbuflen);
//destroyaacdecoder
void_destroy_aac_decoder();
//parseAACADTSheader,getframelength
uint32_t_get_frame_length(constchar*aac_header)const;
//AACdecoderproperties
NeAACDecHandle_handle;
unsignedlong_samplerate;
unsignedchar_channel;
Buffer_mybuffer;
WaveFormat_wave_format;
};
#endif/*__MYAACDECODER_H__*/
MyAACDecoder.cpp:
#include"MyAACDecoder.h"
MyAACDecoder::MyAACDecoder():_handle(NULL),_samplerate(44100),_channel(2),_mybuffer(4096,4096){
}
MyAACDecoder::~MyAACDecoder(){
_destroy_aac_decoder();
}
int32_tMyAACDecoder::Decode(char*aacbuf,uint32_taacbuflen){
int32_tres=0;
if(!_handle){
if(_init_aac_decoder(aacbuf,aacbuflen)!=0){
ERR1("::::initaacdecoderfailed::::");
return-1;
}
}
//clean_mybuffer
_mybuffer.Clean();
uint32_tdonelen=0;
uint32_twav_data_len=0;
while(donelen<aacbuflen){
uint32_tframelen=_get_frame_length(aacbuf+donelen);
if(donelen+framelen>aacbuflen){
break;
}
//decode
NeAACDecFrameInfoinfo;
void*buf=NeAACDecDecode(_handle,&info,(unsignedchar*)aacbuf+donelen,framelen);
if(buf&&info.error==0){
if(info.samplerate==44100){
//44100Hz
//src:2048samples,4096bytes
//dst:2048samples,4096bytes
uint32_ttmplen=info.samples*16/8;
_mybuffer.Fill((constchar*)buf,tmplen);
wav_data_len+=tmplen;
}elseif(info.samplerate==22050){
//22050Hz
//src:1024samples,2048bytes
//dst:2048samples,4096bytes
short*ori=(short*)buf;
shorttmpbuf[info.samples*2];
uint32_ttmplen=info.samples*16/8*2;
for(int32_ti=0,j=0;i<info.samples;i+=2){
tmpbuf[j++]=ori[i];
tmpbuf[j++]=ori[i+1];
tmpbuf[j++]=ori[i];
tmpbuf[j++]=ori[i+1];
}
_mybuffer.Fill((constchar*)tmpbuf,tmplen);
wav_data_len+=tmplen;
}
}else{
ERR1("NeAACDecDecode()failed");
}
donelen+=framelen;
}
//generateWaveheader
_wave_format.setSampleRate(_samplerate);
_wave_format.setChannel(_channel);
_wave_format.setSampleBit(16);
_wave_format.setBandWidth(_samplerate*16*_channel/8);
_wave_format.setDataLength(wav_data_len);
_wave_format.setTotalLength(wav_data_len+44);
_wave_format.GenerateHeader();
return0;
}
uint32_tMyAACDecoder::_get_frame_length(constchar*aac_header)const{
uint32_tlen=*(uint32_t*)(aac_header+3);
len=ntohl(len);//LittleEndian
len=len<<6;
len=len>>19;
returnlen;
}
int32_tMyAACDecoder::_init_aac_decoder(char*aacbuf,int32_taacbuflen){
unsignedlongcap=NeAACDecGetCapabilities();
_handle=NeAACDecOpen();
if(!_handle){
ERR1("NeAACDecOpen()failed");
_destroy_aac_decoder();
return-1;
}
NeAACDecConfigurationPtrconf=NeAACDecGetCurrentConfiguration(_handle);
if(!conf){
ERR1("NeAACDecGetCurrentConfiguration()failed");
_destroy_aac_decoder();
return-1;
}
NeAACDecSetConfiguration(_handle,conf);
longres=NeAACDecInit(_handle,(unsignedchar*)aacbuf,aacbuflen,&_samplerate,&_channel);
if(res<0){
ERR1("NeAACDecInit()failed");
_destroy_aac_decoder();
return-1;
}
//fprintf(stdout,"SampleRate=%d\n",_samplerate);
//fprintf(stdout,"Channel=%d\n",_channel);
//fprintf(stdout,"::::initaacdecoderdone::::\n");
return0;
}
voidMyAACDecoder::_destroy_aac_decoder(){
if(_handle){
NeAACDecClose(_handle);
_handle=NULL;
}
}
AAC音频格式分析
AAC音频格式有ADIF和ADTS:
ADIF:AudioDataInterchangeFormat音频数据交换格式。这种格式的特征是可以确定的找到这个音频数据的开始,不需进行在音频数据流中间开始的解码,即它的解码必须在明确定义的开始处进行。故这种格式常用在磁盘文件中。
ADTS:AudioDataTransportStream音频数据传输流。这种格式的特征是它是一个有同步字的比特流,解码可以在这个流中任何位置开始。它的特征类似于mp3数据流格式。
简单说,ADTS可以在任意帧解码,也就是说它每一帧都有头信息。ADIF只有一个统一的头,所以必须得到所有的数据后解码。且这两种的header的格式也是不同的,目前一般编码后的和抽取出的都是ADTS格式的音频流。
语音系统对实时性要求较高,基本是这样一个流程,采集音频数据,本地编码,数据上传,服务器处理,数据下发,本地解码
ADTS是帧序列,本身具备流特征,在音频流的传输与处理方面更加合适。
ADTS帧结构:
header | body |
序号 | 域 | 长度(bits) | 说明 |
1 | Syncword | 12 | allbitsmustbe1 |
2 | MPEGversion | 1 | 0forMPEG-4,1forMPEG-2 |
3 | Layer | 2 | always0 |
4 | ProtectionAbsent | 1 | etto1ifthereisnoCRCand0ifthereisCRC |
5 | Profile | 2 | the |
6 | MPEG-4SamplingFrequencyIndex | 4 | |
7 | PrivateStream | 1 | setto0whenencoding,ignorewhendecoding |
8 | MPEG-4ChannelConfiguration | 3 | PCE) |
9 | Originality | 1 | setto0whenencoding,ignorewhendecoding |
10 | Home | 1 | setto0whenencoding,ignorewhendecoding |
11 | CopyrightedStream | 1 | setto0whenencoding,ignorewhendecoding |
12 | CopyrightedStart | 1 | setto0whenencoding,ignorewhendecoding |
13 | FrameLength | 13 | thisvaluemustinclude7or9bytesofheaderlength:FrameLength=(ProtectionAbsent==1?7:9)+size(AACFrame) |
14 | BufferFullness | 11 | bufferfullness |
15 | NumberofAACFrames | 2 | numberofAACframes(RDBs)inADTSframeminus1,formaximumcompatibilityalwaysuse1AACframeperADTSframe |
16 | CRC | 16 | CRCifprotectionabsentis0 |
在解码方面,使用了开源的FAAD,
sdk解压缩后,docs目录有详细的api说明文档,主要用到的有以下几个:
NeAACDecHandleNEAACAPINeAACDecOpen(void);
创建解码环境并返回一个句柄
voidNEAACAPINeAACDecClose(NeAACDecHandlehDecoder);
关闭解码环境
NeAACDecConfigurationPtrNEAACAPINeAACDecGetCurrentConfiguration(NeAACDecHandlehDecoder);
获取当前解码器库的配置
unsignedcharNEAACAPINeAACDecSetConfiguration(NeAACDecHandlehDecoder,NeAACDecConfigurationPtrconfig);
为解码器库设置一个配置结构
longNEAACAPINeAACDecInit(NeAACDecHandlehDecoder,unsignedchar*buffer,unsignedlongbuffer_size,unsignedlong*samplerate,unsignedchar*channels);
初始化解码器库
void*NEAACAPINeAACDecDecode(NeAACDecHandlehDecoder,NeAACDecFrameInfo*hInfo,unsignedchar*buffer,unsignedlongbuffer_size);
解码AAC数据
对以上api做了简单封装,写了一个解码类,涵盖了FAAD库的基本用法,感兴趣的朋友可以看看
MyAACDecoder.h:
/**
*
*filename:MyAACDecoder.h
*summary:convertaactowave
*author:caosiyang
*email:csy3228@gmail.com
*
*/
#ifndef__MYAACDECODER_H__
#define__MYAACDECODER_H__
#include"Buffer.h"
#include"mytools.h"
#include"WaveFormat.h"
#include"faad.h"
#include<iostream>
usingnamespacestd;
classMyAACDecoder{
public:
MyAACDecoder();
~MyAACDecoder();
int32_tDecode(char*aacbuf,uint32_taacbuflen);
constchar*WavBodyData()const{
return_mybuffer.Data();
}
uint32_tWavBodyLength()const{
return_mybuffer.Length();
}
constchar*WavHeaderData()const{
return_wave_format.getHeaderData();
}
uint32_tWavHeaderLength()const{
return_wave_format.getHeaderLength();
}
private:
MyAACDecoder(constMyAACDecoder&dec);
MyAACDecoder&operator=(constMyAACDecoder&rhs);
//initAACdecoder
int32_t_init_aac_decoder(char*aacbuf,int32_taacbuflen);
//destroyaacdecoder
void_destroy_aac_decoder();
//parseAACADTSheader,getframelength
uint32_t_get_frame_length(constchar*aac_header)const;
//AACdecoderproperties
NeAACDecHandle_handle;
unsignedlong_samplerate;
unsignedchar_channel;
Buffer_mybuffer;
WaveFormat_wave_format;
};
#endif/*__MYAACDECODER_H__*/
MyAACDecoder.cpp:
#include"MyAACDecoder.h"
MyAACDecoder::MyAACDecoder():_handle(NULL),_samplerate(44100),_channel(2),_mybuffer(4096,4096){
}
MyAACDecoder::~MyAACDecoder(){
_destroy_aac_decoder();
}
int32_tMyAACDecoder::Decode(char*aacbuf,uint32_taacbuflen){
int32_tres=0;
if(!_handle){
if(_init_aac_decoder(aacbuf,aacbuflen)!=0){
ERR1("::::initaacdecoderfailed::::");
return-1;
}
}
//clean_mybuffer
_mybuffer.Clean();
uint32_tdonelen=0;
uint32_twav_data_len=0;
while(donelen<aacbuflen){
uint32_tframelen=_get_frame_length(aacbuf+donelen);
if(donelen+framelen>aacbuflen){
break;
}
//decode
NeAACDecFrameInfoinfo;
void*buf=NeAACDecDecode(_handle,&info,(unsignedchar*)aacbuf+donelen,framelen);
if(buf&&info.error==0){
if(info.samplerate==44100){
//44100Hz
//src:2048samples,4096bytes
//dst:2048samples,4096bytes
uint32_ttmplen=info.samples*16/8;
_mybuffer.Fill((constchar*)buf,tmplen);
wav_data_len+=tmplen;
}elseif(info.samplerate==22050){
//22050Hz
//src:1024samples,2048bytes
//dst:2048samples,4096bytes
short*ori=(short*)buf;
shorttmpbuf[info.samples*2];
uint32_ttmplen=info.samples*16/8*2;
for(int32_ti=0,j=0;i<info.samples;i+=2){
tmpbuf[j++]=ori[i];
tmpbuf[j++]=ori[i+1];
tmpbuf[j++]=ori[i];
tmpbuf[j++]=ori[i+1];
}
_mybuffer.Fill((constchar*)tmpbuf,tmplen);
wav_data_len+=tmplen;
}
}else{
ERR1("NeAACDecDecode()failed");
}
donelen+=framelen;
}
//generateWaveheader
_wave_format.setSampleRate(_samplerate);
_wave_format.setChannel(_channel);
_wave_format.setSampleBit(16);
_wave_format.setBandWidth(_samplerate*16*_channel/8);
_wave_format.setDataLength(wav_data_len);
_wave_format.setTotalLength(wav_data_len+44);
_wave_format.GenerateHeader();
return0;
}
uint32_tMyAACDecoder::_get_frame_length(constchar*aac_header)const{
uint32_tlen=*(uint32_t*)(aac_header+3);
len=ntohl(len);//LittleEndian
len=len<<6;
len=len>>19;
returnlen;
}
int32_tMyAACDecoder::_init_aac_decoder(char*aacbuf,int32_taacbuflen){
unsignedlongcap=NeAACDecGetCapabilities();
_handle=NeAACDecOpen();
if(!_handle){
ERR1("NeAACDecOpen()failed");
_destroy_aac_decoder();
return-1;
}
NeAACDecConfigurationPtrconf=NeAACDecGetCurrentConfiguration(_handle);
if(!conf){
ERR1("NeAACDecGetCurrentConfiguration()failed");
_destroy_aac_decoder();
return-1;
}
NeAACDecSetConfiguration(_handle,conf);
longres=NeAACDecInit(_handle,(unsignedchar*)aacbuf,aacbuflen,&_samplerate,&_channel);
if(res<0){
ERR1("NeAACDecInit()failed");
_destroy_aac_decoder();
return-1;
}
//fprintf(stdout,"SampleRate=%d\n",_samplerate);
//fprintf(stdout,"Channel=%d\n",_channel);
//fprintf(stdout,"::::initaacdecoderdone::::\n");
return0;
}
voidMyAACDecoder::_destroy_aac_decoder(){
if(_handle){
NeAACDecClose(_handle);
_handle=NULL;
}
}
1.ADTS是个啥
ADTS全称是(AudioDataTransportStream),是AAC的一种十分常见的传输格式。记得第一次做demux的时候,把AAC音频的ES流从FLV封装格式中抽出来送给硬件解码器时,不能播;保存到本地用pc的播放器播时,我靠也不能播。当时崩溃了,后来通过查找资料才知道。一般的AAC解码器都需要把AAC的ES流打包成ADTS的格式,一般是在AACES流前添加7个字节的ADTSheader。也就是说你可以吧ADTS这个头看作是AAC的frameheader。
ADTSAAC | ||||||
ADTS_header | AACES | ADTS_header | AACES | [align=center]...[/align] | ADTS_header | AACES |
2.ADTS内容及结构
ADTS头中相对有用的信息采样率、声道数、帧长度。想想也是,我要是解码器的话,你给我一堆得AAC音频ES流我也解不出来。每一个带ADTS头信息的AAC流会清晰的告送解码器他需要的这些信息。一般情况下ADTS的头信息都是7个字节,分为2部分:
adts_fixed_header();
adts_variable_header();
syncword:同步头总是0xFFF,allbitsmustbe1,代表着一个ADTS帧的开始
ID:MPEGVersion:0forMPEG-4,1forMPEG-2
Layer:always:'00'
profile:表示使用哪个级别的AAC,有些芯片只支持AACLC。在MPEG-2AAC中定义了3种:
sampling_frequency_index:表示使用的采样率下标,通过这个下标在SamplingFrequencies[]数组中查找得知采样率的值。
Thereare13supportedfrequencies:
0:96000Hz
1:88200Hz
2:64000Hz
3:48000Hz
4:44100Hz
5:32000Hz
6:24000Hz
7:22050Hz
8:16000Hz
9:12000Hz
10:11025Hz
11:8000Hz
12:7350Hz
13:Reserved
14:Reserved
15:frequencyiswrittenexplictly
channel_configuration:表示声道数
0:DefinedinAOTSpecifcConfig
1:1channel:front-center
2:2channels:front-left,front-right
3:3channels:front-center,front-left,front-right
4:4channels:front-center,front-left,front-right,back-center
5:5channels:front-center,front-left,front-right,back-left,back-right
6:6channels:front-center,front-left,front-right,back-left,back-right,LFE-channel
7:8channels:front-center,front-left,front-right,side-left,side-right,back-left,back-right,LFE-channel
8-15:Reserved
frame_length:一个ADTS帧的长度包括ADTS头和AAC原始流.
adts_buffer_fullness:0x7FF说明是码率可变的码流
3.将AAC打包成ADTS格式
如果是通过嵌入式高清解码芯片做产品的话,一般情况的解码工作都是由硬件来完成的。所以大部分的工作是把AAC原始流打包成ADTS的格式,然后丢给硬件就行了。通过对ADTS格式的了解,很容易就能把AAC打包成ADTS。我们只需得到封装格式里面关于音频采样率、声道数、元数据长度、aac格式类型等信息。然后在每个AAC原始流前面加上个ADTS头就OK了。
贴上ffmpeg中添加ADTS头的代码,就可以很清晰的了解ADTS头的结构:
[html]view
plain
intff_adts_write_frame_header(ADTSContext*ctx,
uint8_t*buf,intsize,intpce_size)
{
PutBitContextpb;
init_put_bits(&pb,buf,ADTS_HEADER_SIZE);
/*adts_fixed_header*/
put_bits(&pb,12,0xfff);/*syncword*/
put_bits(&pb,1,0);/*ID*/
put_bits(&pb,2,0);/*layer*/
put_bits(&pb,1,1);/*protection_absent*/
put_bits(&pb,2,ctx->objecttype);/*profile_objecttype*/
put_bits(&pb,4,ctx->sample_rate_index);
put_bits(&pb,1,0);/*private_bit*/
put_bits(&pb,3,ctx->channel_conf);/*channel_configuration*/
put_bits(&pb,1,0);/*original_copy*/
put_bits(&pb,1,0);/*home*/
/*adts_variable_header*/
put_bits(&pb,1,0);/*copyright_identification_bit*/
put_bits(&pb,1,0);/*copyright_identification_start*/
put_bits(&pb,13,ADTS_HEADER_SIZE+size+pce_size);/*aac_frame_length*/
put_bits(&pb,11,0x7ff);/*adts_buffer_fullness*/
put_bits(&pb,2,0);/*number_of_raw_data_blocks_in_frame*/
flush_put_bits(&pb);
return0;
}
相关文章推荐
- Bean获取Spring容器
- Android系统下载管理DownloadManager功能介绍及使用示例
- 浅析那些大型职业技术交流群是怎么被玩变质的?
- 浅析那些大型职业技术交流群是怎么被玩变质的?
- Schematic - "DesignEntry 30 ERROR Instance referencing symbol is out of date"
- iOS 8以上的设置的跳转
- BZOJ-3231 递归数列 矩阵连乘+快速幂
- 测试必备技能系列1 :通过mysql命令进行脚本数据导入
- LeetCode88——Merge Sorted Array
- 使用位运算转换大小写
- Java设计模式---单例模式
- BZOJ-3231 递归数列 矩阵连乘+快速幂
- android 时间滚动控件 底部弹出
- 你知道哪些linux命令,能把文件上传到远程linux服务器
- Demo9:简单晒单效果
- mybatis学习笔记(3)-入门程序二
- 真是悟了
- 浅析那些大型职业技术交流群是怎么被玩变质的?
- mysql主从复制 设置中继日志自动清除 主从同步失败,如何快速同步
- androidstudio入门-新建模拟器