wake-up-neo.net

Wie erstelle ich ein Video aus einem Array von Bildern in Android?

Ich möchte eine Funktion aufrufen und ein Video aus der Liste der Bilder erstellen und dann lokal auf dem Gerät speichern:

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

Versuche:

  • Nach Java/xuggle-kodiertes Array von Bildern in einen Film ist der Link in der Antwort ein toter Link

  • Folgen Sie Wie werden durch Programmieren Bilder in eine Videodatei in Java codiert? , Die vorgeschlagene Bibliothek in der akzeptierten Antwort unterstützt Android nicht.

  • Die nächste Antwort in der obigen Beschreibung hat einen Ansatz für Android-Benutzer, jedoch ist mir die Eingabe und Ausgabe dieser Funktion nicht klar (woher hat er die Bilder gegeben und woher hat er das Video?)

  • Die nächste Antwort oben enthält eine ganze Klasse. Die erforderliche Bibliothek enthält jedoch eine beschädigte Datei (wenn ich versuche, sie über den bereitgestellten Link herunterzuladen). Ich habe einen Fragekommentar hinterlassen

  • Nach Java: Wie erstelle ich einen Film aus einem Array von Bildern? , Verwendet die vorgeschlagene Bibliothek in der obersten Antwort Befehle, die mir nicht vertraut sind, und ich weiß nicht, wie ich sie verwenden soll. Mögen:

Erstellen einer MPEG-4 -Datei aus allen JPEG-Dateien im aktuellen Verzeichnis:

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

Ich weiß nicht, wie ich das in einem Java/Android-Projekt verwenden kann. 

Kann jemand helfen, mich zu leiten oder/und mir einen Ansatz für meine Aufgabe zu geben? Danke im Voraus.

25
Khalil Khalaf

Sie können jcodec SequenceEncoder verwenden, um die Folge von Bildern in eine MP4-Datei zu konvertieren.

Beispielcode :

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

Da es sich um eine Java-Bibliothek handelt, ist es einfach, sie in ein Android-Projekt zu importieren. Im Gegensatz zu ffmpeg müssen Sie nicht NDK verwenden.

Siehe http://jcodec.org/ für Beispielcode & Downloads.

26
Abhishek V

JCodec verwenden

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

Jetzt können Sie zum Konvertieren Ihrer Bitmap in BufferedImage diese Klasse verwenden:

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 Frage und Antwort hier.

12
Vinicius DSL

Wenn die Mindestversion Ihrer Anwendung Android SDK größer oder gleich 16 (Android 4.1) ist, ist die beste Art der Videokodierung die Verwendung von Android Media Codec API .

Von Android 4.3 APIs

Beim Kodieren von Video müssen Sie für Android 4.1 (SDK 16) angeben das Medium mit einem ByteBuffer-Array, aber Android 4.3 (SDK 18) erlaubt jetzt Sie können eine Oberfläche als Eingabe für einen Encoder verwenden. Zum Beispiel dieses Ermöglicht das Kodieren von Eingaben aus einer vorhandenen Videodatei oder mithilfe von Frames generiert aus OpenGL ES.

Media Muxer wurde in Android 4.3 (SDK 18) hinzugefügt. Für das bequeme Schreiben von mp4-Dateien mit Media Muxer sollten Sie SDK> = 18 haben.

Durch die Verwendung der Media Codec API-Methode erhalten Sie eine hardwarebeschleunigte Codierung und können problemlos bis zu 60 FPS codieren. 

Sie können mit 1 beginnen.)Wie codiere ich Bitmaps mit MediaCodec in ein Video? oder verwenden Sie 2)Google Grafika oder 3)Bigflake .

Ausgehend von Grafika RecordFBOActivity.Java . Ersetzen Sie das Choreographer-Ereignis durch ein eigenes Bitmap, das Sie kodieren möchten, entfernen Sie die Zeichnung auf dem Bildschirm, laden Sie das Bitmap als Open GL Texture und zeichnen Sie es auf der Media Codec-Eingabefläche.

6
AndreyICE

jCodec hat Android-Unterstützung hinzugefügt. 

Sie müssen diese zu Ihrem Abschluss hinzufügen ...

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

...und

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

Ich kann bestätigen, dass dies wie erwartet funktioniert, jedoch mit Vorbehalten. Als erstes probierte ich einige Bilder in voller Größe aus und schrieb die Datei, gab aber einen Fehler bei der Wiedergabe aus. Wenn ich verkleinert wurde, würde ich einen Fehler erhalten, wenn die Breite oder Höhe des Bilds nicht einmal war, da für YUV420J ein Vielfaches von 2 erforderlich ist.

Bemerkenswert ist auch, dass Ihr Paket schwer, schwer ist. Mein kleines Projekt überschritt die Dex-Grenze, indem es dies hinzufügte und die Aktivierung von Multidex erforderlich war. 

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

Sie können Bitmp4 verwenden, um eine Folge von Bildern in eine MP4-Datei zu konvertieren.

Beispielcode :

...

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

 startExport()

 stopExport()

 addFrame(bitmap) //called intervally

Da es sich um eine Java-Bibliothek handelt, ist es einfach, sie in ein Android-Projekt zu importieren. Im Gegensatz zu ffmpeg müssen Sie nicht NDK verwenden.

Siehe https://github.com/dbof10/Bitmp4 für Beispielcode & Downloads.

1
Ken Zira

Abhishek V hatte recht, mehr Informationen über jcodec SequenceEncoder: See Android-Animationsvideo aus der Liste der Bilder erstellen

Vor kurzem habe ich ein Echtzeit-Videosystem mit Raspberry Pi und Android-Geräten entwickelt. Das gleiche Problem wie bei Ihnen. Anstatt eine Liste von Bilddateien zu speichern, verwendete ich Echtzeit-Streaming-Protokolle wie RTP/RTCP, um den Datenstrom an den Benutzer zu übertragen. Wenn Ihre Anforderung in etwa so ist, könnten Sie möglicherweise Ihre Strategien ändern.

Ein anderer Vorschlag ist, dass Sie einige C/C++ - Bibliotheken untersuchen und NDK/JNI verwenden, um die Einschränkungen von Java zu durchbrechen.

Hoffen Sie, dass die Vorschläge für Sie Sinn machen :)

1
JY0284

sie haben Bitmaps, die Sie mit JCodec in Videos umwandeln können 

Hier ist ein Beispiel für einen Bildsequenz-Encoder: 

Sie können es für Ihre Zwecke ändern, indem Sie BufferedImage durch Bitmap ersetzen.

Verwenden Sie diese Methoden entsprechend Ihren Bedürfnissen.

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

Sie können die Bibliothek ffmpeg verwenden, um ein Video aus einem Array von Bildern zu erstellen. Die FFMPEG-Bibliothek ist sehr nützlich, um ein Video zu erstellen. Möglicherweise hilft Ihnen der folgende Link . http://osric.com/chris/accidental-developer/2012/04/using-ffmpeg-to-programmatic-slice-and-splice-video/https://groups.google.com/forum/#!topic/Android- ndk/sxDYlGYK-Xg

0
Sergey