Java使用javacv獲取視頻時長以及截取幀圖片

前言:前段時間碰到這麼一個項目開發需求,需要對上傳的視頻集獲取視頻時長以及幀圖片用作前端顯示,下面簡單闡述一下我的處理

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);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章