¿Cómo inicializar MediaFormat para configurar un MediaCodec para decodificar datos AAC sin procesar?
Tengo un extraño problema con mi StreamPlayer y necesito cualquier ayuda que pueda obtener.
El objetivo principal que necesito lograr es StreamPlayer, que es capaz de reproducir los flujos de transporte MPEG-2 con la menor latencia posible. Para esto estoy siguiendo este enfoque:
- Audio Android demasiado rápido en algunos dispositivos con MediaCodec y AudioTrack
- No se puede mux tanto de audio como de vídeo
- Cómo puedo extraer datos rtsp en MediaCodec
- ¿Podemos volver a escribir la API de MediaCodec en C?
- Uso de renderscript para procesamiento y mediacodec para codificación
El flujo es analizado por un analizador de TS basado en Java. He implementado un TSExtractor que es similar al MediaExtractor y que funciona bien. Puedo recibir todos los ejemplos de medios para una pista seleccionada de la misma manera que es posible usando el MediaExtractor con
extractor.readSampleData(...); extractor.advance();
Para decodificar los datos AAC quiero crear y configurar una instancia de MediaCodec. Usando la clase MediaExtractor esto se hace generalmente por
MediaFormat mediaFormat = extractor.getTrackFormat(i); decoder = MediaCodec.createDecoderByType(mediaFormat.getString(MediaFormat.KEY_MIME)); decoder.configure(mediaFormat, null, null, 0);
Como tengo que inicializar el MediaFormat en el método TSExtractor.getTrackFormat (int track) que uso
MediaFormat mf = MediaFormat.createAudioFormat ("audio/mp4a-latm", getSampleRate(), getChannelCount());
Y porque todas mis muestras de AAC incluyen un ADTS que hago
mediaFormat.setInteger(MediaFormat.KEY_IS_ADTS, 1);
Después de leer esta publicación, finalmente agrego un marco de ESDS usando la tecla "csd-0"
mediaFormat.setByteBuffer("csd-0", ByteBuffer.allocate(2).put(new byte[]{(byte) 0x11, (byte)0x90}));
Donde los valores 0x11 y 0x90 se extraen del ADTS.
Cuando ahora quiero decodificar las muestras de AAC, los puestos de descodificador
AAC decoder returned error 4097, substituting silence
Al Registro.
Para comprobar que mi TSExtractor extrae correctamente las muestras, grabé la misma secuencia utilizando VLC remuxing a un archivo mp4 sin transcodificación para que el flujo sin procesar permanezca inalterado. Ahora puedo inicializar el MediaExtractor con el archivo mp4 grabado y comparar las muestras creadas por mi TSExtractor y el MediaExtractor. Usando rastro y error encontré un comportamiento muy extraño:
Cuando configuro el MediaCodec usando el mediaFormat creado por el MediaExtractor el MediaCodec decodifica las muestras de AAC devueltas por mi TSExtractor sin ningún problema. Comparando el MediaFormat, que básicamente envuelve un HashMap, creado por mi TSExtractor y el creado por el MediaExtractor da estas diferencias:
Creado por MediaExtractor:
MediaFormat: {max-input-size = 1212, durationUs = 77428875, is-adts = 1, channel-count = 2, mime = audio / mp4a-latm, csd-0 = java.nio.ByteArrayBuffer [position = 0, limit = 2, capacidad = 2], tasa de muestreo = 48000}
Creado por TSExtractor:
MediaFormat: {is-adts = 1, channel-count = 2, mime = audio / mp4a-latm, csd-0 = java.nio.ByteArrayBuffer [posición = 2, límite = 2, capacidad = 2] 48000}
Incluso cuando adopto el MediaFormat creado por el TSExtractor para ser similar a la creada por el MediaExtractor el decodificador da el mismo error utilizando el auto creado y decodifica sin ningún problema con el otro.
Cualquier ayuda sería realmente útil.
- Cómo proporcionar datos de audio y datos de vídeo a MediaMux
- Cambiar el tamaño de la vista de la superficie para el cambio de la relación de aspecto en la visualización de vídeo en android
- Descodificación del búfer de salida de MediaCodec
- ¿Por qué el decodificador de MediaCodec no emite un formato YUV unificado (como YUV420P)?
- Cómo utilizar MediaCodec sin MediaExtractor para H264
- Android MediaExtractor y la corriente mp3
- El video de la API de MediaCodec de Android se reproduce demasiado rápido
- Reducción de tamaño de archivo de vídeo MediaMuxer (compresión, disminución de resolución)
Realmente no sé por qué, pero resulta que la inicialización de la "csd-0" ByteBuffer de esta manera
mediaFormat.setByteBuffer("csd-0", ByteBuffer.allocate(2).put(new byte[]{(byte) 0x11, (byte)0x90}));
No funciona, pero lo inicializa de esta manera
byte[] bytes = new byte[]{(byte) 0x11, (byte)0x90}; ByteBuffer bb = ByteBuffer.wrap(bytes); mediaFormat.setByteBuffer("csd-0", bb);
hace.
BTW, comparando estos dos byteBuffers usando
bb1.equals(bb2);
Devuelve true.
¡Muy extraño!
En el caso de fallo u puede ser necesario llamar al método de rebobinado ByteBuffer primero. Si u mirada cuidadosamente u'll ver la posición es diferente entre el MediaExtractor y el TSExtractor:
Csd-0 = java.nio.ByteArrayBuffer [ posición = 0 , límite = 2, capacidad = 2]
Vs
Csd-0 = java.nio.ByteArrayBuffer [ posición = 2 , límite = 2, capacidad = 2]
Los iguales de ByteBuffer sólo comparan los bytes después de la posición hasta que no coincidan; En el caso de ur un búfer ya está posicionado en el extremo por lo tanto no hay desajuste.
Gracias por el código anterior para calcular CSD. Por desgracia, esto no funcionaba para mí. Mi decodificador estaba fallando con el ajuste csd anterior. Finalmente encontré el problema. De acuerdo con la documentación primero "5 bits" de CSD es el tipo de objeto (Perfil). El perfil de código arriba se agrega a sólo 4 bits. Así que cambiar el código de abajo funciona bien para mí
int profile = (header[2] & 0xC0) >> 6; int srate = (header[2] & 0x3C) >> 2; int channel = ((header[2] & 0x01) << 2) | ((header.[3] & 0xC0) >> 6) ByteBuffer csd = ByteBuffer.allocate(2); csd.put(0, (byte)(profile << 3 | srate >> 1)); csd.put(1, (byte)((srate & 0x01) << 7 | channel << 3));
Los valores en csd-0 dependen del encabezado ADTS.
La longitud del encabezado ADTS es de hasta 9 bytes. Para generar csd-0 se necesita el segundo y el tercer byte del encabezado.
int profile = (header[2] & 0xC0) >> 6; int srate = (header[2] & 0x3C) >> 2; int channel = ((header[2] & 0x01) << 2) | ((header.[3] & 0xC0) >> 6) ByteBuffer csd = ByteBuffer.allocate(2); csd.put(0, (byte)( ((profile + 1) << 3) | srate >> 1 ) ); csd.put(1, (byte)( ((srate << 7) & 0x80) | channel << 3 ) );
Ahora tienes csd-0 válido para esta secuencia de audio aac.
- No se puede obtener Android ServiceTestCase para ejecutarse
- Dos elementos del menú Spinner en el ancho de ActionBar