前言:前段時間碰到這麼一個項目開發需求,需要對上傳的視頻集獲取視頻時長以及幀圖片用作前端顯示,下面簡單闡述一下我的處理
1、開發需要依賴
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.4.4</version>
</dependency>
上述這段 maven 依賴包含了完整的 javacv 功能 (非常多,依賴
Jar
就佔大概有500MB),由於這裏只使用到了其中ffmpeg
這塊的特性,因此也可以像下面這樣排除掉無關的部分
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv</artifactId>
<version>1.4.4</version>
<exclusions>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>flycapture</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>libdc1394</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>libfreenect</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>libfreenect2</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>librealsense</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>videoinput</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>opencv</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>tesseract</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>leptonica</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>flandmark</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>artoolkitplus</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.4.4</version>
<exclusions>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>javacv</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>flycapture-platform</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>libdc1394-platform</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>libfreenect-platform</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>libfreenect2-platform</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>librealsense-platform</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>videoinput-platform</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>opencv-platform</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>tesseract-platform</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>leptonica-platform</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>flandmark-platform</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>artoolkitplus-platform</artifactId>
</exclusion>
</exclusions>
</dependency>
2、 封裝視頻處理工具類
package com.sinosoft.gzgdjcmcs.util;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
import javax.imageio.ImageIO;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.FrameGrabber.Exception;
import org.bytedeco.javacv.Java2DFrameConverter;
import com.layduo.projects.common.Global;
/**
* 視頻處理工具
* @author layduo
*
*/
public class FFmpegUtil {
private static final String IMAGE_SUFFIX = "png";
/**
* 獲取視頻時長,單位爲秒S
* @param file 視頻源
* @return
*/
@SuppressWarnings("resource")
public static long videoDuration(File file) {
long duration = 0L;
FFmpegFrameGrabber ff = new FFmpegFrameGrabber(file);
try {
ff.start();
duration = ff.getLengthInTime() / (1000 * 1000);
ff.stop();
} catch (Exception e) {
e.printStackTrace();
}
return duration;
}
/**
* 獲取視頻幀圖片
* @param file 視頻源
* @param number 第幾幀
* @param dir 文件存放根目錄
* @param args 文件存放根目錄
* @return
*/
@SuppressWarnings("resource")
public static String videoImage(File file, Integer number, String dir, Object...args) {
String picPath = StringUtils.EMPTY;
FFmpegFrameGrabber ff = new FFmpegFrameGrabber(file);
try {
ff.start();
int i = 0;
int length = ff.getLengthInFrames();
Frame frame = null;
while (i < length) {
frame = ff.grabFrame();
//截取第幾幀圖片
if ((i > number) && (frame.image != null)) {
//獲取生成圖片的路徑
picPath = getImagePath(args);
//執行截圖並放入指定位置
doExecuteFrame(frame, dir + File.separator + picPath);
break;
}
i++;
}
ff.stop();
} catch (FrameGrabber.Exception e) {
e.printStackTrace();
}
return picPath;
}
/**
* 截取縮略圖
* @param frame
* @param targerFilePath 圖片存放路徑
*/
public static void doExecuteFrame(Frame frame, String targerFilePath) {
//截取的圖片
if (null == frame || null == frame.image) {
return;
}
Java2DFrameConverter converter = new Java2DFrameConverter();
BufferedImage srcImage = converter.getBufferedImage(frame);
int srcImageWidth = srcImage.getWidth();
int srcImageHeight = srcImage.getHeight();
//對幀圖片進行等比例縮放(縮略圖)
int width = 480;
int height = (int) (((double) width / srcImageWidth) * srcImageHeight);
BufferedImage thumbnailImage = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
thumbnailImage.getGraphics().drawImage(srcImage.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 0, null);
File output = new File(targerFilePath);
try {
ImageIO.write(thumbnailImage, IMAGE_SUFFIX, output);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 生成圖片的相對路徑
* @param args 傳入生成圖片的名稱、爲空則用UUID命名
* @return 例如 upload/images.png
*/
public static String getImagePath(Object...args) {
if (StringUtils.isNotEmpty(args)) {
return String.valueOf(args[0]) + "." + IMAGE_SUFFIX;
}
return getUUID() + "." + IMAGE_SUFFIX;
}
/**
* 生成唯一的uuid
* @return uuid
*/
private static String getUUID(){
return UUID.randomUUID().toString().replace("-","");
}
/**
* 時長格式換算
* @param duration 時長
* @return HH:mm:ss
*/
public static String formatDuration(Long duration) {
String formatTime = StringUtils.EMPTY;
double time = Double.valueOf(duration);
if (time > -1) {
int hour = (int) Math.floor(time / 3600);
int minute = (int) (Math.floor(time / 60) % 60);
int second = (int) (time % 60);
if (hour < 10) {
formatTime = "0";
}
formatTime += hour + ":";
if (minute < 10) {
formatTime += "0";
}
formatTime += minute + ":";
if (second < 10) {
formatTime += "0";
}
formatTime += second;
}
return formatTime;
}
}
3、測試用例
public static void main(String[] args) throws IOException {
//獲取文件存放目錄
String uploadpath = Global.getUploadPath();
String filepath = uploadpath + File.separator + "視頻.mp4";
File file = new File(filepath);
System.out.println("視頻時長:" + formatDuration(videoDuration(file)));
System.out.println("===============================================");
//爲了避免截取圖片全黑情況,截取位置應該大於5,看具體情況而定
videoImage(file, 100, uploadpath);
}