最近项目中有一个需要用JAVA获取视频文件中的帧图片的场景,之前没有用过JAVA对视频文件进行编辑,于是上网看看有没有现成的方案,
搜索的结果是要用到第三方的工具库,其中有一个是开源的javacv,做过模式识别和图像处理的肯定知道openCV,而javacv就是利用jni封装了
调用openCV中的方法,而且javacv中还封装了对ffmpeg的调用,于是我就决定研究一下怎么用javacv来获取视频中的帧。
1、获取相关的JAR包和DLL库
我的个人习惯是到Maven的中央仓库上找,果然一下就找到了,下载了bin.zip文件包。
在工程中我用到了其中几个jar包,其实这几个包是一次一次试出来的,因为运行时会报class not found,所以少什么我就加什么。具体如下:
2、如何开始写代码
由于网上我没找到用javacv截视频的demo,后来没办法只能读源码来猜了,于是我又找到这几个包的源码包,首先是从javacv包开始
仔细看了一下,发现了一个相关的英文单词:Grabber(抓取的意思),我猜差不多就是FFmpegFrameGrabber了,开是我打开了这个类(FFmpegFrameGrabber),发现应该是猜对了,可是源码里一点有帮助的注释也没有,但是我发现了有帧相关的方法,如下所示:
于是我就从构造函数开始,然后一步步试,最终可以运行的代码如下
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 Grabber {
/**
* 获取指定视频的帧并保存为图片至指定目录
* @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 = 270;
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 {
Grabber.fetchFrame("E:\\ceshi\\11.mp4", "E:\\ceshi\\test.jpg");
} catch (Exception e) {
e.printStackTrace();
}
}
}
3、关于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文件放入其中的一个。
4、测试结果
我的测试数据是一个19MB的MP4文件,结果生成的图片只有8KB:
程序的运行结果如下:
4654
Input #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