CedarX中代码技术的应用借鉴 (四)CdxContainerOf

前言

CedarX是全志科技开源的多媒体SDK,其解码的调用基于自研的解码器接口,向上可对接MediaPlayer接口。本文记录与分析其源代码中对于C语言方面的代码技术的应用,仅作记录与借鉴。
源码参考于https://github.com/FREEWING-JP/OrangePi_CedarX

问题

知道如何创建一个Parser,也了解到一个Parser有哪些接口,那么问题来了,譬如,FLV解析器的内部变量与对象放在哪里,因为CdxParserT定义中只有一个CdxParserTypeE 和一个CdxParserOpsS。

//cedarx\libcore\parser\include\CdxParser.h
typedef struct CdxParserS CdxParserT;
//Ops类型,函数指针,接口统一
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 *);
};
//Parser的定义
struct CdxParserS
{
    enum CdxParserTypeE type;
    struct CdxParserOpsS *ops;
};

CdxContainerOf与Impl的搭配使用

我们看__CdxFlvParserCreate函数。在创建Parser时,创建了一个CdxFlvParserImplT 对象,如果一切顺利,返回的是CdxFlvParserImplT 对象的成员base,其类型就是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;
}

我们看一下CdxFlvParserImplT 的定义。可见,关于FLV Parser内部实现的成员,都在这个CdxFlvParserImplT 对象中。那么,其他接口的内部实现,如何获取到这个CdxFlvParserImplT 对象,毕竟给到每个接口的参数,并没有直接看到CdxFlvParserImplT类型参数,只看到CdxParserT类型参数。

typedef struct CdxFlvParserImplS
{
    CdxParserT      base;//关键点,包含CdxParserT对象,名字为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;

我们看一下__CdxFlvParserInit接口,内部如何获取到impl对象。

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

    CDX_CHECK(parser);
    impl = CdxContainerOf(parser, CdxFlvParserImplT, base);//关键点

    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;
}

关键点就在于CdxContainerOf,实质与Linux内核的container_of宏一致。通过传参parser找到CdxFlvParserImplT 类型的impl,继而获取到其他内部变量与结构体对象!由此,CdxParserT 的定义并不需要包含具体的Parser的成员对象,每个具体的Parser在创建的时候,会创建自己的ParserImplT,并将CdxParserT 作为ParserImplT的第一个成员,名字为base,这样,CdxParserT 就与ParserImplT关联到一起,通过CdxParserT 就可以找到ParserImplT!
对于Linux内核的container_of宏,这个宏的作用其实很简单,就是通过一个容器(结构体)中某个成员的指针得到指向这个容器(结构体)的指针,简单的说就是通过成员找容器,在这个案例中,就是通过CdxParserT 就可以找到CdxFlvParserImplT!
关于container_of的解释,可以参考以下文章。
Linux内核container_of详解(图解)
container of()函数简介

你可能感兴趣的:(CedarX编码技术,c语言,音视频)