前言
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;
};
 
                   
                   
                   
                   
                             本文详细解析了CedarX SDK中如何通过C语言创建格式解析器,包括根据URL选择合适的Parser,如mp4、mp3等,并介绍了Parser的初始化过程和关键代码段。着重讲解了源码中对封装格式探测和Parser Creator的使用。
本文详细解析了CedarX SDK中如何通过C语言创建格式解析器,包括根据URL选择合适的Parser,如mp4、mp3等,并介绍了Parser的初始化过程和关键代码段。着重讲解了源码中对封装格式探测和Parser Creator的使用。
           
                     
       
           
                 
                 
                 
                 
                 
                
               
                 
                 
                 
                 
                
               
                 
                 扫一扫
扫一扫
                     
              
             
                  
 被折叠的  条评论
		 为什么被折叠?
被折叠的  条评论
		 为什么被折叠?
		 
		  到【灌水乐园】发言
到【灌水乐园】发言                                
		 
		 
    
   
    
   
             
            


 
            