android录制avi视频时aac音频的正确配置方法

        在android上面,用avilib.c开源库录制过avi视频加aac音频的同学们,应该都有一段为了aac音频无法正确录制和播放而抓狂的经历。我本人也经历了这样的折磨,现在将踩过的坑记录下来,让后来者少走些弯路。

        先从camera app上讲起,在app应用里,要录制一个avi(我这里讲的是利用在系统新增的avi接口,具体参考我前几篇博客。当然纯粹在app上也可以实现,这里侧重讲系统上的坑,其实都差不多的),需要先按下面类似的代码来配置:

        Camera camera = mCameraDevice.getCamera();
        // If the camera device is null, the camera proxy is stale and recording
        // should be ignored.
        if (camera == null) {
            Log.w(TAG, "null camera within proxy, not recording");
            return;
        }

        MediaRecorder mMediaRecorder;
        mMediaRecorder.setCamera(camera);
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);

		

		mProfile.fileFormat = 10;//avi  
		mProfile.videoCodec = 5; //V4L2_PIX_FMT_MJPEG
		mProfile.audioBitRate = 128000;
		mProfile.audioCodec = 3;// AUDIO_ENCODER_AAC;   
		mProfile.audioSampleRate = 44100;
		mProfile.audioChannels = 1;  

        其中fileFormat和videoCodec,这两个配置项都是我自己在系统里新增的,专门用来录制avi视频。这里需要特别注意的是,在app这里设置的比特率bitrate、采样率audioSampleRate、通道数audioChannels,都必须和avilib里的AVI_set_audio参数保持一致,否则很容易出现录下来的音视频数据索引错乱的问题。

        比如按上面的配置,对应的在系统framework里的AVIWriter.cpp里设置音频时,应该如下设置音频参数:

AVI_set_audio(avi_fd, 1, 44100, 16, 255, 128);   

        这里的16是指的是16位PCM 编码每个声音采样点用16bit表示,对应的有8位的PCM,但8位PCM 编码所使用的数据量小一半,音质要差些。这里的44100和128就分别对应了app里设置的采样率和比特率。这里的255,就对应了app层上的audioCodec,表示AAC音频编码。

        这里设置好后,还没有完。如果只录视频,是没什么问题的。但是音频,网上流传的avilib在这一块处理得非常糟糕,起码aac音频的编码,用网上的avilib是无法正常录制和播放的。因为avilib里面,在音频头,对aac没有做处理,导致录制的音频参数有问题。我们需要重新对aac的BlockAlign、a_fmt、Length、Scale、rate做计算。

        这个计算过程比较复杂,我将我计算好了的avilib.c已经上传到了我的博客上,有兴趣的可以去下载。这其中有一点特别说明一下,BlockAlign我设置的是768,这个值是怎么来的呢?因为我们android系统解码acc时,调用的是开源的fdk-acc(目录在external\aac),而fdk-acc在调用aacDecoder_DecodeFrame去解码的时候,会去分配一个过渡性的decoder-internal input buffer,这个buffer大小按RANSPORTDEC_INBUF_SIZE规定,可以任意设定但必须满足两个条件:1:each input channel requires 768 bytes, 2:the whole buffer must be of size ,所以我们这里的BlockAlign必须配置成768。

        将这些计算好后,录下来的视频,基本上就可以正常播放了。但是还有一部份视频,是无法播放的。比如有一些同学录制的avi视频,里面的音频第一帧里,没有配置对应的Audio Specific Config。导致用开源的HANDLE_AACDECODER来解码时失败。这个audio specific config是什么东西呢?它是aac的数据头,长度固定为2,数据内容由高位到低位依次为:aacObjectType(5bits),sampleRateIdx(4bits),numChannels(4bits),

例如:音频编码参数为:

aacObjectType:AAC_LC,对应值为2,用5bit二进制表示为00010;

sampleRate:44100KHz, 对应的IDX值为4, 用4bit二进制表示为0100; 

numChannels:2,对应的值为2,用4bit二进制表示为0010;

将它们由高位到低位串起来:0001,0010,0001,0000,

则,对应的十六进制值为:0x1220

再比如:音频编码参数为:

aacObjectType:AAC_LC,对应值为2,用5bit二进制表示为00010;

sampleRate:44100KHz, 对应的IDX值为4, 用4bit二进制表示为0100; 

numChannels:1,对应的值为1,用4bit二进制表示为0001;

将它们由高位到低位串起来:0001,0010,0000,1000,

则,对应的十六进制值为:0x1208

        这一点相当重要,如果少了这个头,这个aac音频是没有办法解码出来的。如果大伙手上的avi文件里的音频在电脑上能播放出来,索引也没有乱,但是在手机上播放不出,则可以检查下第一个音频数长度是不是为2,内容是不是自己配置的那些参数。如果不是的话,则可以在解码前加上这个头就可以了。       

        这个AudioSpecificConfig,在FLV格式里称为AAC sequence header。在正式播放ADTS AAC数据包之前,需要用AudioSpecificConfig生成一个audio_tag交给播放器。其实AudioSpecificConfig的内容,完全可以通过ADTS的7字节头生成,具体来说,AudioSpecificConfig需要3部分数据:profile(LC,Main,HE),sample_rate, channel,这3个数据在ADTS头里都可以找到。具体算法见代码。有了这3个数据以后,可以进行合并生成2个字节,就是所谓的AudioSpecificConfig,见代码部分

config1/config2的算法。

//output AAC config header:
var profile:int = ((payload[2]&0xc0)>>6)+1;
var sample_rate:int = (payload[2]&0x3c)>>2;
var channel:int = ((payload[2]&0x1)<<2)|((payload[3]&0xc0)>>6);
var config1:int = (profile<<3)|((sample_rate&0xe)>>1);
var config2:int = ((sample_rate&0x1)<<7)|(channel<<3);
var aacSeqHeader:ByteArray = new ByteArray();
aacSeqHeader.writeByte(config1);
aacSeqHeader.writeByte(config2);

        根据第一个ADTS包生成的aacSeqHeader,打包到audio_tag里,交给NetStream,后面的ADTS包就可以直接打包成audio_tag播放了。对应的也有视频的avc sequence header。 

        在做直播流的时候,一般都会先向客户端发送一次avc sequence header和aac sequence header。所以当视频的分辨率和帧率,或者AAC的音频参数发生了变化(比如采样率、通道数等),这时候这两个header的内容也会发生变化。如果不重新拉流,就会出现绿屏或者声音无法播放的现象。如果出现了这个问题,客户端只需要重新拉流,也就是重新接收这两个header,画面和声音就会恢复正常。

        上面说的aacObjectType,表示使用哪个级别的AAC,一共有3种:

index     profile
  0       Main profile

  1       Low Complexity profile (LC)

  2       Scalable Sampling Rate profile(SSR)

  3       (reserved)

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

        

There are 13 supported frequencies:

0: 96000 Hz
1: 88200 Hz
2: 64000 Hz
3: 48000 Hz
4: 44100 Hz
5: 32000 Hz
6: 24000 Hz
7: 22050 Hz
8: 16000 Hz
9: 12000 Hz
10: 11025 Hz
11: 8000 Hz
12: 7350 Hz
13: Reserved
14: Reserved
15: frequency is written explictly

        numChannels表示声道数 :

        

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

         根据上面的这几个值,就可以准确的配置出对应的Audio Specific Config了。

        另外,展迅和高通的AVIExtractor.cpp对avi文件里的aac音频处理得也不太友好,也不能正常播放。我这里参考mtk对它做了修改,使期能正常播放。文件也已经上传到了博客资源里,有兴趣的可以去下载。

        补充一点,aac每一帧播放的时间是固定的,计算方法如下:

对采样率为44.1kHz的AAC(Advanced Audio Coding)音频进行解码时,一帧的解码时间须控制在23.22毫秒内。

通常是按1024个采样点一帧。

一个AAC原始帧包含某段时间内1024个采样点相关数据。 

用1024主要是因为AAC是用的1024点的mdct。

音频帧的播放时间 = 一个AAC帧对应的采样样本的个数 / 采样频率(单位为s)。

采样率(samplerate)为 44100Hz,表示每秒 44100个采样点, 所以,根据公式,

音频帧的播放时长 = 一个AAC帧对应的采样点个数 / 采样频率

则,当前一帧的播放时间 = 1024 * 1000/44100= 22.32ms(单位为ms)

16kHz采样率, 

则,当前一帧的播放时间 = 1024 * 1000/16000= 64ms(单位为ms)

22.05kHz采样率, 

则,当前一帧的播放时间 = 1024 * 1000/22050= 46.43ms(单位为ms)

        好了,就讲到这里了,如果有什么疑问的,可以留言咱们一块交流。

        本文参考了下面几篇博客:

        https://blog.csdn.net/ai2000ai/article/details/85114110

        http://blog.sina.com.cn/s/blog_73c85f0f0101f2qe.html

猜你喜欢

转载自blog.csdn.net/xuhui_7810/article/details/96487058