服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - JavaCV实现读取视频信息及自动截取封面图详解

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

2023-02-20 15:17陕西颜值扛把子 Java教程

javacv可以帮助我们在java中很方便的使用OpenCV以及FFmpeg相关的功能接口。本文将利用Javacv实现在视频网站中常见的读取视频信息和自动获取封面图的功能,感兴趣的可以了解一下

概述

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

视频网站项目地址

GitHub

码云

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

javacv 介绍

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

项目地址

引入 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实现读取视频信息及自动截取封面图详解

以上就是JavaCV实现读取视频信息及自动截取封面图详解的详细内容,更多关于JavaCV读取视频信息 自动截图的资料请关注服务器之家其它相关文章!

原文链接:https://www.cnblogs.com/puzhiwei/p/16353037.html

延伸 · 阅读

精彩推荐