简体   繁体   English

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

[英]Unable to encode series of JPEG images to MPEG video

I need to convert JPEG images to MPEG video and to be able to easily set the duration that each frame displayed. 我需要将JPEG图像转换为MPEG视频,并能够轻松设置每帧显示的持续时间。

I have tried JMF, Xuggler, & JCodec but all have their problems. 我已经尝试过JMF,Xuggler和JCodec,但是都有问题。

I will appreciate any solution that is well explained for either JMF , Xuggler, JCode, or even JavaCV(Have not tried this). 对于JMF,Xuggler,JCode甚至JavaCV(没有尝试过)的任何解决方案,我都将不胜感激。

Below are my coding attempts. 以下是我的编码尝试。

My testing menu & main, used to test either Xuggler approach or JCodec: 我的测试菜单和主菜单,用于测试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\1.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 (includes Xuggler & JCodec dependencies): 我的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 Attempt – According their data format site , they don't support MPEG “write” or encoding only “read” decoding, see their formats ( http://www.oracle.com/technetwork/java/javase/formats-138492.html ). JMF Attempt –根据他们的数据格式站点,他们不支持MPEG“写”或仅编码“读”解码,请参见其格式( http://www.oracle.com/technetwork/java/javase/formats-138492。 html )。

Xuggler Attempt (XugglerJpegImagesToMpegVideo.java) – 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- The container fails to open, here is the exact snippet: 问题-容器无法打开,下面是确切的代码段:

//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 Attempt (jCodecJpegImagesToMpegVideo.java): JCodec尝试(jCodecJpegImagesToMpegVideo.java):

With jCodec I had 2 approaches first using the built in org.jcodec.api.SequenceEncoder and later based on other posts online creating my own SequenceEncoder class called MySequenceEncoder, because the org.jcodec.api.SequenceEncoder did not have any methods to set duration on each frame. 使用jCodec时,我有2种方法,首先使用内置的org.jcodec.api.SequenceEncoder,然后根据其他在线帖子,创建自己的称为MySequenceEncoder的SequenceEncoder类,因为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

Here is my sequence encoder (MySequenceEncoder.java) , I took the source from JCodec's github: 这是我的序列编码器(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

Issue - Whether using SequenceEncoder.java or MySequenceEncoder.java the video that gets created doesnt play, there are no errors either, the video opens but nothing is displayed. 问题-无论使用SequenceEncoder.java还是MySequenceEncoder.java,创建的视频都不会播放,也没有错误,该视频会打开但什么也不显示。

I'm also unable to figure what are correct parameters to pass into: 我也无法弄清楚要传入的正确参数是什么:

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

Again a working JMF, Xuggler, JCodec , or JavaCV solution , with full instructions & dependency information will be greatly appreciated. 再次感谢具有完整指令和依赖项信息的有效JMF,Xuggler,JCodec或JavaCV解决方案。

Thank you all in advance. 谢谢大家。

I finally got a working solution that converts JPEG images into MPEG extension ".mp4" video using the Xuggler library: 我终于有了一个可行的解决方案,可以使用Xuggler库将JPEG图像转换为MPEG扩展名“ .mp4”视频:

Here is my modified "XugglerJpegImagesToMpegVideo.java" : 这是我修改的“ 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 

Here is the main class that instantiates the XugglerJpegImagesToMpegVideo.java and set the parameters such JPEG file paths, fps, and frame duration: 这是实例化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 

This created a 16 second MPEG video with 2 secs duration for each frame. 这样就创建了一个16秒的MPEG视频,每个帧的持续时间为2秒。

Thats all folks! 那就是所有人!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM