Codec2类的解析

1. 概述

Codec2框架内有多个类,关系并不容易一下子缕清,涉及到接口与实现、HIDL调用、组件化、工厂模式与建造者模式等内容。

先看一张UML图,大概描绘了Codec2框架的大多数类及其之间的关系,可能存在疏漏与错误。

在这里插入图片描述

最顶层为Codec2类,对接到MediaCodec,其代码文件为CCodec.h,主要实现编解码功能,它主要与三个类打交道,包括CCodecBufferChannel、Codec2Client以及CCodecConfig类。

CCodecBufferChannel类主要封装与Buffer相关的操作接口,其代码文件为CCodecBufferChannel.h,包括送输入数据、渲染、获取输入与输出缓冲区、操作surface以及处理底层回调上来的输入数据与输出数据事件响应。

CCodecConfig类主要封装与参数交互相关的操作接口,其代码文件为CCodecConfig.h,包括向下层组件配置参数、从下层组件获取参数以及更新参数配置等。

Codec2Client类继承于Codec2ConfigurableClient父类,从而具备参数交互的操作接口,而本身提供与组件(component, interface)创建相关的操作接口,它可以创建component,创建与component相关联的interface,可以获取ParamReflector。该类的代码文件为Client.h及Client.cpp。

Codec2Client::Listener类是用于回调消息到CCodec的,在CCodec类中,CCodec::ClientListener是继承于Codec2Client::Listener的。关于上下层如何进行回调的流程,可以参考《Codec2入门:框架解析》一文。

//Client.h
struct CCodec::ClientListener : public Codec2Client::Listener {

    explicit ClientListener(const wp<CCodec> &codec) : mCodec(codec) {}

    virtual void onWorkDone(
            const std::weak_ptr<Codec2Client::Component>& component,
            std::list<std::unique_ptr<C2Work>>& workItems) override {
        ......
        codec->onWorkDone(workItems);
    }
	......
    virtual void onInputBufferDone(
            uint64_t frameIndex, size_t arrayIndex) override {
        sp<CCodec> codec(mCodec.promote());
        if (codec) {
            codec->onInputBufferDone(frameIndex, arrayIndex);
        }
    }
};

2. Codec2Client

CCodec调用Codec2Client::Component对象,而Codec2Client::Component对象是经由Codec2Client创建的。CCodec调用Codec2Client::Component对象进行的主要操作包括:

comp->start(),在 CCodec::start() 中调用。

comp->stop(),在CCodec::stop()中调用。

comp->release(),在CCodec::release()中调用。

comp->flush(),在CCodec::flush()中调用。

comp->query,在configure()中调用。

在Codec2Client中,ComponentStore对象mBase是如何来的呢?在std::shared_ptr Codec2Client::_CreateFromIndex(size_t index)函数中,创建Codec2Client对象的过程中,传入了ComponentStore对象。

//client.cpp
std::shared_ptr<Codec2Client> Codec2Client::_CreateFromIndex(size_t index) {
    std::string const& name = GetServiceNames()[index];
    LOG(INFO) << "Creating a Codec2 client to service \"" << name << "\"";
    //获取到ComponentStore服务,内部具体实现可能是software服务,也可能是default服务
    //即厂商提供的store,store可以理解为插件集,插件集可以由谷歌原生提供,也可以由厂商提供
    sp<Base> baseStore = Base::getService(name);
    CHECK(baseStore) << "Codec2 service \"" << name << "\""
                        " inaccessible for unknown reasons.";
    LOG(INFO) << "Client to Codec2 service \"" << name << "\" created";
    return std::make_shared<Codec2Client>(baseStore, index);//在此创建了Codec2Client对象,并以baseStore传参
}

接下来,先讲ComponentStore,再讲Component。

ComponentStore类向上层Codec2Client提供store的接口,向下层连接IComponentStore/software或者IComponentStore/default服务,连接的桥梁为HIDL接口,可以看作是C/S模型。ComponentStore实现的是IComponentStore类的纯虚接口,调用的mStore对象,可以是C2PlatformComponentStore,也可以是厂商提供的C2VendorComponentStore,这两者都实现的是C2ComponentStore的纯虚接口。

以谷歌原生提供的C2PlatformComponentStore为例,这到底是什么个东西?我们可以把这个类理解为平台提供的Codec2组件集,每一个组件可以是解码器,也可以是编码器,这个组件集负责创建与管理这些组件。从该类的接口可以看出,这个组件集可以创建组件,可以创建Interface,可以向组件配置参数与获取参数,这些参数交互的接口确实令人郁闷。对比omx标准,setParameter/getParameter,setConfig/getConfig这些接口见名知义,通俗易懂,而下面这些接口,就令人费解。

//C2Component.h,该头文件有详细注释,但我还是看不懂,特别是params对象与fields对象
//设计这些接口的家伙一点都不友好!
//后缀sm表示可能有短时的阻塞,须在5ms内返回响应
//后缀nb表示non-blocking,须在1ms内返回响应
virtual c2_status_t query_sm(
        const std::vector<C2Param*> &stackParams,
        const std::vector<C2Param::Index> &heapParamIndices,
        std::vector<std::unique_ptr<C2Param>>* const heapParams) const = 0;
virtual c2_status_t config_sm(
            const std::vector<C2Param*> &params,
            std::vector<std::unique_ptr<C2SettingResult>>* const failures) = 0;
virtual c2_status_t querySupportedParams_nb(
            std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) const = 0;
virtual c2_status_t querySupportedValues_sm(
            std::vector<C2FieldSupportedValuesQuery> &fields) const = 0;
//这个返回一个parameter reflector,只可意会,不可直译,只能意译为参数器对象
virtual std::shared_ptr<C2ParamReflector> getParamReflector() const = 0;

Component类与C2Component类之间的关系类似于ComponentStore与C2ComponentStore。Component调用C2Component类对象,其具体实现是SimpleC2Component,如果存在厂商的服务,则可以为VendorC2Component,SimpleC2Component与SimpleC2Interface相关联,SimpleC2Interface大部分工作由C2InterfaceHelper所完成,厂商在实现自己的组件集时,可以不必自主实现一个VendorC2Interface,直接借用SimpleC2Interface即可。SimpleC2Interface实现的是C2ComponentInterface的抽象接口,而SimpleC2Interface的子类SimpleC2Interface::BaseParams继承于C2InterfaceHelper,由此,SimpleC2Interface的基本工作都由C2InterfaceHelper完成。

3. SimpleC2Component

我们看一下C2Component类的定义。

//C2Component.h
class C2Component {
public:
    //监听类,用于回调事件到上层(Component类)
	class Listener {
    	public:
        virtual void onWorkDone_nb(std::weak_ptr<C2Component> component,
                                std::list<std::unique_ptr<C2Work>> workItems) = 0;

        virtual void onTripped_nb(std::weak_ptr<C2Component> component,
                               std::vector<std::shared_ptr<C2SettingResult>> settingResult) = 0;

        virtual void onError_nb(std::weak_ptr<C2Component> component,
                             uint32_t errorCode) = 0;

        // virtual void onTunnelReleased(<from>, <to>) = 0;

        // virtual void onComponentReleased(<id>) = 0;

        virtual ~Listener() = default;
    };
    //用于上层设置回调函数,相当于监听,上层其实就是Component类
    //如果mayBlock为true,则该监听对象可能为temporarily blocking(暂时性阻塞),须等待其他pending listener callback处理完
    //如果mayBlock为false,则该监听对象为non-blocking(非阻塞)
    virtual c2_status_t setListener_vb(
            const std::shared_ptr<Listener> &listener, c2_blocking_t mayBlock) = 0;
    //送一个work事务对象给component,work这个对象可以理解为事务对象,包含着输入、输出以及其他参数信息
    //上下层的数据沟通,基本通过work来进行
    virtual c2_status_t queue_nb(std::list<std::unique_ptr<C2Work>>* const items) = 0;
    //暂时没有用
    virtual c2_status_t announce_nb(const std::vector<C2WorkOutline> &items) = 0;
    //冲刷当前数据,一般用于跳播与分辨率切换
	virtual c2_status_t flush_sm(flush_mode_t mode, std::list<std::unique_ptr<C2Work>>* const 	flushedWork) = 0;
    //渲染
	virtual c2_status_t drain_nb(drain_mode_t mode) = 0;
    //开始运行组件
	virtual c2_status_t start() = 0;
    //停止运行组件
	virtual c2_status_t stop() = 0;
    //重置组件
	virtual c2_status_t reset() = 0;
    //释放组件
	virtual c2_status_t release() = 0;
    //这里是C2Component与C2ComponentInterface关联的地方
	virtual std::shared_ptr<C2ComponentInterface> intf() = 0;
	virtual ~C2Component() = default;
}

C2Component只是一个抽象类,其实现为SimpleC2Component。

//SimpleC2Component.h
class SimpleC2Component
        : public C2Component, public std::enable_shared_from_this<SimpleC2Component> {
public:
    explicit SimpleC2Component(
            const std::shared_ptr<C2ComponentInterface> &intf);
    virtual ~SimpleC2Component();

    // C2Component
    // From C2Component
    virtual c2_status_t setListener_vb(
            const std::shared_ptr<Listener> &listener, c2_blocking_t mayBlock) override;
    virtual c2_status_t queue_nb(std::list<std::unique_ptr<C2Work>>* const items) override;
    virtual c2_status_t announce_nb(const std::vector<C2WorkOutline> &items) override;
    virtual c2_status_t flush_sm(
            flush_mode_t mode, std::list<std::unique_ptr<C2Work>>* const flushedWork) override;
    virtual c2_status_t drain_nb(drain_mode_t mode) override;
    virtual c2_status_t start() override;
    virtual c2_status_t stop() override;
    virtual c2_status_t reset() override;
    virtual c2_status_t release() override;
    virtual std::shared_ptr<C2ComponentInterface> intf() override;
}

SimpleC2Component类实现的是各个组件的共同操作,相当于一个公共适配层,每一个组件都继承于SimpleC2Component类,譬如,C2SoftAvcDec类在构造的同时,父类SimpleC2Component亦构造。组件的创建过程可参考《Codec2入门:解码组件》一文,简单来说就是通过插件化与工厂模式创建具体的组件。

//C2SoftAvcDec.h
struct C2SoftAvcDec : public SimpleC2Component {
    class IntfImpl;

    C2SoftAvcDec(const char *name, c2_node_id_t id, const std::shared_ptr<IntfImpl> &intfImpl);
    virtual ~C2SoftAvcDec();

    // From SimpleC2Component
    //以下函数都是实现了父类SimpleC2Component的私有虚函数
    c2_status_t onInit() override;
    c2_status_t onStop() override;
    void onReset() override;
    void onRelease() override;
    c2_status_t onFlush_sm() override;
    //处理一个work事务
    //对于avc decoder而言,work中包含码流输入数据,当调用返回时,work将包含yuv解码数据
    void process(
            const std::unique_ptr<C2Work> &work,
            const std::shared_ptr<C2BlockPool> &pool) override;
    c2_status_t drain(
            uint32_t drainMode,
            const std::shared_ptr<C2BlockPool> &pool) override;
 }

4. SimpleC2Interface

SimpleC2Interface实现于C2ComponentInterface纯虚类,SimpleC2Interface的定义如下:

//SimpleC2Interface.h
/**
 * Wrap a common interface object (such as Codec2Client::Interface, or C2InterfaceHelper into
 * a C2ComponentInterface.
 *
 * \param T common interface type
 */
template <typename T>
class SimpleC2Interface : public C2ComponentInterface {
public:
    SimpleC2Interface(const char *name, c2_node_id_t id, const std::shared_ptr<T> &impl)
        : mName(name),
          mId(id),
          mImpl(impl) {
    }

    ~SimpleC2Interface() override = default;

    // From C2ComponentInterface
    C2String getName() const override { return mName; }
    c2_node_id_t getId() const override { return mId; }
     //获取参数
    c2_status_t query_vb(
            const std::vector<C2Param*> &stackParams,
            const std::vector<C2Param::Index> &heapParamIndices,
            c2_blocking_t mayBlock,
            std::vector<std::unique_ptr<C2Param>>* const heapParams) const override {
        return mImpl->query(stackParams, heapParamIndices, mayBlock, heapParams);
    }
    //配置参数
    c2_status_t config_vb(
            const std::vector<C2Param*> &params,
            c2_blocking_t mayBlock,
            std::vector<std::unique_ptr<C2SettingResult>>* const failures) override {
        return mImpl->config(params, mayBlock, failures);
    }
    //暂时没有用
    c2_status_t createTunnel_sm(c2_node_id_t) override { return C2_OMITTED; }
    //暂时没有用
    c2_status_t releaseTunnel_sm(c2_node_id_t) override { return C2_OMITTED; }
    c2_status_t querySupportedParams_nb(
            std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) const override {
        return mImpl->querySupportedParams(params);
    }
    c2_status_t querySupportedValues_vb(
            std::vector<C2FieldSupportedValuesQuery> &fields,
            c2_blocking_t mayBlock) const override {
        return mImpl->querySupportedValues(fields, mayBlock);
    }

private:
    C2String mName;
    const c2_node_id_t mId;
    const std::shared_ptr<T> mImpl;
};

接下来讨论上述的一个问题,SimpleC2Component如何与SimpleC2Interface相关联。

我们看一下C2SoftAvcDecFactory类的定义。SimpleC2Component在构造的时候,SimpleC2Interface的一个成员类SimpleC2Interface::BaseParams被作为入参传递给了组件,组件继而可以调用该成员类的相关接口完成参数配置,主要是addParameter接口。

//C2SoftAvcDec.cpp
class C2SoftAvcDecFactory : public C2ComponentFactory {
public:
    C2SoftAvcDecFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
        GetCodec2PlatformComponentStore()->getParamReflector())) {
    }

    virtual c2_status_t createComponent(
            c2_node_id_t id,
            std::shared_ptr<C2Component>* const component,
            std::function<void(C2Component*)> deleter) override {
        //C2SoftAvcDec构造函数的一个入参为C2SoftAvcDec::IntfImpl类型成员
        //C2SoftAvcDec::IntfImpl是继承于SimpleInterface<void>::BaseParams的
        *component = std::shared_ptr<C2Component>(
                new C2SoftAvcDec(COMPONENT_NAME,
                                 id,
                                 std::make_shared<C2SoftAvcDec::IntfImpl>(mHelper)),
                deleter);
        return C2_OK;
    }

    virtual c2_status_t createInterface(
            c2_node_id_t id,
            std::shared_ptr<C2ComponentInterface>* const interface,
            std::function<void(C2ComponentInterface*)> deleter) override {
        //interface的创建来自于SimpleInterface对象的构建,该对象类型为C2SoftAvcDec::IntfImpl,
        //构造参数包括COMPONENT_NAME、id以及C2SoftAvcDec::IntfImpl对象
        //这个构造过程有点难懂......为什么SimpleInterface的类型为C2SoftAvcDec::IntfImpl??
        *interface = std::shared_ptr<C2ComponentInterface>(
                new SimpleInterface<C2SoftAvcDec::IntfImpl>(
                        COMPONENT_NAME, id, std::make_shared<C2SoftAvcDec::IntfImpl>(mHelper)),
                deleter);
        return C2_OK;
    }

    virtual ~C2SoftAvcDecFactory() override = default;

private:
    std::shared_ptr<C2ReflectorHelper> mHelper;
};

我们看一下组件如何调用SimpleC2Interface::BaseParams成员类的相关接口完成参数配置。

//C2SoftAvcDec.cpp
class C2SoftAvcDec::IntfImpl : public SimpleInterface<void>::BaseParams {
public:
    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
        : SimpleInterface<void>::BaseParams(
                helper,
                COMPONENT_NAME,
                C2Component::KIND_DECODER,
                C2Component::DOMAIN_VIDEO,
                MEDIA_MIMETYPE_VIDEO_AVC) {
        noPrivateBuffers(); // TODO: account for our buffers here
        noInputReferences();
        noOutputReferences();
        noInputLatency();
        noTimeStretch();
		......//省略部分
        // coded and output picture size is the same for this codec
        //配置默认解码输出宽高为320x240
        //这里采用了Builder模式,即建造者模式
        addParameter(
                DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
                .withDefault(new C2StreamPictureSizeInfo::output(0u, 320, 240))
                .withFields({
                    C2F(mSize, width).inRange(2, 4080, 2),
                    C2F(mSize, height).inRange(2, 4080, 2),
                })
                .withSetter(SizeSetter)
                .build());
        //配置默认的颜色信息
        addParameter(
                DefineParam(mColorInfo, C2_PARAMKEY_CODED_COLOR_INFO)
                .withConstValue(defaultColorInfo)
                .build());
        //配置默认的颜色格式为HAL_PIXEL_FORMAT_YCBCR_420_888
        addParameter(
                DefineParam(mPixelFormat, C2_PARAMKEY_PIXEL_FORMAT)
                .withConstValue(new C2StreamPixelFormatInfo::output(
                                     0u, HAL_PIXEL_FORMAT_YCBCR_420_888))
                .build());
}

C2SoftAvcDec::IntfImpl调用C2InterfaceHelper::addParameter完成默认参数配置,而正是由于SimpleInterface::BaseParams继承于C2InterfaceHelper(查看UML图),才得以调用父类的该接口。addParameter接口会将参数添加到参数器Reflector中,这样,上层通过参数器Reflector便可获知组件的默认配置,实现参数交互。

SimpleInterface::BaseParams属于SimpleInterface类,继承于C2InterfaceHelper。

//SimpleC2Interface.h
/**
 * Utility classes for common interfaces.
 */
template<>
class SimpleC2Interface<void> {
public:
    /**
     * Base Codec 2.0 parameters required for all components.
     */
    struct BaseParams : C2InterfaceHelper {
        explicit BaseParams(
                const std::shared_ptr<C2ReflectorHelper> &helper,
                C2String name,
                C2Component::kind_t kind,
                C2Component::domain_t domain,
                C2String mediaType,
                std::vector<C2String> aliases = std::vector<C2String>());
}

其中第一个入参为C2ReflectorHelper类型,我们看一下它的定义。

//C2InterfaceHelper.h
/**
 * Helper class to implement parameter reflectors. This class is dynamic and is designed to be
 * shared by multiple interfaces. This allows interfaces to add structure descriptors as needed.
 */
class C2ReflectorHelper : public C2ParamReflector {
public:
    C2ReflectorHelper() = default;
    virtual ~C2ReflectorHelper() = default;
    virtual std::unique_ptr<C2StructDescriptor> describe(
            C2Param::CoreIndex paramIndex) const override;

    /**
     * Adds support for describing the given parameters.
     *
     * \param Params types of codec 2.0 structs (or parameters) to describe
     */
    template<typename... Params>
    C2_INLINE void addStructDescriptors() {
        std::vector<C2StructDescriptor> structs;
        addStructDescriptors(structs, (_Tuple<Params...> *)nullptr);
    }

    /**
     * Adds support for describing a specific struct.
     *
     * \param strukt descriptor for the struct that will be moved out.
     */
    void addStructDescriptor(C2StructDescriptor &&strukt);
}

C2ReflectorHelper继承于C2ParamReflector类,并实现了C2ParamReflector唯一的虚函数describe。如何理解C2ReflectorHelper这个类呢?可以把它看作是一个参数器,它提供两个操作,一个是往参数器里添加参数对象,一个是从参数器里取出参数对象,这里的参数对象指的是structure descriptors,如注释。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值