Cómo crear un video de una matriz de imágenes en Android?

Idea:

Estoy construyendo esta aplicación de proyecto senior en Android. Cuenta con una función de transmisión de vídeo en directo donde la aplicación recibe 20 fps y los muestra a los usuarios. La característica que estoy atascado en es para grabar lo que está siendo vivir para que los usuarios pueden ver más tarde.

Esta rutina es lo que sucede 20 veces por segundo:

dsocket.receive(packetReceived); // receive a packet byte[] buff = packetReceived.getData(); // convert packet to byte[] final Bitmap ReceivedImage = BitmapFactory.decodeByteArray(buff, 0, buff.length); // convert byte[] to bitmap image runOnUiThread(new Runnable() { @Override public void run() { imageView.setImageBitmap(ReceivedImage); // display on the screen } }); 

Tarea:

Ahora, al hacer clic en un botón (StartRecording), quiero comenzar a rellenar una matriz de Imágenes recibidas, algo así:

 List<Bitmap> MyBitmapArray; // .. MyBitmapArray.Add(ReceivedImage); 

Y en otro clic de botón (StopRecording) quiero llamar a esta función que debe construir mi vídeo y guardarlo localmente en el dispositivo:

 public void CreateAndSaveVideoFile(List<Bitmap> MyBitmapArray) { } 

Pruebas:

  • Siguiendo java / xuggle – codifica la matriz de imágenes en una película , el enlace en la respuesta es un enlace inactivo

  • Siguiente ¿Cómo codificar imágenes en un archivo de vídeo en Java a través de la programación? , La biblioteca sugerida en la respuesta aceptada no es compatible con Android.

  • La siguiente respuesta en el anterior tiene un enfoque para los usuarios de Android, sin embargo, no está claro para mí la entrada y la salida de esa función (¿dónde dio las imágenes y dónde obtuvo el video?) – Dejé un comentario pregunta

  • La respuesta siguiente en la anterior proporciona una clase completa, sin embargo la biblioteca necesaria para ser incluido tiene un archivo dañado (cuando intento y descargarlo desde el enlace proporcionado) – Dejé un comentario de la pregunta

  • Después de Java: ¿Cómo puedo crear una película a partir de una matriz de imágenes? , la biblioteca sugerida en la respuesta superior utiliza comandos que no conozco y que ni siquiera sé cómo usarlos. Me gusta:

Creación de un archivo MPEG-4 desde todos los archivos JPEG del directorio actual:

mencoder mf://*.jpg -mf w=800:h=600:fps=25:type=jpg -ovc lavc \ -lavcopts vcodec=mpeg4:mbd=2:trell -oac copy -o output.avi

No sé cómo puedo utilizar lo anterior en un proyecto de Java / Android.

¿Puede alguien ayudarme a guiarme y / o proporcionarme un enfoque para mi tarea? Gracias por adelantado.

Puede utilizar jcodec SequenceEncoder para convertir secuencias de imágenes en archivos MP4.

Código de muestra :

 import org.jcodec.api.awt.SequenceEncoder; ... SequenceEncoder enc = new SequenceEncoder(new File("filename")); // GOP size will be supported in 0.2 // enc.getEncoder().setKeyInterval(25); for(...) { BufferedImage image = ... // Obtain an image to encode enc.encodeImage(image); } enc.finish(); 

Es una biblioteca de Java por lo que es fácil de importar en el proyecto de Android, no tiene que utilizar NDK a diferencia de ffmpeg.

Consulte http://jcodec.org/ para obtener ejemplos de código y descargas.

Uso de JCodec

 public static void main(String[] args) throws IOException { SequenceEncoder encoder = new SequenceEncoder(new File("video.mp4")); for (int i = 1; i < 100; i++) { BufferedImage bi = ImageIO.read(new File(String.format("img%08d.png", i))); encoder.encodeImage(bi); } encoder.finish();} 

Ahora para convertir su mapa de bits a BufferedImage puede utilizar esta clase:

 import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.io.IOException; import java.io.InputStream; /** * Utility class for loading windows bitmap files * <p> * Based on code from author Abdul Bezrati and Pepijn Van Eeckhoudt */ public class BitmapLoader { /** * Static method to load a bitmap file based on the filename passed in. * Based on the bit count, this method will either call the 8 or 24 bit * bitmap reader methods * * @param file The name of the bitmap file to read * @throws IOException * @return A BufferedImage of the bitmap */ public static BufferedImage loadBitmap(String file) throws IOException { BufferedImage image; InputStream input = null; try { input = ResourceRetriever.getResourceAsStream(file); int bitmapFileHeaderLength = 14; int bitmapInfoHeaderLength = 40; byte bitmapFileHeader[] = new byte[bitmapFileHeaderLength]; byte bitmapInfoHeader[] = new byte[bitmapInfoHeaderLength]; input.read(bitmapFileHeader, 0, bitmapFileHeaderLength); input.read(bitmapInfoHeader, 0, bitmapInfoHeaderLength); int nSize = bytesToInt(bitmapFileHeader, 2); int nWidth = bytesToInt(bitmapInfoHeader, 4); int nHeight = bytesToInt(bitmapInfoHeader, 8); int nBiSize = bytesToInt(bitmapInfoHeader, 0); int nPlanes = bytesToShort(bitmapInfoHeader, 12); int nBitCount = bytesToShort(bitmapInfoHeader, 14); int nSizeImage = bytesToInt(bitmapInfoHeader, 20); int nCompression = bytesToInt(bitmapInfoHeader, 16); int nColoursUsed = bytesToInt(bitmapInfoHeader, 32); int nXPixelsMeter = bytesToInt(bitmapInfoHeader, 24); int nYPixelsMeter = bytesToInt(bitmapInfoHeader, 28); int nImportantColours = bytesToInt(bitmapInfoHeader, 36); if (nBitCount == 24) { image = read24BitBitmap(nSizeImage, nHeight, nWidth, input); } else if (nBitCount == 8) { image = read8BitBitmap(nColoursUsed, nBitCount, nSizeImage, nWidth, nHeight, input); } else { System.out.println("Not a 24-bit or 8-bit Windows Bitmap, aborting..."); image = null; } } finally { try { if (input != null) input.close(); } catch (IOException e) { } } return image; } /** * Static method to read a 8 bit bitmap * * @param nColoursUsed Number of colors used * @param nBitCount The bit count * @param nSizeImage The size of the image in bytes * @param nWidth The width of the image * @param input The input stream corresponding to the image * @throws IOException * @return A BufferedImage of the bitmap */ private static BufferedImage read8BitBitmap(int nColoursUsed, int nBitCount, int nSizeImage, int nWidth, int nHeight, InputStream input) throws IOException { int nNumColors = (nColoursUsed > 0) ? nColoursUsed : (1 & 0xff) << nBitCount; if (nSizeImage == 0) { nSizeImage = ((((nWidth * nBitCount) + 31) & ~31) >> 3); nSizeImage *= nHeight; } int npalette[] = new int[nNumColors]; byte bpalette[] = new byte[nNumColors * 4]; readBuffer(input, bpalette); int nindex8 = 0; for (int n = 0; n < nNumColors; n++) { npalette[n] = (255 & 0xff) << 24 | (bpalette[nindex8 + 2] & 0xff) << 16 | (bpalette[nindex8 + 1] & 0xff) << 8 | (bpalette[nindex8 + 0] & 0xff); nindex8 += 4; } int npad8 = (nSizeImage / nHeight) - nWidth; BufferedImage bufferedImage = new BufferedImage(nWidth, nHeight, BufferedImage.TYPE_INT_ARGB); DataBufferInt dataBufferByte = ((DataBufferInt) bufferedImage.getRaster().getDataBuffer()); int[][] bankData = dataBufferByte.getBankData(); byte bdata[] = new byte[(nWidth + npad8) * nHeight]; readBuffer(input, bdata); nindex8 = 0; for (int j8 = nHeight - 1; j8 >= 0; j8--) { for (int i8 = 0; i8 < nWidth; i8++) { bankData[0][j8 * nWidth + i8] = npalette[((int) bdata[nindex8] & 0xff)]; nindex8++; } nindex8 += npad8; } return bufferedImage; } /** * Static method to read a 24 bit bitmap * * @param nSizeImage size of the image in bytes * @param nHeight The height of the image * @param nWidth The width of the image * @param input The input stream corresponding to the image * @throws IOException * @return A BufferedImage of the bitmap */ private static BufferedImage read24BitBitmap(int nSizeImage, int nHeight, int nWidth, InputStream input) throws IOException { int npad = (nSizeImage / nHeight) - nWidth * 3; if (npad == 4 || npad < 0) npad = 0; int nindex = 0; BufferedImage bufferedImage = new BufferedImage(nWidth, nHeight, BufferedImage.TYPE_4BYTE_ABGR); DataBufferByte dataBufferByte = ((DataBufferByte) bufferedImage.getRaster().getDataBuffer()); byte[][] bankData = dataBufferByte.getBankData(); byte brgb[] = new byte[(nWidth + npad) * 3 * nHeight]; readBuffer(input, brgb); for (int j = nHeight - 1; j >= 0; j--) { for (int i = 0; i < nWidth; i++) { int base = (j * nWidth + i) * 4; bankData[0][base] = (byte) 255; bankData[0][base + 1] = brgb[nindex]; bankData[0][base + 2] = brgb[nindex + 1]; bankData[0][base + 3] = brgb[nindex + 2]; nindex += 3; } nindex += npad; } return bufferedImage; } /** * Converts bytes to an int * * @param bytes An array of bytes * @param index * @returns A int representation of the bytes */ private static int bytesToInt(byte[] bytes, int index) { return (bytes[index + 3] & 0xff) << 24 | (bytes[index + 2] & 0xff) << 16 | (bytes[index + 1] & 0xff) << 8 | bytes[index + 0] & 0xff; } /** * Converts bytes to a short * * @param bytes An array of bytes * @param index * @returns A short representation of the bytes */ private static short bytesToShort(byte[] bytes, int index) { return (short) (((bytes[index + 1] & 0xff) << 8) | (bytes[index + 0] & 0xff)); } /** * Reads the buffer * * @param in An InputStream * @param buffer An array of bytes * @throws IOException */ private static void readBuffer(InputStream in, byte[] buffer) throws IOException { int bytesRead = 0; int bytesToRead = buffer.length; while (bytesToRead > 0) { int read = in.read(buffer, bytesRead, bytesToRead); bytesRead += read; bytesToRead -= read; } } } 

Pregunta y respuesta originales aquí.

Si la versión mínima de su aplicación Android SDK es mayor o igual a 16 (Android 4.1) la mejor forma de codificación de vídeo es utilizar Android Media Codec API .

Desde las API de Android 4.3 .

Al codificar vídeo, Android 4.1 (SDK 16) requirió que usted proporcione a los medios una matriz ByteBuffer, pero Android 4.3 (SDK 18) ahora le permite usar una superficie como la entrada a un codificador. Por ejemplo, esto le permite codificar la entrada de un archivo de vídeo existente o usar marcos generados desde OpenGL ES.

Media Muxer añadido en Android 4.3 (SDK 18) por lo que para la forma conveniente de escribir archivo mp4 con Media Muxer debe tener SDK> = 18.

Usando Media Codec API de manera que obtendrá aceleración de hardware de codificación y que son fácilmente codificar hasta 60 FPS.

Usted puede comenzar a partir de 1) ¿Cómo codificar mapas de bits en un video usando MediaCodec? o usar 2) Google Grafika o 3) Bigflake .

A partir de Grafika RecordFBOActivity.java . Reemplace el evento Choreographer con su propio mapa de bits que contiene para codificar, elimine el dibujo en pantalla, cargue el mapa de bits como textura GL abierta y dibujelo en la superficie de entrada de Media Codec.

Abhishek V tenía razón, más información acerca de jcodec SequenceEncoder : vea Android hacer vídeo animado de la lista de imágenes

Recientemente he construido un sistema de video en tiempo real usando pi de frambuesa y dispositivos Android, cumplió con el mismo problema que el tuyo. En lugar de guardar una lista de archivos de imagen, he utilizado algunos protocolos de streaming en tiempo real como RTP / RTCP para transferir flujo de datos al usuario. Si su requisito es algo como esto, tal vez podría cambiar sus estrategias.

Otra sugerencia es que puede explorar algunas bibliotecas de C / C ++, utilizando NDK / JNI para romper la limitación de Java.

Espero que las sugerencias tienen sentido para usted 🙂

Usted puede utilizar la biblioteca de ffmpeg para hacer el vídeo del arsenal de la imagen. La biblioteca FFMPEG es muy útil para hacer video. Tal vez el siguiente enlace le ayudará. http://osric.com/chris/accidental-developer/2012/04/using-ffmpeg-to-programmatically-slice-and-splice-video/ https://groups.google.com/forum/#!topic/ android-ndk / sxDYlGYK-Xg

usted tiene mapas de bits se puede voltear en vídeo con JCodec

Aquí hay un codificador de secuencia de imagen de ejemplo:

Puede modificarlo para sus propósitos reemplazando BufferedImage con Bitmap.

Utilice estos métodos de acuerdo a su necesidad.

 public static Picture fromBitmap(Bitmap src) { Picture dst = Picture.create((int)src.getWidth(), (int)src.getHeight(), RGB); fromBitmap(src, dst); return dst; } public static void fromBitmap(Bitmap src, Picture dst) { int[] dstData = dst.getPlaneData(0); int[] packed = new int[src.getWidth() * src.getHeight()]; src.getPixels(packed, 0, src.getWidth(), 0, 0, src.getWidth(), src.getHeight()); for (int i = 0, srcOff = 0, dstOff = 0; i < src.getHeight(); i++) { for (int j = 0; j < src.getWidth(); j++, srcOff++, dstOff += 3) { int rgb = packed[srcOff]; dstData[dstOff] = (rgb >> 16) & 0xff; dstData[dstOff + 1] = (rgb >> 8) & 0xff; dstData[dstOff + 2] = rgb & 0xff; } } } public static Bitmap toBitmap(Picture src) { Bitmap dst = Bitmap.create(pic.getWidth(), pic.getHeight(), ARGB_8888); toBitmap(src, dst); return dst; } public static void toBitmap(Picture src, Bitmap dst) { int[] srcData = src.getPlaneData(0); int[] packed = new int[src.getWidth() * src.getHeight()]; for (int i = 0, dstOff = 0, srcOff = 0; i < src.getHeight(); i++) { for (int j = 0; j < src.getWidth(); j++, dstOff++, srcOff += 3) { packed[dstOff] = (srcData[srcOff] << 16) | (srcData[srcOff + 1] << 8) | srcData[srcOff + 2]; } } dst.setPixels(packed, 0, src.getWidth(), 0, 0, src.getWidth(), src.getHeight()); } 
  • Mostrar enormes imágenes en Android
  • Una sola intención de permitir que el usuario tome una foto O escoge una imagen de la galería en Android
  • Android ImageButton con imagen y texto
  • Establecer el ancho y la altura de la imagen en ImageView
  • Cómo obtener la altura y el ancho de una imagen utilizada en android
  • Añadir sombras en la parte inferior de la imagen
  • Redimensionamiento de mapa de bits en Android con interpolación bicúbica
  • Android - Imagen en botón presionado evento - Aplicar superposición de imagen a todos
  • Android ImageView cómo adaptarse a la parte inferior izquierda, como setScaleType (ScaleType.FIT_END) ;, pero no se ajusta a la parte inferior derecha
  • Vista de la imagen de Android desde url
  • Android programaticamente mosaico imagen como fondo
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.