您现在的位置是:首页 > 正文

FFmpegFrameGrabber视频抽帧工具类

2024-04-01 04:54:04阅读 2

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);
        }

    }
}

网站文章

  • 各种浏览器的缓存文件的位置和修改缓存目录的方法 热门推荐

    一、手动清空缓存 1、Opera 清除Opera浏览器的缓存的方法:打开Opera,工具-&gt;首选项-&gt;高级-&gt;历史-&gt;点击立即清空按钮就可以清除Opera浏览器的缓存。 2、I...

    2024-04-01 04:53:59
  • 一、需求测试-需求规格说明书规范(测试方向)

    有测试的小伙伴不太清楚需求测试的范围及规范,整理了下,希望可以帮到大家。需求规格说明书应该遵循的原则:1.功能和实现分离,即描述要实现的结果结果,不要描述实现的过程2.要求使用面向处理的规格说明语言,...

    2024-04-01 04:53:52
  • 关于Vue Element组件el-checkbox与el-select默认选中值的几点注意事项

    关于Vue Element组件el-checkbox与el-select默认选中值的几点注意事项

    el-select 示例: 代码: &amp;l

    2024-04-01 04:53:13
  • 从泊松方程到泊松融合(Poisson Matting) 热门推荐

    数学无疑是现代数字图像处理技术的一个重要基石,一些效果显著的同时也非常popular的图像处理技术中大量地借鉴和利用了经典数学理论中的一些著名的成果。泊松方程(Poisson Equation)在泊松图像编辑(Poisson Image Editing)以及泊松融合(Poisson Matting)中的应用就是一个典型的例子

    2024-04-01 04:53:06
  • 【线代】矩阵转置性质及代码证明

    矩阵转置 **定义:**把矩阵A的行换成同序列数的列得到一个新矩阵,叫做A的转置矩阵 ,记作ATA^TAT 矩阵转置的性质 (AT)T=A(A^T)^T=A(AT)T=A import numpy as np A = np.random.randint(0, 100, [3, 3]) print((A.T).T == A) [OUT]: [[ True True True] [ Tr...

    2024-04-01 04:52:58
  • Ubuntu 非root 权限(普通用户)安装OpenCV 2.4.9

          实验室GPU服务器一般使用者是没有Root权限的,也就是只是普通用户权限。我们实验室服务器安装的Opencv都是最新的OpenCV 3.2版本,而我要跑的程序(GPU提取视频光流特征)是依...

    2024-04-01 04:52:16
  • 面向对象知识点(3)

    面向对象知识点(3)

    一. 类的继承 1. 继承的概念 在现实生活中,继承一般是指子女继承父辈的财产。在程序中,继承描述的是事物之间的所属关系,通过继承可以使多种事物之间形成一种关系体系。 在Java中,类的继承是指在一个...

    2024-04-01 04:52:08
  • node----模块的加载机制

    使用require()加载自定义模块时,必须指定./或…/开头的路径标识符。在加载自定义模块时,如果没有指定./或…/这样的路径标识符,则node会把它当作内置模块或第三方模块进行加载。按照确切的文件名进行加载补全.js扩展名进行加载补全.json扩展名进行加载补全.node扩展名进行加载加载失败,终端报错。

    2024-04-01 04:52:00
  • JavaScript---随机点名小案例

    【代码】JavaScript---随机点名小案例。

    2024-04-01 04:51:53
  • HTTP首部(3)

    HTTP首部(3)

    1、响应首部字段  响应首部字段是由服务器端向客户端返回响应报文中所使用的字段,用于补充响应的附加信息、服务器信息以及对客户端的附加要求等。  1.1、Accept-Range    Accept-Range:告知服务器是否能处理范围请求,以指定获取服务器端某个部分的资源,值有两种:bytes和none。  1.2、age    Age:告知客户端,源服务器多久前穿件...

    2024-04-01 04:51:12