CedarX中代码技术的应用借鉴 (二)多态的方式创建格式解析器

前言

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

创建格式解析器

demuxComponent.c中根据source中的URL创建对应的格式解析器,譬如url为/sdcard/movie.mp4,则创建一个mp4的一个Parser,如果url是一个/sdcard/movie.mp3,则创建一个mp3的Parser。URL的后缀其实并不重要,一方面,可以根据后缀猜测这个片源是什么格式,然后加以验证,另一方面,也可以根据片源的二进制格式逐一判断,每种封装格式总一个魔数在文件开头。

//cedarx\xplayer\demuxComponent.c
int ret = CdxParserPrepare(&demux->source, flags, &demux->mutex, &demux->bCancelPrepare,
                &demux->pParser, &demux->pStream, &parserContorlTask, &parserContorlTask);

该函数定义在CdxParser.c中。

//cedarx\libcore\parser\base\CdxParser.c
int CdxParserPrepare(CdxDataSourceT *source, cdx_uint32 flags, pthread_mutex_t *mutex,
        cdx_bool *exit, CdxParserT **parser, CdxStreamT **stream, ContorlTask *parserTasks,
        ContorlTask *streamTasks)
{

    CDX_LOGD("source uri '%s'", source->uri);
    //创建source对应的stream对象,如果本地文件,则创建FILE类型的stream对象
    //如果是HTTP/RTMP片源,则创建对应的HTTP/RTMP流对象。
    int ret = CdxStreamOpen(source, mutex, exit, stream, streamTasks);
    if (ret < 0)
    {
        CDX_LOGE("open stream fail, uri(%s)", source->uri);
        goto out;
    }
	//根据流对象,创建对应的格式解析器parser,如果文件格式是mp4,则创建mp4 parser
    ret = CdxParserOpen(*stream, flags, mutex, exit, parser, parserTasks);
    if (ret < 0)
    {
        CDX_LOGE("open parser fail, uri(%s)", source->uri);
        goto out;
    }

out:
    return ret;
}

查看CdxStreamOpen的定义。

//cedarx\libcore\parser\base\CdxParser.c
int CdxParserOpen(CdxStreamT *stream, cdx_uint32 flags, pthread_mutex_t *mutex, cdx_bool *exit,
    CdxParserT **parser, ContorlTask *parserTasks)
{
    if(mutex)
        pthread_mutex_lock(mutex);

    if(exit && *exit)
    {
        CDX_LOGW("open parser user cancel.");
        if(mutex)
            pthread_mutex_unlock(mutex);
        return -1;
    }
    //在这里创建解析器
    *parser = CdxParserCreate(stream, flags);

    if(mutex)
        pthread_mutex_unlock(mutex);

    //省略......
    
    //初始化解析器
    return CdxParserInit(*parser);

}

追踪创建解析器的代码。

//cedarx\libcore\parser\base\CdxParser.c
CdxParserT *CdxParserCreate(CdxStreamT *stream, cdx_uint32 flags)
{
    cdx_char *sampleUri = NULL;
    struct CdxParserNodeS *psrNode;
    struct CdxParserNodeS *maxScoreNode = NULL;
    cdx_uint32 score = 0, maxScore = 0;
    CdxParserT *parser = NULL;
    CdxStreamProbeDataT *probeDataOld = NULL;
    union {
        CdxStreamProbeDataT probeData;
        char buf[sizeof(*probeDataOld) + sizeof(probeDataOld->uri[0])];
    } probeDataUnion;
    CdxStreamProbeDataT *probeData = &probeDataUnion.probeData;

    /*fast guess*/
    CdxStreamGetMetaData(stream, STREAM_METADATA_REDIRECT_URI, (void **)&sampleUri);

    if (!sampleUri)
    {
        CdxStreamGetMetaData(stream, STREAM_METADATA_ACCESSIBLE_URI, (void **)&sampleUri);
    }
    if (!sampleUri)
    {
        CdxStreamGetMetaData(stream, "uri", (void **)&sampleUri);
    }
     //根据后缀猜是什么封装格式,譬如.mp4
    probeDataOld = CdxStreamGetProbeData(stream);
    probeData->buf = probeDataOld->buf;
    probeData->len = probeDataOld->len;
    probeData->uri[0] = sampleUri;
    psrNode = ParserTypeGuess(sampleUri);
	//试着创建一个mp4解析器,检测是否文件是否mp4格式,得分100分则确定是MP4
    if (psrNode)
    {
        if (psrNode->creator->probe(probeData) == 100)
        {
            maxScoreNode = psrNode;
            goto found;
        }
    }

    /*fast guess not work, should ask all parser*/
    //如果快速判断失效,则只能一个封装一个封装地探测了,注意这里有个全局的parserList
    CdxListForEachEntry(psrNode, &parserList.list, node)
    {
        CDX_CHECK(psrNode->creator);
        CDX_CHECK(psrNode->creator->probe);
        score = psrNode->creator->probe(probeData);

        if (score == 100)
        {
            maxScoreNode = psrNode;
            goto found;
        }
        else if(score > maxScore)
        {
            maxScore = score;
            maxScoreNode = psrNode;
        }

    }
    if(!maxScoreNode)
    {
        CDX_LOGW("Sorry, I don't know what it is!");
        return NULL;
    }
found:
    CDX_LOGD("Good, it's '%s'",
            maxScoreNode->keyInfo->comment ? maxScoreNode->keyInfo->comment : "unknow");
    //根据找到的节点创建parser
    parser = maxScoreNode->creator->create(stream, flags);
    parser->type = maxScoreNode->type;
    CDX_LOGD("parser type(%d)", parser->type);
    //返回具体的parser
    return parser;
}

全局的parserList是一个链表,包含了所有支持的封装格式解析器节点。它的初始化是在main函数之前。

//cedarx\libcore\parser\base\CdxParser.c
static void AwParserInit(void) __attribute__((constructor));//初始化是在main函数之前
//注册所有的解析器,即parser,可见parser非常多,越常用的parser放置在越前边,更快探测到。
void AwParserInit(void)
{
    CDX_LOGI("aw parser init...");

    /* Make HLS be the first one since:
     * 1. HLS is widely used
     * 2. HLS parser's probe funtion is simple and reliable
     * 3. Other parsers' probe function are not reliable, so m3u8 can be
     * identified as other formats like TS.
     *
     * zhaozhili, 2016-11-21
     */
    AwParserRegister(&hlsParserCtor, CDX_PARSER_HLS, &hlsKeyInfo);

    AwParserRegister(&asfParserCtor, CDX_PARSER_ASF, &asfKeyInfo);
    AwParserRegister(&movParserCtor, CDX_PARSER_MOV, &movKeyInfo);
    AwParserRegister(&remuxParserCtor, CDX_PARSER_REMUX, &remuxKeyInfo);

    AwParserRegister(&flvParserCtor, CDX_PARSER_FLV, &flvKeyInfo);
    AwParserRegister(&aviParserCtor, CDX_PARSER_AVI, &aviKeyInfo);
    AwParserRegister(&tsParserCtor, CDX_PARSER_TS, &tsKeyInfo);

#ifdef __ANDROID__
    AwParserRegister(&dashParserCtor, CDX_PARSER_DASH, &dashKeyInfo);
    AwParserRegister(&mmsParserCtor, CDX_PARSER_MMS, &mmsKeyInfo);
    AwParserRegister(&mmshttpParserCtor, CDX_PARSER_MMSHTTP, &mmshttpKeyInfo);
#endif

    AwParserRegister(&mkvParserCtor, CDX_PARSER_MKV, &mkvKeyInfo);

#ifdef __ANDROID__
    AwParserRegister(&bdParserCtor, CDX_PARSER_BD, &bdKeyInfo);
    AwParserRegister(&pmpParserCtor, CDX_PARSER_PMP, &pmpKeyInfo);
#endif

    AwParserRegister(&oggParserCtor, CDX_PARSER_OGG, &oggKeyInfo);

#ifdef __ANDROID__
    AwParserRegister(&m3u9ParserCtor, CDX_PARSER_M3U9, &m3u9KeyInfo);
    AwParserRegister(&playlistParserCtor, CDX_PARSER_PLAYLIST, &playlistKeyInfo);

//* do not support widewine in cedarx for Android N,
//* just stagefright
#if (CONF_ANDROID_MAJOR_VER < 7)
    AwParserRegister(&wvmParserCtor, CDX_PARSER_WVM, &wvmKeyInfo);
#endif

    AwParserRegister(&envParserCtor, CDX_PARSER_ENV, &envKeyInfo);
#endif

    AwParserRegister(&mpgParserCtor, CDX_PARSER_MPG, &mpgKeyInfo);
    AwParserRegister(&apeParserCtor, CDX_PARSER_APE, &apeKeyInfo);
    AwParserRegister(&flacParserCtor, CDX_PARSER_FLAC, &flacKeyInfo);
    AwParserRegister(&amrParserCtor, CDX_PARSER_AMR, &amrKeyInfo);
#ifdef __ANDROID__
    AwParserRegister(&atracParserCtor, CDX_PARSER_ATRAC, &atracKeyInfo);
#endif
    AwParserRegister(&mp3ParserCtor, CDX_PARSER_MP3, &mp3KeyInfo);
    AwParserRegister(&aacParserCtor, CDX_PARSER_AAC, &aacKeyInfo);
    AwParserRegister(&wavParserCtor, CDX_PARSER_WAV, &wavKeyInfo);

#ifdef __ANDROID__
    AwParserRegister(&awtsParserCtor, CDX_PARSER_AWTS, &awtsKeyInfo);
    AwParserRegister(&sstrParserCtor, CDX_PARSER_SSTR, &sstrKeyInfo);
    AwParserRegister(&cafParserCtor, CDX_PARSER_CAF, &cafKeyInfo);
    AwParserRegister(&g729ParserCtor, CDX_PARSER_G729, &g729KeyInfo);
    AwParserRegister(&dsdParserCtor, CDX_PARSER_DSD, &dsdKeyInfo);
    AwParserRegister(&aiffParserCtor, CDX_PARSER_AIFF, &aiffKeyInfo);
#endif

    AwParserRegister(&id3ParserCtor, CDX_PARSER_ID3, &id3KeyInfo);
    CDX_LOGD("aw parser size:%d",parserList.size);
    return ;
}

先不管节点是怎样的结构体对象,当找到节点后,创建对应的parser, parser = maxScoreNode->creator->create(stream, flags)。creator的定义如下:

//cedarx\libcore\parser\include\CdxParser.h
struct CdxParserCreatorS
{
	//创建parser
    CdxParserT *(*create)(CdxStreamT *, cdx_uint32 /*flags*/);
    //探测是否某种封装格式,返回分数值
    cdx_uint32 (*probe)(CdxStreamProbeDataT *);/*return score(0-100)*/
};

CdxParserT 也就是我们要找到的parser对象。通过其Ops成员的各个函数指针,实现解析器的各个需要的接口,譬如探测数据,读取一笔数据,获取媒体信息,跳播到某个时间点,初始化与关闭等。

//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;
};
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值