使用JavaCV实现读取视频信息及自动截取封面图
概述
最近在对之前写的一个 Spring Boot 的视频网站项目做功能完善,需要利用 FFmpeg 实现读取视频信息和自动截图的功能,查阅资料后发现网上这部分的内容非常少,于是就有了这篇文章。
视频网站项目地址 GitHub:https://github.com/PuZhiweizuishuai/PornTube
码云: https://gitee.com/puzhiweizuishuai/VideoWeb
本文将介绍如何利用Javacv实现在视频网站中常见的读取视频信息和自动获取封面图的功能。
javacv 介绍
javacv可以帮助我们在java中很方便的使用 OpenCV 以及 FFmpeg 相关的功能接口
项目地址:https://github.com/bytedeco/javacv
引入 javacv
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>${javacv.version}</version>
</dependency>
读取视频信息
创建 VideoInfo 类
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 "";
}
}
}
使用 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.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实现读取视频信息及自动截取封面图的更多相关文章
- .NET读取视频信息、视频截图
在.NET中处理视频是一件痛苦的事情,.NET并没有提供视频处理的类.于是咱们只能找一些第三方的类库或者自己实现,在项目时间比较赶的情况下,自己实现是不可能的了,而且说不定会留下很多坑.所以一般情况下 ...
- 上传视频使用ffmpeg自动截取缩略图
上传视频之后,有的需要显示缩略图,而不是仅仅显示视频名称的列表,这时候就需要对上传的视频截取缩略图. 简单粗暴点,将以下代码作为工具类复制粘贴即可: package com.util; import ...
- 给虚拟机添加新硬盘并分区,fdisk查看分区,分区,重新读取分区表信息partprobe,格式化,挂载,查看分区挂载信息,自动挂载文件/etc/fstab,/etc/fstab文件错误导致重启崩溃后的修复
1.虚拟机关机断电 2.添加硬盘 2.开机 3.fdisk -l查看刚才新添加的硬盘 [root@localhost ~]# fdisk -l 磁盘 /dev/sda:21.5 GB, 2147483 ...
- 【计算机视觉】OpenCV读取视频获取时间戳等信息(PS:经测试并不是时间戳,与FFMPEG时间戳不一样)
OpenCV中通过VideoCaptrue类对视频进行读取操作以及调用摄像头,下面是该类的API. 1.VideoCapture类的构造函数: C++: VideoCapture::VideoCapt ...
- ASP.NET Core的配置(1):读取配置信息
提到"配置"二字,我想绝大部分.NET开发人员脑海中会立马浮现出两个特殊文件的身影,那就是我们再熟悉不过的app.config和web.config,多年以来我们已经习惯了将结构化 ...
- Opencv读取视频
CvCapture 是一个结构体,用来保存图像捕获所需要的信息. opencv提供两种方式从外部捕获图像 一种是从摄像头中, 一种是通过解码视频得到图像. 两种方式都必须从第一帧开始一帧一帧的按顺序获 ...
- JavaWEB中读取配置信息
第一种方法是使用java.io和java.util包,缺点是路径的概念要清晰, 例子: Properties prop = new Properties(); InputStream in = get ...
- 解惑:NFC手机如何轻松读取银行卡信息?
自支付宝钱包8.0推出了NFC新功能,只要将支持NFC功能的手机靠近公交卡.银行卡等带有芯片的IC卡上,可迅速读取卡内余额.卡的信息,还可以给卡进行充值,非常贴心实用. 但是很多网友表示担忧,要是别人 ...
- OpenCV3读取视频或摄像头
我们可以利用OpenCV读取视频文件或者摄像头的数据,将其保存为图像,以用于后期处理.下面的实例代码展示了简单的读取和显示操作: // This is a demo introduces you to ...
随机推荐
- SpringBoot 项目搭建(详细介绍+案例源码)
SpringBoot 项目搭建 SpringBoot 项目整合源码 SpringBoot 项目整合 一.项目准备 1.1 快速创建 SpringBoot 项目 1.2 标准项目结构图如下 1.3 添加 ...
- 微信小程序人脸识别
参考:https://cloud.tencent.com/document/product/1007/31071
- C# 一个基于.NET Core3.1的开源项目帮你彻底搞懂WPF框架Prism
--概述 这个项目演示了如何在WPF中使用各种Prism功能的示例.如果您刚刚开始使用Prism,建议您从第一个示例开始,按顺序从列表中开始.每个示例都基于前一个示例的概念. 此项目平台框架:.NET ...
- 学生管理系统 C++课设
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<iostream> u ...
- java_类的访问控制符
1.分类: public protected default private是java中的访问控制修饰符. public String name; protected String name; Str ...
- border 流光高光
<template> <div> <div class="conic"></div> <div class="c ...
- Promise了解
Promise是一个构造函数,excutor是他的执行函数,同时也是构造函数的参数. new Promise(excutor) excutor有两个参数(resolve,reject) Promise ...
- 判断是否微信,qq等登陆。进去不同的页面下载
<!DOCTYPE html><html> <head> <meta charset="utf-8"> <title>安 ...
- Java语言学习day09--7月08日
今日内容介绍 1.方法基础知识 2.方法高级内容 3.方法案例 ###01方法的概述 * A: 为什么要有方法 * 提高代码的复用性 * B: 什么是方法 ...
- Myeclipse+svn相关文章
Myeclipse安装svn插件https://www.cnblogs.com/liuyk-code/p/7519886.html 使用svn https://jingyan.baidu.com/ar ...