Cómo utilizar ByteBuffer en el contexto de MediaCodec en android
Hasta ahora soy capaz de configurar un MediaCodec para codificar una secuencia de vídeo. El objetivo es guardar las ilustraciones generadas por el usuario en un archivo de vídeo.
Utilizo objetos de mapa de bits de android de las ilustraciones de usuario para enviar marcos a la secuencia.
- ¿Cómo puedo consultar los álbumes con MediaBrowser?
- Archivo de vídeo sdk cut / trim de Android
- ¿Cómo administrar el estado de Android MediaPlayer, errores y excepciones?
- Android SDK Mediaplayer.create devuelve al azar null
- ¿Hay alguna forma de obtener canciones que cuenten canciones o canciones que se hayan reproducido recientemente en Android?
Vea el fragmento de código que utilizo al final de este post (es el código completo, no se recorta nada):
MediaCodec utiliza ByteBuffer para hacer frente a flujos de vídeo / audio.
Los mapas de bits se basan en int [], que si se convierte en byte [] requerirá x4 el tamaño de la int []
Hice algunas investigaciones para averiguar qué contratos existen en el lugar para el ByteBuffer cuando se trata de flujos de vídeo / audio en MediaCodec, pero la información está casi cerca de zilch.
¿Cuáles son los contratos de uso de ByteBuffer en MediaCodec?
¿Especificar las dimensiones del marco en el MediaFormat significa automáticamente que el ByteBuffers tiene anchura * altura * capacidad de 4 bytes?
(Yo uso un objeto de mapa de bits a la vez para cada marco)
Gracias por cualquier ayuda.
(Editado, código añadido)
import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.nio.ByteBuffer; import android.graphics.Rect; import android.graphics.Bitmap.CompressFormat; import android.media.MediaCodec; import android.media.MediaCodec.BufferInfo; import android.media.CamcorderProfile; import android.media.MediaCodecInfo; import android.media.MediaFormat; import android.util.Log; import android.view.View; public class VideoCaptureManager { private boolean running; private long presentationTime; public void start(View rootView, String saveFilePath){ Log.e("OUT", saveFilePath); this.running = true; this.presentationTime = 0; this.capture(rootView, saveFilePath); } private void capture(final View rootView, String saveFilePath){ if(rootView != null){ rootView.setDrawingCacheEnabled(true); final Rect drawingRect = new Rect(); rootView.getDrawingRect(drawingRect); try{ final File file = new File(saveFilePath); if(file.exists()){ // File exists return return; } else { File parent = file.getParentFile(); if(!parent.exists()){ parent.mkdirs(); } } new Thread(){ public void run(){ try{ DataOutputStream dos = new DataOutputStream(new FileOutputStream(file)); MediaCodec codec = MediaCodec.createEncoderByType("video/mp4v-es"); MediaFormat mediaFormat = null; if(CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P)){ mediaFormat = MediaFormat.createVideoFormat("video/mp4v-es", 720, 1280); } else { mediaFormat = MediaFormat.createVideoFormat("video/mp4v-es", 480, 720); } mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 700000); mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 10); mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar); mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5); codec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); codec.start(); ByteBuffer[] inputBuffers = codec.getInputBuffers(); ByteBuffer[] outputBuffers = codec.getOutputBuffers(); while(VideoCaptureManager.this.running){ try{ int inputBufferIndex = codec.dequeueInputBuffer(-2); if(inputBufferIndex >= 0){ // Fill in the bitmap bytes // inputBuffers[inputBufferIndex]. ByteArrayOutputStream baos = new ByteArrayOutputStream(); rootView.getDrawingCache().compress(CompressFormat.JPEG, 80, baos); inputBuffers[inputBufferIndex].put(baos.toByteArray()); codec.queueInputBuffer(inputBufferIndex, 0, inputBuffers[inputBufferIndex].capacity(), presentationTime, MediaCodec.BUFFER_FLAG_CODEC_CONFIG); presentationTime += 100; } BufferInfo info = new BufferInfo(); int outputBufferIndex = codec.dequeueOutputBuffer(info, -2); if(outputBufferIndex >= 0){ // Write the bytes to file byte[] array = outputBuffers[outputBufferIndex].array(); // THIS THORWS AN EXCEPTION. WHAT IS THE CONTRACT TO DEAL WITH ByteBuffer in this code? if(array != null){ dos.write(array); } codec.releaseOutputBuffer(outputBufferIndex, false); } else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED){ outputBuffers = codec.getOutputBuffers(); } else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){ // codec format is changed MediaFormat format = codec.getOutputFormat(); } Thread.sleep(100); }catch(Throwable th){ Log.e("OUT", th.getMessage(), th); } } codec.stop(); codec.release(); codec = null; dos.flush(); dos.close(); }catch(Throwable th){ Log.e("OUT", th.getMessage(), th); } } }.start(); }catch(Throwable th){ Log.e("OUT", th.getMessage(), th); } } } public void stop(){ this.running = false; } }
- Android: El uso de los fallos de MIC (a través de setAudioSource)
- Mejor manera de combinar archivos de audio en Android
- Uso del caché en ExoPlayer
- Android, ¿cómo establecer los metadatos en el archivo MP4?
- Cómo obtener una lista de reproductores multimedia instalados
- Hacer un bucle de audio en Android
- Android MediaPlayer seekTo no funciona
- Android MediaPlayer OnPreparedListener
La configuración exacta del ByteBuffer
está determinada por el codec para el formato de entrada que ha elegido. No todos los dispositivos admiten todos los formatos de entrada posibles (por ejemplo, algunos codificadores AVC requieren 420 YUV planar, otros requieren semi-planar). Las versiones anteriores de Android (<= API 17) no proporcionaban una forma portátil de generar imágenes de video para MediaCodec
.
En Android 4.3 (API 18), tiene dos opciones. En primer lugar, MediaCodec
ahora acepta la entrada de una superficie, lo que significa que cualquier cosa que se puede dibujar con OpenGL ES se puede grabar como una película. Véase, por ejemplo, el ejemplo EncodeAndMuxTest .
Segundo, usted todavía tiene la opción de usar buffers YUV 420 generados por software, pero ahora es más probable que funcionen porque hay pruebas CTS que los ejercitan. Usted todavía tiene que hacer la detección de tiempo de ejecución de planar o semi-planar, pero sólo hay dos diseños. Consulte las variantes de buffer-to-buffer de EncodeDecodeTest para ver un ejemplo.
- Llamada múltiple de getView () en GridView
- Por qué las funciones de OpenGL ES no se pueden llamar desde otro subproceso