2013-09-18 2 views
19

Что я пытаюсь сделать: использовать MediaCodec для Android, чтобы кодировать необработанные образцы аудио PCM в исходный файл AAC.Как создать элементарный поток AAC ADTS с Android MediaCodec

Проблема: У меня есть:, когда я использую FFMPEG для упаковки сгенерированного необработанного файла 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); 

Затем я кормить кодер с 10мс ценность образцов ПКМ на кадр. Кодер принимает каждый кадр, генерирует кадр битового потока, и я пишу бит-поток в 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 

Мои вопросы:

  1. Я настроил кодировщик перед вызовом codec.start(). Почему в сгенерированном файле AAC отсутствуют параметры кодека?
  2. В исходном примере видеокодека параметры «csd-0» передаются от кодировщика к декодеру, но не записываются в файл потока бит явно. Мне нужно явно записать их в файл AAC?
  3. Я делю входные образцы PCM на 10 мс на кадр, что не обязательно дает полный выходной пакет. Для каждого входного кадра я просто записываю все, что выводит кодер в файл. Это вызывает беспокойство?

Любые полезные советы будут глубоко оценены. Было бы здорово, если бы был пример проекта, который делает то, что я пытаюсь сделать здесь. Если мой исходный код поможет вам помочь, я отправлю его. Мне нужно сделать чистку. Благодаря!

Редактировать: Изменено название от «Элементарная файла AAC, порожденного MediaCodec отсутствующего параметров кодека» в «Как сформировать AAC ADTS элементарный поток с Android MediaCodec»

+0

FWIW, тест аудиокодера CTS - https://android.googlesource.com/platform/cts/+/jb-mr2-release/tests/tests/media/src/android/media/cts/EncoderTest.java – fadden

+0

@fadden: Я тоже посмотрел на этот тест. Он не генерирует выходной файл AAC. Я попытался добавить код для записи потока бит в файл. Сгенерированный файл имеет ту же проблему: ffmpeg не распознает его как файл AAC. Я только что отредактировал свой вопрос, чтобы добавить, что я не могу найти образец кода, который генерирует бит-файл (AAC в моем случае). – hubeir

+0

После прочтения [этого сообщения] (http://stackoverflow.com/questions/12942201/decoding-aac-using-mediacodec-api-on-android) и [этот пост] (http://stackoverflow.com/questions/18784781/how-to-initialize-mediaformat-to-configure-a-mediacodec-to-decode-raw-aac-data), я проверил вывод энкодера, когда _italic_ ** bold ** '(info.flags & MediaCodec. BUFFER_FLAG_CODEC_CONFIG)! = 0'. Выходной сигнал составляет 4 байта: 0xF8, 0xE8, 0x40, 00. Они не являются ESDS (дескриптор элементарного потока). Кто они такие? – hubeir

ответ

37

я, наконец, генерируемый AAC файлы, которые воспроизводятся на как на Android-устройстве, так и на главном компьютере Windows. Я размещаю свое решение здесь, надеясь, что это может помочь другим.

Во-первых, мое предыдущее предположение о том, что кодировщик Android MediaCodec генерирует элементарный поток AAC, не является точным. Кодер MediaCodec генерирует необработанный поток AAC. Вот почему файлы не могут быть воспроизведены. Необработанный поток AAC необходимо преобразовать в воспроизводимый формат, например поток ADTS. Я изменил название этого сообщения, чтобы отразить мое новое понимание. Был another post, который задал аналогичный вопрос и получил отличный ответ.Однако новичок, возможно, не всегда понимает краткие описания. Я не совсем понял это в первый раз, когда прочитал этот пост.

Итак, для генерации битового потока AAC, который может воспроизводиться медиаплеером, я начал с примера EncoderTest, данного fadden в его первом комментарии, но изменил исходный код, чтобы добавить заголовок 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; 
} 

Я также добавляю ed, чтобы управлять тем, как прекратить генерировать поток AAC ADTS, но это специфично для приложения, поэтому я не буду здесь останавливаться. При всех этих изменениях сгенерированные файлы AAC можно воспроизводить на устройстве Android, на моем ПК с ОС Windows, и ffmpeg довольна ими.

+5

Человек, эти 7 строк спасли мне несколько дней, пытаясь понять это. Потрясающие! – Feras

+0

Это здорово, @hubeir вам удалось преобразовать aac в m4a с помощью ffmpeg? – Soham

+1

Есть одна вещь, которую я обнаружил, что я хотел бы добавить. Мы должны избегать написания самого первого пакета deque'd в файл. Этот пакет содержит информацию о кодеке, которая испортит воспроизведение в музыкальных плеерах Android. Также не пишите конец пакета потока по той же причине. Все, что должно быть в файле, это пакеты данных с заголовками ADTS. –

Смежные вопросы