您好,登录后才能下订单哦!
这篇文章主要介绍了JavaCV如何实现读取视频信息及自动截取封面图的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇JavaCV如何实现读取视频信息及自动截取封面图文章都会有所收获,下面我们一起来看看吧。
javacv可以帮助我们在java中很方便的使用 OpenCV 以及 FFmpeg 相关的功能接口
项目地址
<dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv-platform</artifactId> <version>${javacv.version}</version> </dependency>
package com.buguagaoshu.porntube.vo; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Getter; import lombok.Setter; /** * @author Pu Zhiwei {@literal puzhiweipuzhiwei@foxmail.com} * create 2022-06-06 19:15 */ @Getter @Setter public class VideoInfo { /** * 总帧数 **/ private int lengthInFrames; /** * 帧率 **/ private double frameRate; /** * 时长 **/ private double duration; /** * 视频编码 */ private String videoCode; /** * 音频编码 */ private String audioCode; private int width; private int height; private int audioChannel; private String md5; /** * 音频采样率 */ private Integer sampleRate; public String toJson() { try { ObjectMapper objectMapper = new ObjectMapper(); return objectMapper.writeValueAsString(this); } catch (Exception e) { return ""; } } }
public static VideoInfo getVideoInfo(File file) { VideoInfo videoInfo = new VideoInfo(); FFmpegFrameGrabber grabber = null; try { grabber = new FFmpegFrameGrabber(file); // 启动 FFmpeg grabber.start(); // 读取视频帧数 videoInfo.setLengthInFrames(grabber.getLengthInVideoFrames()); // 读取视频帧率 videoInfo.setFrameRate(grabber.getVideoFrameRate()); // 读取视频秒数 videoInfo.setDuration(grabber.getLengthInTime() / 1000000.00); // 读取视频宽度 videoInfo.setWidth(grabber.getImageWidth()); // 读取视频高度 videoInfo.setHeight(grabber.getImageHeight()); videoInfo.setAudioChannel(grabber.getAudioChannels()); videoInfo.setVideoCode(grabber.getVideoCodecName()); videoInfo.setAudioCode(grabber.getAudioCodecName()); // String md5 = MD5Util.getMD5ByInputStream(new FileInputStream(file)); videoInfo.setSampleRate(grabber.getSampleRate()); return videoInfo; } catch (Exception e) { e.printStackTrace(); return null; } finally { try { if (grabber != null) { // 此处代码非常重要,如果没有,可能造成 FFmpeg 无法关闭 grabber.stop(); grabber.release(); } } catch (FFmpegFrameGrabber.Exception e) { log.error("getVideoInfo grabber.release failed 获取文件信息失败:{}", e.getMessage()); } } }
读取信息没有什么难度,但是在对视频截图的过程中,出现了一些问题,在我查找截图实现的代码时,大多数的代码都是这么写的
/** * 获取视频缩略图 * @param filePath:视频路径 * @param mod:视频长度/mod获取第几帧 * @throws Exception */ public static String randomGrabberFFmpegImage(String filePath, int mod) { String targetFilePath = ""; try{ FFmpegFrameGrabber ff = FFmpegFrameGrabber.createDefault(filePath); ff.start(); //图片位置是否正确 String rotate = ff.getVideoMetadata(ROTATE); //获取帧数 int ffLength = ff.getLengthInFrames(); Frame f; int i = 0; //设置截取帧数 int index = ffLength / mod; while (i < ffLength) { f = ff.grabImage(); if(i == index){ if (null != rotate && rotate.length() > 1) { OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage(); IplImage src = converter.convert(f); f = converter.convert(rotate(src, Integer.parseInt(rotate))); } targetFilePath = getImagePath(filePath, i); doExecuteFrame(f, targetFilePath); break; } i++; } ff.stop(); }catch (Exception e){ log.error("获取视频缩略图异常:" + e.getMessage()); } return targetFilePath; }
这样写本身没有什么问题,但是在获取需要截取帧数的部分,使用的是通过循环来一帧一帧的判断,这样在视频较短的时候没有什么问题,但是如果视频较长,就会出现严重的性能问题。
while (i < ffLength) { f = ff.grabImage(); if(i == index){ ...... break; } i++; }
FFmpeg 的命令行参数有一个 -ss
的参数,使用 -ss
可以快速的帮助我们跳到视频的指定位置,完成操作,不用一帧一帧的判断。
所以现在的问题就是如何在 javacv 中实现 -ss
参数
我在 javacv 的 GitHub Issues 中发现了这个操作,即使用 setTimestamp()
方法,使用 setTimestamp()
方法可以使 FFmpeg 跳转到指定时间,完成截图,于是,最后的截图代码就变成了这样
/** * 随机获取视频截图 * @param videFile 视频文件 * @param count 输出截图数量 * @return 截图列表 * */ public static List<FileTableEntity> randomGrabberFFmpegImage(File videFile, int count, long userId) { FFmpegFrameGrabber grabber = null; String path = FileTypeEnum.filePath(); try { List<FileTableEntity> images = new ArrayList<>(count); grabber = new FFmpegFrameGrabber(videFile); grabber.start(); // 获取视频总帧数 // int lengthInVideoFrames = grabber.getLengthInVideoFrames(); // 获取视频时长, / 1000000 将单位转换为秒 long delayedTime = grabber.getLengthInTime() / 1000000; Random random = new Random(); for (int i = 0; i < count; i++) { // 跳转到响应时间 grabber.setTimestamp((random.nextInt((int)delayedTime - 1) + 1) * 1000000L); Frame f = grabber.grabImage(); Java2DFrameConverter converter = new Java2DFrameConverter(); BufferedImage bi = converter.getBufferedImage(f); String imageName = FileTypeEnum.newFilename(SUFFIX); File out = Paths.get(path, imageName).toFile(); ImageIO.write(bi, "jpg", out); FileTableEntity fileTable = FileUtils.createFileTableEntity(imageName, SUFFIX, path, f.image.length, "系统生成截图", userId, FileTypeEnum.VIDEO_PHOTO.getCode()); images.add(fileTable); } return images; } catch (Exception e) { return null; } finally { try { if (grabber != null) { grabber.stop(); grabber.release(); } } catch (FFmpegFrameGrabber.Exception e) { log.error("getVideoInfo grabber.release failed 获取文件信息失败:{}", e.getMessage()); } } }
这样我们就能快速的实现截图了。
关于“JavaCV如何实现读取视频信息及自动截取封面图”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“JavaCV如何实现读取视频信息及自动截取封面图”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注亿速云行业资讯频道。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。