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



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

我尝试过JMF,Xuggler和JCodec,但都有他们的问题。

我将不胜感激任何为JMF,Xuggler,JCode甚至JavaCV解释的解决方案(没有尝试过这个)。

以下是我的编码尝试。

我的测试菜单和main,用于测试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:UsersPublicPicturesSample_Pictures_21.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);
    }
    }

My 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 Try (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 Try (jCodecJpegImagesToMpegVideo.java):

使用

jCodec,我有2种方法,首先使用内置的org.jcodec.api.SequenceEncoder,后来基于其他在线帖子创建了我自己的SequenceEncoder类,称为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 Stackoverflow 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,JCotec或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 和帧持续时间:

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 秒。

这就是所有人!

最新更新