最近項目中有一個須要用JAVA獲取視頻文件中的幀圖片的場景,以前沒有用過JAVA對視頻文件進行編輯,因而上網看看有沒有現成的方案,java
搜索的結果是要用到第三方的工具庫,其中有一個是開源的javacv,作過模式識別和圖像處理的確定知道openCV,而javacv就是利用jni封裝了linux
調用openCV中的方法,並且javacv中還封裝了對ffmpeg的調用,因而我就決定研究一下怎麼用javacv來獲取視頻中的幀。windows
一、獲取相關的JAR包和DLL庫網絡
個人我的習慣是到Maven的中央倉庫上找,果真一下就找到了,下載了bin.zip文件包。maven
在工程中我用到了其中幾個jar包,其實這幾個包是一次一次試出來的,由於運行時會報class not found,因此少什麼我就加什麼。具體以下:ide
二、如何開始寫代碼工具
因爲網上我沒找到用javacv截視頻的demo,後來沒辦法只能讀源碼來猜了,因而我又找到這幾個包的源碼包,首先是從javacv包開始測試
仔細看了一下,發現了一個相關的英文單詞:Grabber(抓取的意思),我猜差很少就是FFmpegFrameGrabber了,開是我打開了這個類(FFmpegFrameGrabber),發現應該是猜對了,但是源碼裏一點有幫助的註釋也沒有,可是我發現了有幀相關的方法,以下所示:fetch
話很少說,直接上代碼:spa
/** * Created by jinwentao on 2018/4/8 */ import java.awt.Image; import java.awt.image.BufferedImage; import java.io.File; import javax.imageio.ImageIO; import org.bytedeco.javacpp.opencv_core.IplImage; import org.bytedeco.javacv.FFmpegFrameGrabber; import org.bytedeco.javacv.Frame; public class demo { /** * 獲取指定視頻的幀並保存爲圖片至指定目錄 * @param videofile 源視頻文件路徑 * @param framefile 截取幀的圖片存放路徑 * @throws Exception */ public static void fetchFrame(String videofile, String framefile) throws Exception { long start = System.currentTimeMillis(); File targetFile = new File(framefile); FFmpegFrameGrabber ff = new FFmpegFrameGrabber(videofile); ff.start(); int lenght = ff.getLengthInFrames(); int i = 0; Frame f = null; while (i < lenght) { // 過濾前100幀 f = ff.grabFrame(); if ((i > 100) && (f.image != null)) { break; } i++; } IplImage img = f.image; int owidth = img.width(); int oheight = img.height(); // 對截取的幀進行等比例縮放 int width = 1600; int height = (int) (((double) width / owidth) * oheight); BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); bi.getGraphics().drawImage(f.image.getBufferedImage().getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 0, null); ImageIO.write(bi, "jpg", targetFile); //ff.flush(); ff.stop(); // System.out.println(System.currentTimeMillis() - start); } public static void main(String[] args) { try { demo.fetchFrame("http://101.132.110.90/group1/M00/00/05/rBN4LFq8p5SAJT0wA5k4vpHKf7Q325.mp4", "D:\\images\\test2.jpg"); } catch (Exception e) { e.printStackTrace(); } } }
在使用過程當中,若是須要獲取本地視頻的第一幀圖片的話,能夠直接將main方法中的網絡地址換成本地地址便可
"D:\\images\\test2.jpg":圖片存儲路徑
剛開始我也不知道ff.grabFrame()是每次取一幀,並且是按順序一直往下取,因此對於有的視頻會出現幀截圖是全黑的,我還覺得是我寫的有問題,後來我換了好幾個視頻測試,才發現是由於第一幀自己就是全黑的,因而我加了個過濾,把開始的幾十或幾百幀過濾掉,取中間的幀圖片。
三、關於JNI與DLL
代碼寫好後不是立馬就能運行的,因爲這個工具用的是jni,因此咱們要把DLL文件放在java.library.path裏,我是直接把ffmpeg-windows-x86.jar和opencv-windows-x86.jar裏的DLL文件直接解壓放在C:\Program Files\Java\jdk1.7.0_60\bin裏的,對於64位和linux的環境,則要用javacv-bin文件夾裏其餘的平臺jar:
若是DLL文件放的不對的話,則會報相似以下的錯誤:
Caused by: java.lang.UnsatisfiedLinkError: no opencv_core249 in java.library.path
也能夠在java代碼裏輸出java.library.path,而後把DLL文件放入其中的一個。
四、測試結果
個人測試數據是一個19MB的MP4文件,結果生成的圖片只有8KB:
程序的運行結果以下:
4654Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'E:\ceshi\11.mp4': Metadata: major_brand : isom minor_version : 1 compatible_brands: isomavc1 creation_time : 2010-12-18 05:39:40 Duration: 00:03:29.54, start: 0.000000, bitrate: 744 kb/s Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 752x424 [SAR 424:437 DAR 752:437], 680 kb/s, SAR 729:752 DAR 729:424, 25 fps, 25 tbr, 25k tbn, 50 tbc (default) Metadata: creation_time : 2010-12-18 05:39:40 handler_name : GPAC ISO Video Handler Stream #0:1(und): Audio: aac (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 61 kb/s (default) Metadata: creation_time : 2010-12-18 05:39:41 handler_name : GPAC ISO Audio Handler