web-dev-qa-db-fra.com

Comment créer une vidéo à partir d'un tableau d'images sous Android?

Je souhaite appeler une fonction et créer une vidéo à partir de la liste d'images, puis l'enregistrer localement sur le périphérique:

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

Essais:

  • Après Java/xuggle - code un tableau d’images dans un film , le lien dans la réponse est un lien mort

  • Suivre Comment encoder des images dans un fichier vidéo en Java via la programmation? , La bibliothèque suggérée dans la réponse acceptée ne prend pas en charge Android.

  • La réponse suivante dans ce qui précède a une approche pour les utilisateurs d'Android, mais il n'est pas clair pour moi l'entrée et la sortie de cette fonction (où a-t-il donné les images? Et où a-t-il obtenu la vidéo?) - J'ai laissé un commentaire de question

  • La réponse suivante dans ce qui précède fournit une classe entière. Cependant, la bibliothèque requise à inclure contient un fichier corrompu (lorsque je tente de le télécharger à partir du lien fourni) - j'ai laissé un commentaire sur la question.

  • Suivant Java: Comment créer un film à partir d’un tableau d’images? , la bibliothèque suggérée dans la réponse du haut utilise des commandes avec lesquelles je ne suis pas familier et que je ne sais même pas comment les utiliser. Comme:

Création d'un fichier MPEG-4 à partir de tous les fichiers JPEG du fichier .__ actuel. annuaire:

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

Je ne sais pas comment utiliser ce qui précède dans un projet Java/Android. 

Quelqu'un peut-il m'aider à me guider et/ou à me donner une approche de ma tâche? Merci d'avance.

25
Khalil Khalaf

Vous pouvez utiliser jcodec SequenceEncoder pour convertir une séquence d’images en fichier MP4.

Exemple de code:

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();

C'est une bibliothèque Java donc il est facile de l'importer dans un projet Android, vous n'avez pas besoin d'utiliser NDK contrairement à ffmpeg.

Référez-vous à http://jcodec.org/ pour obtenir un exemple de code et des téléchargements.

26
Abhishek V

Utiliser 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();}

Maintenant, pour convertir votre Bitmap en BufferedImage, vous pouvez utiliser cette classe:

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;
    }
}
}

Original question et réponse ici.

12
Vinicius DSL

Si la version minimale de votre application Android SDK est supérieure ou égale à 16 (Android 4.1), le meilleur moyen d'encodage vidéo est d'utiliser Android Media Codec API .

À partir de API Android 4.3

Lors du codage vidéo, Android 4.1 (SDK 16) nécessite que vous fournissiez les médias avec un tableau ByteBuffer, mais Android 4.3 (SDK 18) autorise désormais vous utilisez une surface comme entrée dans un encodeur. Par exemple, ceci vous permet d'encoder une entrée à partir d'un fichier vidéo existant ou en utilisant des images généré à partir d'OpenGL ES.

Media Muxer ajouté dans Android 4.3 (SDK 18), pour un moyen pratique d’écrire un fichier mp4 avec Media Muxer, vous devez avoir SDK> = 18.

En utilisant l'API Media Codec, vous obtiendrez un encodage accéléré et vous encoderez facilement jusqu'à 60 FPS. 

Vous pouvez commencer à partir de 1)Comment encoder des images bitmap dans une vidéo à l'aide de MediaCodec? .__ ou utilisez 2)Google Grafika ou 3)Bigflake .

À partir de Grafika RecordFBOActivity.Java . Remplacez l’événement Choreographer par votre propre bitmap à encoder, supprimez le dessin à l’écran, chargez-le en tant que texture Ouvrir GL et dessinez-le sur la surface d’entrée du codec Media.

6
AndreyICE

jCodec a ajouté le support Android. 

Vous devez les ajouter à votre diplôme ...

compile 'org.jcodec:jcodec:0.2.3'
compile 'org.jcodec:jcodec-Android:0.2.3'

...et

Android {
    ...
    configurations.all {
        resolutionStrategy.force 'com.google.code.findbugs:jsr305:3.0.2'
    }
}

Je peux confirmer que cela fonctionne comme prévu, mais avec des mises en garde. D'abord, j'ai essayé des images en taille réelle et le fichier a été écrit, mais j'ai donné une erreur lors de la lecture. Lorsque je réduisais, j'obtenais une erreur si la largeur ou la hauteur de l'image n'était pas égale car elle nécessite un multiple de 2 pour l'espace de couleurs YUV420J.

Il convient également de noter que cela rend votre paquet lourd, lourd. Mon petit projet a dépassé la limite dex en ajoutant ceci et a nécessité l'activation de multidex. 

FileChannelWrapper out = null;
File dir = what ever directory you use...
File file = new File(dir, "test.mp4");

try { out = NIOUtils.writableFileChannel(file.getAbsolutePath());
      AndroidSequenceEncoder encoder = new AndroidSequenceEncoder(out, Rational.R(15, 1));
      for (Bitmap bitmap : bitmaps) {
          encoder.encodeImage(bitmap);
      }
      encoder.finish();
} finally {
    NIOUtils.closeQuietly(out);
}
3
a54studio

Vous pouvez utiliser Bitmp4 pour convertir une séquence d’images en fichier MP4.

Exemple de code:

...

val encoder = MP4Encoder()
     encoder.setFrameDelay(50)
     encoder.setOutputFilePath(exportedFile.path)
     encoder.setOutputSize(width, width)

 startExport()

 stopExport()

 addFrame(bitmap) //called intervally

C'est une bibliothèque Java donc il est facile de l'importer dans un projet Android, vous n'avez pas besoin d'utiliser NDK contrairement à ffmpeg.

Référez-vous à https://github.com/dbof10/Bitmp4 pour obtenir un exemple de code et des téléchargements.

1
Ken Zira

Abhishek V avait raison, plus d'informations sur jcodec SequenceEncoder: Voir Android créer une vidéo animée à partir d'une liste d'images

Récemment, j'ai construit un système vidéo en temps réel utilisant des appareils Raspberry Pi et Android, rencontrant le même problème que le vôtre. Au lieu d'enregistrer une liste de fichiers image, j'ai utilisé des protocoles de diffusion en temps réel tels que RTP/RTCP pour transférer le flux de données à l'utilisateur. Si vos exigences ressemblent à cela, vous pourriez peut-être modifier vos stratégies.

Une autre suggestion est que vous puissiez explorer certaines bibliothèques C/C++, en utilisant NDK/JNI pour briser la limitation de Java.

J'espère que les suggestions ont un sens pour vous :)

1
JY0284

vous avez des images bitmap que vous pouvez convertir en vidéo avec JCodec 

Voici un exemple d'encodeur de séquence d'images: 

Vous pouvez le modifier pour vos besoins en remplaçant BufferedImage par Bitmap.

Utilisez ces méthodes selon vos besoins.

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());
}
1
Riyaz Parasara

Vous pouvez utiliser la bibliothèque ffmpeg pour créer une vidéo à partir d’un tableau d’images . La bibliothèque FFMPEG est très utile pour créer une vidéo . Le lien suivant vous aidera peut-être . http://osric.com/chris/accidental-developer/2012/04/using-ffmpeg-à-programme-slice-splice-vidéo/ ./ https://groups.google.com/forum/#!topic/Android- ndk/sxDYlGYK-Xg

0
Sergey