最近项目中有一个须要用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