java使用ffmpeg获取上传视频的缩略图和时长

介绍

FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多code都是从头开发的。

FFmpeg在Linux平台下开发,但它同样也可以在其它操作系统环境中编译运行,包括Windows、Mac OS X等。这个项目最早由Fabrice Bellard发起,2004年至2015年间由Michael Niedermayer主要负责维护。许多FFmpeg的开发人员都来自MPlayer项目,而且当前FFmpeg也是放在MPlayer项目组的服务器上。项目的名称来自MPEG视频编码标准,前面的"FF"代表"Fast Forward"。

官网地址

git地址

springboot使用场景

maven依赖

因为 FFmpeg 依赖做了多平台兼容,整体的包有200多MB,这边我们只取我们需要的功能
(查看依赖树会发现就像老太婆的裹脚布)


        
        <dependency>
            <groupId>org.bytedecogroupId>
            <artifactId>javacppartifactId>
            <version>1.5.8version>
        dependency>
        <dependency>
            <groupId>org.bytedecogroupId>
            <artifactId>javacvartifactId>
            <version>1.5.8version>
            <exclusions>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>javacppartifactId>
                exclusion>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>opencvartifactId>
                exclusion>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>flycaptureartifactId>
                exclusion>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>libdc1394-platformartifactId>
                exclusion>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>libfreenectartifactId>
                exclusion>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>libfreenect2artifactId>
                exclusion>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>librealsenseartifactId>
                exclusion>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>librealsense2artifactId>
                exclusion>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>artoolkitplusartifactId>
                exclusion>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>flandmarkartifactId>
                exclusion>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>leptonicaartifactId>
                exclusion>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>tesseractartifactId>
                exclusion>
            exclusions>
        dependency>
        <dependency>
            <groupId>org.bytedecogroupId>
            <artifactId>javacv-platformartifactId>
            <version>1.5.8version>
            <exclusions>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>ffmpeg-platformartifactId>
                exclusion>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>openblas-platformartifactId>
                exclusion>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>opencv-platformartifactId>
                exclusion>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>flycapture-platformartifactId>
                exclusion>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>libdc1394-platformartifactId>
                exclusion>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>libfreenect-platformartifactId>
                exclusion>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>libfreenect2-platformartifactId>
                exclusion>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>librealsense-platformartifactId>
                exclusion>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>librealsense2-platformartifactId>
                exclusion>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>artoolkitplus-platformartifactId>
                exclusion>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>flandmark-platformartifactId>
                exclusion>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>leptonica-platformartifactId>
                exclusion>
                <exclusion>
                    <groupId>org.bytedecogroupId>
                    <artifactId>tesseract-platformartifactId>
                exclusion>
                <exclusion>
                    <artifactId>javacppartifactId>
                    <groupId>org.bytedecogroupId>
                exclusion>
                <exclusion>
                    <artifactId>openblasartifactId>
                    <groupId>org.bytedecogroupId>
                exclusion>
            exclusions>
        dependency>
        <dependency>
            <groupId>org.bytedecogroupId>
            <artifactId>ffmpegartifactId>
            <version>5.1.2-1.5.8version>
            <classifier>windows-x86_64classifier>
        dependency>
        <dependency>
            <groupId>org.bytedecogroupId>
            <artifactId>ffmpegartifactId>
            <version>5.1.2-1.5.8version>
            <classifier>linux-x86_64classifier>
        dependency>

编写工具类

package com.ruoyi.common.utils.file;

import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.Java2DFrameConverter;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 获取视频首帧图片用于界面展示
 *
 * @author zzxjson
 * 基本信息描述:https://www.cnblogs.com/liangjingfu/p/12858018.html
 * 

* Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'F:\temporary\123456789.mp4': * Metadata: * major_brand : isom * minor_version : 512 * compatible_brands: isomiso2avc1mp41 * encoder : Lavf58.12.100 * description : Tencent SID MTS * Duration: 00:04:13.21, start: 0.000000, bitrate: 3167 kb/s * Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 3033 kb/s, 24 fps, 24 tbr, 90k tbn, 48 tbc (default) * Metadata: * handler_name : VideoHandler * Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 128 kb/s (default) * Metadata: * handler_name : SoundHandler */ public class FFmpegUtil { /** * @throws * @Title: getTempPath * @Description: 生成视频的首帧图片方法 * @author: Zing * @param: @param tempPath 生成首帧图片的文件地址 * @param: @param filePath 传进来的线上文件 * @param: @return * @param: @throws Exception * @return: boolean */ public static Video getTempPath(File targetFile, File file2) throws Exception { Video video = new Video(false,"00:00:00:00"); if (!targetFile.getParentFile().exists()) { targetFile.getParentFile().mkdirs(); } System.out.println("确认文件是否是视频..."); //判断文件是否为视频 if (isVideo(file2.getAbsolutePath())) { System.out.println("确认成功!"); //判断文件是否存在 if (file2.getParentFile().exists()) { FFmpegFrameGrabber ff = new FFmpegFrameGrabber(file2); //获取时长 video.setDuration(durationlFormat(ff)); //转缩略图 video.setGetThumbnail(getThumbnail(file2, targetFile)); ff.close(); } else { System.out.println("路径内容错误!" + file2.getParentFile()); } } else { System.out.println("确认失败!"); } return video; } private static Boolean getThumbnail(File sourceFile, File targetFile) throws FrameGrabber.Exception { FFmpegFrameGrabber ff = null; Frame frame = null; try { ff = new FFmpegFrameGrabber(sourceFile); ff.setVideoCodec(avcodec.AV_CODEC_ID_MJPEG); ff.start(); //转缩略图 int length = ff.getLengthInFrames(); int i = 0; while (i < length) { // 过滤前20帧,避免出现全黑的图片() frame = ff.grabFrame().clone(); if ((i > 20) && (frame.image != null)) { break; } i++; } int tmpWidth = frame.imageWidth; int tmpHeight = frame.imageHeight; // 对截取的帧进行等比例缩放 int width = 300; int height = (int) (((double) width / tmpWidth) * tmpHeight ); BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); BufferedImage renderedImage = new Java2DFrameConverter().getBufferedImage(frame); bi.getGraphics().drawImage(renderedImage.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 0, null); if (ImageIO.write(bi, "png", targetFile)) { System.out.println("输出图片成功!"); return true; } else { System.out.println("输出图片失败!"); return false; } } catch (Exception e) { e.printStackTrace(); return false; } finally { if (ff != null) { try { ff.close(); } catch (Exception e) { e.printStackTrace(); } } if (frame != null) { frame.close(); } } } /** * @throws * @Title: isVideo * @Description:判断是不是视频 * @author: Zing * @param: @param path 文件路径 * @param: @return * @return: boolean true是视频 false非视频 */ public static boolean isVideo(String path) { //设置视频后缀 List<String> typeList = new ArrayList<>(); typeList.add("mp4"); typeList.add("flv"); typeList.add("avi"); typeList.add("rmvb"); typeList.add("rm"); typeList.add("wmv"); //获取文件名和后缀 String suffix = path.substring(path.lastIndexOf(".") + 1); System.out.println(suffix); for (String type : typeList) { //统一为大写作比较 if (type.toUpperCase().equals(suffix.toUpperCase())) { return true; } } return false; } /** * 格式化时长 * @return 00:00:00:00 */ private static String durationlFormat(FFmpegFrameGrabber ff) throws FrameGrabber.Exception { try{ ff.start(); long duration = ff.getLengthInTime() / 1000; ff.close(); return String.format("%02d:%02d:%02d", TimeUnit.MILLISECONDS.toHours(duration), TimeUnit.MILLISECONDS.toMinutes(duration) % TimeUnit.HOURS.toMinutes(1), TimeUnit.MILLISECONDS.toSeconds(duration) % TimeUnit.MINUTES.toSeconds(1)); }catch (Exception e){ throw e; }finally { ff.close(); } } public static class Video{ private Boolean getThumbnail; private String duration; public Video(Boolean getThumbnail, String duration) { this.getThumbnail = getThumbnail; this.duration = duration; } public Boolean getGetThumbnail() { return getThumbnail; } public void setGetThumbnail(Boolean getThumbnail) { this.getThumbnail = getThumbnail; } public String getDuration() { return duration; } public void setDuration(String duration) { this.duration = duration; } } }

实际应用

public AjaxResult add(MultipartFile file){
        //临时文件位置
        String newPath = profilePath + "wareTemporary/";
        String originalFilename = file.getOriginalFilename();
        UUID uuid = UUID.randomUUID();
        //临时视频地址
        String temporaryVideoPath = newPath + uuid + originalFilename.substring(originalFilename.lastIndexOf("."));
        // 将上传的文件保存到临时文件中
        File tempFile = FileUtil.file(temporaryVideoPath);
        //临时图片地址
        String temporaryThumbnailPath = newPath + uuid + ".png";
        File thumbnail = new File(temporaryThumbnailPath);
        try{
            file.transferTo(tempFile);
            //获取课件的时长和缩略图
            FFmpegUtil.Video tempPath = FFmpegUtil.getTempPath(thumbnail,tempFile);
            if (!tempPath.getGetThumbnail()){
                return AjaxResult.error("确认文件类型出错");
            }
            //上传缩略图
            String thumbnailUrl = minioUtil.uploadFile(thumbnail);
            //上传视频
            String videoUrl = minioUtil.uploadFile(tempFile);
            return AjaxResult.success(videoUrl).put("thumbnail",thumbnailUrl).put("duration",tempPath.getDuration());
        }catch (Exception e){
            logger.error("上传文件失败",e);
            return AjaxResult.error(e.getMessage());
        }finally {
            //删除临时文件
            thumbnail.delete();
            tempFile.delete();
        }
    }

你可能感兴趣的:(ffmpeg,java,音视频)