概述

最近在对之前写的一个 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());
}
}
}

这样我们就能快速的实现截图了。

版权

本文首发于 https://www.buguagaoshu.com/archives/shi-yong-javacv-shi-xian-du-qu-shi-pin-xin-xi-ji-zi-dong-jie-qu-feng-mian-tu

转载请注明来源

使用JavaCV实现读取视频信息及自动截取封面图的更多相关文章

  1. .NET读取视频信息、视频截图

    在.NET中处理视频是一件痛苦的事情,.NET并没有提供视频处理的类.于是咱们只能找一些第三方的类库或者自己实现,在项目时间比较赶的情况下,自己实现是不可能的了,而且说不定会留下很多坑.所以一般情况下 ...

  2. 上传视频使用ffmpeg自动截取缩略图

    上传视频之后,有的需要显示缩略图,而不是仅仅显示视频名称的列表,这时候就需要对上传的视频截取缩略图. 简单粗暴点,将以下代码作为工具类复制粘贴即可: package com.util; import ...

  3. 给虚拟机添加新硬盘并分区,fdisk查看分区,分区,重新读取分区表信息partprobe,格式化,挂载,查看分区挂载信息,自动挂载文件/etc/fstab,/etc/fstab文件错误导致重启崩溃后的修复

    1.虚拟机关机断电 2.添加硬盘 2.开机 3.fdisk -l查看刚才新添加的硬盘 [root@localhost ~]# fdisk -l 磁盘 /dev/sda:21.5 GB, 2147483 ...

  4. 【计算机视觉】OpenCV读取视频获取时间戳等信息(PS:经测试并不是时间戳,与FFMPEG时间戳不一样)

    OpenCV中通过VideoCaptrue类对视频进行读取操作以及调用摄像头,下面是该类的API. 1.VideoCapture类的构造函数: C++: VideoCapture::VideoCapt ...

  5. ASP.NET Core的配置(1):读取配置信息

    提到"配置"二字,我想绝大部分.NET开发人员脑海中会立马浮现出两个特殊文件的身影,那就是我们再熟悉不过的app.config和web.config,多年以来我们已经习惯了将结构化 ...

  6. Opencv读取视频

    CvCapture 是一个结构体,用来保存图像捕获所需要的信息. opencv提供两种方式从外部捕获图像 一种是从摄像头中, 一种是通过解码视频得到图像. 两种方式都必须从第一帧开始一帧一帧的按顺序获 ...

  7. JavaWEB中读取配置信息

    第一种方法是使用java.io和java.util包,缺点是路径的概念要清晰, 例子: Properties prop = new Properties(); InputStream in = get ...

  8. 解惑:NFC手机如何轻松读取银行卡信息?

    自支付宝钱包8.0推出了NFC新功能,只要将支持NFC功能的手机靠近公交卡.银行卡等带有芯片的IC卡上,可迅速读取卡内余额.卡的信息,还可以给卡进行充值,非常贴心实用. 但是很多网友表示担忧,要是别人 ...

  9. OpenCV3读取视频或摄像头

    我们可以利用OpenCV读取视频文件或者摄像头的数据,将其保存为图像,以用于后期处理.下面的实例代码展示了简单的读取和显示操作: // This is a demo introduces you to ...

随机推荐

  1. 大数据学习之路之ambari配置(四)

    试了很多遍,内存还是不够,电脑不太行的,不建议用ambari!!! 放弃了

  2. 使用 ssm 实现登录日志记录

    使用 ssm 实现登录日志记录 学习总结 一.基础准备 1. 实现效果 2. 数据表 2.1 登陆日志信息表 2.3 员工表 二.代码实现 1. SysLogLogin 实体类 2. LogAspec ...

  3. 从零搭建react开发环境

    早在六年前,前端开发已经实现了模块化.工程化开发,既然是模块化工程化开发那就少不了包管理工具,所以我们的第一步就是先从安装nodejs开始(安装nodejs携带JavaScript的包管理工具npm) ...

  4. flex布局控制最后一个元素右浮动

    可以在最后一个元素添加css属性 margin-left: auto; 例如我一排排列的元素 ,子元素并没有完全排列撑开父元素的宽度,这时候要使最后一个元素想最右 可以让最后一个元素的 margin- ...

  5. IP和静态路由技术概述

    1. IP地址的构成 IP地址:32比特的二进制数字,通常采用点分十进制方式表示. IP地址由两部分组成. 网络号码字段(Net-id)用于区分不同的网络.网络号码字段的前几位成为类别字段(又称为类别 ...

  6. matplotlib---设置坐标轴

    import matplotlib.pyplot as plt import numpy as np # 一维数组, 元素为从-3到3之间均匀地产生50个点 x = np.linspace(-3, 3 ...

  7. 使用加强堆结构解决topK问题

    作者:Grey 原文地址: 使用加强堆结构解决topK问题 题目描述 LintCode 550 · Top K Frequent Words II 思路 由于要统计每个字符串的次数,以及字典序,所以, ...

  8. Cf #709 Div. 2 B. Restore Modulo 一个只有三千多人过的b题, 妙啊!

    传送门: https://codeforces.com/contest/1484/problem/B 原题 Example input 6 6 1 9 17 6 14 3 3 4 2 2 3 7 3 ...

  9. 集合——Collection接口,List接口

    集合:对象的容器,定义了对多个对象进行操作的常用方法.可实现数组的功能 集合和数组的区别: 数组长度固定,集合长度不固定 数组可以存储基本数据类型和引用数据类型,集合只能存储引用数据类型. 集合的位置 ...

  10. netty系列之:netty中的核心解码器json

    目录 简介 java中对json的支持 netty对json的解码 总结 简介 程序和程序之间的数据传输方式有很多,可以通过二进制协议来传输,比较流行的像是thrift协议或者google的proto ...