supported for codecs android video-streaming android-camera h.264 video-encoding

for - Codificación H.264 de la cámara con Android MediaCodec



audio format supported by android (5)

Estoy intentando que esto funcione en Android 4.1 (usando una tableta Asus Transformer mejorada). Gracias a la respuesta de Alex a mi pregunta anterior , ya pude escribir algunos datos en bruto H.264 en un archivo, pero este archivo solo se puede reproducir con ffplay -f h264 , y parece que se ha perdido toda la información relacionada con la tasa de cuadros (extremadamente reproducción rápida). Además, el espacio de color parece incorrecto (la atmósfera utiliza el valor predeterminado de la cámara en el lado del codificador).

public class AvcEncoder { private MediaCodec mediaCodec; private BufferedOutputStream outputStream; public AvcEncoder() { File f = new File(Environment.getExternalStorageDirectory(), "Download/video_encoded.264"); touch (f); try { outputStream = new BufferedOutputStream(new FileOutputStream(f)); Log.i("AvcEncoder", "outputStream initialized"); } catch (Exception e){ e.printStackTrace(); } mediaCodec = MediaCodec.createEncoderByType("video/avc"); MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", 320, 240); mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 125000); mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 15); mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar); mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5); mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); mediaCodec.start(); } public void close() { try { mediaCodec.stop(); mediaCodec.release(); outputStream.flush(); outputStream.close(); } catch (Exception e){ e.printStackTrace(); } } // called from Camera.setPreviewCallbackWithBuffer(...) in other class public void offerEncoder(byte[] input) { try { ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers(); ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers(); int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1); if (inputBufferIndex >= 0) { ByteBuffer inputBuffer = inputBuffers[inputBufferIndex]; inputBuffer.clear(); inputBuffer.put(input); mediaCodec.queueInputBuffer(inputBufferIndex, 0, input.length, 0, 0); } MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo,0); while (outputBufferIndex >= 0) { ByteBuffer outputBuffer = outputBuffers[outputBufferIndex]; byte[] outData = new byte[bufferInfo.size]; outputBuffer.get(outData); outputStream.write(outData, 0, outData.length); Log.i("AvcEncoder", outData.length + " bytes written"); mediaCodec.releaseOutputBuffer(outputBufferIndex, false); outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0); } } catch (Throwable t) { t.printStackTrace(); } }

Cambiar el tipo de codificador a "video / mp4" aparentemente resuelve el problema de la tasa de cuadros, pero como el objetivo principal es hacer un servicio de transmisión, esta no es una buena solución.

Soy consciente de que eliminé parte del código de Alex teniendo en cuenta los NALU de SPS y PPS, pero esperaba que esto no fuera necesario ya que esa información también provenía de outData y asumí que el codificador lo formatearía correctamente. Si este no es el caso, ¿cómo debo organizar los diferentes tipos de NALU en mi archivo / flujo?

Entonces, ¿qué me estoy perdiendo aquí para hacer una transmisión H.264 válida y en funcionamiento? ¿Y qué ajustes debo usar para hacer una coincidencia entre el espacio de color de la cámara y el espacio de color del codificador?

Tengo la sensación de que esto es más una cuestión relacionada con H.264 que un tema de Android / MediaCodec. ¿O todavía no estoy usando la API MediaCodec correctamente?

Gracias por adelantado.


Android 4.3 (API 18) proporciona una solución fácil. La clase MediaCodec ahora acepta entradas de Superficies, lo que significa que puede conectar la vista previa de la superficie de la cámara al codificador y omitir todos los problemas de formato YUV.

También hay una nueva clase de MediaMuxer que convertirá su flujo H.264 en bruto en un archivo .mp4 (opcionalmente mezclando un flujo de audio).

Consulte la fuente CameraToMpegTest para ver un ejemplo de cómo hacer exactamente esto. (También demuestra el uso de un sombreador de fragmentos OpenGL ES para realizar una edición trivial en el video mientras se graba).


Para su reproducción rápida - problema de velocidad de cuadros, no hay nada que tenga que hacer aquí. Dado que se trata de una solución de transmisión, al otro lado se le debe indicar la velocidad de cuadros por adelantado o las marcas de tiempo con cada cuadro. Ambos de estos no son parte de la corriente elemental. Se elige una tasa de cuadros predeterminada o se pasa algún SDP o algo así, o se usan los protocolos existentes como rtsp. En el segundo caso, las marcas de tiempo son parte del flujo enviado en forma de algo como rtp. Luego el cliente tiene que depositar la transmisión rtp y reproducirla. Así es como funciona la transmisión elemental. [o bien corrija su velocidad de fotogramas si tiene un codificador de frecuencia fija o indique las marcas de tiempo]

La reproducción local de PC será rápida porque no conocerá los fps. Al dar el parámetro fps antes de la entrada por ejemplo

ffplay -fps 30 in.264

Puedes controlar la reproducción en la PC.

En cuanto al archivo que no se puede reproducir: ¿Tiene un SPS y un PPS? También debe tener los encabezados NAL habilitados - formato anexo b. No sé mucho acerca de Android, pero este es un requisito para que se pueda reproducir cualquier flujo elemental h.264 cuando no están en ningún contenedor y se debe descargar y reproducir más tarde. Si el valor predeterminado de Android es mp4, pero las cabeceras anexas predeterminadas se desactivarán, entonces quizás haya un interruptor para habilitarlo. O si está obteniendo datos cuadro por cuadro, solo agréguelo usted mismo.

En cuanto al formato de color: supongo que el predeterminado debería funcionar. Así que intenta no configurarlo. Si no, intente con los formatos intercalados 422 Planar o UVYV / VYUY. Normalmente las cámaras son una de esas. (pero no es necesario, estos pueden ser los que he encontrado más a menudo).


Puede consultar el formato de mapa de bits compatible con MediaCodec y consultar su vista previa. El problema es que algunos MediaCodecs solo admiten formatos YUV empaquetados propietarios que no puede obtener de la vista previa. Particularmente 2130706688 = 0x7F000100 = COLOR_TI_FormatYUV420PackedSemiPlanar. El formato predeterminado para la vista previa es 17 = NV21 = MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV411Planar = YCbCr 420 Semi Planar


Puede convertir espacios de color como este, si ha establecido el espacio de color de vista previa en YV12:

public static byte[] YV12toYUV420PackedSemiPlanar(final byte[] input, final byte[] output, final int width, final int height) { /* * COLOR_TI_FormatYUV420PackedSemiPlanar is NV12 * We convert by putting the corresponding U and V bytes together (interleaved). */ final int frameSize = width * height; final int qFrameSize = frameSize/4; System.arraycopy(input, 0, output, 0, frameSize); // Y for (int i = 0; i < qFrameSize; i++) { output[frameSize + i*2] = input[frameSize + i + qFrameSize]; // Cb (U) output[frameSize + i*2 + 1] = input[frameSize + i]; // Cr (V) } return output; }

O

public static byte[] YV12toYUV420Planar(byte[] input, byte[] output, int width, int height) { /* * COLOR_FormatYUV420Planar is I420 which is like YV12, but with U and V reversed. * So we just have to reverse U and V. */ final int frameSize = width * height; final int qFrameSize = frameSize/4; System.arraycopy(input, 0, output, 0, frameSize); // Y System.arraycopy(input, frameSize, output, frameSize + qFrameSize, qFrameSize); // Cr (V) System.arraycopy(input, frameSize + qFrameSize, output, frameSize, qFrameSize); // Cb (U) return output; }


Si no solicitó explícitamente otro formato de píxeles, los búferes de vista previa de la cámara llegarán en un formato YUV 420 conocido como NV21 , para el que COLOR_FormatYCrYCb es el equivalente de MediaCodec.

Desafortunadamente, como mencionan otras respuestas en esta página, no hay garantía de que en su dispositivo, el codificador AVC admita este formato. Tenga en cuenta que existen algunos dispositivos extraños que no son compatibles con NV21, pero no conozco ninguno que pueda actualizarse a API 16 (por lo tanto, tenga MediaCodec).

La documentación de Google también afirma que YV12 planar YUV debe YV12 como formato de vista previa de la cámara para todos los dispositivos con API> = 12. Por lo tanto, puede ser útil probarlo (el equivalente de MediaCodec es COLOR_FormatYUV420Planar que usa en su fragmento de código).

Actualización : como me recordó Andrew Cottrell, YV12 aún necesita intercambio de croma para convertirse en COLOR_FormatYUV420Planar.