1、C2Work的基本数据结构分析
众所周知,野生的CCodec的架构采用了C2Work这个对象进行和底层硬件解码数据轮转。但是CCodec的架构比较大,往往一入源码深似海,就云里雾里的不知道怎么分析。所以整理阅读笔记是一个好习惯。那我们就开始吧。首先看下C2Work的定义,它究竟是个什么东西?
C2Work定义的头文件:
/frameworks/av/media/codec2/core/include/C2Work.h
具体的定义如下:
struct C2Work {
//public:
std::shared_ptr<C2WorkChainInfo> chainInfo;
C2FrameData input;
std::list<std::unique_ptr<C2Worklet>> worklets;
uint32_t workletsProcessed;
c2_status_t result;
};
上面的C2Work定义中,chainInfo是用作以后拓展用的,暂时是这么理解的,不过后续怎么修改还不一定。C2FramwData input是C2Work中关于input解码数据的定义,后面再详细分析它。std::list<std::unique_ptr> worklets;是解码完后的数据的定义,上层调用的时候从里面拿到解码之后的数据。而workletsProcessed一看就应该是和解码完数据worklets一起使用的,暂时可以不用管它,result是结果。
基本的C2Work顶层我们大概有一个脉络了,但是还是不具体,我就简单画了一个类图,具体如下:
2、C2Work Input C2FrameData的分析
从上面的C2Work的基本数据结构,我们可以得知Input数据是放在C2FrameData input中的,那么我们下面开始追踪一下C2FrameData里面都是些什么妖魔鬼怪。代码的定义如下:
struct C2FrameData {
//public:
enum flags_t : uint32_t {
FLAG_DROP_FRAME = (1 << 0),
FLAG_END_OF_STREAM = (1 << 1),
FLAG_DISCARD_FRAME = (1 << 2),
FLAG_INCOMPLETE = (1 << 3),
FLAG_CORRECTED = (1 << 4),
FLAG_CORRUPT = (1 << 5),
FLAG_CODEC_CONFIG = (1u << 31),
};
flags_t flags;
C2WorkOrdinalStruct ordinal;
std::vector<std::shared_ptr<C2Buffer>> buffers;
std::vector<std::unique_ptr<C2Param>> configUpdate;
std::vector<C2InfoBuffer> infoBuffers;
};
我们可以看到,它上来就是定义一个flag_t的flags,这个具体是干嘛的呢?看这这定义也不难猜出,标识着这帧数据的状态,丢弃、流的结束、初始化等等。好的,下面我们开始对里面的数据进行一个详细的扩展分析:
2.1 C2WorkOrdinalStruct分析
C2WorkOrdinalStruct相对比较简单,里面就是基本的成员定义,具体如下:
C2WorkOrdinalStruct ordinal;的定义是基本信息
{
/**
* Information for ordering work items on a component port.
*/
struct C2WorkOrdinalStruct {
//public:
c2_cntr64_t timestamp;//帧显示的PTS
c2_cntr64_t frameIndex;//帧的index
c2_cntr64_t customOrdinal;//待更新
DEFINE_AND_DESCRIBE_C2STRUCT(WorkOrdinal)
C2FIELD(timestamp, "timestamp")
C2FIELD(frameIndex, "frame-index")
C2FIELD(customOrdinal, "custom-ordinal")
};
}
2.2 C2Buffer的分析
C2Buffer通常是作为Buffer的基类的,一把采用share_ptr的指针操作。里面有一个实现的impl的类中类,在类中类中完成了buffer的设置,其中比较重要的是维护了一个std::map<C2Param::CoreIndex, shared_ptr> mInfos的映射。
我们可以从源码中直接看到C2Buffer的调用是通过impl的类种类完成操作的。包含设置Info信息(PS:)、查询Info、注册Buffer销毁回调函数等操作。创建线性的Buffer对象、创建图像的buffer对象是在C2Buffer内部自己实现的。
C2Buffer的定义如下:
class C2Buffer {
public:
const C2BufferData data() const;
typedef void (*OnDestroyNotify) (const C2Buffer *buf, void *arg);
c2_status_t registerOnDestroyNotify(OnDestroyNotify onDestroyNotify, void *arg = nullptr);
c2_status_t unregisterOnDestroyNotify(OnDestroyNotify onDestroyNotify, void *arg = nullptr);
virtual ~C2Buffer() = default;
const std::vector<std::shared_ptr<const C2Info>> info() const;
c2_status_t setInfo(const std::shared_ptr<C2Info> &info);
bool hasInfo(C2Param::Type index) const;
std::shared_ptr<const C2Info> getInfo(C2Param::Type index) const;
std::shared_ptr<C2Info> removeInfo(C2Param::Type index);
static std::shared_ptr<C2Buffer> CreateLinearBuffer(const C2ConstLinearBlock &block);
static std::shared_ptr<C2Buffer> CreateGraphicBuffer(const C2ConstGraphicBlock &block);
protected:
// no public constructor
explicit C2Buffer(const std::vector<C2ConstLinearBlock> &blocks);
explicit C2Buffer(const std::vector<C2ConstGraphicBlock> &blocks);
private:
class Impl;
std::shared_ptr<Impl> mImpl;
};
////////////// C2Buffer::Impl 的定义 /////////////////////
class C2Buffer::Impl {
public:
Impl(C2Buffer *thiz, const std::vector<C2ConstLinearBlock> &blocks)
: mThis(thiz), mData(blocks) {}
Impl(C2Buffer *thiz, const std::vector<C2ConstGraphicBlock> &blocks)
: mThis(thiz), mData(blocks) {}
~Impl(){}
const C2BufferData &data() const { return mData; }
c2_status_t registerOnDestroyNotify(OnDestroyNotify onDestroyNotify, void *arg){}
c2_status_t unregisterOnDestroyNotify(OnDestroyNotify onDestroyNotify, void *arg){}
std::vector<std::shared_ptr<const C2Info>> info(){}
c2_status_t setInfo(const std::shared_ptr<C2Info> &info){}
bool hasInfo(C2Param::Type index){}
std::shared_ptr<const C2Info> getInfo(C2Param::Type index){}
std::shared_ptr<C2Info> removeInfo(C2Param::Type index){};
private:
C2Buffer * const mThis;
BufferDataBuddy mData;
std::map<C2Param::CoreIndex, std::shared_ptr<C2Info>> mInfos;
std::list<std::pair<OnDestroyNotify, void *>> mNotify;
};
2.2.1 C2BufferData的分析
C2BufferData是封装数据的对象,其可以包含任何一个线性块或者图像数据块,我们可以根据它的类型拿到数据。linearBlocks返回的是线性的数据,graphicBlocks是图像的数据。我们可以继续追踪到C2ConstLinearBlock内部的一个map函数,map函数返回的是一个C2ReadView指针,其C2ReadView对象中的data函数指向的是最后的数据地址。
class C2BufferData {
public:
enum type_t : uint32_t {
INVALID, ///< invalid buffer type. Do not use.
LINEAR, ///< the buffer contains a single linear block
LINEAR_CHUNKS, ///< the buffer contains one or more linear blocks
GRAPHIC, ///< the buffer contains a single graphic block
GRAPHIC_CHUNKS, ///< the buffer contains one of more graphic blocks
};
type_t type() const;
const std::vector<C2ConstLinearBlock> linearBlocks() const;
const std::vector<C2ConstGraphicBlock> graphicBlocks() const;
private:
class Impl;
std::shared_ptr<Impl> mImpl;
protected:
explicit C2BufferData(const std::vector<C2ConstLinearBlock> &blocks);
explicit C2BufferData(const std::vector<C2ConstGraphicBlock> &blocks);
};
2.3 C2Param的分析
C2Param 是C2Work被送到解码器接口的配置参数,其C2Param内部定义三个类CoreIndex、Type 、Index,这三个的关系在我上面的整体图的左侧已经给出来了,具体C2Param的作用呢就是C2组件和架构通信的时候的载体。
例如我们需要告诉上层视频的分辨率变化了,我们可以这样操作:
work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(*(C2StreamPictureSizeInfo::output)));
C2Param可以设置数据输入所携带的信息,也就可以在输出的时候设置。当然了,后面会罗列些基本的调用。
struct C2Param {
public:
/**
* C2Param kinds, usable as bitmaps.
*/
enum kind_t : uint32_t {
NONE = 0,
STRUCT = (1 << 0),
INFO = (1 << 1),
SETTING = (1 << 2),
TUNING = (1 << 3) | SETTING, // tunings are settings
};
typedef uint32_t type_index_t;
enum : uint32_t {
TYPE_INDEX_VENDOR_START = 0x00008000, ///< vendor indices SHALL start after this
};
////////////////////////////////// CoreIndex Begin ///////////////////////////////////////////
struct CoreIndex {
//public:
enum : uint32_t {
IS_FLEX_FLAG = 0x00010000,
IS_REQUEST_FLAG = 0x00020000,
};
protected:
enum : uint32_t {
KIND_MASK = 0xC0000000,
KIND_STRUCT = 0x00000000,
KIND_TUNING = 0x40000000,
KIND_SETTING = 0x80000000,
KIND_INFO = 0xC0000000,
DIR_MASK = 0x30000000,
DIR_GLOBAL = 0x20000000,
DIR_UNDEFINED = DIR_MASK, // MUST have all bits set
DIR_INPUT = 0x00000000,
DIR_OUTPUT = 0x10000000,
IS_STREAM_FLAG = 0x02000000,
STREAM_ID_MASK = 0x01F00000,
STREAM_ID_SHIFT = 20,
MAX_STREAM_ID = STREAM_ID_MASK >> STREAM_ID_SHIFT,
STREAM_MASK = IS_STREAM_FLAG | STREAM_ID_MASK,
IS_VENDOR_FLAG = 0x00008000,
TYPE_INDEX_MASK = 0x0000FFFF,
CORE_MASK = TYPE_INDEX_MASK | IS_FLEX_FLAG,
};
public:
inline CoreIndex(uint32_t index) : mIndex(index) { }
inline CoreIndex(uint64_t index) = delete;
inline bool isVendor() const { return mIndex & IS_VENDOR_FLAG; }
inline bool isFlexible() const { return mIndex & IS_FLEX_FLAG; }
inline uint32_t coreIndex() const { return mIndex & CORE_MASK; }
inline type_index_t typeIndex() const { return mIndex & TYPE_INDEX_MASK; }
DEFINE_FIELD_AND_MASK_BASED_COMPARISON_OPERATORS(CoreIndex, mIndex, CORE_MASK)
protected:
uint32_t mIndex;
};
////////////////////////////////// CoreIndex End///////////////////////////////////////////
////////////////////////////////// Type Begin ///////////////////////////////////////////
struct Type : public CoreIndex {
inline bool isGlobal() const { return (mIndex & DIR_MASK) == DIR_GLOBAL; }
inline bool forInput() const { return (mIndex & DIR_MASK) == DIR_INPUT; }
inline bool forOutput() const { return (mIndex & DIR_MASK) == DIR_OUTPUT; }
inline bool forStream() const { return mIndex & IS_STREAM_FLAG; }
inline bool forPort() const { return !forStream() && !isGlobal(); }
inline uint32_t type() const { return mIndex & (~STREAM_ID_MASK); }
inline kind_t kind(){}
inline Type(uint32_t index) : CoreIndex(index) { }
inline Type(uint64_t index) = delete;
DEFINE_FIELD_AND_MASK_BASED_COMPARISON_OPERATORS(Type, mIndex, ~STREAM_ID_MASK)
private:
friend struct C2Param; // for setPort()
friend struct C2Tuning; // for KIND_TUNING
friend struct C2Setting; // for KIND_SETTING
friend struct C2Info; // for KIND_INFO
template<typename T, typename S, int I, class F> friend struct C2GlobalParam;
template<typename T, typename S, int I, class F> friend struct C2PortParam; // for kDir*
template<typename T, typename S, int I, class F> friend struct C2StreamParam; // for kDir*
friend struct _C2ParamInspector;
inline bool setPort(bool output){}
}
////////////////////////////////// Type End ///////////////////////////////////////////
////////////////////////////////// Index Begin //////////////////////////////////////////
struct Index : public Type {
inline operator uint32_t() const { return mIndex; }
inline Index(uint32_t index) : Type(index) { }
inline Index(const Index &index) = default;
inline Index(uint64_t index){}
inline unsigned stream() {}
inline Index withStream(unsigned stream){}
inline Index withPort(bool output){}
DEFINE_FIELD_BASED_COMPARISON_OPERATORS(Index, mIndex)
private:
friend class C2InfoBuffer; // for convertTo*
friend struct C2Param; // for setStream, MakeStreamId, isValid, convertTo*
friend struct _C2ParamInspector; // for testing
inline bool isValid()
inline unsigned rawStream()
inline static uint32_t MakeStreamId(unsigned stream) {}
inline bool convertToStream(bool output, unsigned stream){}
inline void convertToPort(bool output){}
inline void convertToGlobal() {}
inline void convertToRequest() {}
inline bool setStream(unsigned stream){}
}
////////////////////////////////// Index End ///////////////////////////////////////////
public:
inline bool isVendor() const { return _mIndex.isVendor(); }
inline bool isFlexible() const { return _mIndex.isFlexible(); }
inline bool isGlobal() const { return _mIndex.isGlobal(); }
inline bool forInput() const { return _mIndex.forInput(); }
inline bool forOutput() const { return _mIndex.forOutput(); }
inline bool forStream() const { return _mIndex.forStream(); }
inline bool forPort() const { return _mIndex.forPort(); }
inline unsigned stream() const { return _mIndex.stream(); }
inline Type type() const { return _mIndex.type(); }
inline uint32_t index() const { return (uint32_t)_mIndex; }
inline CoreIndex coreIndex() const { return _mIndex.coreIndex(); }
inline kind_t kind() const { return _mIndex.kind(); }
inline size_t size() const { return _mSize; }
inline operator bool() const { return _mIndex.isValid() && _mSize > 0; }
inline bool operator!() const { return !operator bool(); }
inline bool operator==(const C2Param &o) {}
inline bool operator!=(const C2Param &o){}
inline static C2Param* From(void *addr, size_t len){}
inline static std::unique_ptr<C2Param> Copy(const C2Param &orig) {}
inline static std::unique_ptr<C2Param> CopyAsStream(
const C2Param &orig, bool output, unsigned stream) {}
inline static std::unique_ptr<C2Param> CopyAsPort(const C2Param &orig, bool output) {}
inline static std::unique_ptr<C2Param> CopyAsGlobal(const C2Param &orig) {}
inline static std::unique_ptr<C2Param> CopyAsRequest(const C2Param &orig) {}
protected:
inline bool setStream(unsigned stream){}
inline bool setPort(bool output) {}
inline void setSize(size_t size){}
public:
inline void invalidate(){}
inline bool updateFrom(const C2Param &other){}
protected:
inline static const C2Param* IfSuitable(
const C2Param* o, size_t size, Type type, size_t flexSize = 0, bool checkDir = true){}
inline C2Param(uint32_t paramSize, Index paramIndex){}
inline C2Param(uint32_t paramSize, Index paramIndex, unsigned stream){}
private:
friend struct _C2ParamInspector;
inline bool equals(const C2Param &o);
uint32_t _mSize;
Index _mIndex;
};
2.4 C2InfoBuffer分析
C2InfoBuffer这里面包含一个关于C2Param::Index的定义,一个C2BufferData的定义,给我的感觉就是将C2Param::Index和C2BufferData进行的封装,可以直接通过操作C2InfoBuffer拿到Index和C2BufferData内部的数据。不过这点需要以后再做修改。
class C2InfoBuffer {
public:
const C2Param::Index index();
const C2BufferData data();
C2InfoBuffer asGlobal();
C2InfoBuffer asPort(bool output);
C2InfoBuffer asStream(bool output, unsigned stream);
static C2InfoBuffer CreateLinearBuffer(C2Param::CoreIndex index, const C2ConstLinearBlock &block);
static C2InfoBuffer CreateGraphicBuffer(C2Param::CoreIndex index, const C2ConstGraphicBlock &block);
protected:
explicit C2InfoBuffer(C2Param::Index index, const std::vector<C2ConstLinearBlock> &blocks);
explicit C2InfoBuffer(C2Param::Index index, const std::vector<C2ConstGraphicBlock> &blocks);
private:
C2Param::Index mIndex;
C2BufferData mData;
explicit C2InfoBuffer(C2Param::Index index, const C2BufferData &data);
};
3、C2Work 输出数据 C2WorkLets的分析
在C2Work中关于C2WorkLets的定义是一个std::list<std::unique_ptr的链表结构。我们就很方便的追踪下其的定义,如下所示:
struct C2Worklet {
//public:
// IN
c2_node_id_t component;
/** Configuration changes to be applied before processing this worklet. */
std::vector<std::unique_ptr<C2Tuning>> tunings;
std::vector<std::unique_ptr<C2SettingResult>> failures;
// OUT
C2FrameData output;
};
在上面中,其余不重要的就不必做分析。C2FrameData output应该是借鉴输入Buffer的操作,将C2FrameData也作为output的结构使用了。
4、总结
本篇只是简单的分析了C2Work的数据结构,具体的调用分析,但是我们先弄清楚内部怎么定义的才能更好的分清楚其为什么这样调用。
比如说怎么拿到具体的需要解码的原始数据?
C2ReadView rView = mDummyReadView;
rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
inSize = rView.capacity();
又比如说,怎么拿到HDR数据?
for (const std::unique_ptr<C2Param> ¶m: work->input.configUpdate) {
if (param) {
C2StreamHdr10PlusInfo::input *hdr10PlusInfo =
C2StreamHdr10PlusInfo::input::From(param.get());
if (hdr10PlusInfo != nullptr) {
std::vector<std::unique_ptr<C2SettingResult>> failures;
std::unique_ptr<C2Param> outParam = C2Param::CopyAsStream(
*param.get(), true /*output*/, param->stream());
c2_status_t err = intf->config(
{ outParam.get() }, C2_MAY_BLOCK, &failures);
if (err == C2_OK) {
work->worklets.front()->output.configUpdate.push_back(
C2Param::Copy(*outParam.get()));
} else {
ALOGE("finishWork: Config update size failed");
}
break;
}
}
}
又或者说怎么设置参数?
// TODO: may need to make this explicit (have .set member)
typedef C2StreamParam<C2Info, C2ProfileLevelStruct, kParamIndexProfileLevel>
C2StreamProfileLevelInfo;
constexpr char C2_PARAMKEY_PROFILE_LEVEL[] = "coded.pl";
//VPX 软解内部定义Profile
private:
std::shared_ptr<C2StreamProfileLevelInfo::input> mProfileLevel;
//添加参数
addParameter(
DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
.withConstValue(new C2StreamProfileLevelInfo::input(0u,
C2Config::PROFILE_UNUSED, C2Config::LEVEL_UNUSED))
.build());
等等这些,不得不佩服人家设计的巧妙。