javacv实现上传视频后自动保存封面缩略图(视频第一帧)

首先,使用场景:

现在视频网站展示列表都是用img标签展示的,动图用的是gif,但是我们上传视频时并没有视屏封面,就这需要上传到服务器时自动成功封面并保存

JavaCV 是一款开源的视觉处理库,基于GPLv2协议,对各种常用计算机视觉库封装后的一组jar包,封装了OpenCV、libdc1394、OpenKinect、videoInput和ARToolKitPlus等计算机视觉编程人员常用库的接口。

此方法的好处是不需要再服务器上安装插件,直接代码中就可以实现视频截取。

我们需要截取视频第一帧,主要用到了ffmpeg和opencv。

我用到的maven的目前最新javacv版本,1.4.2,它应该支持jdk1.7及以上,我项目用的还是jdk1.7.

1.引入maven依赖

坑:

<dependency>
      <groupId>org.bytedeco</groupId>
      <artifactId>javacv-platform</artifactId>
      <version>1.4.2</version>
</dependency>

网上有说用0.8版本的,但用maven打包编译时总是报错,所以索性用最高版本

本来maven直接引用这段会自动下载依赖包,但是全部下载下载我看有500多兆,因为它包括了android,linux,macosx等。一个截取封面功能要给项目增加五百多兆内存这是不能容忍的。

我的服务器是windows 64系统的,所以我只需要windows-x86_64需要的jar包

所以我精简了一下pom依赖

<!-- https://mvnrepository.com/artifact/org.bytedeco/javacv -->
<dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>javacv</artifactId>
    <version>1.4.2</version>
    <exclusions>
        <exclusion>
            <groupId>org.bytedeco.javacpp-presets</groupId>
            <artifactId>*</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.bytedeco.javacpp-presets</groupId>
    <artifactId>opencv</artifactId>
    <version>3.4.2-1.4.2</version>
</dependency>
<dependency>
    <groupId>org.bytedeco.javacpp-presets</groupId>
    <artifactId>opencv</artifactId>
    <version>3.4.2-1.4.2</version>
    <classifier>windows-x86_64</classifier>
</dependency>
<!-- https://mvnrepository.com/artifact/org.bytedeco.javacpp-presets/ffmpeg -->
<dependency>
    <groupId>org.bytedeco.javacpp-presets</groupId>
    <artifactId>ffmpeg</artifactId>
    <version>4.0.1-1.4.2</version>
</dependency>
<dependency>
    <groupId>org.bytedeco.javacpp-presets</groupId>
    <artifactId>ffmpeg</artifactId>
    <version>4.0.1-1.4.2</version>
    <classifier>windows-x86_64</classifier>
</dependency>

这样我只用了ffmpeg和opencv,占用内存成功缩小到几十兆。

其他平台需要用可以在classifier里面修改成对应的

<javacpp.platform.android-arm>android-arm</javacpp.platform.android-arm>
    <javacpp.platform.android-arm64>android-arm64</javacpp.platform.android-arm64>
    <javacpp.platform.android-x86>android-x86</javacpp.platform.android-x86>
    <javacpp.platform.android-x86_64>android-x86_64</javacpp.platform.android-x86_64>
    <javacpp.platform.ios-arm>ios-arm</javacpp.platform.ios-arm>
    <javacpp.platform.ios-arm64>ios-arm64</javacpp.platform.ios-arm64>
    <javacpp.platform.ios-x86>ios-x86</javacpp.platform.ios-x86>
    <javacpp.platform.ios-x86_64>ios-x86_64</javacpp.platform.ios-x86_64>
    <javacpp.platform.linux-armhf>linux-armhf</javacpp.platform.linux-armhf>
    <javacpp.platform.linux-arm64>linux-arm64</javacpp.platform.linux-arm64>
    <javacpp.platform.linux-ppc64le>linux-ppc64le</javacpp.platform.linux-ppc64le>
    <javacpp.platform.linux-x86>linux-x86</javacpp.platform.linux-x86>
    <javacpp.platform.linux-x86_64>linux-x86_64</javacpp.platform.linux-x86_64>
    <javacpp.platform.macosx-x86_64>macosx-x86_64</javacpp.platform.macosx-x86_64>
    <javacpp.platform.windows-x86>windows-x86</javacpp.platform.windows-x86>
    <javacpp.platform.windows-x86_64>windows-x86_64</javacpp.platform.windows-x86_64>

至此引入jar结束。

2.java代码实现

引入jar包

import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

保存视频的方法

@RequestMapping(value = "uploadVideoFiles", method = RequestMethod.POST, produces = "application/json;charset=utf-8")
@ResponseBody
public String uploadVideoFiles(
        @RequestParam(value = "file") MultipartFile[] files,  //这样接收文件
        String classId,
        HttpServletRequest request
) {
    try {
        Map<String,Object> params=new HashMap<String, Object>();
        //视频路径
        String path="/resource/videos/";
        //缩略图
        String framefile="/resource/frames/";
        //根路径
        String basePath=request.getSession().getServletContext().getRealPath("/");
        int userId=((TSystemUser)request.getSession().getAttribute("USER")).getUserId();
        params.put("classId",classId);
        params.put("attachmentType","VIDEO");
        params.put("userId",userId);
        for (MultipartFile file : files) {    //循环保存文件
            Map<String,String> name=uploadFile(path,file, request);
            params.put("attachmentUrl",path+name.get("saveName"));
            params.put("attachmentName",name.get("fileName"));
            String thumbpath=framefile+name.get("saveName").substring(0,name.get("saveName").length()-4)+".jpg";
            params.put("attachmentThumbnail",thumbpath);
            //调用保存缩略图方法
            this.fetchFrame(basePath+path+name.get("saveName"),basePath+thumbpath);
            //保存入库
            attachmentService.saveFile(params);
            // attachmentService.saveImg(path);
        }
        // 返回前台
        return JSON.toJSONString("success");

    } catch (Exception e) {
        e.printStackTrace();
        return JSON.toJSONString("fail");
    }

}


public Map<String,String> uploadFile(String path,MultipartFile file, HttpServletRequest request) throws IOException {
    Map<String,String> result=new HashMap<String,String>();
    //文件原名
    String fileName = file.getOriginalFilename();
    String basePath=request.getSession().getServletContext().getRealPath("/");
    path=basePath+path;            //设置文件保存路径
    //File tempFile = new File(path, new Date().getTime() + String.valueOf(fileName));
    //文件类型
    String fileType = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length()).toLowerCase();
    //保存的文件名
    String saveName=String.valueOf((new Date()).getTime()).substring(8)+(int)((Math.random()*999+1))+'.'+fileType;

    File tempFile = new File(path, String.valueOf(saveName));
    if (!tempFile.getParentFile().exists()) {    //创建文件夹
        tempFile.getParentFile().mkdir();
    }
    if (!tempFile.exists()) {
        tempFile.createNewFile();
    }
    file.transferTo(tempFile);
    result.put("fileName",fileName);
    result.put("saveName",saveName);
    return result;
}

 截取缩略图的方法

//参数:视频路径和缩略图保存路径
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 length = ff.getLengthInFrames();
    int i = 0;
    Frame f = null;
    while (i < length) {
        // 去掉前5帧,避免出现全黑的图片,依自己情况而定
        f = ff.grabImage();
        if ((i > 5) && (f.image != null)) {
            break;
        }
        i++;
    }
    ImageIO.write(FrameToBufferedImage(f), "jpg", targetFile);
    //ff.flush();
    ff.stop();
    System.out.println(System.currentTimeMillis() - start);
}

public static BufferedImage FrameToBufferedImage(Frame frame) {
    //创建BufferedImage对象
    Java2DFrameConverter converter = new Java2DFrameConverter();
    BufferedImage bufferedImage = converter.getBufferedImage(frame);
    return bufferedImage;
}

 

至此,一个视频文件上传后成功生成视频的缩略图

还有很多其他问题,日后再来总结

 

 

  • 5
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

豆趣编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值