Cómo generar el flujo elemental AAC ADTS con Android MediaCodec
audio encoding (1)
Lo que estoy tratando de hacer: usar el MediaCodec de Android para codificar muestras de audio PCM sin procesar en un archivo AAC sin formato.
El problema que tengo: cuando uso FFMPEG para empaquetar el archivo AAC sin procesar generado en un contenedor M4A, FFMPEG se queja de que faltan parámetros de códec en el archivo.
Detalles:
Como no puedo encontrar ningún código de muestra de MediaCodec para el codificador de audio que genera un archivo AAC de salida, intenté modificar el codificador de video para convertirlo en un codificador de audio. El código original está aquí: source_code
Configuré el codificador de audio así:
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);
Luego alimento el codificador con 10 ms de muestras de PCM por fotograma. El codificador toma cada cuadro, genera un cuadro de flujo de bits y escribo el flujo de bits en un FileOutputStream. El bucle continúa hasta el final del archivo de entrada.
El código se ejecuta hasta el final. Hago ''adb pull'' para obtener el archivo AAC generado desde el dispositivo a mi PC, y uso FFMPEG para leerlo. A continuación se muestra el comando y el error que escupe 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
Mis preguntas:
- He configurado el codificador antes de llamar a codec.start (). ¿Por qué el archivo AAC generado carece de los parámetros del códec?
- En el ejemplo del códec de video original, los parámetros "csd-0" se pasan del codificador al decodificador, pero no se escriben explícitamente en el archivo de flujo de bits. ¿Necesito escribirlos explícitamente en el archivo AAC?
- Divido las muestras PCM de entrada en 10 ms por trama, lo que no necesariamente produce un paquete de salida completo. Para cada cuadro de entrada, simplemente escribo lo que sea que el codificador produzca en el archivo. ¿Es eso un motivo de preocupación?
Cualquier ayuda será profundamente apreciada. Sería genial si hubiera un proyecto de muestra que haga lo que estoy tratando de hacer aquí. Si mi código fuente puede ayudarlo a ayudarme, lo publicaré. Necesito hacer algo de limpieza. ¡Gracias!
Edición : se cambió el título de "Archivo AAC elemental generado por los parámetros del códec faltantes de MediaCodec" a "Cómo generar el flujo elemental ADTS de AAC con MediaCodec de Android"
Finalmente, generé archivos AAC que se pueden reproducir tanto en el dispositivo Android como en la computadora host de Windows. Estoy publicando mi solución aquí, esperando que pueda ayudar a otros.
Primero, mi suposición anterior de que el codificador de MediaCodec de Android genera la transmisión de AAC elemental no era precisa. El codificador MediaCodec genera la secuencia AAC sin formato. Es por eso que los archivos no se pudieron reproducir. El flujo AAC sin procesar debe convertirse en un formato reproducible, como el flujo ADTS . He cambiado el título de esta publicación para reflejar mi nueva comprensión. Hubo otro post que hizo una pregunta similar, y tuvo una excelente respuesta. Sin embargo, un principiante no necesariamente entiende las descripciones breves allí. No lo entendí del todo la primera vez que leí ese post.
Entonces, para generar un flujo de bits AAC que pueda reproducir un reproductor multimedia, comencé con el ejemplo de EncoderTest dado por fadden en su primer comentario, pero modifiqué el código original para agregar el encabezado ADTS por marco de salida (unidad de acceso). y para escribir la secuencia resultante en un archivo (se reemplazaron las líneas 248 a 267 del código original con el siguiente fragmento de código):
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();
}
Fuera del bucle, definí la función addADTStoPacket así:
/**
* 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;
}
También agregué código para controlar cómo dejar de generar el flujo ATS ADTS, pero eso es específico de la aplicación, por lo que no detallaré aquí. Con todos estos cambios, los archivos AAC generados se pueden reproducir en el dispositivo Android, en mi PC con Windows, y ffmpeg está satisfecho con ellos.