JavaCV实现读取视频信息及自动截取封面图详解

网友投稿 538 2022-07-26


目录概述javacv 介绍引入 javacv读取视频信息创建 VideoInfo 类使用 FFmpegFrameGrabber 读取视频信息截图

概述

最近在对之前写的一个 Spring Boot 的视频网站项目做功能完善,需要利用 FFmpeg 实现读取视频信息和自动截图的功能,查阅资料后发现网上这部分的内容非常少,于是就有了这篇文章。

视频网站项目地址

github

码云

本文将介绍如何利用Javacv实现在视频网站中常见的读取视频信息和自动获取封面图的功能。

javacv 介绍

javacv可以帮助我们在java中很方便的使用 OpenCV 以及 FFmpeg 相关的功能接口

项目地址

引入 javacv

org.bytedeco

javacv-platform

${javacv.version}

读取视频信息

创建 VideoInfo 类

package com.buguagaoshu.porntube.vo;

import com.fasterxml.jackson.databind.ObjectMapper;

import lombok.Getter;

import lombqPjTbPFok.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 "";

}

}

}

使用 FFmpegFrameGrabber 读取视频信息

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.getAudiqPjTbPFoCodecName());

// 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 randomGrabberFFmpegImage(File videFile, int count, long userId) {

FFmpegFrameGrabber grabber = null;

String path = FileTypeEnum.filePath();

try {

List 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读取视频信息 自动截图的资料请关注我们其它相关文章!


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java深入解析接口interface
下一篇:Java实例讲解枚举enum的实现
相关文章

 发表评论

暂时没有评论,来抢沙发吧~