无法将一系列 JPEG 图像编码为 MPEG 视频

Unable to encode series of JPEG images to MPEG video

我需要将 JPEG 图像转换为 MPEG 视频,并且能够轻松设置每帧显示的持续时间。

我已经尝试过 JMF、Xuggler 和 JCodec,但都有各自的问题。

对于 JMF、Xuggler、JCode 或什至 JavaCV(尚未尝试过)得到很好解释的任何解决方案,我将不胜感激。

以下是我的编码尝试。

我的测试菜单和主菜单,用于测试 Xuggler 方法或 JCodec:

package com.conuretech.video_assembler;

    import com.xuggle.xuggler.IContainerFormat;
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;

    /**
     *
     * @author ANaim
     */
    public class TestVideo {

        public static void main (String [] args)
        {
            if (args[0].equalsIgnoreCase("1"))
            {
             System.out.println("Testing Xuggler ...");
            Iterator<IContainerFormat> iterator = IContainerFormat.getInstalledOutputFormats().iterator();
            while (iterator.hasNext())
            {
                System.out.println(iterator.next().toString());
            }
                    System.out.println("Testing xuggler...");
              //C:\Users\Public\Pictures\Sample_Pictures_2.jpg
            XugglerJpegImagesToMpegVideo newjpegImagesToMpegVideo = new XugglerJpegImagesToMpegVideo(); 
            newjpegImagesToMpegVideo.setFrameRate(25);
            newjpegImagesToMpegVideo.setOutputFilenamePath("C:/Users/Public/Pictures/Sample_Pictures_2/video.mpg");
            List<String> jpegImages = new ArrayList<String>();
            jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/1.jpg");
            jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/2.jpg");
            jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/3.jpg");
            jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/4.jpg");
            jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/5.jpg");
            jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/6.jpg");
            jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/7.jpg");
            jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/8.jpg");
            newjpegImagesToMpegVideo.setJpegFilePathList(jpegImages);
            newjpegImagesToMpegVideo.convertJpegFramesToMpegVideo();
        }     
            else if (args[0].equalsIgnoreCase("2"))

            {
                 System.out.println("Testing JCodec...");
            jCodecJpegImagesToMpegVideo newJCodecJpegImagesToMpegVideo = new  jCodecJpegImagesToMpegVideo(); 
             List<String> jpegImages = new ArrayList<String>();
            jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/1.jpg");
            jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/2.jpg");
            jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/3.jpg");
            jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/4.jpg");
            jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/5.jpg");
            jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/6.jpg");
            jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/7.jpg");
            jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/8.jpg");
            newJCodecJpegImagesToMpegVideo.setJpegFilePathList(jpegImages);
            newJCodecJpegImagesToMpegVideo.convertJpegFramesToMpegVideo();

            }
            else
            {

            }

             System.exit(0);
    }

    }

我的pom.xml(包括 Xuggler 和 JCodec 依赖项):

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.conuretech</groupId>
    <artifactId>Video_Assembler</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>Video_Assembler</name>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!--<mainClass>com.conuretech.video_assembler.TestVideo</mainClass>-->
        <mainClass>com.conuretech.video_assembler.MainApp</mainClass>
    </properties>
    <organization>
        <!-- Used as the 'Vendor' for JNLP generation -->
        <name>conuretech</name>
    </organization>
    <repositories>
   <repository>
   <id>xuggle repo</id>
   <url>http://xuggle.googlecode.com/svn/trunk/repo/share/java/</url>
  </repository>
  </repositories>
  <dependencies>
 <dependency>
  <groupId>org.jcodec</groupId>
  <artifactId>jcodec</artifactId>
  <version>0.1.9</version>
</dependency>
  <dependency>
   <groupId>xuggle</groupId>
   <artifactId>xuggle-xuggler</artifactId>
   <version>5.4</version>
  </dependency>
     <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.6.4</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>commons-cli</groupId>
      <artifactId>commons-cli</artifactId>
      <version>1.1</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-core</artifactId>
      <version>1.0.0</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.0.0</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.5</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>xuggle</groupId>
      <artifactId>xuggle-utils</artifactId>
      <version>1.22</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.6</version>
                <executions>
                    <execution>
                        <id>unpack-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>unpack-dependencies</goal>
                        </goals>
                        <configuration>
                            <excludeScope>system</excludeScope>
                            <excludeGroupIds>junit,org.mockito,org.hamcrest</excludeGroupIds>
                            <outputDirectory>${project.build.directory}/classes</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2.1</version>
                <executions>
                    <execution>
                        <id>unpack-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                        <configuration>
                            <executable>${java.home}/../bin/javafxpackager</executable>
                            <arguments>
                                <argument>-createjar</argument>
                                <argument>-nocss2bin</argument>
                                <argument>-appclass</argument>
                                <argument>${mainClass}</argument>
                                <argument>-srcdir</argument>
                                <argument>${project.build.directory}/classes</argument>
                                <argument>-outdir</argument>
                                <argument>${project.build.directory}</argument>
                                <argument>-outfile</argument>
                                <argument>${project.build.finalName}.jar</argument>
                            </arguments>
                        </configuration>
                    </execution>
                    <execution>
                        <id>default-cli</id>
                        <goals>
                            <goal>exec</goal>                            
                        </goals>
                        <configuration>
                            <executable>${java.home}/bin/java</executable>
                            <commandlineArgs>${runfx.args}</commandlineArgs>
                        </configuration>
                    </execution>
                </executions>  
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                    <compilerArguments>
                        <bootclasspath>${sun.boot.class.path}${path.separator}${java.home}/lib/jfxrt.jar</bootclasspath>
                    </compilerArguments>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.16</version>
                <configuration>
                    <additionalClasspathElements>
                        <additionalClasspathElement>${java.home}/lib/jfxrt.jar</additionalClasspathElement>
                    </additionalClasspathElements>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

JMF 尝试 – 根据他们的数据格式站点,他们不支持 MPEG“写入”或仅编码“读取”解码,请参阅他们的格式 (http://www.oracle.com/technetwork/java/javase/formats-138492.html ).

Xuggler 尝试 (XugglerJpegImagesToMpegVideo.java) –

package com.conuretech.video_assembler;

import com.xuggle.mediatool.IMediaWriter;
import com.xuggle.mediatool.ToolFactory;
import com.xuggle.xuggler.IContainer;
import com.xuggle.xuggler.IContainerFormat;
import java.util.ArrayList;
import java.util.List;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;


/**
 *
 * @author ANaim
 */
public class XugglerJpegImagesToMpegVideo {


    //how many frames will be displayed per minute 
    private int frameRate = 1;
    //total number of frames to be assembled into a video file
    private int numberOfFrames = 0;
    //path to output mpeg video 
    private String outputFilenamePath = "";
    //list of JPEG pictures to be converted 
    private List<String> jpegFilePathList = new ArrayList<String>();
     private List<BufferedImage> jpegFileList = new ArrayList<BufferedImage>();

    public void convertJpegFramesToMpegVideo() {
        System.out.println("convertJpegFramesToMpegVideo");
        // BufferedImage to store JPEG data from file
     BufferedImage img  = null; 

         IContainer container = IContainer.make();
          IMediaWriter writer = null; 
          long startTime = 0;
          //ISSUE - container.open() fails to open 
          if (container.open(getOutputFilenamePath(), IContainer.Type.WRITE, null) > 0) 
          {
        writer = ToolFactory.makeWriter(getOutputFilenamePath(),container);

        this.setNumberOfFrames(getJpegFilePathList().size());
          }
          else
          {
                  throw new RuntimeException("class jpegImagesToMpegVideo,convertJpegFramesToMpegVideo(), failed to open"); 
          }

        File imgFile = null; 
       //loop to load data from paths to BufferedImage objects
         for (int i = 0; i < jpegFilePathList.size(); i++)
         {
            imgFile =  new File(getJpegFilePathList().get(i));
            if (imgFile.exists())
            {
                 //System.out.println("convertJpegFramesToMpegVideo, file path: "+imgFile.getPath()+" exists");  
             try
             {

               img = ImageIO.read(imgFile);

               jpegFileList.add(img);
             }
              catch (IOException e) {
                e.printStackTrace();
            }
            }
            else
            {
                 System.out.println("convertJpegFramesToMpegVideo, file path: "+imgFile.getPath()+" does not exist!");  
            }
         }//end for to load path to images 

        long nextEncodeTime = 0L; 
        startTime = System.nanoTime();
        //for loop to encode each BufferedImage 
        for (int i = 0; i < jpegFileList.size(); i++) {
        System.out.println("convertJpegFramesToMpegVideo encode counter: "+i);

              img = jpegFileList.get(i);
              nextEncodeTime = System.nanoTime() - startTime;
              //encode 
              writer.encodeVideo(0, img,nextEncodeTime, TimeUnit.NANOSECONDS);

            try {
         /*Duration of each image - sleep 1000 millisecs (1 sec) 
         in order to shift outcome of the next nextEncodeTime = System.nanoTime() - startTime calcultation
         by 1 second in order to have each frame displayed for 1 sec*/ 
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                Logger.getLogger(XugglerJpegImagesToMpegVideo.class.getName()).log(Level.SEVERE, null, ex);
            }
        }//end loop 
       // after encoding all BufferedImages close 
        writer.close();

    }//end

    public int getFrameRate() {
        return frameRate;
    }

    public void setFrameRate(int frameRate) {
        this.frameRate = frameRate;
    }

    public int getNumberOfFrames() {
        return numberOfFrames;
    }

    public void setNumberOfFrames(int numberOfFrames) {
        this.numberOfFrames = numberOfFrames;
    }

    public String getOutputFilenamePath() {
        return outputFilenamePath;
    }

    public void setOutputFilenamePath(String outputFilenamePath) {
        this.outputFilenamePath = outputFilenamePath;
    }

    public List<String> getJpegFilePathList() {
        return jpegFilePathList;
    }

    public void setJpegFilePathList(List<String> jpegFilePathList) {
        this.jpegFilePathList = jpegFilePathList;
    }

}

问题- 容器无法打开,这里是确切的片段:

//ISSUE - container.open() fails to open 
              if (container.open(getOutputFilenamePath(), IContainer.Type.WRITE, null) > 0) 
              {
            writer = ToolFactory.makeWriter(getOutputFilenamePath(),container);

            this.setNumberOfFrames(getJpegFilePathList().size());
              }

JCodec 尝试(jCodecJpegImagesToMpegVideo.java):

对于 jCodec,我有 2 种方法,首先使用内置 org.jcodec.api.SequenceEncoder,然后根据其他在线帖子创建我自己的 SequenceEncoder class,称为 MySequenceEncoder,因为 org.jcodec.api.SequenceEncoder 没有任何设置每帧持续时间的方法。

package com.conuretech.video_assembler;

import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import org.jcodec.api.SequenceEncoder;
import org.jcodec.codecs.h264.H264Encoder;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Picture;

/**
 *
 * @author ANaim
 */
public class jCodecJpegImagesToMpegVideo {

        //list of JPEG pictures to be converted 
    private List<String> jpegFilePathList = new ArrayList<String>();
     private List<BufferedImage> jpegFileList = new ArrayList<BufferedImage>();

        public void convertJpegFramesToMpegVideo() {
        System.out.println("convertJpegFramesToMpegVideo");
        //TODO: make dimensions dynanmic 

         BufferedImage img  = null; 
        File imgFile = null; 
         for (int i = 0; i < jpegFilePathList.size(); i++)
         {
            imgFile =  new File(getJpegFilePathList().get(i));
            if (imgFile.exists())
            {
                 //System.out.println("convertJpegFramesToMpegVideo, file path: "+imgFile.getPath()+" exists");  
             try
             {

               img = ImageIO.read(imgFile);

               jpegFileList.add(img);
             }
              catch (IOException e) {
                e.printStackTrace();
            }
            }
            else
            {
                 System.out.println("convertJpegFramesToMpegVideo, file path: "+imgFile.getPath()+" does not exist!");  
            }
         }//end for to load path to images 


          File outVideoFile = new File("C:/Users/Public/Pictures/Sample_Pictures_2/video.mp4"); 
          //Class that encodes images to a video 
          try
          { 

         /*Attempt # 1 using org.jcodec.api.SequenceEncoder -  SequenceEncoder class does not have method to specify duration,fps,etc..
          SequenceEncoder newSequenceEncoder = new SequenceEncoder(outVideoFile);*/

         /*Attempt # 2 -  MySequenceEncoder  is based on JCodec SequenceEncoder class src from GitHub, 
         were on can have finer access regarding duration & fps via " outTrack.addFrame(new MP4Packet(result,25,1,1,frameNo,true,null,1,0));"
         however there is no official documentation describing how to set  outTrack.addFrame(new MP4Packet(result,25,1,1,frameNo,true,null,1,0));*/ 
         MySequenceEncoder newSequenceEncoder = new MySequenceEncoder(outVideoFile);
         //H264 (aka mpeg) encoder 
       H264Encoder encoder = new  H264Encoder();

 //JCode class that holds media data before final processing 
  Picture toEncode = null; 
           //for loop to convert images to mpeg video 
  System.out.println("SupportedColorSpaces: "+encoder.getSupportedColorSpaces()[0]);
         for (int i=0; i<jpegFileList.size(); i++)
         {
             img = jpegFileList.get(i);

  toEncode = makeFrame(img, encoder.getSupportedColorSpaces()[0]);
  //encode 
  newSequenceEncoder.encodeNativeFrame(toEncode);


         }//end loop 
         //end encoding close sequence
         newSequenceEncoder.finish();
          }
          catch (IOException exc)
          {
              exc.printStackTrace();
          }

    }//end
//I took this code from Whosebug because JCodecs AWTUtil was deprecated and this method converts BufferedImage to Picture class
private Picture makeFrame(BufferedImage bi, ColorSpace cs)
{   
    DataBuffer imgdata = bi.getRaster().getDataBuffer();
    int[] ypix = new int[imgdata.getSize()];
    int[] upix = new int[ imgdata.getSize() >> 2 ];
    int[] vpix = new int[ imgdata.getSize() >> 2 ];
    int ipx = 0, uvoff = 0;

    for (int h = 0; h < bi.getHeight(); h++) {
        for (int w = 0; w < bi.getWidth();  w++) {
            int elem = imgdata.getElem(ipx);
            int r = 0x0ff & (elem >>> 16);
            int g = 0x0ff & (elem >>> 8);
            int b = 0x0ff & elem;
            ypix[ipx] = ((66 * r + 129 * g + 25 * b) >> 8) + 16;
            if ((0 != w % 2) && (0 != h % 2)) {
                upix[uvoff] = (( -38 * r + -74 * g + 112 * b) >> 8) + 128;
                vpix[uvoff] = (( 112 * r + -94 * g + -18 * b) >> 8) + 128;
                uvoff++;
            }
            ipx++;
        }
    }
    int[][] pix = { ypix, upix, vpix, null };
    return new Picture(bi.getWidth(), bi.getHeight(), pix, cs);
}

    public List<String> getJpegFilePathList() {
        return jpegFilePathList;
    }

    public void setJpegFilePathList(List<String> jpegFilePathList) {
        this.jpegFilePathList = jpegFilePathList;
    }

    public List<BufferedImage> getJpegFileList() {
        return jpegFileList;
    }

    public void setJpegFileList(List<BufferedImage> jpegFileList) {
        this.jpegFileList = jpegFileList;
    }


}//end class

这是我的序列编码器(MySequenceEncoder.java),我从 JCodec 的 github:

中获取源代码
package com.conuretech.video_assembler;

import org.jcodec.api.SequenceEncoder;
    import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;

import org.jcodec.codecs.h264.H264Encoder;
import org.jcodec.codecs.h264.H264Utils;
import org.jcodec.common.NIOUtils;
import org.jcodec.common.SeekableByteChannel;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Picture;
import org.jcodec.containers.mp4.Brand;
import org.jcodec.containers.mp4.MP4Packet;
import org.jcodec.containers.mp4.TrackType;
import org.jcodec.containers.mp4.muxer.FramesMP4MuxerTrack;
import org.jcodec.containers.mp4.muxer.MP4Muxer;
import org.jcodec.scale.ColorUtil;
import org.jcodec.scale.Transform;

public class MySequenceEncoder {


/**
 * This class is part of JCodec ( www.jcodec.org ) This software is distributed
 * under FreeBSD License
 * 
 * @author The JCodec project
 * 
 */

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

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

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

        // Add video track to muxer
        outTrack = muxer.addTrack(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();

        // Transform to convert between RGB and YUV
        transform = ColorUtil.getTransform(ColorSpace.RGB, encoder.getSupportedColorSpaces()[0]);

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

    }

    public void encodeNativeFrame(Picture pic) throws IOException {
        if (toEncode == null) {
            toEncode = Picture.create(pic.getWidth(), pic.getHeight(), encoder.getSupportedColorSpaces()[0]);
        }

        // Perform conversion
        transform.transform(pic, toEncode);

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

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

        //ISSUE - Im not sure what values to supply to MP4Packet() in order to control the duration of each frame to 1 sec
        outTrack.addFrame(new MP4Packet(result,25,1,1,frameNo,true,null,1,0));

        frameNo++;
    }

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

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

问题-不管是用SequenceEncoder.java还是MySequenceEncoder.java制作出来的视频都播放不了,也没有错误,视频打开了,但是什么都没有显示。

我也无法确定要传递给哪些参数是正确的:

 outTrack.addFrame(new MP4Packet(result,25,1,1,frameNo,true,null,1,0));

再次提供有效的 JMF、Xuggler、JCodec 或 JavaCV 解决方案,以及完整的说明和依赖信息,我们将不胜感激。

提前谢谢大家。

我终于找到了一个可以使用 Xuggler 库将 JPEG 图像转换为 MPEG 扩展名“.mp4”视频的工作解决方案:

这是我修改后的 "XugglerJpegImagesToMpegVideo.java" :

package com.conuretech.video_assembler;
import com.xuggle.mediatool.IMediaWriter;
import com.xuggle.mediatool.ToolFactory;
import com.xuggle.xuggler.IContainer;
import java.util.ArrayList;
import java.util.List;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.imageio.ImageIO;
import com.xuggle.xuggler.ICodec;
import com.xuggle.xuggler.IRational;
/**
 *
 * @author Aryan Naim 
 * This class converts JPEG images to MPEG (.mp4) extension video using Xuggler library
 */
public class XugglerJpegImagesToMpegVideo {

    //fps = how many frames will be displayed per sec, default 24
    private int frameRate = 24;
    //number of millisecs each frame will be displayed for the viewer , default 1000 millisecs
    private long durationPerFrame = 1000; 
    //path to output mpeg video 
    private String outputFilenamePath = "";
    //list of JPEG pictures to be converted 
    private List<String> jpegFilePathList = new ArrayList<String>();
    //list of actual images in memory to be iterated through & encoded by Xuggler 
    private List<BufferedImage> jpegFileList = new ArrayList<BufferedImage>();

    /* VERY IMPORTANT regarding setDurationPerFrame(), I noticed that fpr 24 fps , 
    the expected duration fell short 15% or 150 millisec per 1 second, 
    therefore I adjusted this by adding 15% to the user specified duration, 
    For example 3000 millisec duration will be adjusted to 3450 millisecs. 
    This adjustment was even more severe for lower fps*/

      public void setDurationPerFrame(long  durationPerFrame) {
        this.durationPerFrame = new Double(Math.ceil(durationPerFrame * 1.15)).longValue();
    }

     public long getDurationPerFrame() {
        return durationPerFrame;
    }
    /* 
     Actual method that converts JPEG images to MPEG (.mp4) extension video using Xuggler library
     */
    public void convertJpegFramesToMpegVideo() {
        System.out.println("convertJpegFramesToMpegVideo, start...");
        // BufferedImage to store JPEG data from file
        BufferedImage img = null;
        IContainer container = IContainer.make();
        IMediaWriter writer = null;
        long nextEncodeTime = getDurationPerFrame();
        writer = ToolFactory.makeWriter(getOutputFilenamePath(), container);
        //the framerate is set at 24 fps , could be adjusted
        writer.addVideoStream(0, 0, ICodec.ID.CODEC_ID_MPEG4,IRational.make(frameRate),1024,768);

        File imgFile = null;
        //loop to load data from paths to BufferedImage objects
        for (int i = 0; i < jpegFilePathList.size(); i++) {
            imgFile = new File(getJpegFilePathList().get(i));
            if (imgFile.exists()) {

                try {

                    img = ImageIO.read(imgFile);

                    jpegFileList.add(img);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else {
                System.out.println("convertJpegFramesToMpegVideo, file path: " + imgFile.getPath() + " does not exist!");
            }
        }//end for to load path to images 


        //for loop to encode each BufferedImage 
        for (int i = 0; i <jpegFileList.size(); i++) {
            System.out.println("convertJpegFramesToMpegVideo encoded frame counter: " + i);
            img = jpegFileList.get(i);
            writer.encodeVideo(0, img, nextEncodeTime, TimeUnit.MILLISECONDS);
            nextEncodeTime = nextEncodeTime + getDurationPerFrame();
        }//end loop 
        // after encoding all BufferedImages close 
        writer.close();
   System.out.println("convertJpegFramesToMpegVideo, end!");
    }//end

    public int getFrameRate() {
        return frameRate;
    }

    public void setFrameRate(int frameRate) {
        this.frameRate = frameRate;
    }
    public String getOutputFilenamePath() {
        return outputFilenamePath;
    }

    public void setOutputFilenamePath(String outputFilenamePath) {
        this.outputFilenamePath = outputFilenamePath;
    }

    public List<String> getJpegFilePathList() {
        return jpegFilePathList;
    }

    public void setJpegFilePathList(List<String> jpegFilePathList) {
        this.jpegFilePathList = jpegFilePathList;
    }

    public List<BufferedImage> getJpegFileList() {
        return jpegFileList;
    }

    public void setJpegFileList(List<BufferedImage> jpegFileList) {
        this.jpegFileList = jpegFileList;
    }

}//end class 

这是实例化 XugglerJpegImagesToMpegVideo.java 并设置 JPEG 文件路径、fps 和帧持续时间等参数的主要 class:

package com.conuretech.video_assembler;
import com.xuggle.xuggler.IContainerFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 *
 * @author ANaim
 */
public class TestVideo {

    public static void main (String [] args)
    {
        if (args[0].equalsIgnoreCase("1"))
        {


          System.out.println("Testing xuggler...");
          XugglerJpegImagesToMpegVideo newjpegImagesToMpegVideo = new XugglerJpegImagesToMpegVideo(); 
    newjpegImagesToMpegVideo.setFrameRate(24);
    //duration in millisecs, for example 2 secs will be 2000 
    newjpegImagesToMpegVideo.setDurationPerFrame(2000);
    newjpegImagesToMpegVideo.setOutputFilenamePath("C:/Users/Public/Pictures/Sample_Pictures_2/video.mp4");
    List<String> jpegImages = new ArrayList<String>();
    jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/1.jpg");
    jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/2.jpg");
    jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/3.jpg");
    jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/4.jpg");
    jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/5.jpg");
    jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/6.jpg");
    jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/7.jpg");
    jpegImages.add("C:/Users/Public/Pictures/Sample_Pictures_2/8.jpg");
    newjpegImagesToMpegVideo.setJpegFilePathList(jpegImages);
    newjpegImagesToMpegVideo.convertJpegFramesToMpegVideo();

    }     

        else
        {

        }

         System.exit(0);
}

}//end main 

这创建了一个 16 秒的 MPEG 视频,每帧持续 2 秒。

这就是所有人!