> 文章列表 > 关于ffmpeg中的AV_CLASS 和 priv_data的设计技巧

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

关于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,可以有效地实现数据的封装和隐藏,从而保护内部的属性和操作不受外部的干扰。