FFmpegFrameGrabber视频抽帧工具类

news2024/11/16 23:59:06

Bytedeco

通过视频链接进行关键帧抽取图片,利用FFmpegFrameGrabber对视频流进行抽帧处理。

一、引入POM依赖

        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv</artifactId>
            <version>1.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.bytedeco.javacpp-presets</groupId>
            <artifactId>ffmpeg-platform</artifactId>
            <version>3.4.2-1.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacpp</artifactId>
            <version>1.5.8</version>
        </dependency>

二、抽帧代码实现

import lombok.extern.slf4j.Slf4j;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * 视频抽帧工具
 */
@Slf4j
public class VideoFrame {

    public static Map<Integer, InputStream> process(String videoUrl, Integer stepSecond, Integer count, String uuid, String ipProxy) {

        Map<Integer, InputStream> result = null;
        int num = 0;
        while (num < 2) {
            log.info("开始抽帧");
            result = videoUrlIntercept(videoUrl, stepSecond, count, uuid, ipProxy);
            if (result.size() > 0) {
                log.info("uuid:{},第{}次抽帧成功", uuid, num + 1);
                break;
            } else {
                //第一次使用ipProxy不成功就更换
                log.info("uuid:{},第{}次抽帧保存失败,ipProxy:{}", uuid, num + 1, ipProxy);
                ipProxy = null;
                num++;
            }
        }
        return result;
    }

    /**
     * 视频文件边下载边抽帧1秒1帧
     *
     * @param videoUrl   网络视频文件URL
     * @param stepSecond 每隔几秒取一帧,默认1s
     * @param count      需要截取的帧个数
     * @param uuid       uuid
     * @return
     */
    public static Map<Integer, InputStream> videoUrlIntercept(String videoUrl, Integer stepSecond, Integer count, String uuid, String ipProxy) {

        Map<Integer, InputStream> files = new HashMap<>();
        stepSecond = stepSecond == null ? 1 : stepSecond;

        FFmpegFrameGrabber ff = new FFmpegFrameGrabber(videoUrl);
        // 设置超时时间为40秒
        ff.setOption("timeout", "40000000");

        ff.setOption("user_agent", UserAgent.getUserAgent());

        try {
            ff.start();
            long timeLength = ff.getLengthInTime();
            Frame frame = ff.grabImage();
            long startTime = frame.timestamp;
            long timestamp = 0;
            int second = 0;
            int picNum = 0;
            while (timestamp <= timeLength) {
                log.info("uuid:{},抽取第{}帧,video_url:{}",uuid,picNum,videoUrl);
                timestamp = startTime + second * 1000000L;
                ff.setTimestamp(timestamp);
                frame = ff.grabImage();
                if (frame != null) {
                    if (frame.image != null) {
                        InputStream inputStream = doExecuteFrame(frame, picNum);
                        if (inputStream != null) {
                            files.put(picNum, inputStream);
                        }
                        picNum++;
                        if (count != null && picNum == count) {
                            break;
                        }
                    }
                }
                second += stepSecond;
                if(picNum > 60) {
                    break;
                }
            }
            ff.stop();

        } catch (Exception e) {
            log.error("下载抽帧失败,uuid:{},ipPort:{},videoUrl:{},msg:{}", uuid, null, videoUrl, e.getMessage());
            e.printStackTrace();
        }

        return files;
    }

    /**
     * 视频文件指定时间段的帧截取
     *
     * @param videoUrl  视频文件URL
     * @param start     视频开始的帧
     * @param count     需要截取的帧个数
     * @param isAvgTime 在截帧时 是否均匀分布计算时间
     * @return
     */
    public static Map<Integer, InputStream> videoIntercept(String videoUrl, int start, int count, boolean isAvgTime) {
        log.info("开始抽取视频帧数,videoUrl:{}",videoUrl);
        Frame frame = null;
        //<时间, 图片流>
        Map<Integer, InputStream> files = new HashMap<>();
        FFmpegFrameGrabber fFmpegFrameGrabber = new FFmpegFrameGrabber(videoUrl);
        fFmpegFrameGrabber.setOption("timeout", "40000000");

        try {
            fFmpegFrameGrabber.start();
            long frameTime = 1;
            if (isAvgTime) {
                frameTime = fFmpegFrameGrabber.getLengthInTime() / count / 1000000L;
                if (frameTime < 0) {
                    frameTime = 1;
                }
            }
            for (int i = start; i <= count; i++) {
                fFmpegFrameGrabber.setTimestamp(i * frameTime * 1000 * 1000);
                frame = fFmpegFrameGrabber.grabImage();
                InputStream inputStream = doExecuteFrame(frame, i);
                if (inputStream != null) {
                    files.put(i, inputStream);
                }
            }
            fFmpegFrameGrabber.stop();
        } catch (Exception E) {
            log.info("下载的视频抽帧失败,msg:" + E.getMessage());
            E.printStackTrace();
        }
        return files;
    }

    public static InputStream doExecuteFrame(Frame frame, int index) {
        if (frame == null || frame.image == null) {
            return null;
        }
        Java2DFrameConverter converter = new Java2DFrameConverter();
        BufferedImage bi = converter.getBufferedImage(frame);
        InputStream inputStream = bufferedImageToInputStream(bi);

        return inputStream;
    }

    public static InputStream bufferedImageToInputStream(BufferedImage image) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        try {
            ImageIO.write(image, "jpg", os);
            InputStream input = new ByteArrayInputStream(os.toByteArray());
            return input;
        } catch (IOException e) {
        }
        return null;
    }

    public static void main(String[] args) throws IOException {
        String videoUrl = "http://vd2.bdstatic.com/mda-pej1ztfufz8axvtu/360p/h264/1684545921389774683/mda-pej1ztfufz8axvtu.mp4";
        Map<Integer, InputStream> integerInputStreamMap = videoIntercept(videoUrl, 1, 13, false);
        System.out.println(integerInputStreamMap.size());
        for (Integer seconds : integerInputStreamMap.keySet()) {
            InputStream inputStream = integerInputStreamMap.get(seconds);
            String fileName = MD5Util.createMd5(System.currentTimeMillis()+"抖音测试3" + "_" + seconds) + ".jpg";
            String filePath = "/test/01/"+fileName;
            //本地磁盘存储
            String uploadURL = PictureDownload.downloadFrame(fileName, "D:/image" + filePath, inputStream, "douyin", true);

            System.out.println("seconds: " + seconds + ", uploadURL: " + uploadURL);
        }

    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/707560.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TCP 拥塞状态机演进

下面是 TCP 拥塞状态机&#xff1a; 但它只是冰山一角&#xff0c;这只是 loss-based 状态机&#xff0c;实现一个完全的 delay-based cc 就对不上这个状态机。 该状态机来自 RFC5681&#xff0c;源自 RFC2581&#xff0c;RFC2001&#xff0c;大概在 1990 年代&#xff0c;l…

10分钟内创意爆发,这些头脑风暴技巧让你IDEA满满

当初道叔刚入广告行业&#xff0c;与同事经历了一次困扰的头脑风暴&#xff0c;老板让他们想出一个大集团公司年会的主题口号。我们7-8个团队成员耗费了一个下午的时间&#xff0c;提出了几十个提议&#xff0c;但最终硬是没有一个能满足需求。许多人可能也有过道叔这样的经历。…

Js保留树型数据指定层级

自定义一个树型的数据 const tree [{value: 1, label: "1",children: [{value: 11, label: "1-1",children: [{value: 111, label: "1-1-1"}]}]}, {value: 2, label: "2"}]保留指定层级的方法 function keepNodesAtLevel(data, level…

vue源码阅读之Watcher类

我们上次分析vue源码讲的是收集依赖&#xff0c;数据变化之后我们把依赖收集到dep类中&#xff0c;通过这个管理器进行管理。 里面有一个subs数组&#xff0c;用来存放依赖&#xff0c;并且定义了几个实例方法用来依赖进行添加&#xff0c;删除&#xff0c;通过操作。 比如ad…

mysql基础5——mysql主从

文章目录 一、基本了解二、主从原理三、主从复制3.1 从无到有3.1.1 服务器初始化3.1.2 配置主库3.1.3 配置从库3.1.4 效果验证 3.2 从有到无3.2.1 主库全备&#xff0c;并同步到从库3.2.2 配置主库3.2.3 配置从库3.2.4 效果验证 四、数据库运维4.1 几个参数4.2 查看进程列表 一…

【案例实战】高并发业务的多级缓存架构一致性解决方案

我们在高并发的项目中基本上都离不开缓存&#xff0c;那么既然引入缓存&#xff0c;那就会有一个缓存与数据库数据一致性的问题。 首先&#xff0c;我们先来看看高并发项目里面Redis常见的三种缓存读写模式。 Cache Aside 读写分离模式&#xff0c;是最常见的Redis缓存模式&a…

树莓派教程2023

摘要&#xff1a;本教程只适用于最新版树莓派64位系统&#xff08;debian11&#xff09; 树莓派官网&#xff08;之前是org&#xff09;&#xff1a; https://www.raspberrypi.com/ 一.烧录系统 1.官网下载官方烧写器 https://downloads.raspberrypi.org/imager/imager_late…

部署Redis到云服务器

购买某服务商的云服务器 在云服务器中设置TCP端口号 配置云服务器的开放端口 通过telneat测试端口是否可用 配置redis服务器 需要配置一下自己云服务器的ip 保护模式关闭、允许其他远端连接 设置和云服务器开放一致的端口号 打开此项设置redis的连接密码&#xff0c;更加…

一、计算复杂度

补充说明&#xff1a; 1、决定性问题&#xff1a; 一个决定性问题(decision problem)是指其输出&#xff0c;只有“是”或“否”的问题。 一、P问题&#xff08;polynomial time class&#xff09; 1、问题描述&#xff1a; 可于确定型图灵机以多项式时间求解的决定性问题…

STM32学习之keil5环境搭建

1.keil5是开发ARM内核芯片最常用的集成开发环境IDE。STM32开发环境的搭建主要分为两步&#xff1a; 第一步&#xff0c;安装Kile5.到keil官网下载&#xff1a;Keil Embedded Development Tools for Arm, Cortex-M, Cortex-R4, 8051, C166, and 251 processor families. 第二步…

“SRP模型+”多技术融合在生态环境脆弱性评价模型构建、时空格局演变分析与RSEI指数的生态质量评价及拓展应用

第一章 生态环境脆弱性评价内涵及基于文献可视化方法研究热点分析 一、基本原理 生态环境脆弱性内涵 生态环境脆弱性评价研究 生态环境脆弱性驱动力研究 生态环境脆弱性研究热点 二、文献可视化软件介绍及常用功能介绍 VOS Viewer文献可视化及研究热点分析&#xff…

工业物联网时代需要什么样的数据采集设备

引言&#xff1a; 随着物联网技术的飞速发展&#xff0c;数据成为推动企业决策和创新的核心资源。然而&#xff0c;要实现高效的数据收集和分析&#xff0c;需要可靠的数据采集设备。本文将探讨物联网时代对数据采集设备的需求&#xff0c;并介绍一款优秀的工业边缘网关产品——…

软考A计划-系统集成项目管理工程师-项目范围管理(三)

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff…

DevOps系列文章之 DevOps 运维服务体系

DevOps 体系是从原始运维一步步走过来的&#xff0c;原始运维好比是本&#xff0c;有了本进而想继续提升效率、减少出错、优化流程&#xff0c;就发展到了 DevOps&#xff0c;AIOps……各种Ops 首先&#xff0c;运维的业务职能规范后形成章程、纲领&#xff0c;在互联网快速发…

flutter手写一个底部导航栏

使用了一下flutter提供的导航栏&#xff0c;NavigationBar&#xff0c;不过感觉使用起来不是很方便。 譬如说&#xff1a; 不能直接使用图片资源&#xff0c;需要中间加几层转换把图片转化成icon文本大小及颜色不太好控制状态栏的上边来一个横线也没有相应的样式&#xff0c;等…

对 tcp out-of-window 的安全建议

TCP 收到一个 out of window 报文后会立即回复一个 ack&#xff0c;这是 RFC793 中 SEGMENT ARRIVES 段的要求。但这是为什么&#xff1f;难道不是默默丢弃才对吗&#xff1f; 对 oow 报文回复 ack&#xff0c;岂不是把正确的 ack 号回过去了吗&#xff0c;这样攻击者盲打一番…

设计模式篇---建造者模式

文章目录 概念结构实例总结 概念 建造者模式的定义&#xff1a;将一个复杂对象的构建与它的表示分离&#xff0c;使得同样的构造过程可以创建不同的表示。 这么看起来有点难以理解&#xff0c;翻译一下就是 用户无需关注创建复杂对象的过程&#xff0c;只需要指定创建的对象即…

一文带你了解云HIS

一、简介 基于云计算技术的B/S架构的HIS系统&#xff0c;为基层医院机构提供标准化的、信息化的、可共享的医疗信息管理系统&#xff0c;实现医患事务管理和临床诊疗管理等标准管理信息系统的功能。系统利用健康云计算平台的技术优势&#xff0c;建立统一的健康档案存储平台&am…

spark调试中常见的错误集锦

示例一、没有引入jar错误 错误描述&#xff1a; 以spark在pyspark环境解析xml为例 spark核心包不支持解析xml&#xff0c;所以需要引入引用依赖包配置为: config("spark.jars.packages", "com.databricks:spark-xml_2.12:0.16.0") 在spark-submit部署提交…

ctfshow_反序化漏洞

web254 首先进行一个代码审计 <?php error_reporting(0); highlight_file(__FILE__); include(flag.php);class ctfShowUser{public $usernamexxxxxx;public $passwordxxxxxx;public $isVipfalse;public function checkVip(){return $this->isVip;}public function logi…