前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android FFmpeg系列09--抽帧与快速抽帧

Android FFmpeg系列09--抽帧与快速抽帧

作者头像
雪月清
发布2022-11-19 09:57:09
1.5K0
发布2022-11-19 09:57:09
举报
文章被收录于专栏:雪月清的随笔

视频抽帧的实现方式是seek+解码的结合,在剪辑软件和播放器中都存在不少应用场景,比如剪辑软件导入视频后展示的封面图、视频时间轴等

(剪映导入演示视频oceans.mp4)

本篇文章基于之前的Demo工程实现一个抽帧的utils并仿照系统相册展示一个视频缩略图轨道

(系统相册导入演示视频oceans.mp4)

抽帧实现

FFMpegUtils.kt

对外工具类

代码语言:javascript
复制
object FFMpegUtils {

    interface VideoFrameArrivedInterface {
        /**
         * @param duration
         * 给定视频时长,返回待抽帧的pts arr,单位为s
         */
        fun onFetchStart(duration: Double): DoubleArray

        /**
         * 每抽帧一次回调一次
         */
        fun onProgress(frame: ByteBuffer, timestamps: Double, width: Int, height: Int, index: Int): Boolean

        /**
         * 抽帧动作结束
         */
        fun onFetchEnd()
    }

    fun getVideoFrames(path: String,
                       width: Int,
                       height: Int,
                       cb: VideoFrameArrivedInterface) {
        getVideoFramesCore(path, width, height, cb)
    }

    private external fun getVideoFramesCore(path: String,
                                            width: Int,
                                            height: Int,
                                            cb: VideoFrameArrivedInterface)
}

FFReader.h

封装一个Reader基类,用于读取音频、视频avpacket

代码语言:javascript
复制
#ifndef FFMPEGDEMO_FFREADER_H
#define FFMPEGDEMO_FFREADER_H

#include <string>

extern "C" {
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
}

enum TrackType {
    Track_Video,
    Track_Audio
};

/**
 * read AVPacket class
 */
class FFReader {

public:
    FFReader();
    virtual ~FFReader();

    bool init(std::string &path);

    bool selectTrack(TrackType type);

    int fetchAvPacket(AVPacket *pkt);

    bool isKeyFrame(AVPacket *pkt);

    /**
     * 获取timestamp对应的关键帧index,基于BACKWARD
     * @param timestamp: 时间单位s
     * @return
     */
    int getKeyFrameIndex(int64_t timestamp);

    double getDuration();

    /**
     * seek
     * @param timestamp: 时间单位s
     */
    void seek(int64_t timestamp);

    void flush();

    void release();

private:
    // ....略
};


#endif //FFMPEGDEMO_FFREADER_H

FFVideoReader.h

继承自FFReader,负责解码视频帧、resize、格式转化(通过libyuv统一输出RGBA数据)等

代码语言:javascript
复制
#ifndef FFMPEGDEMO_FFVIDEOREADER_H
#define FFMPEGDEMO_FFVIDEOREADER_H

#include "FFReader.h"

class FFVideoReader: public FFReader{

public:
    FFVideoReader(std::string &path);
    ~FFVideoReader();

    void getFrame(int64_t pts, int width, int height, uint8_t *buffer);

private:
    // ...略

};


#endif //FFMPEGDEMO_FFVIDEOREADER_H

限于文章篇幅就没有贴具体的实现代码了,感兴趣的同学可以参考提交到github的源码

在MainActity的调用

快速抽帧

不同的应用场景有不同的优化思路,针对上面的视频缩略图抽帧的场景,我们的优化方向有两个

  • 缩略图size小,那么可以充分利用缓存
  • 抽帧的时间戳是可预测的,那么可以利用预解码、多解码器分段解码、seek跳过非参考帧等手段;

当然这里只提思路,具体的实现在Demo工程中就没有提供啦,最终的Demo效果如下

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-10-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 雪月清的随笔 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档