当前位置:   article > 正文

Android直播开发之旅(3):AAC编码格式分析与MP4文件封装(MediaCodec+MediaMuxer)_mediacodec encoder mediamuxer

mediacodec encoder mediamuxer

Android直播开发之旅(3):AAC编码格式分析与MP4文件封装(MediaCodec+MediaMuxer)

(码字不易,转载请声明出处:http://blog.csdn.net/andrexpert/article/details/72523408)

 

1.  AAC编码格式分析

(1)  AAC简介

    高级音频编码(AdvancedAudio Coding,AAC)一种基于MPEG-4的音频编码技术,它由杜比实验室、AT&T等公司共同研发,目的是替换MP3编码方式。作为一种高压缩比的音频压缩算法,AAC的数据压缩比约为18:1,压缩后的音质可以同未压缩的CD音质相媲美。因此,相对于MP3、WMA等音频编码标准来说,在相同质量下码率更低,有效地节约了传输带宽,被广泛得应用于互联网流媒体、IPTV等领域(低码率,高音质)。主要有以下特点:

    a) 比特率:AAC- 最高512kbps(双声道时)/MP3- 32~320kbps

    b)  采样率:AAC- 最高96kHz / MP3 - 最高48kHz

    c) 声道数:AAC– 最高48个全音域声道/MP3 - 两声道

    d) 采样精度:AAC- 最高32bit / MP3 - 最高16bit

    AAC的不足之处是,它属于有损压缩的格式,相对于APE和FLAC等主流无损压缩,音色“饱满度”差距比较大。另外,除了流媒体网络传输,其所能支持的设备较少。

(2)  AAC编码封装格式

    音频数据在压缩编码之前,要先进行采样与量化,以样值的形式存在。音频压缩编码的输出码流,以音频帧的形式存在。每个音频帧包含若干个音频采样的压缩数据,AAC的一个音频帧包含960或1024个样值,这些压缩编码后的音频帧称为原始数据块(RawData Block),由于原始数据块以帧的形式存在,即简称为原始帧。原始帧是可变的,如果对原始帧进行ADTS的封装,得到的原始帧为ADTS帧;如果对原始帧进行ADIF封装,得到的原始帧为ADIF帧。它们的区别如下:

    a)  ADIF:AudioData Interchange Format,音频数据交换格式。这种格式明确解码必须在明确定义的音频数据流的开始处进行,常用于磁盘文件中;

    b)  ADTS:AudioData Transport Stream,音频数据传输流。这种格式的特点是它一个有同步字的比特流,且允许在音频数据流的任意帧解码,也就是说,它每一帧都有信息头。

     一个AAC原始数据库长度是可变的,对原始帧加上ADTS头进行ADTS封装就形成了ADTS帧。AAC音频的每一帧(ADTS帧)体由ADTS Header和AAC Audio Data(包含1~4个音频原始帧)组成,其中,ADTS Header占7个字节或9个字节,由两部分组成:固定头信息(adts_fixed_header)、可变头信息(adts_variable_header)。固定头信息中的数据每一帧都是相同的,主要定义了音频的采样率、声道数、帧长度等关键信息,这是解码AAC所需关键信息;可变头信息则在帧与帧之间可变。

下面是多个ADTS帧组成的AAC数据流结构,示意图如下:

a)  固定信息头

说明:

* syncword:占12bits。同步头,表示一个ADTS帧的开始,总是0xFFF。正是因为它的存在,才支持解码任意帧;

* ID:            占1bit。MPEG的版本,0为MPGE-4,1为MPGE-2;

* Layer:      占2bits。总是”00”;

* protection_absent:占1bit。=0时,ADTS Header长度占9字节;=1时,ADTS Header占7字节;

* profile:     占2bit。使用哪个级别的AAC,值00、01、10分别对应Mainprofile、LC、SSR;

* sampling_frequency_index:占4bits。表示使用的采样率下标,通过这个下标在Sampling Frequencies[ ]数组中查找得知采样率的值,如0xb,对应的采样率为8000Hz;

* channel_configuration:表示声道数,如1-单声道,2-立体声

(b)可变信息头

 

说明:

* frame_length:占13bits。表示一个ADTS帧的长度,即ADTS头(7或9字节)+sizeof(AAC Frame);

* adts_buffer_fullness:占11bits。值0x7FF,说明是码率可变的码流

* number_of_raw_data_blocks_In_frame:占2bits。表示ADTS帧中有(number_of_raw_data_blocks_In_frame+1)个AAC原始帧

(3)  将AAC打包成ADTS格式

    众所周知,在使用MediaCodec将PCM压缩编码为AAC时,编码器输出的AAC是没有ADTS头的原始帧,如果我们直接保存为AAC文件或推流,VLC等工具是无法将AAC数据流解码播放的。因此,我们需要对MediaCodec编码PCM输出的AAC原始帧添加ADTS数据头,然后再进行文件保存或者推流。MediaCodec部分代码如下:

  1. private void encodeBytes(byte[] audioBuf, int readBytes) {
  2. ByteBuffer[] inputBuffers = mAudioEncoder.getInputBuffers();
  3. ByteBuffer[] outputBuffers = mAudioEncoder.getOutputBuffers();
  4. int inputBufferIndex = mAudioEncoder.dequeueInputBuffer(TIMES_OUT);
  5. if(inputBufferIndex >= 0){
  6. ByteBuffer inputBuffer = null;
  7. if(!isLollipop()){
  8. inputBuffer = inputBuffers[inputBufferIndex];
  9. }else{
  10. inputBuffer = mAudioEncoder.getInputBuffer(inputBufferIndex);
  11. }
  12. if(audioBuf==null || readBytes<=0){
  13. mAudioEncoder.queueInputBuffer(inputBufferIndex,0,0,getPTSUs(),MediaCodec.BUFFER_FLAG_END_OF_STREAM);
  14. }else{
  15. inputBuffer.clear();
  16. inputBuffer.put(audioBuf);
  17. mAudioEncoder.queueInputBuffer(inputBufferIndex,0,readBytes,getPTSUs(),0);
  18. }
  19. }
  20. // 返回一个输出缓存区句柄,当为-1时表示当前没有可用的输出缓存区
  21. // mBufferInfo参数包含被编码好的数据,timesOut参数为超时等待的时间
  22. MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
  23. int outputBufferIndex = -1;
  24. do{
  25. outputBufferIndex = mAudioEncoder.dequeueOutputBuffer(mBufferInfo,TIMES_OUT);
  26. if(outputBufferIndex == MediaCodec. INFO_TRY_AGAIN_LATER){
  27. Log.i(TAG,"获得编码器输出缓存区超时");
  28. }else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED){
  29. }else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){
  30. }else{
  31. if((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0){
  32. mBufferInfo.size = 0;
  33. }
  34. if((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0){
  35. break;
  36. }
  37. // 获取一个只读的输出缓存区inputBuffer ,它包含被编码好的数据
  38. ByteBuffer mBuffer = ByteBuffer.allocate(10240);
  39. ByteBuffer outputBuffer = null;
  40. if(!isLollipop()){
  41. outputBuffer = outputBuffers[outputBufferIndex];
  42. }else{
  43. outputBuffer = mAudioEncoder.getOutputBuffer(outputBufferIndex);
  44. }
  45. if(mBufferInfo.size != 0){
  46. Log.i(TAG,"AAC流添加ADTS头,缓存到mBuffer");
  47. mBuffer.clear();
  48. // 拷贝outputBuffer编码好的AAC原始帧到mBuffer,从第8个字节存放
  49. // mBuffer的前7个字节留用(数组下标0~6)
  50. outputBuffer.get(mBuffer.array(), 7, mBufferInfo.size);
  51. outputBuffer.clear();
  52. // 将buffer的position置7 + mBufferInfo.size
  53. mBuffer.position(7 + mBufferInfo.size);
  54. // 添加ADTS头,其中(mBufferInfo.size + 7)为ADTS帧长度
  55. addADTStoPacket(mBuffer.array(), mBufferInfo.size + 7);
  56. // 将buffer的position置0
  57. mBuffer.flip();
  58. // 推流AAC
  59. ...
  60. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/684824
推荐阅读
相关标签
  

闽ICP备14008679号