java获取视频详细信息并截图做封面的两种实现方式
目录
两种视频处理方式介绍
- 一种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