Как создать элементарный поток AAC ADTS с Android MediaCodec
что я пытаюсь сделать: используйте MediaCodec Android для кодирования необработанных образцов аудио PCM в необработанный файл AAC.
проблема у меня: когда я использую FFMPEG для упаковки сгенерированного файла raw AAC в контейнер M4A, FFMPEG жалуется на отсутствие параметров кодека в файле.
детали:
поскольку я не могу найти образец кода MediaCodec для аудиокодера, который генерирует выходной файл AAC, я попытался измените видеокодер на аудиокодер. Исходный код здесь:source_code
Я настроил аудиокодер следующим образом:
mEncoderFormat = MediaFormat.createAudioFormat("audio/mp4a-latm", (int)mAudioSampleRate, 2);
// redundant?
mEncoderFormat.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
mEncoderFormat.setInteger(MediaFormat.KEY_AAC_PROFILE,
MediaCodecInfo.CodecProfileLevel.AACObjectELD);
mEncoderFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, kSampleRates);
mEncoderFormat.setInteger(MediaFormat.KEY_BIT_RATE, kBitRates);
mEncoderFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 2);
testEncoderWithFormat("audio/mp4a-latm", mEncoderFormat);
try {
codec.configure(
mEncoderFormat,
null /* surface */,
null /* crypto */,
MediaCodec.CONFIGURE_FLAG_ENCODE);
} catch (IllegalStateException e) {
Log.e(TAG, "codec '" + componentName + "' failed configuration.");
return;
}
Log.d(TAG, " testEncoder configured with format = " + format);
после этого я кормлю шифратор с стоимостью 10ms образцов PCM в рамку. Кодировщик берет каждый кадр, генерирует кадр битового потока, и я записываю битовый поток в FileOutputStream. Цикл продолжается до конца входного файла.
код выполняется до конца. Я делаю "adb pull", чтобы получить сгенерированный файл AAC с устройства на мой компьютер и используйте FFMPEG для его чтения. Ниже приведена команда, и ошибка FFMPEG выплевывает:
$ ffmpeg -f aac -i BlessedNoColor_nexus7_api18.aac
ffmpeg version N-45739-g04bf2e7 Copyright (c) 2000-2012 the FFmpeg developers
built on Oct 20 2012 00:20:36 with gcc 4.7.2 (GCC)
configuration: --enable-gpl --enable-version3 --disable-pthreads --enable-runt
ime-cpudetect --enable-avisynth --enable-bzlib --enable-frei0r --enable-libass -
-enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libfreetype --enab
le-libgsm --enable-libmp3lame --enable-libnut --enable-libopenjpeg --enable-libo
pus --enable-librtmp --enable-libschroedinger --enable-libspeex --enable-libtheo
ra --enable-libutvideo --enable-libvo-aacenc --enable-libvo-amrwbenc --enable-li
bvorbis --enable-libvpx --enable-libx264 --enable-libxavs --enable-libxvid --ena
ble-zlib
libavutil 51. 76.100 / 51. 76.100
libavcodec 54. 67.100 / 54. 67.100
libavformat 54. 33.100 / 54. 33.100
libavdevice 54. 3.100 / 54. 3.100
libavfilter 3. 19.103 / 3. 19.103
libswscale 2. 1.101 / 2. 1.101
libswresample 0. 16.100 / 0. 16.100
libpostproc 52. 1.100 / 52. 1.100
[aac @ 00000000002efae0] channel element 2.0 is not allocated
[aac @ 00000000003cf520] decoding for stream 0 failed
[aac @ 00000000003cf520] Could not find codec parameters for stream 0 (Audio: aac, 0 channels, s16): unspecified sample rate
Consider increasing the value for the 'analyzeduration' and 'probesize' options
[aac @ 00000000003cf520] Estimating duration from bitrate, this may be inaccurate
BlessedNoColor_nexus7_api18.aac: could not find codec parameters
мои вопросы:
- я настроил кодер перед вызовом кодека.начать.)( Почему в сгенерированном файле AAC отсутствуют параметры кодека?
- в исходном примере видеокодека параметры "csd-0" передаются из кодера в декодер, но не записываются в файл битового потока явно. Нужно ли записывать их в файл AAC явно?
- я разделяю входные образцы PCM на 10 МС на кадр, что не обязательно создает полный выходной пакет. Для каждого входного кадра я просто записываю все, что кодировщик выводит в файл. Это повод для беспокойства?
любая помощь будет высоко оценили. Было бы здорово, если бы был образец проекта, который делает то, что я пытаюсь сделать здесь. Если мой исходный код может помочь вам помочь мне, я опубликую его. Я нужно кое-что убрать. Спасибо!
редактировать: изменен заголовок с " элементарный файл AAC, сгенерированный MediaCodec отсутствуют параметры кодека "на"Как создать элементарный поток AAC ADTS с Android MediaCodec"
1 ответов
Я, наконец, сгенерировал файлы AAC, которые воспроизводятся как на устройстве Android, так и на главном компьютере Windows. Я размещаю свое решение здесь, надеясь, что оно может помочь другим.
во-первых, мое предыдущее предположение о том, что кодировщик Android MediaCodec генерирует элементарный поток AAC, не было точным. Кодировщик MediaCodec генерирует необработанный поток AAC. Вот почему файлы не могут быть воспроизведены. Необработанный поток AAC должен быть преобразован в воспроизводимый формат, такой как ADTS поток. Я изменил название этого поста, чтобы отразить мое новое понимание. Там было следующий пост который задал аналогичный вопрос и получил отличный ответ. Тем не менее, новичок может не обязательно понимать краткие описания там. Я не совсем понял это в первый раз, когда прочитал этот пост.
Итак, чтобы создать битовый поток AAC, который может воспроизводиться медиаплеером, я начал с примера EncoderTest, приведенного fadden в его 1st комментарий, но изменил исходный код, чтобы добавить заголовок ADTS на выходной кадр (блок доступа) и записать полученный поток в файл (заменил строки 248-267 исходного кода следующим фрагментом кода):
if (index >= 0) {
int outBitsSize = info.size;
int outPacketSize = outBitsSize + 7; // 7 is ADTS size
ByteBuffer outBuf = codecOutputBuffers[index];
outBuf.position(info.offset);
outBuf.limit(info.offset + outBitsSize);
try {
byte[] data = new byte[outPacketSize]; //space for ADTS header included
addADTStoPacket(data, outPacketSize);
outBuf.get(data, 7, outBitsSize);
outBuf.position(info.offset);
mFileStream.write(data, 0, outPacketSize); //open FileOutputStream beforehand
} catch (IOException e) {
Log.e(TAG, "failed writing bitstream data to file");
e.printStackTrace();
}
numBytesDequeued += info.size;
outBuf.clear();
codec.releaseOutputBuffer(index, false /* render */);
Log.d(TAG, " dequeued " + outBitsSize + " bytes of output data.");
Log.d(TAG, " wrote " + outPacketSize + " bytes into output file.");
}
else if (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
}
else if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
codecOutputBuffers = codec.getOutputBuffers();
}
вне цикла я определил функцию addADTStoPacket следующим образом:
/**
* Add ADTS header at the beginning of each and every AAC packet.
* This is needed as MediaCodec encoder generates a packet of raw
* AAC data.
*
* Note the packetLen must count in the ADTS header itself.
**/
private void addADTStoPacket(byte[] packet, int packetLen) {
int profile = 2; //AAC LC
//39=MediaCodecInfo.CodecProfileLevel.AACObjectELD;
int freqIdx = 4; //44.1KHz
int chanCfg = 2; //CPE
// fill in ADTS data
packet[0] = (byte)0xFF;
packet[1] = (byte)0xF9;
packet[2] = (byte)(((profile-1)<<6) + (freqIdx<<2) +(chanCfg>>2));
packet[3] = (byte)(((chanCfg&3)<<6) + (packetLen>>11));
packet[4] = (byte)((packetLen&0x7FF) >> 3);
packet[5] = (byte)(((packetLen&7)<<5) + 0x1F);
packet[6] = (byte)0xFC;
}
Я также добавил код для управления тем, как остановить создание потока ADTS AAC, но это специфично для приложения, поэтому я не буду подробно здесь. Со всем этим изменения, сгенерированные файлы AAC можно воспроизводить на устройстве Android, на моем ПК с Windows, и ffmpeg доволен ими.