java - 在 Java 8 中通过序列 PNG 图像生成 MP4 视频文件
问题描述
在测试了网络上找到的几个适用于 Windows 10 的工具后,我很难找到一个可以依次携带大约 5,000 张 PNG 图像并将其转换为以我想要的速度传输的视频的工具。我在看似简单的事情上浪费了很多时间。
已知的 ffmpeg 将是一个不错的选择,但它在我的 Windows 10 上不起作用(权限被拒绝)。我测试了很多免费软件,但大多数都突然崩溃或关闭。
在其他人可以在这里提出一个好的解决方案(最好是免费的)之前,我将在下面发布我的答案,并使用基于 Java 8 本身构建的解决方案。
解决方案
下面是我在 Java 8 中使用 NIO 和 JCodec 的简单解决方案。
JCodec Maven 依赖项
<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>
JCodecPNGtoMP4.java 源码
/** * 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"); } }
推荐阅读
- ffmpeg - 如何使用ffmpeg将带有两个切片包的捕获视频流转换为带有一个切片包的传输流?
- javascript - 如何从连接值的数组中返回整数?
- ios - UIPickerView 中的文本字段
- python - 尝试在 Pycharm 中通过 pip 安装 numpy 时出错
- javascript - 从 Forge externalId 转换为 IfcGuid [model-derivative-api]
- reactjs - 如何在 PropTypes 中使用 Redux 的 useSelector 和 useDispatch hooks
- php - 如何将 PDO::FETCH_CLASS 与构造函数带参数的类一起使用?
- node.js - React.js 数据未保存到状态变量
- python - 使用嵌套列表创建字典,其中第一个列表是列,每个列表是一行?
- drone.io - 登录自托管 Drone.io 所需的批准组织