2014-07-03 15 views
5

Sto facendo la ricerca su come creare un video mp4 da immagini in Java. Dopo alcuni giorni di ricerche, so che JCodec può farlo (http://jcodec.org/). Ecco la dimostrazione che ho trovato su Android make animated video from list of images (ho cambiato solo l'ingresso e il collegamento in uscita):Come creare un video mp4 da immagini in Java usando la libreria JCodec?

private SeekableByteChannel ch; 
private Picture toEncode; 
private RgbToYuv420 transform; 
private H264Encoder encoder; 
private ArrayList<ByteBuffer> spsList; 
private ArrayList<ByteBuffer> ppsList; 
private CompressedTrack outTrack; 
private ByteBuffer _out; 
private int frameNo; 
private MP4Muxer muxer; 

public SequenceImagesEncoder(File out) throws IOException { 
    this.ch = NIOUtils.writableFileChannel(out); 

    // Transform to convert between RGB and YUV 
    transform = new RgbToYuv420(0, 0); 

    // Muxer that will store the encoded frames 
    muxer = new MP4Muxer(ch, Brand.MP4); 

    // Add video track to muxer 
    outTrack = muxer.addTrackForCompressed(TrackType.VIDEO, 25); 

    // Allocate a buffer big enough to hold output frames 
    _out = ByteBuffer.allocate(1920 * 1080 * 6); 

    // Create an instance of encoder 
    encoder = new H264Encoder(); 

    // Encoder extra data (SPS, PPS) to be stored in a special place of 
    // MP4 
    spsList = new ArrayList<ByteBuffer>(); 
    ppsList = new ArrayList<ByteBuffer>(); 

} 

public void encodeImage(BufferedImage bi) throws IOException { 
    if (toEncode == null) { 
     toEncode = Picture.create(bi.getWidth(), bi.getHeight(), ColorSpace.YUV420); 
    } 

    // Perform conversion 
    for (int i = 0; i < 3; i++) 
     Arrays.fill(toEncode.getData()[i], 0); 
    transform.transform(AWTUtil.fromBufferedImage(bi), toEncode); 

    // Encode image into H.264 frame, the result is stored in '_out' buffer 
    _out.clear(); 
    ByteBuffer result = encoder.encodeFrame(_out, toEncode); 

    // Based on the frame above form correct MP4 packet 
    spsList.clear(); 
    ppsList.clear(); 
    H264Utils.encodeMOVPacket(result, spsList, ppsList); 

    // Add packet to video track 
    outTrack.addFrame(new MP4Packet(result, frameNo, 25, 1, frameNo, true, null, frameNo, 0)); 

    frameNo++; 
} 

public void finish() throws IOException { 
    // Push saved SPS/PPS to a special storage in MP4 
    outTrack.addSampleEntry(H264Utils.createMOVSampleEntry(spsList, ppsList)); 

    // Write MP4 header and finalize recording 
    muxer.writeHeader(); 
    NIOUtils.closeQuietly(ch); 
} 

public static void main(String[] args) throws IOException { 
    SequenceImagesEncoder encoder = new SequenceImagesEncoder(new File("D:/workspace/JCodecMakeMP4/out.mp4")); 
    for (int i = 1; i < 100; i++) { 
     BufferedImage bi = ImageIO.read(new File(String.format("D:/workspace/JCodecMakeMP4/bin/frame" + i + ".jpeg", i))); 
     encoder.encodeImage(bi); 
    } 
    encoder.finish(); 
} 

Quando uso jcodec-0.1.0.jar, i NIOUtils di classe non ha una funzione: writableFileChannel (del file).

Quando uso jcodec-0.1.3.jar, tutto sembra essere ok, ma eseguo il debug del codice, questo porta a "Source Not Found" quando mi metto in linea: muxer = new MP4Muxer (ch, Brand. MP4);

Qualcuno sa come risolverlo.

Grazie in anticipo!

+0

[jcodec -? Qualcuno ha documentazione visto su questa libreria] (http://stackoverflow.com/questions/10969423/jcodec-has -solo-visto-documentazione-su-questa-libreria) potrebbe aiutare (vedi il codice nella risposta) – DavidPostill

+0

Ho anche trovato quel codice su questo webside: http://stackoverflow.com/questions/10284708/android-make-animated -video-from-list-of-images Quando provo a utilizzare jcodec-0.1.0.jar, le funzioni: NIOUtils.writableFileChannel (out); non appare. Io uso jcodec-0.1.3.jar tutto sembra essere ok ma quando eseguo il codice, causa un errore: "Origine non trovata" quando raggiungi la riga di codice: muxer = new MP4Muxer (ch, Brand.MP4); – LuongTruong

+0

Se hai bisogno di ulteriore aiuto, il tuo prossimo passo è probabilmente quello di fornire il tuo codice [Come creare un esempio minimo, completo e verificabile] (http://stackoverflow.com/help/mcve) – DavidPostill

risposta

9

È solo condividere la mia esperienza. Io uso JpegImagesToMovie per risolvere come il tuo problema.

Per ulteriori riferimenti JpegImagesToMovie.

Esempio Programma

public static void makeVideo(String fileName) throws MalformedURLException { 
    Vector<String> imgLst = get images path list. 

    JpegImagesToMovie imageToMovie = new JpegImagesToMovie(); 
    MediaLocator oml; 
    if ((oml = imageToMovie.createMediaLocator(fileName)) == null) { 
     System.err.println("Cannot build media locator from: " + fileName); 
     System.exit(0); 
    } 
    int interval = 50; 
    imageToMovie.doIt(screenWidth, screenHeight, (1000/interval), imgLst, oml); 

} 

JpegImagesToMovie.java

/* 
* @(#)JpegImagesToMovie.java 1.3 01/03/13 
* 
* Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved. 
* 
* Sun grants you ("Licensee") a non-exclusive, royalty free, license to use, 
* modify and redistribute this software in source and binary code form, 
* provided that i) this copyright notice and license appear on all copies of 
* the software; and ii) Licensee does not utilize the software in a manner 
* which is disparaging to Sun. 
* 
* This software is provided "AS IS," without a warranty of any kind. ALL 
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY 
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR 
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE 
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING 
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS 
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, 
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER 
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF 
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE 
* POSSIBILITY OF SUCH DAMAGES. 
* 
* This software is not designed or intended for use in on-line control of 
* aircraft, air traffic, aircraft navigation or aircraft communications; or in 
* the design, construction, operation or maintenance of any nuclear 
* facility. Licensee represents and warrants that it will not use or 
* redistribute the Software for such purposes. 
*/ 

import java.awt.Dimension; 
import java.io.File; 
import java.io.IOException; 
import java.io.RandomAccessFile; 
import java.net.MalformedURLException; 
import java.util.Vector; 

import javax.media.Buffer; 
import javax.media.ConfigureCompleteEvent; 
import javax.media.ControllerEvent; 
import javax.media.ControllerListener; 
import javax.media.DataSink; 
import javax.media.EndOfMediaEvent; 
import javax.media.Format; 
import javax.media.Manager; 
import javax.media.MediaLocator; 
import javax.media.PrefetchCompleteEvent; 
import javax.media.Processor; 
import javax.media.RealizeCompleteEvent; 
import javax.media.ResourceUnavailableEvent; 
import javax.media.Time; 
import javax.media.control.TrackControl; 
import javax.media.datasink.DataSinkErrorEvent; 
import javax.media.datasink.DataSinkEvent; 
import javax.media.datasink.DataSinkListener; 
import javax.media.datasink.EndOfStreamEvent; 
import javax.media.format.VideoFormat; 
import javax.media.protocol.ContentDescriptor; 
import javax.media.protocol.DataSource; 
import javax.media.protocol.FileTypeDescriptor; 
import javax.media.protocol.PullBufferDataSource; 
import javax.media.protocol.PullBufferStream; 

/** 
* This program takes a list of JPEG image files and convert them into a 
* QuickTime movie. 
*/ 
public class JpegImagesToMovie implements ControllerListener, DataSinkListener { 

    public boolean doIt(int width, int height, int frameRate, Vector inFiles, 
      MediaLocator outML) throws MalformedURLException { 
     ImageDataSource ids = new ImageDataSource(width, height, frameRate, 
       inFiles); 

     Processor p; 

     try { 
      //System.err 
      //  .println("- create processor for the image datasource ..."); 
      p = Manager.createProcessor(ids); 
     } catch (Exception e) { 
      System.err 
        .println("Yikes! Cannot create a processor from the data source."); 
      return false; 
     } 

     p.addControllerListener(this); 

     // Put the Processor into configured state so we can set 
     // some processing options on the processor. 
     p.configure(); 
     if (!waitForState(p, p.Configured)) { 
      System.err.println("Failed to configure the processor."); 
      return false; 
     } 

     // Set the output content descriptor to QuickTime. 
     p.setContentDescriptor(new ContentDescriptor(
       FileTypeDescriptor.QUICKTIME)); 

     // Query for the processor for supported formats. 
     // Then set it on the processor. 
     TrackControl tcs[] = p.getTrackControls(); 
     Format f[] = tcs[0].getSupportedFormats(); 
     if (f == null || f.length <= 0) { 
      System.err.println("The mux does not support the input format: " 
        + tcs[0].getFormat()); 
      return false; 
     } 

     tcs[0].setFormat(f[0]); 

     //System.err.println("Setting the track format to: " + f[0]); 

     // We are done with programming the processor. Let's just 
     // realize it. 
     p.realize(); 
     if (!waitForState(p, p.Realized)) { 
      System.err.println("Failed to realize the processor."); 
      return false; 
     } 

     // Now, we'll need to create a DataSink. 
     DataSink dsink; 
     if ((dsink = createDataSink(p, outML)) == null) { 
      System.err 
        .println("Failed to create a DataSink for the given output MediaLocator: " 
          + outML); 
      return false; 
     } 

     dsink.addDataSinkListener(this); 
     fileDone = false; 

     System.out.println("Generating the video : "+outML.getURL().toString()); 

     // OK, we can now start the actual transcoding. 
     try { 
      p.start(); 
      dsink.start(); 
     } catch (IOException e) { 
      System.err.println("IO error during processing"); 
      return false; 
     } 

     // Wait for EndOfStream event. 
     waitForFileDone(); 

     // Cleanup. 
     try { 
      dsink.close(); 
     } catch (Exception e) { 
     } 
     p.removeControllerListener(this); 

     System.out.println("Video creation completed!!!!!"); 
     return true; 
    } 

    /** 
    * Create the DataSink. 
    */ 
    DataSink createDataSink(Processor p, MediaLocator outML) { 

     DataSource ds; 

     if ((ds = p.getDataOutput()) == null) { 
      System.err 
        .println("Something is really wrong: the processor does not have an output DataSource"); 
      return null; 
     } 

     DataSink dsink; 

     try { 
      //System.err.println("- create DataSink for: " + outML); 
      dsink = Manager.createDataSink(ds, outML); 
      dsink.open(); 
     } catch (Exception e) { 
      System.err.println("Cannot create the DataSink: " + e); 
      return null; 
     } 

     return dsink; 
    } 

    Object waitSync = new Object(); 
    boolean stateTransitionOK = true; 

    /** 
    * Block until the processor has transitioned to the given state. Return 
    * false if the transition failed. 
    */ 
    boolean waitForState(Processor p, int state) { 
     synchronized (waitSync) { 
      try { 
       while (p.getState() < state && stateTransitionOK) 
        waitSync.wait(); 
      } catch (Exception e) { 
      } 
     } 
     return stateTransitionOK; 
    } 

    /** 
    * Controller Listener. 
    */ 
    public void controllerUpdate(ControllerEvent evt) { 

     if (evt instanceof ConfigureCompleteEvent 
       || evt instanceof RealizeCompleteEvent 
       || evt instanceof PrefetchCompleteEvent) { 
      synchronized (waitSync) { 
       stateTransitionOK = true; 
       waitSync.notifyAll(); 
      } 
     } else if (evt instanceof ResourceUnavailableEvent) { 
      synchronized (waitSync) { 
       stateTransitionOK = false; 
       waitSync.notifyAll(); 
      } 
     } else if (evt instanceof EndOfMediaEvent) { 
      evt.getSourceController().stop(); 
      evt.getSourceController().close(); 
     } 
    } 

    Object waitFileSync = new Object(); 
    boolean fileDone = false; 
    boolean fileSuccess = true; 

    /** 
    * Block until file writing is done. 
    */ 
    boolean waitForFileDone() { 
     synchronized (waitFileSync) { 
      try { 
       while (!fileDone) 
        waitFileSync.wait(); 
      } catch (Exception e) { 
      } 
     } 
     return fileSuccess; 
    } 

    /** 
    * Event handler for the file writer. 
    */ 
    public void dataSinkUpdate(DataSinkEvent evt) { 

     if (evt instanceof EndOfStreamEvent) { 
      synchronized (waitFileSync) { 
       fileDone = true; 
       waitFileSync.notifyAll(); 
      } 
     } else if (evt instanceof DataSinkErrorEvent) { 
      synchronized (waitFileSync) { 
       fileDone = true; 
       fileSuccess = false; 
       waitFileSync.notifyAll(); 
      } 
     } 
    } 

    /*public static void main(String args[]) { 

     if (args.length == 0) 
      prUsage(); 

     // Parse the arguments. 
     int i = 0; 
     int width = -1, height = -1, frameRate = 1; 
     Vector inputFiles = new Vector(); 
     String outputURL = null; 

     while (i < args.length) { 

      if (args[i].equals("-w")) { 
       i++; 
       if (i >= args.length) 
        prUsage(); 
       width = new Integer(args[i]).intValue(); 
      } else if (args[i].equals("-h")) { 
       i++; 
       if (i >= args.length) 
        prUsage(); 
       height = new Integer(args[i]).intValue(); 
      } else if (args[i].equals("-f")) { 
       i++; 
       if (i >= args.length) 
        prUsage(); 
       frameRate = new Integer(args[i]).intValue(); 
      } else if (args[i].equals("-o")) { 
       i++; 
       if (i >= args.length) 
        prUsage(); 
       outputURL = args[i]; 
      } else { 
       inputFiles.addElement(args[i]); 
      } 
      i++; 
     } 

     if (outputURL == null || inputFiles.size() == 0) 
      prUsage(); 

     // Check for output file extension. 
     if (!outputURL.endsWith(".mov") && !outputURL.endsWith(".MOV")) { 
      System.err 
        .println("The output file extension should end with a .mov extension"); 
      prUsage(); 
     } 

     if (width < 0 || height < 0) { 
      System.err.println("Please specify the correct image size."); 
      prUsage(); 
     } 

     // Check the frame rate. 
     if (frameRate < 1) 
      frameRate = 1; 

     // Generate the output media locators. 
     MediaLocator oml; 

     if ((oml = createMediaLocator(outputURL)) == null) { 
      System.err.println("Cannot build media locator from: " + outputURL); 
      System.exit(0); 
     } 

     JpegImagesToMovie imageToMovie = new JpegImagesToMovie(); 
     imageToMovie.doIt(width, height, frameRate, inputFiles, oml); 

     System.exit(0); 
    }*/ 

    static void prUsage() { 
     System.err 
       .println("Usage: java JpegImagesToMovie -w <width> -h <height> -f <frame rate> -o <output URL> <input JPEG file 1> <input JPEG file 2> ..."); 
     System.exit(-1); 
    } 

    /** 
    * Create a media locator from the given string. 
    */ 
    static MediaLocator createMediaLocator(String url) { 

     MediaLocator ml; 

     if (url.indexOf(":") > 0 && (ml = new MediaLocator(url)) != null) 
      return ml; 

     if (url.startsWith(File.separator)) { 
      if ((ml = new MediaLocator("file:" + url)) != null) 
       return ml; 
     } else { 
      String file = "file:" + System.getProperty("user.dir") 
        + File.separator + url; 
      if ((ml = new MediaLocator(file)) != null) 
       return ml; 
     } 

     return null; 
    } 

    // ///////////////////////////////////////////// 
    // 
    // Inner classes. 
    // ///////////////////////////////////////////// 

    /** 
    * A DataSource to read from a list of JPEG image files and turn that into a 
    * stream of JMF buffers. The DataSource is not seekable or positionable. 
    */ 
    class ImageDataSource extends PullBufferDataSource { 

     ImageSourceStream streams[]; 

     ImageDataSource(int width, int height, int frameRate, Vector images) { 
      streams = new ImageSourceStream[1]; 
      streams[0] = new ImageSourceStream(width, height, frameRate, images); 
     } 

     public void setLocator(MediaLocator source) { 
     } 

     public MediaLocator getLocator() { 
      return null; 
     } 

     /** 
     * Content type is of RAW since we are sending buffers of video frames 
     * without a container format. 
     */ 
     public String getContentType() { 
      return ContentDescriptor.RAW; 
     } 

     public void connect() { 
     } 

     public void disconnect() { 
     } 

     public void start() { 
     } 

     public void stop() { 
     } 

     /** 
     * Return the ImageSourceStreams. 
     */ 
     public PullBufferStream[] getStreams() { 
      return streams; 
     } 

     /** 
     * We could have derived the duration from the number of frames and 
     * frame rate. But for the purpose of this program, it's not necessary. 
     */ 
     public Time getDuration() { 
      return DURATION_UNKNOWN; 
     } 

     public Object[] getControls() { 
      return new Object[0]; 
     } 

     public Object getControl(String type) { 
      return null; 
     } 
    } 

    /** 
    * The source stream to go along with ImageDataSource. 
    */ 
    class ImageSourceStream implements PullBufferStream { 

     Vector images; 
     int width, height; 
     VideoFormat format; 

     int nextImage = 0; // index of the next image to be read. 
     boolean ended = false; 

     public ImageSourceStream(int width, int height, int frameRate, 
       Vector images) { 
      this.width = width; 
      this.height = height; 
      this.images = images; 

      format = new VideoFormat(VideoFormat.JPEG, new Dimension(width, 
        height), Format.NOT_SPECIFIED, Format.byteArray, 
        (float) frameRate); 
     } 

     /** 
     * We should never need to block assuming data are read from files. 
     */ 
     public boolean willReadBlock() { 
      return false; 
     } 

     /** 
     * This is called from the Processor to read a frame worth of video 
     * data. 
     */ 
     public void read(Buffer buf) throws IOException { 

      // Check if we've finished all the frames. 
      if (nextImage >= images.size()) { 
       // We are done. Set EndOfMedia. 
       //System.err.println("Done reading all images."); 
       buf.setEOM(true); 
       buf.setOffset(0); 
       buf.setLength(0); 
       ended = true; 
       return; 
      } 

      String imageFile = (String) images.elementAt(nextImage); 
      nextImage++; 

      //System.err.println(" - reading image file: " + imageFile); 

      // Open a random access file for the next image. 
      RandomAccessFile raFile; 
      raFile = new RandomAccessFile(imageFile, "r"); 

      byte data[] = null; 

      // Check the input buffer type & size. 

      if (buf.getData() instanceof byte[]) 
       data = (byte[]) buf.getData(); 

      // Check to see the given buffer is big enough for the frame. 
      if (data == null || data.length < raFile.length()) { 
       data = new byte[(int) raFile.length()]; 
       buf.setData(data); 
      } 

      // Read the entire JPEG image from the file. 
      raFile.readFully(data, 0, (int) raFile.length()); 

      //System.err.println(" read " + raFile.length() + " bytes."); 

      buf.setOffset(0); 
      buf.setLength((int) raFile.length()); 
      buf.setFormat(format); 
      buf.setFlags(buf.getFlags() | buf.FLAG_KEY_FRAME); 

      // Close the random access file. 
      raFile.close(); 
     } 

     /** 
     * Return the format of each video frame. That will be JPEG. 
     */ 
     public Format getFormat() { 
      return format; 
     } 

     public ContentDescriptor getContentDescriptor() { 
      return new ContentDescriptor(ContentDescriptor.RAW); 
     } 

     public long getContentLength() { 
      return 0; 
     } 

     public boolean endOfStream() { 
      return ended; 
     } 

     public Object[] getControls() { 
      return new Object[0]; 
     } 

     public Object getControl(String type) { 
      return null; 
     } 
    } 
} 
+0

Mi chiedo se possiamo creare un video mp4 dalle immagini tramite il tuo codice di esempio? Perché sto lavorando con il formato mp4. – LuongTruong

+0

Eseguo il test con il formato '.mov'. Ma, penso che tu possa ... – CycDemo

+1

@ user3675966 Va bene per il formato 'mp4'. Basta usare il nome del file come 'xxx.mp4'. – CycDemo

Problemi correlati