springboot整合JavaCV實現(xiàn)視頻截取第N幀并保存圖片
前言
springboot(JavaCV )實現(xiàn)視頻截取第N幀并保存圖片
現(xiàn)在視頻網(wǎng)站展示列表都是用img標(biāo)簽展示的,動圖用的是gif,但是我們上傳視頻時并沒有視屏封面,就這需要上傳到服務(wù)器時自動生成封面并保存
本博客使用jar包的方式實現(xiàn)上傳視頻文件并且截取視頻第一幀,保存到阿里云的OSS(也可以保存到本地獲取其他任何地方)。
JavaCV 是一款開源的視覺處理庫,基于GPLv2協(xié)議,對各種常用計算機視覺庫封裝后的一組jar包,
封裝了OpenCV、libdc1394、OpenKinect、videoInput和ARToolKitPlus等計算機視覺編程人員常用庫的接口。
此方法的好處是不需要再服務(wù)器上安裝插件,直接代碼中就可以實現(xiàn)視頻截取。
我們需要截取視頻第一幀,主要用到了ffmpeg和opencv。
一 、引入jar包
我用到的maven的目前最新javacv版本,1.4.3,它應(yīng)該支持jdk1.7及以上,我項目用的還是jdk1.8.
不過需要注意的是在使用的過程當(dāng)中 , maven引入jar的時候 會引入所有平臺的版本
全部引入大小在五百兆左右(不建議使用)
<!--視頻截取第一幀--> <dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv</artifactId> <version>1.4.3</version> </dependency> <dependency> <groupId>org.bytedeco.javacpp-presets</groupId> <artifactId>ffmpeg-platform</artifactId> <version>4.0.2-1.4.3</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>RELEASE</version> </dependency>
二、java 代碼實現(xiàn)
public class ImgTools { //util調(diào)用application.properties private final static ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle("application"); private final static String aliyuVideonImg = RESOURCE_BUNDLE.getString("aliyun.video.img"); // public static void main(String[] args) throws Exception { // ImgTools imgTools = new ImgTools(); // System.out.println(imgTools.randomGrabberFFmpegVideoImage // ("視頻地址,可以是網(wǎng)絡(luò)視頻,也可以是本地視頻")); // } /** * 獲取視頻縮略圖 * * @param filePath:視頻路徑 * @throws Exception */ public String randomGrabberFFmpegVideoImage(String filePath) throws Exception { String targetFilePath = ""; FFmpegFrameGrabber ff = FFmpegFrameGrabber.createDefault(filePath); ff.start(); //判斷是否是豎屏小視頻 String rotate = ff.getVideoMetadata("rotate"); int ffLength = ff.getLengthInFrames(); Frame f; int i = 0; int index = 3;//截取圖片第幾幀 while (i < ffLength) { f = ff.grabImage(); if (i == index) { if (null != rotate && rotate.length() > 1) { targetFilePath = doExecuteFrame(f, true); //獲取縮略圖 } else { targetFilePath = doExecuteFrame(f, false); //獲取縮略圖 } break; } i++; } ff.stop(); return targetFilePath; //返回的是視頻第N幀 } /** * 截取縮略圖,存入阿里云OSS(按自己的上傳類型自定義轉(zhuǎn)換文件格式) * * @param f * @return * @throws Exception */ public String doExecuteFrame(Frame f, boolean bool) throws Exception { if (null == f || null == f.image) { return ""; } Java2DFrameConverter converter = new Java2DFrameConverter(); BufferedImage bi = converter.getBufferedImage(f); if (bool == true) { Image image = (Image) bi; bi = rotate(image, 90);//圖片旋轉(zhuǎn)90度 } ByteArrayOutputStream os = new ByteArrayOutputStream(); ImageIO.write(bi, "png", os); byte[] sdf = os.toByteArray(); InputStream input = new ByteArrayInputStream(os.toByteArray()); MultipartFile multipartFile = new MockMultipartFile("temp.jpg", "temp.jpg", "temp.jpg", input); Aliyunoss aliyunoss = new Aliyunoss(); //如需了解阿里云OSS,請詳讀我的另一篇博客("https://blog.csdn.net/weixin_44401989/article/details/105732856") String url = aliyunoss.uploadAli(multipartFile, aliyuVideonImg); return url; } /** * 圖片旋轉(zhuǎn)角度 * * @param src 源圖片 * @param angel 角度 * @return 目標(biāo)圖片 */ public static BufferedImage rotate(Image src, int angel) { int src_width = src.getWidth(null); int src_height = src.getHeight(null); // calculate the new image size Rectangle rect_des = CalcRotatedSize(new Rectangle(new Dimension( src_width, src_height)), angel); BufferedImage res = null; res = new BufferedImage(rect_des.width, rect_des.height, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = res.createGraphics(); // transform(這里先平移、再旋轉(zhuǎn)比較方便處理;繪圖時會采用這些變化,繪圖默認從畫布的左上頂點開始繪畫,源圖片的左上頂點與畫布左上頂點對齊,然后開始繪畫,修改坐標(biāo)原點后,繪畫對應(yīng)的畫布起始點改變,起到平移的效果;然后旋轉(zhuǎn)圖片即可) //平移(原理修改坐標(biāo)系原點,繪圖起點變了,起到了平移的效果,如果作用于旋轉(zhuǎn),則為旋轉(zhuǎn)中心點) g2.translate((rect_des.width - src_width) / 2, (rect_des.height - src_height) / 2); //旋轉(zhuǎn)(原理transalte(dx,dy)->rotate(radians)->transalte(-dx,-dy);修改坐標(biāo)系原點后,旋轉(zhuǎn)90度,然后再還原坐標(biāo)系原點為(0,0),但是整個坐標(biāo)系已經(jīng)旋轉(zhuǎn)了相應(yīng)的度數(shù) ) g2.rotate(Math.toRadians(angel), src_width / 2, src_height / 2); // //先旋轉(zhuǎn)(以目標(biāo)區(qū)域中心點為旋轉(zhuǎn)中心點,源圖片左上頂點對準(zhǔn)目標(biāo)區(qū)域中心點,然后旋轉(zhuǎn)) // g2.translate(rect_des.width/2,rect_des.height/ 2); // g2.rotate(Math.toRadians(angel)); // //再平移(原點恢復(fù)到源圖的左上頂點處(現(xiàn)在的右上頂點處),否則只能畫出1/4) // g2.translate(-src_width/2,-src_height/2); g2.drawImage(src, null, null); return res; } /** * 計算轉(zhuǎn)換后目標(biāo)矩形的寬高 * * @param src 源矩形 * @param angel 角度 * @return 目標(biāo)矩形 */ private static Rectangle CalcRotatedSize(Rectangle src, int angel) { double cos = Math.abs(Math.cos(Math.toRadians(angel))); double sin = Math.abs(Math.sin(Math.toRadians(angel))); int des_width = (int) (src.width * cos) + (int) (src.height * sin); int des_height = (int) (src.height * cos) + (int) (src.width * sin); return new java.awt.Rectangle(new Dimension(des_width, des_height)); } }
public class ImgTools { //util調(diào)用application.properties private final static ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle("application"); private final static String aliyuVideonImg = RESOURCE_BUNDLE.getString("aliyun.video.img"); // public static void main(String[] args) throws Exception { // ImgTools imgTools = new ImgTools(); // System.out.println(imgTools.randomGrabberFFmpegVideoImage // ("視頻地址,可以是網(wǎng)絡(luò)視頻,也可以是本地視頻")); // } /** * 獲取視頻縮略圖 * * @param filePath:視頻路徑 * @throws Exception */ public String randomGrabberFFmpegVideoImage(String filePath) throws Exception { String targetFilePath = ""; FFmpegFrameGrabber ff = FFmpegFrameGrabber.createDefault(filePath); ff.start(); //判斷是否是豎屏小視頻 String rotate = ff.getVideoMetadata("rotate"); int ffLength = ff.getLengthInFrames(); Frame f; int i = 0; int index = 3;//截取圖片第幾幀 while (i < ffLength) { f = ff.grabImage(); if (i == index) { if (null != rotate && rotate.length() > 1) { targetFilePath = doExecuteFrame(f, true); //獲取縮略圖 } else { targetFilePath = doExecuteFrame(f, false); //獲取縮略圖 } break; } i++; } ff.stop(); return targetFilePath; //返回的是視頻第N幀 } /** * 截取縮略圖,存入阿里云OSS(按自己的上傳類型自定義轉(zhuǎn)換文件格式) * * @param f * @return * @throws Exception */ public String doExecuteFrame(Frame f, boolean bool) throws Exception { if (null == f || null == f.image) { return ""; } Java2DFrameConverter converter = new Java2DFrameConverter(); BufferedImage bi = converter.getBufferedImage(f); if (bool == true) { Image image = (Image) bi; bi = rotate(image, 90);//圖片旋轉(zhuǎn)90度 } ByteArrayOutputStream os = new ByteArrayOutputStream(); ImageIO.write(bi, "png", os); byte[] sdf = os.toByteArray(); InputStream input = new ByteArrayInputStream(os.toByteArray()); MultipartFile multipartFile = new MockMultipartFile("temp.jpg", "temp.jpg", "temp.jpg", input); Aliyunoss aliyunoss = new Aliyunoss(); //如需了解阿里云OSS,請詳讀我的另一篇博客("https://blog.csdn.net/weixin_44401989/article/details/105732856") String url = aliyunoss.uploadAli(multipartFile, aliyuVideonImg); return url; } /** * 圖片旋轉(zhuǎn)角度 * * @param src 源圖片 * @param angel 角度 * @return 目標(biāo)圖片 */ public static BufferedImage rotate(Image src, int angel) { int src_width = src.getWidth(null); int src_height = src.getHeight(null); // calculate the new image size Rectangle rect_des = CalcRotatedSize(new Rectangle(new Dimension( src_width, src_height)), angel); BufferedImage res = null; res = new BufferedImage(rect_des.width, rect_des.height, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = res.createGraphics(); // transform(這里先平移、再旋轉(zhuǎn)比較方便處理;繪圖時會采用這些變化,繪圖默認從畫布的左上頂點開始繪畫,源圖片的左上頂點與畫布左上頂點對齊,然后開始繪畫,修改坐標(biāo)原點后,繪畫對應(yīng)的畫布起始點改變,起到平移的效果;然后旋轉(zhuǎn)圖片即可) //平移(原理修改坐標(biāo)系原點,繪圖起點變了,起到了平移的效果,如果作用于旋轉(zhuǎn),則為旋轉(zhuǎn)中心點) g2.translate((rect_des.width - src_width) / 2, (rect_des.height - src_height) / 2); //旋轉(zhuǎn)(原理transalte(dx,dy)->rotate(radians)->transalte(-dx,-dy);修改坐標(biāo)系原點后,旋轉(zhuǎn)90度,然后再還原坐標(biāo)系原點為(0,0),但是整個坐標(biāo)系已經(jīng)旋轉(zhuǎn)了相應(yīng)的度數(shù) ) g2.rotate(Math.toRadians(angel), src_width / 2, src_height / 2); // //先旋轉(zhuǎn)(以目標(biāo)區(qū)域中心點為旋轉(zhuǎn)中心點,源圖片左上頂點對準(zhǔn)目標(biāo)區(qū)域中心點,然后旋轉(zhuǎn)) // g2.translate(rect_des.width/2,rect_des.height/ 2); // g2.rotate(Math.toRadians(angel)); // //再平移(原點恢復(fù)到源圖的左上頂點處(現(xiàn)在的右上頂點處),否則只能畫出1/4) // g2.translate(-src_width/2,-src_height/2); g2.drawImage(src, null, null); return res; } /** * 計算轉(zhuǎn)換后目標(biāo)矩形的寬高 * * @param src 源矩形 * @param angel 角度 * @return 目標(biāo)矩形 */ private static Rectangle CalcRotatedSize(Rectangle src, int angel) { double cos = Math.abs(Math.cos(Math.toRadians(angel))); double sin = Math.abs(Math.sin(Math.toRadians(angel))); int des_width = (int) (src.width * cos) + (int) (src.height * sin); int des_height = (int) (src.height * cos) + (int) (src.width * sin); return new java.awt.Rectangle(new Dimension(des_width, des_height)); } }
以上就是springboot整合JavaCV實現(xiàn)視頻截取第N幀并保存圖片的詳細內(nèi)容,更多關(guān)于springboot JavaCV視頻截取的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot3實現(xiàn)上傳圖片并返回路徑讓前端顯示圖片
這篇文章主要介紹了SpringBoot3實現(xiàn)上傳圖片并返回路徑讓前端顯示圖片,文中通過圖文和代碼講解的非常詳細,對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-12-12Java開發(fā)微信公眾號接收和被動回復(fù)普通消息
這篇文章主要介紹了Java開發(fā)微信公眾號接收和被動回復(fù)普通消息的相關(guān)資料,需要的朋友可以參考下2016-01-01Springboot微服務(wù)項目整合Kafka實現(xiàn)文章上下架功能
這篇文章主要介紹了Springboot微服務(wù)項目整合Kafka實現(xiàn)文章上下架功能,包括Kafka消息發(fā)送快速入門及相關(guān)功能引入,本文通過示例代碼給大家介紹的非常詳細,需要的朋友可以參考下2022-07-07SpringBoot自定義轉(zhuǎn)換器應(yīng)用實例講解
SpringBoot在響應(yīng)客戶端請求時,將提交的數(shù)據(jù)封裝成對象時,使用了內(nèi)置的轉(zhuǎn)換器,SpringBoot 也支持自定義轉(zhuǎn)換器,這個內(nèi)置轉(zhuǎn)換器在 debug的時候,可以看到,提供了124個內(nèi)置轉(zhuǎn)換器2022-08-08spring boot 集成 shiro 自定義密碼驗證 自定義freemarker標(biāo)簽根據(jù)權(quán)限渲染不同頁面(推薦
這篇文章主要介紹了spring-boot 集成 shiro 自定義密碼驗證 自定義freemarker標(biāo)簽根據(jù)權(quán)限渲染不同頁面,需要的朋友可以參考下2018-12-12Day14基礎(chǔ)不牢地動山搖-Java基礎(chǔ)
這篇文章主要給大家介紹了關(guān)于Java中方法使用的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-08-08