Wie erstelle ich ein Video aus einer Reihe von Bildern in Android?

Lesezeit: 11 Minuten

Benutzer-Avatar
Khalil Khalaf

Ich möchte eine Funktion aufrufen und ein Video aus einer Liste von Bildern erstellen und es dann lokal auf dem Gerät speichern:

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

Versuche:

  • Nach java/xuggle – Array von Bildern in einen Film codieren, ist der Link in der Antwort ein toter Link

  • Folgend Wie kodiere ich Bilder in eine Videodatei in Java durch Programmierung?, Die vorgeschlagene Bibliothek in der akzeptierten Antwort unterstützt Android nicht.

  • Die nächste Antwort oben hat einen Ansatz für Android-Benutzer, aber mir ist die Eingabe und Ausgabe dieser Funktion nicht klar (wo hat er die Bilder gegeben? und woher hat er das Video?) – Ich habe einen Fragekommentar hinterlassen

  • Die nächste Antwort oben bietet eine ganze Klasse, aber die erforderliche einzuschließende Bibliothek hat 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 einer Reihe von Bildern? verwendet die vorgeschlagene Bibliothek in der obersten Antwort Befehle, mit denen ich nicht vertraut bin und von denen ich nicht einmal weiß, wie man sie verwendet. Wie:

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 Obige in einem Java / Android-Projekt verwenden kann.

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

  • Erstellen Sie zunächst keine Bitmaps der Frames. Und fügen Sie sie nicht in eine Liste ein, sobald Sie keinen Speicher mehr haben. Schreiben Sie Ihre Frames einfach direkt in eine Datei.

    – grüne Apps

    29. Oktober 2016 um 9:02 Uhr


  • Bitte Framegröße/-länge mit Bitmap vergleichen, benötigte Speichergröße und melden. Wenn Sie eine Liste verwenden möchten, fügen Sie die Frames besser darin ein. Vergleichen Sie einfach die benötigten Erinnerungen.

    – grüne Apps

    29. Oktober 2016 um 9:06 Uhr


  • @greenapps Klicken Sie auf die Schaltfläche RecordVideo, Sie möchten, dass ich damit beginne, ReceivedImages lokal auf dem Gerät zu speichern. Würden Sie diese Art des Speicherns empfehlen Speichern und Lesen von Bitmaps/Bildern? Ich mache weiter und implementiere, um ein einzelnes Bild zu speichern, und berichte Ihnen dann die Größe davon.

    – Khalil Khalaf

    29. Oktober 2016 um 17:43 Uhr


  • @greenapps Hallo, es gelingt mir immer noch nicht, ein Bild zu speichern. Würde das stattdessen helfen? s11.postimg.org/4a1kdregz/Capture.png

    – Khalil Khalaf

    29. Oktober 2016 um 20:36 Uhr


  • Hosten Sie einen Dienst, dessen Eingaben eine ZIP-Ausgabe (Fotos) sind – Videodateiname für mp4. Wenn es aufgerufen wird, entpackt es die gezippten Fotos und ruft ffmpeg oder etwas anderes mit entpackten Medien als Eingaben auf. Ausgabedatei ist eine MP4. Dies ist ein generischer Medien-Muxing-Dienst, der wahrscheinlich als Knotenbibliothek mit minimalen Revisionen installiert werden kann. wud nicht haben 2 schreiben. Wenn mp4 auf dem Server bereit ist, wird es an Ihr CDN gesendet (das mp4-Medien bereitstellt). Dann fordert der Client die mp4 vom CDN an. Grund für Alt Architect – ein Server ist ein viel besserer Ort, um MP4 aus Medien zu erstellen/zu hosten/bereitzustellen, die auf Mobilgeräten aufgenommen wurden

    – Robert Rowntree

    31. Oktober 2016 um 18:36 Uhr

Sie können jcodec verwenden SequenceEncoder um eine Bildfolge 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();

Es ist eine Java-Bibliothek, daher ist es einfach, sie in ein Android-Projekt zu importieren. Im Gegensatz zu ffmpeg müssen Sie kein NDK verwenden.

Verweisen http://jcodec.org/ für Beispielcode und Downloads.

  • Super, sieht aus wie das, was ich suche. Ich werde es versuchen, danke!

    – Khalil Khalaf

    2. November 2016 um 18:49 Uhr

  • @KhalilKhalaf, wo kannst du es zum Laufen bringen? Ich versuche denselben Code, er generiert eine beschädigte .mp4-Datei.

    – nishant1000

    22. März 2018 um 15:03 Uhr

  • @nishant1000 Diese Funktion wurde eingestellt und ich wurde umgeleitet, um an etwas anderem zu arbeiten. Haben Sie die Methode “Bitmap to BufferedImage” in der anderen Antwort ausprobiert, bevor Sie JCodec für die Bilder ausgeführt haben?

    – Khalil Khalaf

    23. März 2018 um 16:12 Uhr

  • @Abhishek können wir auch Audio zu Video- und Übergangsanimationen hinzufügen, während Frames im Video geändert werden?

    – Mayank Pandya

    5. August 2018 um 10:20 Uhr

  • java awt ist anscheinend nicht in Android verfügbar, daher weiß ich nicht, dass BufferedImage hier eine praktikable Lösung ist

    – caitcoo0odes

    9. Oktober 2019 um 3:38 Uhr

Benutzer-Avatar
Vinicius-DSL

Verwenden JCodec wie Stanislav Vitvitskyy hier demonstriert.

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

Um Ihre Bitmap nun in BufferedImage zu konvertieren, können Sie 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;
    }
}
}

Benutzer-Avatar
AndreiICE

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

Aus Android 4.3-APIs.

Beim Codieren von Videos erforderte Android 4.1 (SDK 16), dass Sie die Medien mit einem ByteBuffer-Array bereitstellen, aber Android 4.3 (SDK 18) ermöglicht Ihnen jetzt, eine Oberfläche als Eingabe für einen Encoder zu verwenden. Auf diese Weise können Sie beispielsweise Eingaben aus einer vorhandenen Videodatei codieren oder Frames verwenden, die von OpenGL ES generiert wurden.

Medien-Muxer hinzugefügt in Android 4.3 (SDK 18), also für eine bequeme Art, MP4-Dateien mit Media Muxer zu schreiben, sollten Sie haben SDK>=18.

Mit der Media Codec API erhalten Sie eine hardwarebeschleunigte Codierung und können problemlos bis zu 60 FPS codieren.

Sie können von beginnen 1) Wie codiert man Bitmaps mit MediaCodec in ein Video? oder verwenden 2) Google Grafika oder 3) Großflocke.

Ausgehend von Grafika RecordFBOActivity.java. Ersetzen Sie das Choreographer-Ereignis durch Ihre eigene enthaltende Bitmap zum Codieren, entfernen Sie die Bildschirmzeichnung, laden Sie Ihre Bitmap als Open GL-Textur und zeichnen Sie sie auf der Media Codec-Eingabeoberfläche.

  • Hallo und danke für die Links. Könnten Sie beim Schreiben der vollständigen Codes helfen?

    – Khalil Khalaf

    7. November 2016 um 0:41 Uhr

  • Siehe Code in stackoverflow.com/questions/20343534/…

    – AndrejICE

    7. November 2016 um 17:41 Uhr

Benutzer-Avatar
a54studio

jCodec hat Android-Unterstützung hinzugefügt.

Sie müssen diese zu Ihrem Gradle hinzufügen …

implementation 'org.jcodec:jcodec:0.2.3'
implementation '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 Einschränkungen. Zuerst habe ich einige Bilder in voller Größe ausprobiert und die Datei geschrieben, aber bei der Wiedergabe gab es einen Fehler. Beim Verkleinern erhielt ich eine Fehlermeldung, wenn die Breite oder Höhe des Bildes nicht gerade war, da für den YUV420J-Farbraum ein Vielfaches von 2 erforderlich ist.

Bemerkenswert ist auch, dass dies Ihr Paket SCHWER, schwer macht. Mein kleines Projekt hat das Dex-Limit überschritten, indem es dies hinzugefügt hat und Multidex aktivieren musste.

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

Sie können Bitmp4 verwenden, um eine Bildsequenz 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

Es ist eine Java-Bibliothek, daher ist es einfach, sie in ein Android-Projekt zu importieren. Im Gegensatz zu ffmpeg müssen Sie kein NDK verwenden.

Verweisen https://github.com/dbof10/Bitmp4 für Beispielcode und Downloads.

Ich habe ein Projekt erstellt, das in der Lage sein sollte, damit umzugehen. Der Code ist leicht und ziemlich geradlinig.

https://github.com/dburckh/bitmap2video

Benutzer-Avatar
Gemeinschaft

Abhishek V hatte Recht, mehr Informationen über jcodec SequenceEncoder: siehe Android animiertes Video aus einer Liste von Bildern erstellen

Kürzlich habe ich ein Echtzeit-Videosystem mit Raspberry Pi und Android-Geräten gebaut, das auf das gleiche Problem gestoßen ist wie Sie. Anstatt eine Liste von Bilddateien zu speichern, habe ich einige Echtzeit-Streaming-Protokolle wie verwendet RTP/RTCP Datenstrom an Benutzer zu übertragen. Wenn Ihre Anforderung in etwa so ist, könnten Sie vielleicht Ihre Strategien ändern.

Ein weiterer Vorschlag ist, dass Sie einige C/C++-Bibliotheken untersuchen können, indem Sie NDK/JNI verwenden, um die Einschränkungen von Java zu überwinden.

Hoffe, die Vorschläge machen Sinn für dich 🙂

  • Ja, es machte Sinn für mich 🙂 Danke für deine Vorschläge und für den Link. Könnten wir in einen Chatroom gehen, vielleicht können wir mehr über die Strategie reden?

    – Khalil Khalaf

    3. November 2016 um 17:28 Uhr

  • Die Inhalte auf dieser Seite haben mir sehr geholfen, das Beispiel dort ist total praxisnah: android-streaming-live-kamera-video-to-web. Wenn diese Strategie Ihren Anforderungen entspricht und Sie weitere Fragen zur Implementierung haben, können wir uns weiter unterhalten

    – JY0284

    4. November 2016 um 6:52 Uhr

1019410cookie-checkWie erstelle ich ein Video aus einer Reihe von Bildern in Android?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy