videos que por pese pesa para optimizar menos mas liviano hacer guardar formato enviar comprimir como android video compression

android - que - optimizar video whatsapp



CompresiĆ³n de video en Android usando la nueva Biblioteca de MediaCodec (3)

Acabo de tropezar con esta biblioteca que puede ayudarlo a crear el contenedor adecuado para que los reproductores de video lo reconozcan: http://code.google.com/p/mp4parser/

En mi aplicación, intento subir algunos videos que el usuario seleccionó de la galería. El problema es que generalmente los archivos de video de Android son demasiado grandes para cargarlos y, por lo tanto, queremos comprimirlos primero con una tasa de bits / resolución más baja.

Acabo de enterarme de la nueva API MediaCodec que se presenta con API 16 (intenté hacerlo con ffmpeg).

Lo que estoy haciendo ahora es lo siguiente: primero decodifique el video de entrada usando un decodificador de video y configúrelo con el formato que se leyó en el archivo de entrada. A continuación, creo un codificador de video estándar con algunos parámetros predefinidos y lo uso para codificar el búfer de salida del decodificador. Luego guardo el buffer de salida del codificador en un archivo.

Todo se ve bien: se escribe y lee la misma cantidad de paquetes desde cada buffer de entrada y salida, pero el archivo final no se ve como un archivo de video y no puede ser abierto por ningún reproductor de video.

Parece que la decodificación está bien, porque la pruebo mostrándola en Surface. Primero configuro el decodificador para que funcione con Surface, y cuando llamamos a releaseOutputBuffer usamos el indicador de renderizado, y podemos ver el video en la pantalla.

Aquí está el código que estoy usando:

//init decoder MediaCodec decoder = MediaCodec.createDecoderByType(mime); decoder.configure(format, null , null , 0); decoder.start(); ByteBuffer[] codecInputBuffers = decoder.getInputBuffers(); ByteBuffer[] codecOutputBuffers = decoder.getOutputBuffers(); //init encoder MediaCodec encoder = MediaCodec.createEncoderByType(mime); int width = format.getInteger(MediaFormat.KEY_WIDTH); int height = format.getInteger(MediaFormat.KEY_HEIGHT); MediaFormat mediaFormat = MediaFormat.createVideoFormat(mime, width, height); mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 400000); mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 25); mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar); mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5); encoder.configure(mediaFormat, null , null , MediaCodec.CONFIGURE_FLAG_ENCODE); encoder.start(); ByteBuffer[] encoderInputBuffers = encoder.getInputBuffers(); ByteBuffer[] encoderOutputBuffers = encoder.getOutputBuffers(); extractor.selectTrack(0); boolean sawInputEOS = false; boolean sawOutputEOS = false; boolean sawOutputEOS2 = false; MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); BufferInfo encoderInfo = new MediaCodec.BufferInfo(); while (!sawInputEOS || !sawOutputEOS || !sawOutputEOS2) { if (!sawInputEOS) { sawInputEOS = decodeInput(extractor, decoder, codecInputBuffers); } if (!sawOutputEOS) { int outputBufIndex = decoder.dequeueOutputBuffer(info, 0); if (outputBufIndex >= 0) { sawOutputEOS = decodeEncode(extractor, decoder, encoder, codecOutputBuffers, encoderInputBuffers, info, outputBufIndex); } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { Log.d(LOG_TAG, "decoding INFO_OUTPUT_BUFFERS_CHANGED"); codecOutputBuffers = decoder.getOutputBuffers(); } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { final MediaFormat oformat = decoder.getOutputFormat(); Log.d(LOG_TAG, "decoding Output format has changed to " + oformat); } else if (outputBufIndex == MediaCodec.INFO_TRY_AGAIN_LATER) { Log.d(LOG_TAG, "decoding dequeueOutputBuffer timed out!"); } } if (!sawOutputEOS2) { int encodingOutputBufferIndex = encoder.dequeueOutputBuffer(encoderInfo, 0); if (encodingOutputBufferIndex >= 0) { sawOutputEOS2 = encodeOuput(outputStream, encoder, encoderOutputBuffers, encoderInfo, encodingOutputBufferIndex); } else if (encodingOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { Log.d(LOG_TAG, "encoding INFO_OUTPUT_BUFFERS_CHANGED"); encoderOutputBuffers = encoder.getOutputBuffers(); } else if (encodingOutputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { final MediaFormat oformat = encoder.getOutputFormat(); Log.d(LOG_TAG, "encoding Output format has changed to " + oformat); } else if (encodingOutputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) { Log.d(LOG_TAG, "encoding dequeueOutputBuffer timed out!"); } } } //clear some stuff here...

y esos son los métodos que uso para decodificar / codificar:

private boolean decodeInput(MediaExtractor extractor, MediaCodec decoder, ByteBuffer[] codecInputBuffers) { boolean sawInputEOS = false; int inputBufIndex = decoder.dequeueInputBuffer(0); if (inputBufIndex >= 0) { ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; input1count++; int sampleSize = extractor.readSampleData(dstBuf, 0); long presentationTimeUs = 0; if (sampleSize < 0) { sawInputEOS = true; sampleSize = 0; Log.d(LOG_TAG, "done decoding input: #" + input1count); } else { presentationTimeUs = extractor.getSampleTime(); } decoder.queueInputBuffer(inputBufIndex, 0, sampleSize, presentationTimeUs, sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); if (!sawInputEOS) { extractor.advance(); } } return sawInputEOS; } private boolean decodeOutputToFile(MediaExtractor extractor, MediaCodec decoder, ByteBuffer[] codecOutputBuffers, MediaCodec.BufferInfo info, int outputBufIndex, OutputStream output) throws IOException { boolean sawOutputEOS = false; ByteBuffer buf = codecOutputBuffers[outputBufIndex]; if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { sawOutputEOS = true; Log.d(LOG_TAG, "done decoding output: #" + output1count); } if (info.size > 0) { output1count++; byte[] outData = new byte[info.size]; buf.get(outData); output.write(outData, 0, outData.length); } else { Log.d(LOG_TAG, "no data available " + info.size); } buf.clear(); decoder.releaseOutputBuffer(outputBufIndex, false); return sawOutputEOS; } private boolean encodeInputFromFile(MediaCodec encoder, ByteBuffer[] encoderInputBuffers, MediaCodec.BufferInfo info, FileChannel channel) throws IOException { boolean sawInputEOS = false; int inputBufIndex = encoder.dequeueInputBuffer(0); if (inputBufIndex >= 0) { ByteBuffer dstBuf = encoderInputBuffers[inputBufIndex]; input1count++; int sampleSize = channel.read(dstBuf); if (sampleSize < 0) { sawInputEOS = true; sampleSize = 0; Log.d(LOG_TAG, "done encoding input: #" + input1count); } encoder.queueInputBuffer(inputBufIndex, 0, sampleSize, channel.position(), sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); } return sawInputEOS; }

¿Alguna sugerencia sobre lo que estoy haciendo mal?

No encontré demasiados ejemplos para codificar con MediaCodec solo unas pocas muestras de código para decodificar ... Muchas gracias por la ayuda


Use la biblioteca SiliCompressor para resolver eso. Comprima videos e imágenes mientras mantiene la calidad de los medios.


La salida de MediaCodec es una transmisión elemental sin formato. Debe empaquetarlo en un formato de archivo de video (posiblemente muteando el audio) antes de que muchos jugadores lo reconozcan. FWIW, he descubierto que Totem Movie Player para Linux, basado en GStreamer, reproducirá archivos de video / avc "en bruto".

Actualización: La forma de convertir H.264 a .mp4 en Android es con la clase MediaMuxer , introducida en Android 4.3 (API 18). Hay un par de ejemplos (EncodeAndMuxTest, CameraToMpegTest) que demuestran su uso.