依赖
<!-- 视频截图 -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.7</version>
</dependency>
<!-- 工具包 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
代码
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.*;
public class Test {
public static void main(String[] args) {
// 你电脑本地的视频全路径,目前发现支持mp4、webm,其他的需要自己去尝试
File videoFile = new File("C:\\download\\谷歌浏览器\\202207\\搞笑--一个字绝_爱给网_aigei_com.webm");
// 封面数量
long coverNum = 1;
// 是否随机生成封面
boolean random = false;
Map<Long, String> coverMap = generateCover(videoFile, coverNum, random);
coverMap.forEach((key, value) -> System.out.printf("截图秒数:%s秒;图片地址:%s\n", key, value));
}
/**
* 生成视频封面
* @author 明快de玄米61
* @date 2022/12/4 16:34
* @param videoFile 视频全路径,目前发现支持mp4、webm,其他的需要自己去尝试
* @param coverNum 封面数量
* @param random 是否随机生成封面
* @return map,其中key是秒数,而value是图片全路径
**/
private static Map<Long, String> generateCover(File videoFile, long coverNum, boolean random) {
// 验证视频
if (!(videoFile != null && videoFile.isFile() && videoFile.exists())) {
throw new RuntimeException("参数:videoFile 不符合要求");
}
// 验证封面数量
if (coverNum <= 0) {
throw new RuntimeException("参数:coverNum 必须大于0");
}
// 存储封面
Map<Long, String> coverMap = new TreeMap<>();
// 生成封面
FFmpegFrameGrabber grabber = null;
try {
// 初始化FFmpegFrameGrabber
grabber = new FFmpegFrameGrabber(videoFile);
grabber.start();
// 获取截取秒数
Set<Long> secondSet = generateSecondList(grabber, coverNum, random);
// 循环生成封面
for (Long second : secondSet) {
// 设置封面截取时间
grabber.setTimestamp(second * 1000000L);
Frame f = grabber.grabImage();
Java2DFrameConverter converter = new Java2DFrameConverter();
BufferedImage bi = converter.getBufferedImage(f);
// 指定封面保存地址
File imgFile = createTempFile("cover.jpg");
// 生成封面图片
ImageIO.write(bi, "jpg", imgFile);
// 装载封面图片
coverMap.put(second, imgFile.getAbsolutePath());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (grabber != null) {
grabber.stop();
grabber.release();
}
} catch (FFmpegFrameGrabber.Exception e) {
e.printStackTrace();
}
}
return coverMap;
}
/**
* 获取封面截图秒数集合
* @author 明快de玄米61
* @date 2022/12/4 17:51
* @param grabber FFmpegFrameGrabber对象
* @param coverNum 封面数量
* @param random 是否随机生成封面
* @return 封面截图秒数集合
**/
@SneakyThrows
private static Set<Long> generateSecondList(FFmpegFrameGrabber grabber, long coverNum, boolean random) {
// 存储封面截图秒数集合
Set<Long> secondSet = new TreeSet<>();
// 获取视频时长秒数;说明:除以1000000可以获取秒数
long totalVideoSecond = grabber.getLengthInTime() / 1000000;
// 随机生成截图时间
if (random)
{
Random secureRandom = SecureRandom.getInstanceStrong();
for (int i = 1; i <= coverNum; i++) {
secondSet.add((long) (secureRandom.nextInt((int) totalVideoSecond - 1) + 1));
}
}
// 自定义截图时间
else
{
coverNum = totalVideoSecond > coverNum ? coverNum : totalVideoSecond;
for (long i = 1; i <= coverNum; i++) {
secondSet.add(i);
}
}
return secondSet;
}
/**
* 生成临时文件
* @author 明快de玄米61
* @date 2022/12/4 18:00
* @param fileName 文件全名称
* @return file文件
**/
public static File createTempFile(String fileName) {
try {
// 参数判断
if (StringUtils.isBlank(fileName) || fileName.lastIndexOf(".") < 0) {
return null;
}
// 创建临时目录
String tmpdirPath = System.getProperty("java.io.tmpdir");
String dateStr = new SimpleDateFormat("yyyyMMdd").format(new Date());
String uuId = UUID.randomUUID().toString().replaceAll("-", "");
String[] params = {tmpdirPath, dateStr, uuId};
// 结果例如:C:\Users\Administrator\AppData\Local\Temp\20220630\0f774122c38b423793cc1c121611c142
String fileUrl = StringUtils.join( params, File.separator);
if (!new File(fileUrl).exists()) {
new File(fileUrl).mkdirs();
}
// 创建临时文件
File file = new File(fileUrl, fileName);
file.createNewFile();
return file;
} catch (Exception e) {
System.out.println("》》》创建临时文件失败,临时文件名称:" + fileName);
e.printStackTrace();
}
return null;
}
}
解释
以下内容是对代码中random参数的解释
- 如果random是true,那么将按照coverNum来随机截取封面
- 如果random是false,那么将按照coverNum来顺序截取封面,返回总封面数量不会超过视频总秒数
结果
截图秒数:1秒;图片地址:C:\Users\mingming\AppData\Local\Temp\20221204\9ce19ca9932947399bf17d9e0facd037\cover.jpg