JavaCV實現(xiàn)讀取視頻信息及自動截取封面圖詳解
概述
最近在對之前寫的一個 Spring Boot 的視頻網(wǎng)站項目做功能完善,需要利用 FFmpeg 實現(xiàn)讀取視頻信息和自動截圖的功能,查閱資料后發(fā)現(xiàn)網(wǎng)上這部分的內(nèi)容非常少,于是就有了這篇文章。
視頻網(wǎng)站項目地址
本文將介紹如何利用Javacv實現(xiàn)在視頻網(wǎng)站中常見的讀取視頻信息和自動獲取封面圖的功能。
javacv 介紹
javacv可以幫助我們在java中很方便的使用 OpenCV 以及 FFmpeg 相關的功能接口
引入 javacv
<dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv-platform</artifactId> <version>${javacv.version}</version> </dependency>
讀取視頻信息
創(chuàng)建 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 { /** * 總幀數(shù) **/ 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(); // 讀取視頻幀數(shù) videoInfo.setLengthInFrames(grabber.getLengthInVideoFrames()); // 讀取視頻幀率 videoInfo.setFrameRate(grabber.getVideoFrameRate()); // 讀取視頻秒數(shù) 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()); } } }
截圖
讀取信息沒有什么難度,但是在對視頻截圖的過程中,出現(xiàn)了一些問題,在我查找截圖實現(xiàn)的代碼時,大多數(shù)的代碼都是這么寫的
/** * 獲取視頻縮略圖 * @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); //獲取幀數(shù) int ffLength = ff.getLengthInFrames(); Frame f; int i = 0; //設置截取幀數(shù) 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; }
這樣寫本身沒有什么問題,但是在獲取需要截取幀數(shù)的部分,使用的是通過循環(huán)來一幀一幀的判斷,這樣在視頻較短的時候沒有什么問題,但是如果視頻較長,就會出現(xiàn)嚴重的性能問題。
while (i < ffLength) { f = ff.grabImage(); if(i == index){ ...... break; } i++; }
FFmpeg 的命令行參數(shù)有一個 -ss
的參數(shù),使用 -ss
可以快速的幫助我們跳到視頻的指定位置,完成操作,不用一幀一幀的判斷。
所以現(xiàn)在的問題就是如何在 javacv 中實現(xiàn) -ss
參數(shù)
我在 javacv 的 GitHub Issues 中發(fā)現(xiàn)了這個操作,即使用 setTimestamp()
方法,使用 setTimestamp()
方法可以使 FFmpeg 跳轉(zhuǎn)到指定時間,完成截圖,于是,最后的截圖代碼就變成了這樣
/** * 隨機獲取視頻截圖 * @param videFile 視頻文件 * @param count 輸出截圖數(shù)量 * @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(); // 獲取視頻總幀數(shù) // int lengthInVideoFrames = grabber.getLengthInVideoFrames(); // 獲取視頻時長, / 1000000 將單位轉(zhuǎn)換為秒 long delayedTime = grabber.getLengthInTime() / 1000000; Random random = new Random(); for (int i = 0; i < count; i++) { // 跳轉(zhuǎn)到響應時間 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, "系統(tǒng)生成截圖", 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()); } } }
這樣我們就能快速的實現(xiàn)截圖了。
以上就是JavaCV實現(xiàn)讀取視頻信息及自動截取封面圖詳解的詳細內(nèi)容,更多關于JavaCV讀取視頻信息 自動截圖的資料請關注腳本之家其它相關文章!
相關文章
Java數(shù)據(jù)結(jié)構(gòu)與算法之棧(Stack)實現(xiàn)詳解
這篇文章主要為大家詳細介紹了Java數(shù)據(jù)結(jié)構(gòu)學習筆記第二篇,Java數(shù)據(jù)結(jié)構(gòu)與算法之棧Stack實現(xiàn),具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-09-09Spring Cloud GateWay 路由轉(zhuǎn)發(fā)規(guī)則介紹詳解
這篇文章主要介紹了Spring Cloud GateWay 路由轉(zhuǎn)發(fā)規(guī)則介紹詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-05-05Redisson分布式閉鎖RCountDownLatch的使用詳細講解
分布式鎖和我們java基礎中學習到的synchronized略有不同,synchronized中我們的鎖是個對象,當前系統(tǒng)部署在不同的服務實例上,單純使用synchronized或者lock已經(jīng)無法滿足對庫存一致性的判斷。本次主要講解基于rediss實現(xiàn)的分布式鎖2023-02-02解決 IDEA 創(chuàng)建 Gradle 項目沒有src目錄問題
這篇文章主要介紹了解決 IDEA 創(chuàng)建 Gradle 項目沒有src目錄問題,本文圖文并茂給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2018-06-06阿里nacos+springboot+dubbo2.7.3統(tǒng)一處理異常的兩種方式
本文主要介紹了阿里nacos+springboot+dubbo2.7.3統(tǒng)一處理異常的兩種方式,文中根據(jù)實例編碼詳細介紹的十分詳盡,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-03-03