视频上传 获取视频的第一帧 截取视频第一帧

4 篇文章 0 订阅
2 篇文章 0 订阅

一. 前端代码

   前端使用layui框架实现视频文件上传。

1.1 html展示

editGoods.html

 <div class="layui-form-item" id="backgBg_div">
	  <label class="layui-form-label">视频上传:</label>
      <div class="img-list-upload fl mt10" id="bannerImagePicture">
          <img src="../../../../static/images/add_upload_img.png" class="img-list-icon" alt="">
          <div id="bannerImagephoto" class="img-list-letter">上传视频</div>
      </div>

      <div class="layui-form-text fl">
          <div class="layui-upload">
              <blockquote class="layui-elem-quote layui-quote-nm preview-img">
                  <div class="layui-upload-list" id="uploadlist2"></div>
              </blockquote>
          </div>
      </div>
      <div id="imgweiPathBanner"></div>
      <div style="color: #999999;font-size: 12px;margin-left: 200px">
          <div style="height: 30px;line-height: 30px">视频上传要求:</div>
          <div>1,视频大小不大于5M,视频格式仅支持mp4格式。</div>
          <div>2,只允许上传一条视频,且默认显示在第一位。</div>
          <div>3,时长暂时无限制,大小小于5M即可(暂不支持后台视频预览)。</div>
      </div>

  </div>

1.2 js逻辑

editGoods.js

    //上传视频
    base.updateVideo('#bannerImagePicture', '#uploadlist2', '#imgweiPathBanner', 2,base.apiVipMall() + 
    '/documents/saveVideo',92,92,'../../../../static/plugin/layui/img/delete.svg');

base.js通用脚本位置

    /**
     * 按照顺序多图片上传修改默认图片长宽
     * @param a     	点击按钮的Id标识
     * @param b     	图片展示的Id
     * @param c    		图片链接存储位置 隐藏域的Id
     * @param d     	传入的数字,用来区分不同类型的图片(同一页面不重复即可)
     * @param url   	文件上传服务器接口
     * @param height 	缩略图页面展示高度
     * @param width  	缩略图页面展示宽度
     * @param imgUrl	删除按钮图片
     */
      //上传视频
    updateVideo:function(a, b, c, d,url,height,width,imgUrl) {
	    var fileSizeLimit = ''; //文件大小限制  默认清空操作
	    upload.render({
	        elem:a,
	        url: base.apiVipMall() + '/documents/saveVideo',
	        // field: "layuiVideo",
	        // data: {"dir": "media"},
	        accept: 'video', //视频
	        exts: 'mp4',//上传视频支持的格式
	        // exts: 'mp4|avi|rmvb|mkv',//上传视频支持的格式
	        headers: {
	            'xxl_sso_sessionid': window.sessionStorage.getItem('sessionid')
	        },
	        choose: function (obj) {   //上传前选择回调方法
	            obj.preview(function (index, file, result) {
	                fileSizeLimit = file.size; //上传视频的大小
	            })
	        },
	        done: function (res, index) {
	            var sign = d + "_imgsign" + index;
	            //如果上传
	            if (res.code != 200) {
	                return layer.msg('上传失败');
	            }
	            if(fileSizeLimit > 1024*1024*5){
	                var sign = $(b).children("div").attr("sign");  //移除显示图片
	                $("[sign='" + sign + "']").remove()
	                $(c).html('');
	                layer.msg('文件大小不得超过5M,请重新上传', {icon: 2});
	                return
	            }
	            var str = '<div sign="' + sign + '" style="width:'+width+"px"+';height:'+height+"px"+';margin-left: 5px;
	            float:left; margin-right: 5px;margin-bottom: 35px;"><img src="' + res.firstFrame.filePath + '" name="videoImg"
	             class="layui-upload-img" width='+width+"px"+' height='+height+"px"+'  sign="' + sign + '" ><div class="showdiv">
	             <img onclick="delimg(this)"  sign="' + sign + '" class="center" src="'+imgUrl+'" /></div></div>';
	            $(b).html(str);
	            //上传成功
	            var videoValue = "videoValue_" + d;
	            var input = "<input type='hidden' name=" + videoValue + " class='weiPic' sign=" + sign + " 
	            value=" + res.result.filePath + " />"
	            $(c).html(input);
	        } ,
	        error: function () {
	            if(fileSizeLimit > 1024*1024*5){
	                var sign = $(b).children("div").attr("sign");  //移除显示图片
	                $("[sign='" + sign + "']").remove();
	                $(c).html('');
	                layer.msg('文件大小不得超过5M,请重新上传', {icon: 2});
	                return
	            }
	        }
	    })

},

二. 服务端业务

   服务端使用spring boot 框架,引用视频处理依赖

	<!--start:视频处理依赖-->
	<dependency>
		<groupId>org.bytedeco</groupId>
		<artifactId>javacv</artifactId>
		<version>1.4.3</version>
	</dependency>

	<dependency>
		<groupId>org.bytedeco.javacpp-presets</groupId>
		<artifactId>ffmpeg-platform</artifactId>
		<version>4.0.2-1.4.3</version>
	</dependency>
	<!--end:视频处理依赖--> 

2.1 Controller层

    @Autowired
    private SaveFileUtil saveFileUtil;

    @CrossOrigin(methods = RequestMethod.POST, origins = "*")
    @PostMapping(value = "/saveVideo", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public Map<String,Object> saveVideo(MultipartFile file){
        Map<String,Object> result= Maps.newHashMap();
        saveFileUtil.saveFile(file, result, true);
        return result;
    }

2.2 util 工具 主逻辑

package com.xlcloud.business.xxx.util.document;

import com.xlcloud.business.vip.util.exceptions.BusinessException;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;

/**
 * @author charles
 * @createTime 2020/8/25 15:07
 * @description 视频帧处理工具
 */
public class VideoFrameUtil  {

    private static final Logger logger = LoggerFactory.getLogger(VideoFrameUtil.class);

    //视频文件路径:
    public static String videoPath = "D:/video";


    /**
     * @author charles
     * @createTime 2020/8/26 10:18
     * @desc 通过视频流  获取视频的第一帧
     */
    public static InputStream grabberVideoFramer(InputStream inputStream) throws IOException{
        FFmpegFrameGrabber fFmpegFrameGrabber = new FFmpegFrameGrabber(inputStream);
        return grabberVideoFramer(fFmpegFrameGrabber);
    }


    /**
     * @author charles
     * @createTime 2020/8/27 9:12
     * @desc 获取本地视频文件 截取第一帧
     */
    public static InputStream grabberVideoFramer(String videoFileName) throws IOException {
        FFmpegFrameGrabber fFmpegFrameGrabber = new FFmpegFrameGrabber(videoPath + "/" + videoFileName);
        return grabberVideoFramer(fFmpegFrameGrabber);
    }

    /**
     * @author charles
     * @createTime 2020/8/27 9:14
     * @desc 获取视频时长
     */
    public static double videoTimeLength(FFmpegFrameGrabber fFmpegFrameGrabber){
        int ftp = fFmpegFrameGrabber.getLengthInFrames();
        return ftp / fFmpegFrameGrabber.getFrameRate();
    }


    /**
     * @author charles
     * @createTime 2020/8/26 10:19
     * @desc 获取视频的第一帧
     */
    private static InputStream grabberVideoFramer(FFmpegFrameGrabber fFmpegFrameGrabber) throws IOException{
        fFmpegFrameGrabber.start();
        int ftp = fFmpegFrameGrabber.getLengthInFrames();
        if (ftp > 0){
            // 获取一帧
            Frame frame = fFmpegFrameGrabber.grabImage();
            BufferedImage bufferedImage = FrameToBufferedImage(frame);
            // 图片输出流
            InputStream inputStream = bufferedImageToInputStream(bufferedImage);
            fFmpegFrameGrabber.stop();
            return inputStream;
        }
        return null;
    }


    /**
     * @author charles
     * @createTime 2020/8/27 8:54
     * @desc 本地测试截取任意帧
     */
    public static void grabberVideoFramerLocal(String videoFileName, int frameNum) throws IOException{
        FFmpegFrameGrabber fFmpegFrameGrabber = new FFmpegFrameGrabber(videoPath + "/" + videoFileName);
        fFmpegFrameGrabber.start();
        double length = videoTimeLength(fFmpegFrameGrabber);
        if (length < frameNum){
            throw new BusinessException("截取的视频帧不能超过视频总帧数!");
        }
        //Frame对象
        Frame frame = null;
        // 输出视频的每一帧
        for (int i = 1; i <= length; i++) {
            //获取帧
            frame = fFmpegFrameGrabber.grabImage();
            if (i == frameNum){
                //文件绝对路径+名字
                String fileName = "D:/video/img_" + i + ".png";
                //文件储存对象
                File outPut = new File(fileName);
                if (frame != null) {
                    ImageIO.write(FrameToBufferedImage(frame), "png", outPut);
                }
            }
        }
        fFmpegFrameGrabber.stop();
    }


    /**
     * @author charles
     * @createTime 2020/8/26 10:32
     * @desc 转换对象
     */
    public static BufferedImage FrameToBufferedImage(Frame frame) {
        //创建BufferedImage对象
        Java2DFrameConverter converter = new Java2DFrameConverter();
        BufferedImage bufferedImage = converter.getBufferedImage(frame);
        return bufferedImage;
    }


    /**
     * 将BufferedImage转换为InputStream
     * @param image
     * @return
     */
    public static InputStream bufferedImageToInputStream(BufferedImage image){
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        try {
            ImageIO.write(image, "png", os);
            return new ByteArrayInputStream(os.toByteArray());
        } catch (IOException e) {
            logger.error("获取视频帧失败", e);
        }
        return null;
    }

    /**
     * @author charles
     * @createTime 2020/8/27 8:58
     * @desc 测试
     */
    public static void main(String[] args) throws IOException {
        // 获取视频的第一帧测试
        String videoFileName = "1586490_A.mp4";
//        InputStream inputStream = grabberVideoFramer(videoFileName);
//        String test = new SaveFileUtil().saveImg(inputStream, "test11111.png");
//        System.out.println(test);
        // 获取视频的任意帧
        grabberVideoFramerLocal(videoFileName, 10);
    }
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值