Tiempo de muestra / tiempo de presentación incoherente durante la descodificación de vídeo

Estoy escribiendo un APP que puede codificar video por entrada de cámara y procesar video por pasos de decodificación-edición-codificación. Para la cámara, utilizo la clase Camera en vez de Intent para configurar los detalles de la cámara. A continuación, alimentar los marcos de la cámara al codificador (MediaCodec en API 16) y el muxer (uso ffmpeg muxer ya que quiero trabajar en 4.1 Dispositivos).

Mido el código de tiempo de los fotogramas de la cámara por el tiempo nano del sistema, y ​​selecciono un subconjunto de cuadros para caber un FPS deseado (actualmente 15). Hay algunos pequeños "ruidos" en los valores de tiempo, por ejemplo (en ms): 0, 60718, 135246, 201049, … en lugar de 0, 66000, 133000, 200000, ….

Después de algunos intentar configurar el muxer correctamente (como esta pregunta ), puedo producir un vídeo (con codec AVC) que puede ser la reproducción por el reproductor de vídeo en los dispositivos. La velocidad de reproducción es correcta, así que creo que el video debe tener información de tiempo correcta de los fotogramas.

Sin embargo, tengo un problema cuando intento descodificar el vídeo para realizar el proceso de edición de vídeo. Utilizo los pasos estándar de extracción / decodificación de video como estas muestras , como esto:

int decode_input_index = decoder.dequeueInputBuffer(TIMEOUT_USEC); if (decode_input_index >= 0) { ByteBuffer decoder_input_buffer = decode_input_buffers[decode_input_index]; int sample_size = extractor.readSampleData(decoder_input_buffer, 0); if (sample_size < 0) { decoder.queueInputBuffer(decode_input_index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); is_decode_input_done = true; } else { long sample_time = extractor.getSampleTime(); decoder.queueInputBuffer(decode_input_index, 0, sample_size, sample_time, 0); extractor.advance(); } } else { Log.v(TAG, "Decoder dequeueInputBuffer timed out! Try again later"); } 

El tiempo de muestreo de getSampleTime () tiene el valor correcto al codificar el video. (Por ejemplo, son exactamente 0, 60718, 135246, 201049, … en nosotros). También es el tiempo de presentación en la entrada de decoder.queueInputBuffer (). Cuando el decodificador procede a decodificar este cuadro, obtengo el tiempo de trama por:

 int decode_output_index = decoder.dequeueOutputBuffer(decode_buffer_info, TIMEOUT_USEC); switch (decode_output_index) { .... (some negative-value flags in MediaCodec) .... default: { ByteBuffer decode_output_buffer = decode_output_buffers[decode_output_index]; long ptime_us = decode_buffer_info.presentationTimeUs; boolean is_decode_EOS = ((decode_buffer_info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0); .... } } 

Espero fijar la misma secuencia de tiempo que el de la entrada del decodificador, pero consigo muchos de 0 del BufferInfo en la salida del decodificador. El contenido del marco decodificado parece correcto, pero la mayoría de los valores de tiempo de presentación son 0. Sólo los últimos cuadros tienen el tiempo de presentación correcto.

Pruebo todo el mismo proceso en un dispositivo con Android 4.3 (incluso con el mismo ffmpeg muxer en lugar de MediaMuxer en API 18), y todo se ve bien. En los dispositivos 4.1 / 4.2, si captura el video por la APP de cámara incorporada en el dispositivo y luego decodifico el vídeo, entonces el tiempo de presentación también es correcto, aunque los valores de tiempo también tienen ruidos debido al retardo de la cámara.

¿Qué hay de malo en el video o en el proceso de decodificación, cuando el vídeo se puede reproducir y decodificar normalmente, pero con tiempo de muestra correcto y tiempo de presentación incorrecto? Puedo tener que utilizar una solución para medir el tiempo de presentación por el tiempo de muestra (es fácil mediante el uso de una cola), pero quiero averiguar si hay alguna parte que falta en mi trabajo.

No hay ninguna garantía de que MediaCodec las MediaCodec tiempo de presentación correctamente antes de Android 4.3. Esto se debe a que las pruebas CTS que confirman el comportamiento PTS no se agregaron hasta entonces.

Recuerdo que hubo problemas con el manejo de la marca de tiempo en los códecs AVC de ciertos proveedores. No recuerdo los detalles de improviso, pero si ejecuta las pruebas buffer-to-buffer y buffer-to-surface de EncodeDecodeTest en una variedad de dispositivos 4.1 / 4.2, aparecerá algunos fallos. (Por supuesto, tendrías que eliminar las pruebas de superficie a superficie).

Su timestamp que maneja el código parece muy bien. La marca de tiempo no es parte de la secuencia H.264, por lo que realmente se está enviando a través del códec como metadatos, y parece que lo está recogiendo y enviándolo en todos los lugares correctos. La conclusión es que, si estás pasando Valores PTS válidos y obteniendo buenos valores de PTS de vídeo pero basura, algo en el códec está mal manejándolos.

Tendrás que trabajar alrededor de él pasando los valores por separado, o – si la tarifa de marco de entrada es siempre regular – computación trivial él. En teoría, el codificador puede reordenar cuadros, por lo que el orden en el que se pasan sellos de tiempo en el codificador puede no ser el mismo orden en el que salen … pero ya sabes que las marcas de tiempo estaban ascendiendo cuando hiciste la película, deberías Ser capaz de sólo ordenar si esto era un problema en la práctica.

En otra nota, los retrasos en el sistema causarán el "bamboleo" que se está viendo en los valores de marca de tiempo si se toma System.nanoTime() cuando el marco llega a la aplicación. Usted puede hacer un poco mejor en Android 4.3 con superficie de entrada porque SurfaceTexture mantiene una marca de tiempo que se establece mucho más cerca de cuando el marco fue capturado. (Sé que no es útil para sus esfuerzos actuales, pero quería dar alguna esperanza para el futuro.)

FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.