关于ffmpeg中的AV_CLASS 和 priv_data的设计技巧

AVClass 和 priv_data 是 FFmpeg 中实现封装和多态的关键设计技巧,下面我将分别介绍它们的设计原理和使用方法。

一、AVClass 的设计原理

AVClass 是一个类似于 C++ 中虚基类(virtual base class)的结构体,用于实现多继承的功能。它定义了一系列函数指针,这些函数指针可以被子类所覆盖,从而实现子类对父类的重载(override)。AVClass 结构体中的函数指针通常都是静态的,也就是说,它们并不依赖于具体的对象实例,从而避免了在运行时动态绑定的成本。

AVClass 在 FFmpeg 中的具体应用有很多,其中比较重要的是作为 AVCodecContext 的一个成员变量,用于表示编解码器的类型信息和参数设置。对于每个 AVCodecContext 实例,都会有一个 AVClass 结构体与之对应。这样,每个编解码器都可以通过 AVClass 中的函数指针来访问对应的解码和编码函数,从而实现多态性。

二、使用 AVClass 的方法

下面给出一个使用 AVClass 的示例:

  1. 定义一个继承了 AVClass 的结构体:
    typedef struct MyAVCodecContext {
        const AVClass *class;
        int my_param;
    } MyAVCodecContext;

  2. 实现 MyAVCodecContext 的构造函数和析构函数:
    static void my_codec_context_init(MyAVCodecContext *ctx)
    {
        ctx->my_param = 42;
    }
    
    static void my_codec_context_free(MyAVCodecContext *ctx)
    {
        // do some cleanup work
    }

  3. 定义一个子类,继承自 MyAVCodecContext:
    typedef struct MyAVCodecContext2 {
        MyAVCodecContext parent;
        int my_param2;
    } MyAVCodecContext2;

  4. 实现 MyAVCodecContext2 的构造函数和析构函数:
    static void my_codec_context_2_init(MyAVCodecContext2 *ctx)
    {
        my_codec_context_init((MyAVCodecContext *) ctx);
        ctx->my_param2 = 24;
    }
    
    static void my_codec_context_2_free(MyAVCodecContext2 *ctx)
    {
        my_codec_context_free((MyAVCodecContext *) ctx);
        // do some additional cleanup work
    }

  5. 实现 AVClass 中的函数指针:
    static const AVClass my_av_class = {
        .class_name = "MyAVCodecContext",
        .item_name  = av_default_item_name,
        .option     = my_codec_context_option,
        .version    = LIBAVUTIL_VERSION_INT,
    };
    
    static int my_codec_context_option(void *obj, const char *key, const char *val)
    {
        MyAVCodecContext *ctx = obj;
    
        if (!strcmp(key, "my_param")) {
            ctx->my_param = atoi(val);
            return 0;
        }
    
        return AVERROR_OPTION_NOT_FOUND;
    }

  6. 在创建时设置类指针:
    MyAVCodecContext2 *ctx2 = av_mallocz(sizeof(MyAVCodecContext2));
    ctx2->parent.class = &my_av_class;
    my_codec_context_2_init(ctx2);

    三、priv_data 的设计原理

    在 FFmpeg 中,很多数据结构都有私有数据(priv_data),私有数据是指某个结构体中仅该结构体所包含、外部无法直接访问的成员变量。使用私有数据的主要目的是为了实现数据的封装和隐藏,从而保护内部的属性和操作不受外部的干扰。

    在 FFmpeg 中,私有数据通常是通过 void 类型的指针来实现的。对于每个数据结构,都有一个指向私有数据的指针,在创建时会动态地分配内存,并将指针赋值给该数据结构的 priv_data 成员变量。这样,外部代码就不能直接访问数据结构中的私有数据,只能通过公共的操作函数来访问或修改私有数据。

    四、使用 priv_data 的方法

    下面给出一个使用 priv_data 的示例:

  1. 定义一个包含私有数据的结构体:
    typedef struct MyAVCodecContext {
        int my_public_param;
        void *priv_data;
    } MyAVCodecContext;

  2. 定义私有数据的结构体:
    typedef struct MyAVCodecContextPriv {
        int my_private_param;
    } MyAVCodecContextPriv;

  3. 实现操作私有数据的函数:
    static void my_codec_context_priv_init(MyAVCodecContextPriv *priv)
    {
        priv->my_private_param = 42;
    }
    
    static void my_codec_context_priv_free(MyAVCodecContextPriv *priv)
    {
        // do some cleanup work
    }

  4. 实现公共的操作函数:
    static void my_codec_context_init(MyAVCodecContext *ctx)
    {
        ctx->priv_data = av_mallocz(sizeof(MyAVCodecContextPriv));
        my_codec_context_priv_init(ctx->priv_data);
        ctx->my_public_param = 24;
    }
    
    static void my_codec_context_free(MyAVCodecContext *ctx)
    {
        my_codec_context_priv_free(ctx->priv_data);
        av_free(ctx->priv_data);
        // do some additional cleanup work
    }

  5. 在创建时设置私有数据的指针:
    MyAVCodecContext *ctx = av_mallocz(sizeof(MyAVCodecContext));
    my_codec_context_init(ctx);

    通过使用 priv_data,可以有效地实现数据的封装和隐藏,从而保护内部的属性和操作不受外部的干扰。

你可能感兴趣的:(ffmpeg)