ffmpeg 模块分析

预置模块

协议protocol

URLContext - av_class是预置ffurl_context_class;

预置URLProtocol,对应外部URLContext (url_protocols预置列表,包含ff_file_protocol)

URLContext- prot和priv_data ,以file为例,分别为ff_file_protocol和FileContext对象指针;

原生protocols.c包含所有的URLProtocol,新生protocol_list.c包含预置的URLProtocol;

  ff_file_protocol 如下:(详见file.c)

const URLProtocol ff_file_protocol = {
    .name                = "file",
    .url_open            = file_open,
    .url_read            = file_read,
    .url_write           = file_write,
    .url_seek            = file_seek,
    .url_close           = file_close,
    .url_get_file_handle = file_get_handle,
    .url_check           = file_check,
    .url_delete          = file_delete,
    .url_move            = file_move,
    .priv_data_size      = sizeof(FileContext),
    .priv_data_class     = &file_class,
    .url_open_dir        = file_open_dir,
    .url_read_dir        = file_read_dir,
    .url_close_dir       = file_close_dir,
    .default_whitelist   = "file,crypto,data"
};

 AVFormatContext聚合了AVIOContext(pb),AVIOContext聚合了URLContext(opaque)

创建URLContext,成员prot关联URLProtocol,成员priv_data关联FileContext(file文件协议,只是分配大小,根据业务强转对应Context),详见avio.c的url_alloc_for_protocol函数)

创建AVIOContext,ffio_fdopen内部avio_alloc_context,实际通过ffio_init_context关联URLContext

常见协议包含:file(file.c) crypto(crypto.c) http(http.c) rtp(rtpproto.c)等

查看FileContext对应AVClass(priv_data_class)的AVOption设置项,avio_open2通过字典设置;

解封装demuxer和封装muxer

AVFormatContext - av_class是预置av_format_context_class;

AVFormatContext - iformat和priv_data(以mp4为例,分别为ff_mov_demuxer和MOVContext)

预置AVInputFormat,获取预置(详见新生demuxer_list.c)

// iterate - all AVInputFormat
const AVInputFormat* pIFormat = NULL;
void* iter = NULL;
while ((pIFormat = av_demuxer_iterate(&iter))){ ; }

  ff_mov_demuxer 如下:(详见movenc.c)

// 描述信息,私有数据,读取操作,标志
AVInputFormat ff_mov_demuxer = {
    .name           = "mov,mp4,m4a,3gp,3g2,mj2",
    .long_name      = NULL_IF_CONFIG_SMALL("QuickTime / MOV"),
    .priv_class     = &mov_class,
    .priv_data_size = sizeof(MOVContext),
    .extensions     = "mov,mp4,m4a,3gp,3g2,mj2,psp,m4b,ism,ismv,isma,f4v",
    .read_probe     = mov_probe,
    .read_header    = mov_read_header,
    .read_packet    = mov_read_packet,
    .read_close     = mov_read_close,
    .read_seek      = mov_read_seek,
    .flags          = AVFMT_NO_BYTE_SEEK | AVFMT_SEEK_TO_PTS,
};
// 第一步 mov_probe
// 通过mov_probe、extensions和mime_type来决定迭代出的demuxer是否符合
// 第二步 mov_read_header
// 从mov_read_default开始读取box,读取size和type进入mov_read_xxx
// mov_read_xxx是容器box,又进入mov_read_default
// 其中mov_read_trak内部avformat_new_stream

AVFormatContext - oformat和priv_data(以mp4为例,分别为ff_mov_muxer和MOVMuxContext)

预置AVOutputFormat,获取预置(详见新生muxer_list.c)​​​​​​​

ff_mov_muxer 如下:(详见movenc.c)

AVOutputFormat ff_mov_muxer = {
    .name              = "mov",
    .long_name         = NULL_IF_CONFIG_SMALL("QuickTime / MOV"),
    .extensions        = "mov",
    .priv_data_size    = sizeof(MOVMuxContext),
    .audio_codec       = AV_CODEC_ID_AAC,
    .video_codec       = CONFIG_LIBX264_ENCODER ?
                         AV_CODEC_ID_H264 : AV_CODEC_ID_MPEG4,
    .init              = mov_init,
    .write_header      = mov_write_header,
    .write_packet      = mov_write_packet,
    .write_trailer     = mov_write_trailer,
    .deinit            = mov_free,
    .flags             = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE,
    .codec_tag         = ...
    .check_bitstream   = mov_check_bitstream,
    .priv_class        = &mov_muxer_class,
};

成员解释
​​​​​​​priv_data_size用于分配空间返回的指针给priv_data(MOVMuxContext *mov = s->priv_data)
​​​​​​​priv_class是AVClass指针用于抽象设置相关参数AVOption(static const AVOption options[])

函数说明
init_muxer 用于通过AVDictionary初始化muxer,从字典中取出键值对,通过键对比从priv_data的AVClass的默认AVOption找出默认配置,并设置到priv_data成员中。例如{ "frag_size", "Maximum fragment size", offsetof(MOVMuxContext, max_fragment_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}表明frag_size用于设置MOVMuxContext结构体中max_fragment_size的成员。

解码decoder和编码encoder

AVCodecContext - av_class是预置avcodec_options;

预置AVCodec,对应外部AVCodecContext,获取预置

// iterate - all AVCodec
const AVCodec *pCodec = NULL;
void *iter = NULL;
while ((pCodec = av_codec_iterate(&iter))) { ; }
// AVCodec - h264_cuvid h264_qsv h264
AVCodec* pVideoCodec = avcodec_find_decoder_by_name("h264");
AVCodec* pVideoCodec = avcodec_find_decoder(AV_CODEC_ID_H264);

 AVCodecContext生命周期(类似面向对象的说法),AVCodec成员priv_data_size和priv_class决定了它的priv_data(强转AVClass)

AVCodecContext - codec和priv_data (以h264为例,分别为ff_h264_decoder和H264Context对象指针)

// alloc open - 通过预置解码器分配解码对象,然后设置参数,再打开
AVCodecContext* pVideoCodecCtx = avcodec_alloc_context3(pVideoCodec);
avcodec_parameters_to_context(pVideoCodecCtx, codecparamter);  // 非必须
avcodec_open2(pVideoCodecCtx, pVideoCodec, NULL);
// use - 送AVPacket收AVFrame
avcodec_send_packet(pVideoCodecCtx, &sMediapacket);
avcodec_receive_frame(pVideoCodecCtx, pMediaFrame);
// close free - 
avcodec_close(pVideoCodecCtx);
avcodec_free_context(&pVideoCodecCtx);
pVideoCodecCtx = NULL;

 ff_h264_decoder 中priv_data_size赋值决定了AVCodecContext的priv_data 如下:

AVCodec ff_h264_decoder = {
    .name                  = "h264",
    .long_name             = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
    .type                  = AVMEDIA_TYPE_VIDEO,
    .id                    = AV_CODEC_ID_H264,
    .priv_data_size        = sizeof(H264Context),
    .init                  = h264_decode_init,
    .close                 = h264_decode_end,
    .decode                = h264_decode_frame,
    .capabilities          = /*AV_CODEC_CAP_DRAW_HORIZ_BAND |*/ AV_CODEC_CAP_DR1 |
                             AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS |
                             AV_CODEC_CAP_FRAME_THREADS,
    ......
    .priv_class            = &h264_class,
}
av_codec_is_decoder(pCodec);
av_codec_is_encoder(pCodec);

ff_alloc_extradata和ff_get_extradata 

ff_h2645_extract_rbsp 从内存中nalu读到H2645NAL结构体

ff_h264_decode_seq_parameter_set        解析sps

ff_h264_decode_picture_parameter_set   解析pps

位流过滤器

预置AVBitStreamFilter,对应外部AVBSFContext,获取预置

const AVBitStreamFilter* pBitStreamFilter = av_bsf_get_by_name("h264_mp4toannexb");
const AVBitStreamFilter* pBitStreamFilter = av_bsf_get_by_name("aac_adtstoasc");
const AVBitStreamFilter* pBitStreamFilter = av_bsf_get_by_name("extract_extradata");

  AVBSFContext生命周期

// alloc
AVBSFContext* pBsfCtx = nullptr;
av_bsf_alloc(pBitStreamFilter, &pBsfCtx);
// init
avcodec_parameters_copy(pBsfCtx->par_in, codecparamter);  // 有待研究
av_bsf_init(pBsfCtx);
// use
av_bsf_send_packet(pBsfCtx, &sMediapacket);
av_bsf_receive_packet(pBsfCtx, &sMediapacket);
// free
av_bsf_free(&pBsfCtx);

硬件加速

AVCodecContext 相关成员hw_device_ctx、hw_frames_ctx和函数get_format
AVBufferRef(AVHWDeviceContext)、AVBufferRef(AVHWFramesContext)和AVPixelFormat [](AVCodecContext *,const enum AVPixelFormat *)

预置HWContextType

const HWContextType ff_hwcontext_type_vulkan = {
    .type                   = AV_HWDEVICE_TYPE_VULKAN,
    .name                   = "Vulkan",

    .device_hwctx_size      = sizeof(AVVulkanDeviceContext),
    .device_priv_size       = sizeof(VulkanDevicePriv),
    .frames_hwctx_size      = sizeof(AVVulkanFramesContext),
    .frames_priv_size       = sizeof(VulkanFramesPriv),

    .device_init            = &vulkan_device_init,
    .device_create          = &vulkan_device_create,
    .device_derive          = &vulkan_device_derive,

    .frames_get_constraints = &vulkan_frames_get_constraints,
    .frames_init            = vulkan_frames_init,
    .frames_get_buffer      = vulkan_get_buffer,
    .frames_uninit          = vulkan_frames_uninit,

    .transfer_get_formats   = vulkan_transfer_get_formats,
    .transfer_data_to       = vulkan_transfer_data_to,
    .transfer_data_from     = vulkan_transfer_data_from,
    ...
    .pix_fmts = (const enum AVPixelFormat []) { AV_PIX_FMT_VULKAN, AV_PIX_FMT_NONE},
};

av_hwdevice_ctx_alloc
​​​​​​​AVHWDeviceContext - av_class是hwdevice_ctx_class;
AVHWDeviceContext - internal->hw_type、hwctx和internal->priv(以vulkan为例,分别为ff_hwcontext_type_vulkan、AVVulkanDeviceContext和VulkanDevicePriv)

av_hwdevice_find_type_by_name通过hw_type_names查看
avcodec_get_hw_config 通过这个找到预置解码AVCodec硬件配置和支持能力和方法
av_hwdevice_ctx_create
​​​​​​​av_hwframe_transfer_data

av_dict_parse_string(&opts,"priority=vod,loglevel=6", "=", ",", 0);
av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_VULKAN,"/dev/transcoder0",
                                 opts, 0);

av_hwframe_ctx_alloc   
AVHWFramesContext - av_class是hwframe_ctx_class;
​​​​​​​AVHWFramesContext -internal->hw_type、hwctx和internal->priv(以vulkan为例,分别为ff_hwcontext_type_vulkan、AVVulkanFramesContext和VulkanFramesPriv);

IO模块

avio_alloc_context

probe模块

通过分析数据buffer得到预置demuxer

  • av_probe_input_buffer     -> 通过AVIOContext分析得到AVInputFormat
  • av_probe_input_format3  -> 通过AVProbeData分析得到AVInputFormat

遍历demuxer,根据demuxer的read_probe、extensions和mime_type得到返回demuxer的分数

其他

AVOption - 设置参数集合,与结构体绑定指出相关参数在结构体中偏移;

typedef struct AVOption {
    const char *name;  // 设置名称
    const char *help;  // 设置解释
    int offset;        // 对应结构体中偏移

    enum AVOptionType type; // 值类型(bool,string,int等)
    union {
        int64_t i64;
        double dbl;
        const char *str;
        AVRational q;
    } default_val;
    double min; double max; // 最小值最大值
    
    int flags; // 只读,编码参数解码参数等
    const char *unit;
} AVOption;

av_opt_set_defaults 将context内部第一个成员AVClass的AVOption值赋值给成员变量;


av_strdup - 分配字符串,并复制内容 
av_q2d      - 分数转化小数
av_compare_ts - 基于timebase比较两个时间戳(AV_TIME_BASE us单位)
​​​​​​​set_string_binary 用于设置二进制字符串,30313233343536373839616263646566变成0123456789abcdef

  url.h

typedef struct URLContext {
    const AVClass *          av_class; 
    const struct URLProtocol *prot;
    void *                   priv_data;
    char *                   filename;           
    int                      flags;
    int                      max_packet_size;       
    int                      is_streamed;           
    int                      is_connected;
    AVIOInterruptCB          interrupt_callback;
    int64_t                  rw_timeout;       
    const char *             protocol_whitelist;
    const char *             protocol_blacklist;
    int                      min_packet_size;       
} URLContext;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值