视频抽帧实现

这篇具有很好参考价值的文章主要介绍了视频抽帧实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

视频抽帧

一、基本概念理解

一个视频由视频帧构成,每一帧在肉眼可见是一张图片成像

1、视频帧

帧的类型:

帧的类型主要参考 视频抽帧处理

  • I帧,Intra Picture,内编码帧,也就是关键帧。拥有完整的图像信息。I帧不需要依赖前后帧信息,可独立进行解码。
    P帧,predictive-frame,前向预测编码帧。P帧需要依赖前面的I帧或者P帧才能进行编解码,因为他存储的是当前帧画面与前一帧的差别,专业的说法是压缩了时间冗余信息,或者说提取了运动特性
  • B帧,bi-directional interpolatedprediction frame,双向预测内插编码帧。B帧存储的是本帧与前后帧的差别,因此带有B帧的视频在解码时的逻辑会更复杂些,CPU开销会更大
  • IDR帧,Instantaneous Decoding Refresh,即时解码刷新。IDR帧是一种特殊的I帧,它是为了服务于编解码而提出的概念,IDR帧的作用是立刻刷新,使错误不致传播,从IDR帧开始,重新算一个新的序列开始编码。
2、名词解释
  • 视频封装格式:相当于视频的后缀格式,mp4 mov mpeg,封装格式与视频编码格式无关,视频封装格式是视频播放软件为了能够播放视频文件而赋予视频文件的一种识别符号
  • 视频编码:对视频进行压缩的程序或设备,常见的编码方式有H.26x系列,MPEG系列

电脑中保存的视频文件实际经过了编码压缩,实际上修改视频文件的后缀,实际对视频本身并没有影响,后缀只是封装格式,视频本身压缩的信息是由编码格式决定。
视频原始流 -> 视频编码方式 -> 视频封装格式

  • 帧率:每秒所含帧数,现在视频帧率一般在22-25之间,也就是每秒所含帧数在22-25之间
  • 码率:视频文件在单位时间内使用的数据流量,它决定了视频的质量和大小,在实际应用中,还需要硬件的处理能力、带宽条件进行选择
  • 分辨率:也就是我们常说的1080p,决定图像显示的大小
  • 三者关系
    分辨率一定时,如果帧率越高,那么每秒的帧数越高,要承载的数据量越高,即码率越高;码率一定时,如果帧率越高,为了保证码率的承载数据量,每帧画面的数据量也就需要压缩,导致画面清晰度降低;帧率也就是每秒包含的帧数,当中包含三种帧 IPB (关键帧、前向预测帧、双向预测内插编码帧),其中I帧承载的数据量最大;关键帧的设置是可由客户端(如pr软件)设置的,导出视频时进行编码压缩,写入文件进行保存

二、FFmpeg视频抽帧实践

也就是说实际抽帧过程,并不是每一帧都是独立的个体,这还取决于关键帧的位置。
笔者这里使用java程序语言实现:

1、导入相关依赖

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

2、主要思路:获取视频的总帧数,循环遍历,根据入参帧率和视频帧率的数量关系,对满足一定条件的帧进行保存

 /**
     * 抽帧
     * @param frameRate 帧率
     * @param storePath 截图图片要存放的路径
     * @param filePath 要截图的视频存放路径
     */
    public static List<String> grabFrameByFilePath(double frameRate, String storePath, String filePath) throws Exception {
        File folder = new File(storePath);
        boolean success = true;
        if (!folder.exists() && !folder.isDirectory()) {
             success = folder.mkdirs();
        }
        if (!success){
            throw new FileNotFoundException("文件夹创建异常");
        }
        List<String> sourceFiles = new ArrayList<>();
        FFmpegFrameGrabber fFmpegFrameGrabber = new FFmpegFrameGrabber(filePath);
        fFmpegFrameGrabber.start();

        grabFrames(fFmpegFrameGrabber,frameRate,storePath,sourceFiles);
        fFmpegFrameGrabber.close();
        return sourceFiles;
    }

    private static void grabFrames(FFmpegFrameGrabber fFmpegFrameGrabber, double frameRate, String storePath, List<String> sourceFiles) throws Exception {
        double videoRate = fFmpegFrameGrabber.getFrameRate();
        if (frameRate >= videoRate){
            
            //每一帧都获取
            doGrabPerFrame(fFmpegFrameGrabber,storePath,sourceFiles);
        }else if (frameRate >= ApolloConfig.VIDEO_RATE){
            //逐帧遍历,只获取需要的
            doGrabFramesByTraverseFrames(fFmpegFrameGrabber,frameRate,storePath,sourceFiles,videoRate);
        }else {
            //稍后会说明
            doGrabFramesBySetTimestamp(fFmpegFrameGrabber,frameRate,storePath,sourceFiles,videoRate);
        }
    }
/**
* 产生的所有文件都暂时以本地存储为主
* fFmpegFrameGrabber ffmpeg抽帧工具
* videoRate 视频帧率
* storePath 为统一放置抽帧图片使用
* sourceFiles 抽帧之后图片存放本地位置
* 
*/

void doGrabFramesByTraverseFrames(FFmpegFrameGrabber fFmpegFrameGrabber, double frameRate, String storePath, List<String> sourceFiles, double videoRate) throws FFmpegFrameGrabber.Exception {
        Java2DFrameConverter converter = new Java2DFrameConverter();
        int lengthInVideoFrames = fFmpegFrameGrabber.getLengthInFrames();
        int frameGapCount = (int) Math.ceil(lengthInVideoFrames * frameRate / videoRate);
        int frameGap = lengthInVideoFrames / frameGapCount;

        Frame f;
        String path;
        boolean flag;
        for (int i = 1; i <= frameGapCount; i++){
            flag = false;
            // 每frameGap取1帧
            for (int j = 1; j <= frameGap; j++) {
                f = fFmpegFrameGrabber.grabImage();
                path = String.format("%s/%s", storePath, i + ".jpg");
                doExecuteFrame(f,path,converter);
                if (PicDetectionUtil.checkPicAvailableBySourcePath(path) && !flag){//图片检测,看你实际使用,并不必须
                    sourceFiles.add(path);
                    flag = true;
                }
            }
        }
        //考虑总帧数不能整除情况
        if (lengthInVideoFrames > frameGap * frameGapCount){
            int diff = lengthInVideoFrames - frameGap * frameGapCount;
            while (diff > 0){
                f = fFmpegFrameGrabber.grabImage();
                path = String.format("%s/%s", storePath, frameGapCount + 1 + ".jpg");
                doExecuteFrame(f,path,converter);
                if (PicDetectionUtil.checkPicAvailableBySourcePath(path)){
                    sourceFiles.add(path);
                    break;
                }
                diff--;
            }
        }
        converter.close();
    }

在基于这段代码以及实际情况,输入帧率一般都是1,那么在整个视频时长足够长的情况下,逐帧读取是不是一个效率相对较慢的方法呢

1、获取视频的总帧数,循环遍历,根据入参帧率和视频帧率的数量关系,对满足一定条件的帧进行保存,也就是我一开始实现的方案

2、获取视频的总时长,根据根据入参帧率和视频帧率的数量关系计算出时间间隔,根据时间间隔通过setTimeStamp获取所要保存的帧数

第二种方案实现:

private static void doGrabFramesBySetTimestamp(FFmpegFrameGrabber fFmpegFrameGrabber, double frameRate, String storePath, List<String> sourceFiles, double videoRate) throws FFmpegFrameGrabber.Exception {
        Java2DFrameConverter converter = new Java2DFrameConverter();
        int lengthInVideoFrames = fFmpegFrameGrabber.getLengthInFrames();
        int frameGapCount = (int) Math.ceil(lengthInVideoFrames * frameRate / videoRate);
        long lengthInTime = fFmpegFrameGrabber.getLengthInTime();
        Frame f;
        String path;

        double v = lengthInTime / ( 1.0 * frameGapCount) ;
        int idx = 1;
        for (long i = 1L; i <= lengthInTime; i += Math.ceil(v)){
            fFmpegFrameGrabber.setTimestamp(i);
            f = fFmpegFrameGrabber.grabImage();
            path = String.format("%s/%s", storePath, idx + ".jpg");
            idx++;
            doExecuteFrame(f,path,converter);
            if (PicDetectionUtil.checkPicAvailableBySourcePath(path)){
                sourceFiles.add(path);
            }
        }
        if (idx == frameGapCount){
            fFmpegFrameGrabber.setTimestamp(lengthInTime);
            f = fFmpegFrameGrabber.grabImage();
            path = String.format("%s/%s", storePath, idx + ".jpg");
            doExecuteFrame(f,path,converter);
            if (PicDetectionUtil.checkPicAvailableBySourcePath(path)){
            	//图片检测,不是必须	
                sourceFiles.add(path);
            }
        }
        converter.close();
    }

两种方案的效率,实际与视频的关键帧数相关,如果所需抽帧的视频的关键帧所摆放的位置恰巧每次定位到指定时间戳之后都需要往前溯关键帧的位置,反而效率会不如原先的方案好。

笔者做了一个简单实验,在帧率从1到视频帧率的区间变化,两种方案的耗时对比,实验结果表示在小于等于视频帧率的1/2时,第二种方案更有效率,而帧率超过1/2后,第一种方案更有效率

这个实验还不具有特别大的说服性,笔者最后是通过设置一个阈值来决定抽帧方案

double videoRate = fFmpegFrameGrabber.getFrameRate();
        if (frameRate >= videoRate){
            
            //每一帧都获取
            doGrabPerFrame(fFmpegFrameGrabber,storePath,sourceFiles);
        }else if (frameRate >= ApolloConfig.VIDEO_RATE){
            //逐帧遍历,只获取需要的
            doGrabFramesByTraverseFrames(fFmpegFrameGrabber,frameRate,storePath,sourceFiles,videoRate);
        }else {
            //定位时间戳
            doGrabFramesBySetTimestamp(fFmpegFrameGrabber,frameRate,storePath,sourceFiles,videoRate);
        }

注:如果若刷到这篇博客的其他大佬有对这方面了解,欢迎指点迷津文章来源地址https://www.toymoban.com/news/detail-567755.html

到了这里,关于视频抽帧实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java音视频处理——JavaCV

    目录   简介 Maven 软件环境 JavaCV-Examples OpenCV Cookbook Examples 概述 示例 OpenCV文档 如何使用JavaCV示例 示例代码的组织结构 示例列表 Why Scala? 学习地址 图像简单处理代码示例 1.打开保存一张图  2.画直线 3.画圆圈 4.画折现 5.添加文字水印 6.裁剪并局部放大 7.人脸检测 视频简单处

    2024年02月03日
    浏览(74)
  • 【Webpack】处理字体图标和音视频资源

    打开阿里巴巴矢量图标库open in new window 选择想要的图标添加到购物车,统一下载到本地 src/fonts/iconfont.ttf src/fonts/iconfont.woff src/fonts/iconfont.woff2 src/css/iconfont.css 注意字体文件路径需要修改 src/main.js public/index.html type: \\\"asset/resource\\\" 和 type: \\\"asset\\\" 的区别: type: \\\"asset/resource\\\"  相当于

    2024年02月20日
    浏览(39)
  • FFmpeg音视频处理工具介绍及应用

    FFmpeg项目由 Fabrice Bellard在2000年创立。到目前为止,FFmpeg项目的开发者仍然与VLC、MPV、dav1d、x264等多媒体开源项目有着广泛的重叠。Ffmpeg(FastForward Mpeg)是一款遵循GPL的开源软件,在音视频处理方面表现十分优秀,几乎囊括了现存所有的视音频格式的编码,解码、转码、混合

    2024年02月08日
    浏览(53)
  • ffmpeg系列学习——FFmpeg的音视频处理

    1.音视频的采样率、采样位深度和声道数 音频和视频的采样率、采样位深度和声道数是媒体文件中的重要参数,它们会直接影响到音视频的质量和文件大小。下面对它们进行详细解释: 采样率 采样率指音频每秒钟采样的次数,用赫兹(Hz)表示。采样率越高,音频的还原度越

    2024年02月04日
    浏览(38)
  • JavaCV与FFmpeg:音视频流处理技巧

    1. JavaCV简介 JavaCV是一个开源的Java接口,为OpenCV、FFmpeg和其他类似工具提供了封装。它允许Java开发者直接在他们的应用程序中使用这些强大的本地库,而无需深入了解复杂的本地代码。JavaCV特别适用于处理图像和视频数据,提供了一系列的功能,如图像捕获、处理和视频编解

    2024年02月04日
    浏览(39)
  • 【图像处理】音视频色彩:RGB/YUV

    目录 1.RGB  1.1介绍        1.2分类 1.2.1RGB16 1)RGB565 2)RGB555 1.2.2RGB24 1.2.3RGB222 /

    2024年02月20日
    浏览(37)
  • 微信小程序音视频格式转换及处理

    在开发微信小程序时,经常会遇到需要对音视频进行格式转换和处理的情况。为了实现这一功能,我们可以借助FFmpeg这个强大的开源多媒体处理工具。本文将介绍如何在微信小程序中使用FFmpeg进行音视频格式转换。 首先,我们需要将FFmpeg引入到微信小程序中。由于微信小程序

    2024年04月23日
    浏览(40)
  • 音视频数据处理-H265/HEVC视频码流分析

    一、H265概述 H265/HEVC(Hight Efficiency Video Coding)是由ITU-T和ISO/IEC两大组织在H264/AVC的基础之上推出的新一代高效视频编码标准,主要为应对高清和超高清视频在网络传输和数据存储方面带来的挑战。上一篇文章对H264/AVC视频码流进行了详细的分析,本文继续从数据处理的角度对

    2024年02月11日
    浏览(36)
  • python+moviepy音视频处理(二):视频添加文字(字幕)、视频添加汉字不显示问题

    目录 添加文字报错处理 中文不显示处理 代码实例 给视频添加滚动文字(一) 给视频添加滚动文字(二) 报错内容 :OSError: MoviePy Error: creation of None failed because of the following error: [WinError 2] 系统找不到指定的文件。. .This error can be due to the fact that ImageMagick is not installed on your comput

    2024年02月09日
    浏览(67)
  • 音视频处理 ffmpeg中级开发 H264编码

    libavcodec/avcodec.h 常用的数据结构 AVCodec 编码器结构体 AVCodecContext 编码器上下文 AVFrame 解码后的帧 结构体内存的分配和释放 av_frame_alloc 申请 av_frame_free() 释放 avcodec_alloc_context3() 创建编码器上下文 avcodec_free_context() 释放编码器上下文 解码步骤 avcodec_find_decoder 查找解码器 avcod

    2024年02月01日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包