今天有个需求:前端只需要传一个视频,后端需要截取视频的第一帧作为封面,将这封面图片上传到对象存储中,然后把上传后的地址存在数据库,最后要返回封面地址
这种情况许多面对视频的业务很可能会遇到
使用
提供依赖
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.6</version>
</dependency>
提供一个工具类,在使用时应注意FFmpeg的版本,统一代码,不同版本是会报错的
/**
* 获取指定视频的帧并保存为图片至指定目录
*
* @param videofile 源视频文件路径
* @param framefile 截取帧的图片存放路径
* @throws Exception
*/
public static void fetchFrame(String videofile, String framefile)
throws Exception {
long start = System.currentTimeMillis();
File targetFile = new File(framefile);
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(videofile);
grabber.startUnsafe(false);
Frame frame;
int i = 0;
do {
frame = grabber.grabFrame();
i++;
} while (frame.image == null && i < 1000);
if (frame.image != null) {
Java2DFrameConverter converter = new Java2DFrameConverter();
BufferedImage image = converter.convert(frame);
ImageIO.write(image, "png", targetFile);
}
grabber.stop();
log.info("Grab frame cost {}, looped {}", System.currentTimeMillis() - start, i);
}
不出意外的话应该是可以直接使用的,然后再看看我写的单元测试进行调用
@Test
public void testFetchFrame() {
String fileName = UUID.randomUUID().toString().replaceAll("-","") + ".MP4";
try {
URL url = new URL("这里是网络上的一个视频文件地址,我就不方便透露了");
String tempDir = System.getProperty("java.io.tmpdir");
File videoFile = new File(tempDir + fileName);
FileUtils.copyURLToFile(url, videoFile);
String videoPath = videoFile.getAbsolutePath();
String pngName = UUID.randomUUID().toString().replaceAll("-", "") + ".png";
VideoUtil.fetchFrame(videoPath, tempDir + pngName);
File pngFile = new File(tempDir + pngName);
videoFile.delete();
pngFile.delete();
log.info("video exists?:{}", videoFile.exists());
log.info("png exists?:{}", pngFile.exists());
} catch (Exception e) {
e.printStackTrace();
}
}
解释:从网络上获取一个视频文件,不局限于MP4,放在临时目录里面。再使用FFmpeg对视频的截取,也放在临时目录里,之后你可以对截取的视频图片进行处理,比如我是将这个图片上传至对象存储里去,上传完成后,这个图片就没用了,我就需要删除,有的临时目录有自动删除的功能,但不是全部,这也是我再Test里删除的原因
FFmpeg真的很强大,对视频的操作远不止如此,有兴趣的小伙伴,咱们讨论讨论?