java获取视频详细信息并截图做封面的两种实现方式

目录

两种视频处理方式介绍

jave方式

Javacv的方式


两种视频处理方式介绍

  • 一种jave获取视频信息,使用FFmpeg方式截取视频第n帧生成图片
    • 需要下载一个ffmpeg.exe文件
  • 一种是通过Javacv的方式获取视频信息和截图

jave方式

public static void main(String[] args) {
    String videoPath = "D:\\szw\\photo\\test\\55f9e02108b642.mp4";
    String ffmpegPath = "src/main/resources/libs/ffmpeg.exe";
    getVideoInfo(videoPath);
    processImg(videoPath, "1", ffmpegPath);
}
  •   几种pom依赖
<dependency>
    <groupId>it.sauronsoftware</groupId>
    <artifactId>jave</artifactId>
    <version>1.0.2</version>
</dependency>

<dependency>
    <groupId>com.github.dadiyang</groupId>
    <artifactId>jave</artifactId>
    <version>1.0.2</version>
</dependency>

<!-- https://mvnrepository.com/artifact/ws.schild/jave-core -->
<dependency>
    <groupId>ws.schild</groupId>
    <artifactId>jave-core</artifactId>
    <version>2.7.3</version>
</dependency>
  •   VideoUtil:
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import it.sauronsoftware.jave.Encoder;
import org.apache.commons.lang.StringUtils;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;

/**
 * 视频工具类
 *
 * @Author: szw
 * @Date: 2020/7/9 9:42
 */
public class VideoUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(VideoUtil.class);

    /**
     * 通过jave方式获取视频信息
     * @param realPath 视频文件路径
     * @return Map<String, Object>
     */
    public static Map<String, Object> getVideoInfo(String realPath) {
        FileInputStream fis;
        File source = new File(realPath);
        Encoder encoder = new Encoder();
        FileChannel fc = null;
        Map<String, Object> result = new HashMap<String, Object>();
        try {
            it.sauronsoftware.jave.MultimediaInfo m = encoder.getInfo(source);
            long ls = m.getDuration();
            LOGGER.debug("此视频时长为:" + ls / 60000 + "分" + (ls) / 1000 + "秒!");
            // 视频帧宽高
            LOGGER.debug("此视频高度为:" + m.getVideo().getSize().getHeight());
            LOGGER.debug("此视频宽度为:" + m.getVideo().getSize().getWidth());
            LOGGER.debug("此视频格式为:" + m.getFormat());
            result.put("videoHigh", m.getVideo().getSize().getHeight());
            result.put("videoWide", m.getVideo().getSize().getWidth());
            result.put("format", m.getFormat());
            fis = new FileInputStream(source);
            fc = fis.getChannel();
            BigDecimal fileSize = new BigDecimal(fc.size());
            String size = fileSize.divide(new BigDecimal(1048576), 2, RoundingMode.HALF_UP) + "MB";
            LOGGER.debug("此视频大小为" + size);
            result.put("size", size);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return result;
        } finally {
            if (null != fc) {
                try {
                    fc.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 使用 FFmpeg 截取视频第几帧生成图片
     *
     * @param videoPath 视频路径
     * @param frame     第几帧
     * @param utilPath  工具包地址
     * @return String    图片的相对路劲
     */
    public static String processImg(String videoPath, String frame, String utilPath) {
        File file = new File(videoPath);
        if (!file.exists()) {
            System.err.println("路径[" + videoPath + "]对应的视频文件不存在!");
            return null;
        }
        List<String> commands = new java.util.ArrayList<String>();
        commands.add(utilPath);
        commands.add("-i");
        commands.add(videoPath);
        commands.add("-y");
        commands.add("-f");
        commands.add("image2");
        commands.add("-ss");
        commands.add(frame);    // 这个参数是设置截取视频多少秒时的画面
        commands.add("-t");
        commands.add("0.001");
        commands.add("-s");
        commands.add("800x1200"); // 宽X高
        String imgPath = videoPath.substring(0, videoPath.lastIndexOf(".")) + "-cover" + ".jpg";
        commands.add(imgPath);
        try {
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(commands);
            builder.redirectErrorStream(true);
            LOGGER.debug("视频截图开始...");
            Process process = builder.start();
            InputStream in = process.getInputStream();
            // 写入本地
            FileUtil.writeToLocal(imgPath, in);
            LOGGER.debug("截取成功");
            return imgPath;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}


Javacv的方式

JavaCV是一个计算机视觉领域的封装库,封装了包括 OpenCV,FFmpeg 等
GitHub: https://github.com/bytedeco/javacv
OpenCV 的文档地址: https://docs.opencv.org/master
 

public static void main(String[] args) {
    String videoPath = "D:\\szw\\photo\\test\\55f9e02108b642.mp4";
    Map<String, Object> screenshot = getScreenshot(videoPath);
    System.out.println(screenshot);
}

  • pom依赖,我的后来版本改到1.5.3了
    <version>1.5.3</version>
<dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>javacv-platform</artifactId>
    <version>1.3.1</version>
</dependency>
  • VideoUtil

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import it.sauronsoftware.jave.Encoder;
import org.apache.commons.lang.StringUtils;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;

/**
 * 视频工具类
 *
 * @Author: szw
 * @Date: 2020/7/9 9:42
 */
public class VideoUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(VideoUtil.class);

    /**
     * 通过Javacv的方式获取视频截图
     *
     * @param filePath 视频文件路径
     * @return Map<String, Object>
     */
    public static Map<String, Object> getScreenshot(String filePath) {
        try {
            LOGGER.debug("截取视频截图开始:" + System.currentTimeMillis());
            Map<String, Object> result = new HashMap<String, Object>();
            FFmpegFrameGrabber grabber = FFmpegFrameGrabber.createDefault(filePath);

            // 第一帧图片存储位置
            String targerFilePath = filePath.substring(0, filePath.lastIndexOf("\\"));
            // 视频文件名
            String fileName = filePath.substring(filePath.lastIndexOf("\\") + 1);
            // 图片名称
            String targetFileName = fileName.substring(0, fileName.lastIndexOf("."));
            LOGGER.debug("视频路径是:" + targerFilePath);
            LOGGER.debug("视频文件名:" + fileName);
            LOGGER.debug("图片名称是:" + targetFileName);

            grabber.start();
            //设置视频截取帧(默认取第一帧)
            Frame frame = grabber.grabImage();
            //视频旋转度
            String rotate = grabber.getVideoMetadata("rotate");
            Java2DFrameConverter converter = new Java2DFrameConverter();
            //绘制图片
            BufferedImage bi = converter.getBufferedImage(frame);
            if (rotate != null) {
                // 旋转图片
                bi = rotate(bi, Integer.parseInt(rotate));
            }
            //图片的类型
            String imageMat = "jpg";
            //图片的完整路径
            String imagePath = targerFilePath + File.separator + targetFileName + "." + imageMat;
            //创建文件
            File output = new File(imagePath);
            ImageIO.write(bi, imageMat, output);

            //拼接Map信息
            result.put("videoWide", bi.getWidth());
            result.put("videoHigh", bi.getHeight());
            long duration = grabber.getLengthInTime() / (1000 * 1000);
            result.put("rotate", StringUtils.isBlank(rotate) ? "0" : rotate);
            result.put("format", grabber.getFormat());
            result.put("imgPath", output.getPath());
            result.put("time", duration);
            LOGGER.debug("视频的宽:" + bi.getWidth());
            LOGGER.debug("视频的高:" + bi.getHeight());
            LOGGER.debug("视频的旋转度:" + rotate);
            LOGGER.debug("视频的格式:" + grabber.getFormat());
            LOGGER.debug("此视频时长(s/秒):" + duration);
            grabber.stop();
            LOGGER.debug("截取视频截图结束:" + System.currentTimeMillis());
            return result;
        } catch (Exception e) {
            LOGGER.error("VideoUtil getScreenshot fail: {}", ExceptionStackTrace.printStackTrace(e));
            return null;
        }
    }

    /**
     * 根据视频旋转度来调整图片
     *
     * @param src   BufferedImage
     * @param angel angel	视频旋转度
     * @return BufferedImage
     */
    public static BufferedImage rotate(BufferedImage src, int angel) {
        int src_width = src.getWidth(null);
        int src_height = src.getHeight(null);
        int type = src.getColorModel().getTransparency();
        Rectangle rect_des = calcRotatedSize(new Rectangle(new Dimension(src_width, src_height)), angel);
        BufferedImage bi = new BufferedImage(rect_des.width, rect_des.height, type);
        Graphics2D g2 = bi.createGraphics();
        g2.translate((rect_des.width - src_width) / 2, (rect_des.height - src_height) / 2);
        g2.rotate(Math.toRadians(angel), src_width / 2, src_height / 2);
        g2.drawImage(src, 0, 0, null);
        g2.dispose();
        return bi;
    }


    /**
     * 计算图片旋转大小
     *
     * @param src   Rectangle
     * @param angel int
     * @return Rectangle
     */
    public static Rectangle calcRotatedSize(Rectangle src, int angel) {
        if (angel >= 90) {
            if (angel / 90 % 2 == 1) {
                int temp = src.height;
                src.height = src.width;
                src.width = temp;
            }
            angel = angel % 90;
        }
        double r = Math.sqrt(src.height * src.height + src.width * src.width) / 2;
        double len = 2 * Math.sin(Math.toRadians(angel) / 2) * r;
        double angel_alpha = (Math.PI - Math.toRadians(angel)) / 2;
        double angel_dalta_width = Math.atan((double) src.height / src.width);
        double angel_dalta_height = Math.atan((double) src.width / src.height);
        int len_dalta_width = (int) (len * Math.cos(Math.PI - angel_alpha - angel_dalta_width));
        int len_dalta_height = (int) (len * Math.cos(Math.PI - angel_alpha - angel_dalta_height));
        int des_width = src.width + len_dalta_width * 2;
        int des_height = src.height + len_dalta_height * 2;
        return new java.awt.Rectangle(new Dimension(des_width, des_height));
    }
}


 

问题解决

main方法测试没问题,项目运行后报系统找不到指定的文件,(虽然能运行但一堆报错看着也不舒服)

java.io.FileNotFoundException: D:\szw\repository\org\bytedeco\tesseract-platform\4.1.1-1.5.3\tesseract-windows-x86_64.jar (系统找不到指定的文件。)

参考这篇:BUG记录-找不到jaxb相关jar包BUG记录-SpringBoot找不到jaxb相关jar包_jaxb.jar_瑶山的博客-CSDN博客

找到报错jar位置,解压(没有打jar包软件用360压缩打开) -> META-INF -> MANIFEST.MF ->打开文件找到Class-Path 删除 ->再打包回jar

重新运行

报错jar修改后就正常了

也可以下载我删减好的->0积分免费下载

javaCV所有相关jar删减完成版:https://download.csdn.net/download/qq_44695727/12597497

javaCV所有相关jar未删减版:https://download.csdn.net/download/qq_44695727/12596763