Java 8中如何使用一系列PNG图像生成MP4视频文件?

3
在网上测试了几个适用于Windows 10的工具后,我发现很难找到一个可以容纳约5,000个PNG图像序列并将其转换为我想要传输速度的视频的工具。我在一些看似简单的事情上浪费了很多时间。
已知的ffmpeg可能是一个不错的选择,但它在我的Windows 10上无法工作(权限被拒绝)。我测试了很多免费软件,但大多数都会崩溃或突然关闭。
在其他人能够提供一个好的解决方案之前(最好是免费的),我会在下面发布我的答案,这个解决方案是基于Java 8本身构建的。

FFmpeg.exe是Windows上最好的免费工具。你尝试使用哪个FFmpeg命令示例来获得这个“权限被拒绝”的错误? - VC.One
1个回答

3

以下是我使用NIO和JCodec在Java 8中提供的简单解决方案。

  1. JCodec maven dependencies

    <dependency>
        <groupId>org.jcodec</groupId>
        <artifactId>jcodec</artifactId>
        <version>0.2.2</version>
    </dependency>
    <dependency>
        <groupId>org.jcodec</groupId>
        <artifactId>jcodec-javase</artifactId>
        <version>0.2.2</version>
    </dependency>
    
  2. JCodecPNGtoMP4.java source

    /**
     * Using NIO e JCodec to convert multiple sequence png images to mp4 video file
     * Copyright (C) 2019  Leonardo Pereira (www.leonardopereira.com.br)
     *
     * This program is free software: you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation, either version 3 of the License, or
     * (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     *
     * You should have received a copy of the GNU General Public License
     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     */
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.nio.file.DirectoryStream;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Comparator;
    import java.util.List;
    
    import javax.imageio.ImageIO;
    
    import org.jcodec.api.awt.AWTSequenceEncoder;
    import org.jcodec.common.io.NIOUtils;
    import org.jcodec.common.io.SeekableByteChannel;
    import org.jcodec.common.model.Rational;
    
    /**
     * @author Leonardo Pereira
     * 18/03/2019 23:01
     */
    public class JCodecPNGtoMP4 {
    
        private static void sortByNumber(File[] files) {
            Arrays.sort(files, new Comparator<File>() {
                @Override
                public int compare(File o1, File o2) {
                    int n1 = extractNumber(o1.getName());
                    int n2 = extractNumber(o2.getName());
                    return n1 - n2;
                }
                private int extractNumber(String name) {
                    int i = 0;
                    try {
                        int s = name.lastIndexOf('_')+1;
                        int e = name.lastIndexOf('.');
                        String number = name.substring(s, e);
                        i = Integer.parseInt(number);
                    } catch(Exception e) {
                        i = 0; // if filename does not match the format then default to 0
                    }
                    return i;
                }
            });
            /*
            for(File f : files) {
                System.out.println(f.getName());
            }
            */
        }
    
        private static void generateVideoBySequenceImages(String videoFilename, String pathImages, String imageExt) throws Exception {
            SeekableByteChannel out = null;
            try {
                out = NIOUtils.writableFileChannel(videoFilename);
    
                // for Android use: AndroidSequenceEncoder
                AWTSequenceEncoder encoder = new AWTSequenceEncoder(out, Rational.R(25, 1));
    
                Path directoryPath = Paths.get(new File(pathImages).toURI());
    
                if (Files.isDirectory(directoryPath)) {
                    DirectoryStream<Path> stream = Files.newDirectoryStream(directoryPath, "*." + imageExt);
    
                    List<File> filesList = new ArrayList<File>();
                    for (Path path : stream) {
                        filesList.add(path.toFile());
                    }
                    File[] files = new File[filesList.size()];
                    filesList.toArray(files);
    
                    sortByNumber(files);
    
                    for (File img : files) {
                        System.err.println("Encoding image " + img.getName());
                        // Generate the image, for Android use Bitmap
                        BufferedImage image = ImageIO.read(img);
                        // Encode the image
                        encoder.encodeImage(image);
                    }
                }
                // Finalize the encoding, i.e. clear the buffers, write the header, etc.
                encoder.finish();
            } finally {
                NIOUtils.closeQuietly(out);
            }
        }
    
        public static void main(String[] args) throws Exception {
            String videoFilename = "C:/outputVideo.mp4";
            String pathImages = "C:/pathImages";
            generateVideoBySequenceImages(videoFilename, pathImages, "png");
        }
    }
    

遇到了同样的问题。有6,600张1600像素宽的图片。这种方法非常慢。我将尝试寻找更快的解决方案,并收藏此页面,如果找到答案,我会回来添加一个答案。 - JohnTD
@JohnTD 你找到更快的解决方案了吗?我也遇到了同样的问题。每一帧需要2秒的时间。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接