Application of Code Technology in CedarX Reference CdxContainerOf

Keywords: C

Preface

CedarX is an open source multimedia SDK of Shizhi Technologies. The invocation of its decoding is based on self-developed decoder interface and MediaPlayer interface can be docked up. This paper records and analyses the application of code technology in C language in its source code for reference only.
Source Referencehttps://github.com/FREEWING-JP/OrangePi_CedarX

problem

Knowing how to create a Parser and what interfaces a Parser has, the problem arises, such as where the internal variables and objects of the FLV parser are located, because there is only one CdxParserTypeE and one CdxParserOpsS in the CdxParserT definition.

//cedarx\libcore\parser\include\CdxParser.h
typedef struct CdxParserS CdxParserT;
//Ops Type, Function Pointer, Interface Unified
struct CdxParserOpsS
{
    cdx_int32 (*control)(CdxParserT *, cdx_int32 /* cmd */, void * /* param */);

    cdx_int32 (*prefetch)(CdxParserT *, CdxPacketT * /* pkt */);

    cdx_int32 (*read)(CdxParserT *, CdxPacketT * /* pkt */);

    cdx_int32 (*getMediaInfo)(CdxParserT *, CdxMediaInfoT * /* MediaInfo */);

    cdx_int32 (*seekTo)(CdxParserT *, cdx_int64 /* timeUs */);

    cdx_uint32 (*attribute)(CdxParserT *); /*return falgs define as open's falgs*/

    cdx_int32 (*getStatus)(CdxParserT *); /*return enum CdxPrserStatusE*/

    cdx_int32 (*close)(CdxParserT *);

    cdx_int32 (*init)(CdxParserT *);
};
//Definition of Parser
struct CdxParserS
{
    enum CdxParserTypeE type;
    struct CdxParserOpsS *ops;
};

Use of CdxContainerOf with Impl

Let's look at the u CdxFlvParserCreate function. When you create a Parser, you create a CdxFlvParserImplT object, and if everything goes well, you return the member base of the CdxFlvParserImplT object, which is of type CdxParserT.

static CdxParserT *__CdxFlvParserCreate(CdxStreamT *stream, cdx_uint32 flags)
{
    CdxFlvParserImplT *impl;
    cdx_int32 result;

    impl = FlvInit(&result);
    CDX_FORCE_CHECK(impl);
    if(result < 0)
    {
        CDX_LOGE("Initiate flv file parser lib module error.");
        goto failure;
    }
    impl->base.ops = &flvParserOps;
    impl->stream = stream;

    impl->nVidPtsOffset = 0;
    impl->nAudPtsOffset = 0;
    impl->nSubPtsOffset = 0;

    impl->hasSyncVideo = 0;
    impl->hasSyncAudio = 0;
    impl->bFirstVidFrm = 1;

    impl->curChunkInfo.nChkType = 0;
    impl->curChunkInfo.nTotSize = 0;
    impl->curChunkInfo.nReadSize = 0;

    pthread_mutex_init(&impl->lock, NULL);
    pthread_cond_init(&impl->cond, NULL);
    impl->mStatus = CDX_FLV_INITIALIZED;
    impl->mErrno = PSR_INVALID;
    return &impl->base;

failure:
    if(impl)
    {
        __CdxFlvParserClose(&impl->base);
        impl = NULL;
    }
    return NULL;
}

Let's take a look at the definition of CdxFlvParserImplT. It is clear that members of the internal implementation of FLV Parser are in this CdxFlvParserImplT object. Then, how do internal implementations of other interfaces get this CdxFlvParserImplT object? After all, the parameters given to each interface do not directly see the CdxFlvSerParserImplT type parameters, only the CdxParserT type parameters.

typedef struct CdxFlvParserImplS
{
    CdxParserT      base;//Key point, containing CdxParserT object, named base
    CdxStreamT      *stream;
    CdxPacketT      pkt;
    void            *privData;
    cdx_int32       exitFlag;
    cdx_int32       mErrno;
    cdx_int32       mStatus;
    cdx_int32       flags;

    cdx_bool        hasVideo;
    cdx_bool        hasAudio;
    cdx_bool        hasSubTitle;

    cdx_int8        bFirstVidFrm;
    cdx_int8        bDiscardAud;        //  1:discard, 0:transport

    cdx_int8        videoStreamIndex;
    cdx_int8        audioStreamIndex;
    cdx_int8        subTitleStreamIndex;

    cdx_uint32      totalFrames;
    cdx_uint32      pictureNum;

    cdx_uint32      nPreFRSpeed;       //previous ff/rr speed, for dynamic adjust
    cdx_uint32      nFRSpeed;          //fast forward and fast backward speed,
                                       //multiple of normal speed
    cdx_uint32      nFRPicShowTime;    //picture show time under fast forward and backward
    cdx_uint32      nFRPicCnt;         //picture count under ff/rr, for check if need delay

    cdx_uint32      nVidPtsOffset;     //video time offset
    cdx_uint32      nAudPtsOffset;     //audio time offset
    cdx_uint32      nSubPtsOffset;     //subtitle time offset

    cdx_int8        hasSyncVideo;      //flag, mark that if has sync video
    cdx_int8        hasSyncAudio;      //flag, mark that if has sync audio

    cdx_uint32      startPos;
    cdx_int64       fileSize;

    cdx_int64       firstVideoTagPos;       //for xunlei 265 seek to first keyframe.

    AudioStreamInfo             aFormat;
    VideoStreamInfo             vFormat;
    SubtitleStreamInfo          tFormat;

    VideoStreamInfo             tempVformat;
    cdx_uint8                    *tempBuf;
    cdx_uint32                   tempBufLen;

    FlvChunkInfoT         curChunkInfo;  //current chunk information

    cdx_int32       h265_vps_sps_pps_state; //3: read vps 2: read sps 1: read pps,
                                            //0: back seek , -1: invalid
    cdx_uint32      h265_start_pos_stored;
    cdx_uint32      h265_data_size_stored;

    pthread_mutex_t lock;
    pthread_cond_t  cond;

    cdx_int32 bNoAvcSequenceHeader;
}CdxFlvParserImplT;

Let's look at the u CdxFlvParserInit interface and see how the impl object is internally obtained.

static int __CdxFlvParserInit(CdxParserT *parser)
{
    cdx_int32 result, ret = 0;
    CdxFlvParserImplT *impl;

    CDX_CHECK(parser);
    impl = CdxContainerOf(parser, CdxFlvParserImplT, base);//key point

    result = FlvOpen(impl);
    if(result < 0)
    {
        CDX_LOGE("Open flv failed.");
        impl->mErrno = PSR_OPEN_FAIL;
        ret = -1;
        goto __exit;
    }
    CDX_LOGI("read flv head finish.");

    impl->mErrno = PSR_OK;
    ret = 0;

__exit:
    pthread_mutex_lock(&impl->lock);
    impl->mStatus = CDX_FLV_IDLE;
    pthread_mutex_unlock(&impl->lock);
    pthread_cond_signal(&impl->cond);
    return ret;
}

The key point is that CdxContainerOf, essentially identical to the container_of macro of the Linux kernel. Pass parser to find impl of type CdxFlvParserImplT, and get other internal variables and structure objects! Thus, CdxParserTThe definition does not need to contain specific Parser member objects. When each specific Parser is created, it creates its own ParserImplT and uses CdxParserT as the first member of ParserImplT, named base, so that CdxParserT is associated with ParserImplT and ParserImplT can be found through CdxParserT!
For the container_of macro of the Linux kernel, the function of this macro is simply to get a pointer to the container (structure) through a pointer to a member of a container (structure). Simply, to find a container through a member, in this case, CdxFlvParserImplT can be found through CdxParserT!
For an explanation of container_of, refer to the following article.
Detailed Linux Kernel container_of (illustration)

Posted by Felex on Sat, 25 Sep 2021 10:22:54 -0700