目录
这里手动解析 aac文件,如果只是本地播放aac文件那么android已经有完善的方法:MediaExtractor + MediaCoddec 或者直接是MediaPlayer, 但有的时候我们有自己的aac帧数据,想利用Mediacode进行解码,里面就有些坑。
完整app源码:github openScreen/clientAndroid/AACPlayer at main · Canok7/openScreen · GitHub
当然,如果使用java mediaplyer播放,使用android现成的功能,简单只需几行代码:
MediaPlayer player = new MediaPlayer();
try {
player.setDataSource("/storage/emulated/0/test.aac");
player.prepare();
} catch (IOException e) {
e.printStackTrace();
}
player.start();
主要流程:
1.这里写了个aac文件的解析类,解析aac-adts文件,从中读取 音频信息如:通道,采样率,编码等级等信息。2.然后从aac文件中逐步取出aac raw (去掉adts头的数据),送到mediacode解码(ndk),3.再将解码的pcm数据调用java的audioTrack(也可以采用opensl 直接在native层渲染,只是ndk还没有开放提供audioTrack的接口,所以这里使用native反过来调用java的audioTrack接口。vlc也是如此)
解析aac源文件:
有很多的blog介绍其组成,这里不赘述,源码中有完整的解析流程:
int64_t iheader=MAKE64_LEFT(headers[0],headers[1],headers[2],headers[3],
headers[4],headers[5],headers[6],0);
//ALOGD("[%s%d]%#lx",__FUNCTION__ ,__LINE__,iheader);
int id = GET64BIT_LEFT(iheader,12,1);
int layer = GET64BIT_LEFT(iheader,13,2);
int protection_absent = GET64BIT_LEFT(iheader,15,1);
int profile = GET64BIT_LEFT(iheader,16,2);
int sampling_frequency_index = GET64BIT_LEFT(iheader,18,4);
int private_bt = GET64BIT_LEFT(iheader,22,1);
int channel_configuration = GET64BIT_LEFT(iheader,23,3);
int original_copy = GET64BIT_LEFT(iheader,26,1);
int home = GET64BIT_LEFT(iheader,27,1);
//adts_variable_header
int copyright_identification_bit = GET64BIT_LEFT(iheader,28,1);
int copyright_identification_start=GET64BIT_LEFT(iheader,29,1);
int aac_frame_length =GET64BIT_LEFT(iheader,30,13);
int adts_buffer_fullness=GET64BIT_LEFT(iheader,33,11);
int number_of_raw_data_blocks_in_frame=GET64BIT_LEFT(iheader,44,2);
一直想找个官方的标准文档看看,就是没找着,大部分文章都是雷同一句话:AAC音频格式在MPEG-2(ISO-13318-7 2003)中有定义,可是没人提及这个文档哪里有, 这个标准据说是收费的,而且很贵,MPEG官网也没找着。
抄一个网络上常见的图记录下:
mediacode解码aac:
mediacode参数 "csd-0",如何设定?
其中有一个参数,"csd-0", 很难找到这个参数的官方完整说明,因为这个参数和AAC格式本身无关,在ffmpeg软解中也没有找到该参数,只是MediaCode(也有可能是omx的)自己使用的一个特殊参数,全称:Codec specific data, 可以在android源码中找到其相关信息,android 源码中:
frameworks/av/media/libstagefight/MetaDataUtils.cpp ::
里面函数:
MakeAVCCodecSpecificData()
MakeAACCodecSpecificData()
bool MakeAACCodecSpecificData(AMediaFormat *meta, unsigned profile, unsigned sampling_freq_index,
unsigned channel_configuration) {
if(sampling_freq_index > 11u) {
return false;
}
uint8_t csd[2];
csd[0] = ((profile + 1) << 3) | (sampling_freq_index >> 1);
csd[1] = ((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3);
static const int32_t kSamplingFreq[] = {
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
16000, 12000, 11025, 8000
};
int32_t sampleRate = kSamplingFreq[sampling_freq_index];
AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CSD_0, csd, sizeof(csd));
AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_AAC);
AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, sampleRate);
AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, channel_configuration);
return true;
}
所以这个参数只是 Mediacode 自定的,和aac文件自身格式无关,没什么好研究的,既然要用Mediacode,就遵循它的设定,直接套用上面函数即可(可以看到这个数据也只是包含了profie, sampleFrequency, channel 三个信息,而这三个参数明明有单独的设置接口,还要重复这么一个信息,真是对它无语)。