温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

JavaCV如何实现读取视频信息及自动截取封面图

发布时间:2022-06-08 10:35:16 来源:亿速云 阅读:201 作者:iii 栏目:开发技术

这篇文章主要介绍了JavaCV如何实现读取视频信息及自动截取封面图的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇JavaCV如何实现读取视频信息及自动截取封面图文章都会有所收获,下面我们一起来看看吧。

    javacv 介绍

    javacv可以帮助我们在java中很方便的使用 OpenCV 以及 FFmpeg 相关的功能接口

    项目地址

    引入 javacv

            <dependency>
                <groupId>org.bytedeco</groupId>
                <artifactId>javacv-platform</artifactId>
                <version>${javacv.version}</version>
            </dependency>

    读取视频信息

    创建 VideoInfo 类

    package com.buguagaoshu.porntube.vo;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import lombok.Getter;
    import lombok.Setter;
    
    /**
     * @author Pu Zhiwei {@literal puzhiweipuzhiwei@foxmail.com}
     * create          2022-06-06 19:15
     */
    @Getter
    @Setter
    public class VideoInfo {
        /**
         * 总帧数
         **/
        private int lengthInFrames;
    
        /**
         * 帧率
         **/
        private double frameRate;
    
        /**
         * 时长
         **/
        private double duration;
    
        /**
         * 视频编码
         */
        private String videoCode;
        /**
         * 音频编码
         */
        private String audioCode;
    
        private int width;
        private int height;
        private int audioChannel;
        private String md5;
        /**
         * 音频采样率
         */
        private Integer sampleRate;
    
        public String toJson() {
            try {
                ObjectMapper objectMapper = new ObjectMapper();
                return objectMapper.writeValueAsString(this);
            } catch (Exception e) {
                return "";
            }
        }
    }

    使用 FFmpegFrameGrabber 读取视频信息

     public static VideoInfo getVideoInfo(File file) {
            VideoInfo videoInfo = new VideoInfo();
            FFmpegFrameGrabber grabber = null;
            try {
                grabber = new FFmpegFrameGrabber(file);
                // 启动 FFmpeg
                grabber.start();
    
                // 读取视频帧数
                videoInfo.setLengthInFrames(grabber.getLengthInVideoFrames());
    
    			// 读取视频帧率
                videoInfo.setFrameRate(grabber.getVideoFrameRate());
    
                // 读取视频秒数
                videoInfo.setDuration(grabber.getLengthInTime() / 1000000.00);
                
                // 读取视频宽度
                videoInfo.setWidth(grabber.getImageWidth());
    
                // 读取视频高度
                videoInfo.setHeight(grabber.getImageHeight());
    
                
                videoInfo.setAudioChannel(grabber.getAudioChannels());
    
                videoInfo.setVideoCode(grabber.getVideoCodecName());
    
                videoInfo.setAudioCode(grabber.getAudioCodecName());
                // String md5 = MD5Util.getMD5ByInputStream(new FileInputStream(file));
    
                videoInfo.setSampleRate(grabber.getSampleRate());
                return videoInfo;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            } finally {
                try {
                    if (grabber != null) {
                        // 此处代码非常重要,如果没有,可能造成 FFmpeg 无法关闭
                        grabber.stop();
                        grabber.release();
                    }
                } catch (FFmpegFrameGrabber.Exception e) {
                    log.error("getVideoInfo grabber.release failed 获取文件信息失败:{}", e.getMessage());
                }
            }
        }

    截图

    读取信息没有什么难度,但是在对视频截图的过程中,出现了一些问题,在我查找截图实现的代码时,大多数的代码都是这么写的

        /**
         * 获取视频缩略图
         * @param filePath:视频路径
         * @param mod:视频长度/mod获取第几帧
         * @throws Exception
         */
        public static String randomGrabberFFmpegImage(String filePath, int mod) {
            String targetFilePath = "";
            try{
                FFmpegFrameGrabber ff = FFmpegFrameGrabber.createDefault(filePath);
                ff.start();
                //图片位置是否正确
                String rotate = ff.getVideoMetadata(ROTATE);
                //获取帧数
                int ffLength = ff.getLengthInFrames();
                Frame f;
                int i = 0;
                //设置截取帧数
                int index = ffLength / mod;
                while (i < ffLength) {
                    f = ff.grabImage();
                    if(i == index){
                        if (null != rotate && rotate.length() > 1) {
                            OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage();
                            IplImage src = converter.convert(f);
                            f = converter.convert(rotate(src, Integer.parseInt(rotate)));
                        }
                        targetFilePath = getImagePath(filePath, i);
                        doExecuteFrame(f, targetFilePath);
                        break;
                    }
                    i++;
                }
                ff.stop();
            }catch (Exception e){
                log.error("获取视频缩略图异常:" + e.getMessage());
            }
            return targetFilePath;
        }

    这样写本身没有什么问题,但是在获取需要截取帧数的部分,使用的是通过循环来一帧一帧的判断,这样在视频较短的时候没有什么问题,但是如果视频较长,就会出现严重的性能问题。

      while (i < ffLength) {
                    f = ff.grabImage();
                    if(i == index){
    					......
                        break;
                    }
                    i++;
                }

    FFmpeg 的命令行参数有一个 -ss 的参数,使用 -ss 可以快速的帮助我们跳到视频的指定位置,完成操作,不用一帧一帧的判断。

    所以现在的问题就是如何在 javacv 中实现 -ss 参数

    我在 javacv 的 GitHub Issues 中发现了这个操作,即使用 setTimestamp() 方法,使用 setTimestamp() 方法可以使 FFmpeg 跳转到指定时间,完成截图,于是,最后的截图代码就变成了这样

      /**
         * 随机获取视频截图
         * @param videFile 视频文件
         * @param count 输出截图数量
         * @return 截图列表
         * */
        public static List<FileTableEntity> randomGrabberFFmpegImage(File videFile, int count, long userId) {
            FFmpegFrameGrabber grabber = null;
    
            String path = FileTypeEnum.filePath();
            try {
                List<FileTableEntity> images = new ArrayList<>(count);
                grabber = new FFmpegFrameGrabber(videFile);
                grabber.start();
                // 获取视频总帧数
                // int lengthInVideoFrames = grabber.getLengthInVideoFrames();
                // 获取视频时长, / 1000000 将单位转换为秒
                long delayedTime = grabber.getLengthInTime() / 1000000;
    
                Random random = new Random();
                for (int i = 0; i < count; i++) {
                    // 跳转到响应时间
                    grabber.setTimestamp((random.nextInt((int)delayedTime - 1) + 1) * 1000000L);
                    Frame f = grabber.grabImage();
                    Java2DFrameConverter converter = new Java2DFrameConverter();
                    BufferedImage bi = converter.getBufferedImage(f);
                    String imageName = FileTypeEnum.newFilename(SUFFIX);
                    File out = Paths.get(path, imageName).toFile();
                    ImageIO.write(bi, "jpg", out);
                    FileTableEntity fileTable = FileUtils.createFileTableEntity(imageName, SUFFIX, path, f.image.length, "系统生成截图", userId, FileTypeEnum.VIDEO_PHOTO.getCode());
                    images.add(fileTable);
                }
                return images;
            } catch (Exception e) {
                return null;
            } finally {
                try {
                    if (grabber != null) {
                        grabber.stop();
                        grabber.release();
                    }
                } catch (FFmpegFrameGrabber.Exception e) {
                    log.error("getVideoInfo grabber.release failed 获取文件信息失败:{}", e.getMessage());
                }
            }
        }

    这样我们就能快速的实现截图了。

    JavaCV如何实现读取视频信息及自动截取封面图

    关于“JavaCV如何实现读取视频信息及自动截取封面图”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“JavaCV如何实现读取视频信息及自动截取封面图”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注亿速云行业资讯频道。

    向AI问一下细节

    免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

    AI