OpenCORE原理和核心代码破解--1

一.OpenCORE整体结构

逻辑层主要由PVPlayer,PlayerDriver,PVPlayerEngine来组成,其中PVPlayer主要接受上层的操作(JNI),PlayerDriver负责连接PVPlayer与PVPlayerEngine,主要是将从PVPlayer的指令交给engine来处理,PVPlayerEngine负责真正的播放处理,如注册节点,建立节点,初始化节点,建立节点间的联系等.

OpenCORE数据处理部分是基于一种节点(Node)的结构,主要由sourcenode(parsernode),decnode,sinknode组成.sourcenode主要负责文件的解析,decnode负责对解析出来的数据进行解码,而sinknode是建立相应的输出管道video/audio,sinknode只是针对输出的一种抽象,真正工作的是PVMediaOutPutNode,PVMediaOutPutNode的作用主要是将数据分发给相应的Video输出和Audio输出.每个node都有一个自身的状态(state),在播放之前必须保证每一个涉及到的节点状态的正确性,才能完成播放,每个节点几乎都有从idle->init->prepare->start的状态,,在播放之前,每个节点必须出于start状态.
  
   OpenCORE的大部分代码基于”命令构建->命令发出->异步处理->处理结果反馈”的一种模式,大部分的代码执行结果都保存在构建的命令或参数之中,因为有命令,就需要去维持一个命令队列,因此OpenCore的代码看起来很烦琐.异步提高了程序的执行效率,但是也会涉及到同步问题,opencore的代码却避免了这个问题,熟悉了opencore的代码之后,架构得也很清晰.例如,在engine中如果要去执行sourcenode的Init(),首先engine中会去构造一个PVPlayerEngineContext这个命令的上下文,然后调用parsernode的init函数,将这个上下文做为参数传递给init函数,init函数中又会去根据这个上下构建一个init的parsernode命令,然后加入到pasernode的命令队列之中,这时候结果已经返回给了engine,返回的结果仅仅是构建的这个命令加入parsernode命令队列的成功与否,在parsernode内部会继续在另一个线程中执行init的操作,最后init的操作执行完成后,通过ReportCmdCompleteEvent这个函数,将执行的结果通知给engine,然后engine就可以根据结果进行下面的处理.

二.文件的播放流程
  
必要的步骤是:

1.文件的识别
   2.根据识别的结果建立相应的sourcenode,初始化sourcenode,解析出流的格式(如音频,视频,文字流等).
   3.建立输出管道,初始化管道状态
  4.根据流的格式建立起相应的decNode,或者如果不需要decNode如字幕,PCM格式的音频等.初始化decNode.
   5.请求port(做为各节点输入输出数据的一个抽象的”端口”)
   6.配置mediaoutput输出环境(ConfigMIO(MediaInput/Output)).如Video需要配置视频的宽和高,视频显示的宽和高,subformattype.音频需要配置声道数,采样率(Hz),formattype.

完成以上的工作后,基本上就可以通过parsernode去解析文件,然后将数据通过相应的port发送出去,完成播放了

OpenCORE原理和核心代码破解--2

三.avi的解析(parserNode的实现).

要完成avi的解析就先要对文件进行识别,识别之后才能建立相应的parsernode节点.工作原理:首先注册avi的识别方法,储存到一个STL容器之中,这个STL中还保存了其它文件的识别方法.文件过来之后,首先遍历这个STL容器,分别调用各个识别方法对文件进行识别,如果成功则说明这个文件是这个识别方法所支持的格式.而parserNode在进行注册的时候会指定这个parsernNode支持文件的格式,这样就可以一一对应起来.

1>  avi的识别recognizer实现
  
   文件的识别模块在/pvmi/recognizer/plugins下,可以仿照其它识别完成相应文件目录建立.类文件也可以参照其它格式的来建立.例如avi这里主要完成了pvaviffrec_factory.h/pvaviffrec_factory.cpp/pvaviff_plugin.h/pvaviff_plugin.cpp几个文件factory类和plugin类可以像其它格式一样继承接口,实现其中的方法.例如
pvaviff_plugin.cpp中实现的方法:

PVMFStatus    PVAVIFFRecognizerPlugin::SupportedFormats(PVMFRecognizerMIMEStringList&aSupportedFormatsList)
{
    // Return AVI assupported type
    OSCL_HeapString<OsclMemAllocator>supportedformat = PVMF_MIME_AVIFF;
   aSupportedFormatsList.push_back(supportedformat);
   returnPVMFSuccess;
}

这个方法用来返回这个识别方法支持的文件格式,在这里我们用了PVMF_MIME_AVIFF,  PVMF_MIME_AVIFF这个宏在Pvmf_format_type.h中被定义,
#definePVMF_MIME_AVIFF        "x-pvmf/mux/avi"
后面的字符串内容并没有特殊的含义,也不影响程序的执行.不过在业界对于各个媒体文件有专门MimeType.

PVMFStatusPVAVIFFRecognizerPlugin::Recognize(PVMFDataStreamFactory&aSourceDataStreamFactory, PVMFRecognizerMIMEStringList*aFormatHint,
       Oscl_Vector<PVMFRecognizerResult,OsclMemAllocator>& aRecognizerResult)
{
  ...
}
这里是文件的识别方法avi是基于RIFF的一种文件结构,avi文件一般前4个字节用来标识RIFF这个标识,后4个字节来标识文件的大小,再后面4个字节,用来标识文件类型”AVI”,因此可以确定文件类型为AVI.

PVMFStatusPVAVIFFRecognizerPlugin::GetRequiredMinBytesForRecognition(uint32&aBytes)
{
    aBytes =AVIFF_MIN_DATA_SIZE_FOR_RECOGNITION; //12
    returnPVMFSuccess;
}
由上分析,avi的识别文件的大小必须至少是12个字节..

2>  avi的识别recognizer的注册.
  
   完成recognizer之后需要将这个recoginzer注册到系统中.在/engines/player/config/core/pv_player_node_registry_populator.cpp中RegisterAllRecognizers方法中进行注册,注意引入相关的头文件.

tmpfac =OSCL_STATIC_CAST(PVMFRecognizerPluginFactory*,OSCL_NEW(PVAVIFFRecognizerFactory, ()));
       if(PVMFRecognizerRegistry::RegisterPlugin(*tmpfac)== PVMFSuccess)
        {
          aRegistry->RegisterRecognizer(tmpfac);
           nodeList->push_back(tmpfac);
       }
        else
       {
           OSCL_DELETE((PVAVIFFRecognizerFactory*)tmpfac);
           tmpfac = NULL;
           return;
       }

这样就完成recognizer部分

3>avi parserNode的实现.
  
  parserNode的主要功能是解析,除了在播放的时候解析出相应的流数据以供播放之外.parserNode还担任者其它节点在准备播放以前所需要的数据以完成各个状态的初始化工作.如engine会调用方法去获得parserNode的流的类型,以此建立相应的decNode,并且初始化decNode.
  parserNode仅仅是一个解析的工作节点,真正的提供的解析类在/fileformats/下的各个文件夹.如avi的parser类是在/fileformats/avi/下.parserNode主要是去调用相应/fileformats/下解析类的API,去完成这个parserNode中各个方法的实现.
  在/nodes/下面有各个媒体的parserNode,可以仿照其它建立起avi的parsernode文件夹.如/nodes/pvaviffparsernode/,目录和类文件的建立也可以仿照其它来创建.这里必须的一个factory类,一个node类和一个port类.

Factory类

factory类主要提供给外界用来创建parserNode类对象的一个工厂类.可以完全按照其它媒体格式文件的方式来完成这个avi的factory类,注意这里需要定义一个PVUuid,uuid是唯一标识.可以在控制台中直接输入uuidgen命令来生成一个uuid,初始化uuid即可.如
//uuidgengenerate:64cf3733-a8bd-4f39-8a48-66951b54bbf2
#defineKPVMFAviFFParserNodeUuidPVUuid(0x64cf3733,0xa8bd,0x4f39,0x8a,0x48,0x66,0x95,0x1b,0x54,0xbb,0xf2)
    
parserNode类

parserNode类包括涉及到的一些其它类规模比较庞大,但是parserNode在OpenCORE框架中已经被定位,所要实现的接口,实现的功能等都已经被设定.因此可以仿照其它媒体格式来写这个avi的parserNode,如Mp4.下面就是aviparsernode中的一些基本类.

pvmf_aviffparser_node.h

在pvmf_aviffparser_node.h这个头文件中大约就有1千行的代码,有下面几个类
PVMFAVIFFParserNode       //parsernode类定义                          必须
PVMFAVIFFParserNodeCommand,//框架内实现avi的command类,可以直接copymp4 .  必须
PVMFAVIFFPortIter,        //                                          可以不要
PVMFAVIParserNodeLoggerDestructDealloc//pvlogger用                      可以不要
  
在源码中可以看到 继承关系
classPVMFAVIFFParserNode:public OsclTimerObject,
    publicPVMFNodeInterface,
    publicPVMFDataSourceInitializationExtensionInterface,
   public PVMFTrackSelectionExtensionInterface,
    publicPvmfDataSourcePlaybackControlInterface,
    publicPVMFMetadataExtensionInterface,
    publicPVMFTrackLevelInfoExtensionInterface,
    publicPVMFCPMStatusObserver,
    publicPvmiDataStreamObserver,
    publicPVMIDatastreamuserInterface,
    publicPVMFFormatProgDownloadSupportInterface,
    publicOsclTimerObserver,
    publicPVMFCPMPluginLicenseInterface,
    publicPvmiCapabilityAndConfig,
    publicPVMFMediaClockStateObserver, // For observing the playback clockstates
    publicPvmfDataSourceDirectionControlInterface
   
其中OsclTimerObject,
    OsclTimerObserver,
     PVMFNodeInterface,
     PVMFDataSourceInitializationExtensionInterface,
     PVMFTrackSelectionExtensionInterface,
    PvmfDataSourcePlaybackControlInterface
    PVMFMediaClockStateObserver

这个几个是必要继承的类,需要去实现其中的纯虚函数.否则在pasernode的初始化过程中会不成功PVMFAVIFFParserNode类的头文件方法定义和变量定义基本上也都可以copymp4的来,有的没有继承的类,是不必要去实现其中的虚方法和相关变量定义的.

pvmf_aviffparser_node.cpp

这里是parserNode的实现,前面提到node部分工作大多基于命令的一种方式,因此我们在parsernode就需要去完成这个一结构.这部分代码基本上都可以参照MP4的来写,适应avi的即可.可以大部分复制mp4的代码过来,比如QueryInterface(查找接口)这一过程:

QueryInterface这个方法是在engine中频繁被调用的,主要是为了获得相应功能的指针,如parserNode是继承了PVMFDataSourceInitializationExtensionInterface这个抽象类,因此我们要实现其中的纯虚方法如SetSourceInitializationData,但是在engine中并不是直接通过用parserNode的指针来调用SetSourceInitializationData这个方法,而是先通过QueryInterface的方式来先查找到这个PVMFDataSourceInitializationExtensionInterface的实例化指针,其实也就是parserNode指针本身,QueryInterface的最终作用就是根据参数uuid来查找这个子类是否支持了相应的抽象类,然后将本身parsernode指针转化为父类的指针,返回给engine,然后engine调用抽象类中被实现的方法,完成功能调用.

首先engine中会通过iSourceNode->QueryInterface来调用这个接口
在queryInterface之中

PVMFAVIFFParserNodeCommandcmd;
cmd.PVMFAVIFFParserNodeCommandBase::Construct(aSessionId,PVMF_GENERIC_NODE_QUERYINTERFACE, aUuid, aInterfacePtr,aContext);
returnQueueCommandL(cmd);

会构造一个PVMF_GENERIC_NODE_QUERYINTERFACE这个类型cmd,然后调用QueueCommandL把这个命令加入到队列之中
QueueCommandL代码:

PVMFCommandId id;
       id = iInputCommands.AddL(aCmd);
       PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger,PVLOGMSG_STACK_TRACE, (0, "PVMFAVIFFParserNode::QueueCommandL()called id=%d", id));
        /*Wakeup the AO */
        RunIfNotReady();    //起动线程的Run来处理这个命令的队列
       returnid;

在run函数中,会调用ProcessCommand来处理命令队列.  
     //Process commands.
   if (!iInputCommands.empty())
    {
       ProcessCommand();
   }

在ProcessCommand中
      
          casePVMF_GENERIC_NODE_QUERYINTERFACE:
           cmdstatus = DoQueryInterface(aCmd);
           CommandComplete(iInputCommands, aCmd,cmdstatus);
           break;

可见真正执行的是DoQueryInterface这个方法,DoQueryInterface中又会调用queryInterface,根据uuid,转化指针.
CommandComplete中结束命令操作,通过  
         //Report completion to the session observer.
          ReportCmdCompleteEvent(session,resp)
将结果通知给engine.

这是这个命令的执行过程,其它的大部分方法如,Init,RequestPort,Pause,Start,Reset等大部分都是以这种形式,因此代码大部分都可以直接copymp4的,然后修改适应avi.

OpenCORE原理和核心代码破解—3

下面主要介绍一些针对avi这种媒体格式,而去实现的一些函数.从node的idle状态到start状态
     
Init过程

PVMFStatusPVMFAVIFFParserNode::SetSourceInitializationData(OSCL_wString&aSourceURL,PVMFFormatType& aSourceFormat,OsclAny*aSourceData)
这个函数主要完成
     //cleanup any prior source.
     CleanupFileSource();
      iFilename =aSourceURL;

其它的一些代码如果是不支持在线播放或者CPM管理的基本上可以忽略

DoInit(),这个过程主要完成创建aviparser 类的指针,

PVMFStatusPVMFAVIFFParserNode::DoInit(PVMFAVIFFParserNodeCommand& aCmd)
{
  ...
}
在其它媒体格式中有去初始化CPM的(ContentPolicyManager)的,这方面了解不多,但也不影响播放,所以没有在avi的parsernode中去实现这一块.Init的主要主要作用就完成parser类指针的创建.
可以看到在avi的源码中还最终回去调用ParseAVIFile这个函数:

PVMFDataStreamFactory*dsFactory = iCPMContentAccessFactory;
    if ((dsFactory== NULL) && (iDataStreamFactory != NULL))
     {
       dsFactory = iDataStreamFactory;
    }

int32 error = 0;

OSCL_TRY(error, iAVIFileHandle =PVAviFile::CreateAviFileParser(iFilename, error,&iFileServer,dsFactory,iFileHandle));
其中dsFactory和iFileHandle如果不是需要在线播放,这两个参数不设置并不会影响.

Prepare过程

在这之前engine需要获得一些媒体的其它信息,如媒体的作者,播放时长等.主要是通过PVMFMetadataExtensionInterface这个抽象类的方法来实现.这个也可以不必实现,不是必须的.
如在avi中,我们只是提供了一个”duration”播放时长,留个engine使用.实现的几个方法
uint32GetNumMetadataKeys(char* aQueryKeyString = NULL);
uint32GetNumMetadataValues(PVMFMetadataList& aKeyList);
PVMFCommandIdGetNodeMetadataKeys(PVMFSessionId aSessionId, PVMFMetadataList&aKeyList, uint32 aStartingKeyIndex, int32 aMaxKeyEntries,
                                           char* aQueryKeyString = NULL, constOsclAny* aContextData = NULL);
PVMFCommandIdGetNodeMetadataValues(PVMFSessionId aSessionId, PVMFMetadataList&aKeyList,
                                             Oscl_Vector<PvmiKvp, OsclMemAllocator>& aValueList,uint32 aStartingValueIndex, int32 aMaxValueEntries, const OsclAny*aContextData = NULL);
PVMFStatusReleaseNodeMetadataKeys(PVMFMetadataList& aKeyList, uint32aStartingKeyIndex, uint32 aEndKeyIndex);
PVMFStatusReleaseNodeMetadataValues(Oscl_Vector<PvmiKvp, OsclMemAllocator>&aValueList, uint32 aStartingValueIndex, uint32aEndValueIndex);

uint32PVMFAVIFFParserNode::GetNumMetadataKeys(char* aQueryString)
{
   uint32 num_entries = 0;
    if(aQueryString ==NULL || iAVIFileHandle == NULL)
        return0;
    if (oscl_strstr("duration",aQueryString) != NULL)
    {
       num_entries++;
    }
   return num_entries;
}
uint32PVMFAVIFFParserNode::GetNumMetadataValues(PVMFMetadataList&aKeyList)
{
    uint32 num_entries = 0;
   uint32 numKeys = aKeyList.size();
    for(uint32 i=0;i <numKeys;i++)
    {
        if (oscl_strcmp(aKeyList[i].get_cstr(), "duration")== 0)
         {
            // Duration
               // Increment thecounter for the number of values found so far
            ++num_entries;
        }
    }
    returnnum_entries;
}
GetNodeMetadataKeys和GetNodeMetadataValues都是以命令形式执行.

PVMFStatusPVMFAVIFFParserNode::DoGetMetadataKeys(PVMFAVIFFParserNodeCommand&aCmd)
{
  ...
}
这个函数主要作用是返回key的的列表,这个key的列表有parser类来决定.依靠解析出来的,我们这里只支持”duration”这个key
PVMFStatusPVMFAVIFFParserNode::DoGetMetadataValues(PVMFAVIFFParserNodeCommand&aCmd)
{
       PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger,PVLOGMSG_STACK_TRACE,
                   (0,"PVMFAVIFFParserNode::DoGetMetadataValues() In"));

PVMFMetadataList* keylistptr_in = NULL;
   PVMFMetadataList* keylistptr = NULL;
   PVMFMetadataList completeKeyList;
   Oscl_Vector<PvmiKvp, OsclMemAllocator>* valuelistptr = NULL;
   uint32 starting_index;
    int32 max_entries;

// Extract parameters from command structure
   aCmd.PVMFAVIFFParserNodeCommand::Parse(keylistptr_in,
                                        valuelistptr,
                                        starting_index,
                                         max_entries);

if (iAVIFileHandle == NULL || keylistptr_in == NULL ||valuelistptr == NULL)
    {
        //The list pointer is invalid, or we cannot access the mp3 fflibrary.
        returnPVMFFailure;
    }

PvmiKvpKeyVal;
    KeyVal.key = NULL;
    uint32KeyLen = 0;
    uint32 numKeys = 0;
   PVMFMetadataList keylistptr_tmp = *keylistptr_in;
   numKeys = keylistptr_tmp.size();
    if (numKeys == 0 ||starting_index > (numKeys - 1) || max_entries == 0)
   {
        return PVMFErrArgument;
   }

//just support get duration here...~~
    for(uint32 i=0;i<numKeys;i++)
   {
        if(oscl_strstr(keylistptr_tmp[i].get_cstr(), "duration"))
       {
            //duration
            uint32 duration = 0;
            duration = iAVIFileHandle->GetFileDuration()*1000;
           if (duration > 0)
           {
               KeyLen =oscl_strlen("duration") + 1; // for "duration;"
               KeyLen +=oscl_strlen(PVMI_KVPVALTYPE_STRING); // for "valtype="
               KeyLen +=oscl_strlen(PVMI_KVPVALTYPE_UINT32_STRING) + 1; // for "uint32;"
               KeyLen +=oscl_strlen("timescale=1000") + 1; // for "timescale=1000"and NULL terminator

// Allocate memory for the string
               int32 leavecode =OsclErrNone;
               KeyVal.key = (char*) AllocateKVPKeyArray(leavecode,PVMI_KVPVALTYPE_CHARPTR, KeyLen);
               if (OsclErrNone == leavecode)
               {
                  // Copy the key string
                   oscl_strncpy(KeyVal.key,"duration", oscl_strlen("duration") + 1);
                  oscl_strncat(KeyVal.key, ";",oscl_strlen(";"));
                   oscl_strncat(KeyVal.key,PVMI_KVPVALTYPE_STRING, oscl_strlen(PVMI_KVPVALTYPE_STRING));
                  oscl_strncat(KeyVal.key,PVMI_KVPVALTYPE_UINT32_STRING,oscl_strlen(PVMI_KVPVALTYPE_UINT32_STRING));
                  oscl_strncat(KeyVal.key, ";",oscl_strlen(";"));
                   oscl_strncat(KeyVal.key,"timescale=1000", oscl_strlen("timescale=1000"));
                  KeyVal.key[KeyLen-1] = NULL_TERM_CHAR;
                   //Copy the value
                   KeyVal.value.uint32_value =duration;
                   // Set the length and capacity
                  KeyVal.length = 1;
                   KeyVal.capacity= 1;
               }
               else
               {
                   // Memory allocation failed
                   KeyVal.key= NULL;
                   break;
               }
           }

}
   }

// Add the KVP to the list if the keystring was created
    if (KeyVal.key != NULL)
   {
            int32leavecode = OsclErrNone;
        leavecode= PushKVPValue(KeyVal, *valuelistptr);
        if(OsclErrNone != leavecode)
        {
           // push kvp failed
           returnPVMFErrNoMemory;
        }
   }

returnPVMFSuccess;
}
这个函数的作用是根据key的值,去找到相应的value值.这里只是取得一个           
          uint32 duration = 0;
            duration =iAVIFileHandle->GetFileDuration()*1000;//单位ms
这部分代码的执行过程基本上都可以copy上面,我们只需要找到相应的key,并且把value赋值就可了.

GetMediaPresentationInfo取得流的类型

此方法是比较重要的一个方法,是必去实现的.它完成的主要功能是对文件进行解析,解析出各个流的格式,比如音频流,视频流,文字流...等,并且为每个流指定一个mimitype,这个mimetype会对应decNode支持的mimeType,从而将parserNode解析出来的流指定decNode.
  Avi中的实现:
PVMFStatusPVMFAVIFFParserNode::GetMediaPresentationInfo(PVMFMediaPresentationInfo&aInfo)
{
     ...
}
这个函数主要作用是填充GetMediaPresentationInfo这个结构体,如在avi的实现中有
aInfo.setDurationValue(iAVIFileHandle->GetFileDuration()*1000);//设置持续时间ms
aInfo.setDurationTimeScale(timeScale);//设置timescale
aInfo.addTrackInfo(tmpTrackInfoVideo);//保存trackinfo,流信息
tmpTrackInfoVideo是一个PVMFTrackInfo的结构体,一个媒体文件可能有多个流,因此我们需要add多个trackinfo.在avi的GetStream2TrackInfo中,可以看到如何去填充这个trackinfo这个结构体.
    aTrack.setTrackID(i); //设置track  id
     aTrack.setPortTag(i);    //设置porttag     
    aTrack.setTrackDurationTimeScale(iAVIFileHandle->GetScale(i));
    aTrack.setTrackDurationValue(iAVIFileHandle->GetStreamDuration(i));
    aTrack.setTrackMimeType(mime_type);
因为针对各个媒体格式,我们解析所获得的mimetype的类型是不同的,所以这部分的实现过程不同.因此需要有一些对这个多媒体格式的一些认识...不需要非常专业.
如在avi中如何获得video的mimetype
      …
     iAVIFileHandle->GetFormatSpecificInfo(i,aFormatSpecificDataFrag);
      ...
       if(oscl_strstr(iAVIFileHandle->GetStreamMimeType(i).get_cstr(),"video"))
     {
           uint8 fmtType[4] = {0};
           uint32 size = 4;
           iAVIFileHandle->GetVideoFormatType((uint8*)fmtType, size,i);
            uint32temp = MAKE_FOURCC(fmtType[0], fmtType[1], fmtType[2],fmtType[3]);

BitmapInfoHhr* videoHdr =OSCL_STATIC_CAST(BitmapInfoHhr*,aFormatSpecificDataFrag.getMemFragPtr());
          if(!oscl_strncmp((char*)fmtType, "DIB ", size))
           {
              if (BITS_PER_SAMPLE12 ==videoHdr->BiBitCount)
                {
                     mime_type= _STRLIT_CHAR(PVMF_MIME_RGB12);
                    iSampleSizeVideo = videoHdr->BiBitCount;
                }
              else if(BITS_PER_SAMPLE24 == videoHdr->BiBitCount)
                {
                 mime_type = _STRLIT_CHAR(PVMF_MIME_RGB24);
                  iSampleSizeVideo =videoHdr->BiBitCount;
                }
              else
                {
                  returnPVMFFailure;
                }
           }
           else if(IsYUVFormat_Supported(temp))
           {
                 mime_type = _STRLIT_CHAR(PVMF_MIME_YUV420);
           }
          else
           {
               return PVMFFailure;
           }

}
这段代码主要是获得视频流的mimetype,主要是看parser类能提供些什么,这而avi的parser类提供了一个GetVideoFormatType这个方法,返回的是一个字符数组uint8fmtType[4],因为我们无decNode只能解未经压缩的数据,因此我们将其和”DIB”比较.然后再获得BitmapInfoHhr,确定是PVMF_MIME_RGB12还是PVMF_MIME_RGB24.另外如果是YUV支持的,直接将其type设置为PVMF_MIME_YUV420.

DoRequestPort请求端口,内存池分配的实现
  
   请求port,port类是一个继承PvmfPortBaseImpl和PvmiCapabilityAndConfigPortFormatImpl.这个类很重要.负责联系decNode或者输出node(因为有可能有的格式不需要decNode)的一个”端口”.每个流会对应一个port.DoRequestPort里面会去每个流实例化一个在avi中PVAVIFFNodeTrackPortInfo对象,这个结构体里面储存了一些流的信息,每个格式可能不同,主要说一些共同部分.
主要有
   PVAVIFFNodeTrackPortInfo trackportinfo;
   trackportinfo.iTrackId = trackid;               //流的id
   trackportinfo.iPortInterface = outport;         //流的”port”端口
   trackportinfo.iFormatType = formattype;         //流的formattype
   trackportinfo.iFormatTypeInteger =PVMF_AVI_PARSER_NODE_AVI_AUDIO;//流的类型
   trackportinfo.iNumSamples = 10;                 //流的采样个数,在取采样数据的时候标识
                                                //去多少个采样数据发送出去.一般视频为1
   trackportinfo.iMimeType = (*mimetype);     //流的mimetyoe
   trackportinfo.iClockConverter = clockconv;     //流的clock
   trackportinfo.iState =PVAVIFFNodeTrackPortInfo::TRACKSTATE_UNINITIALIZED;//流的状态
                                                                         
    trackportinfo.iTrackMaxDataSize= trackmaxqueuedepth*trackmaxdatasize;//流的采样数据大小
                                               //解析的时候要将采样数据发送出去,这个大小就会
                                               //决定我们去数据时,内存池分配的大小.
   trackportinfo.iTrackMaxQueueDepth =8;       //track的队列大小,好象没什么用...
   trackportinfo.iTrackDataMemoryPool =trackdatamempool;//流的内存池
   trackportinfo.iMediaDataImplAlloc =mediadataimplalloc;//为media分配内存的接口
   trackportinfo.iTextMediaDataImplAlloc =textmediadataimplalloc; //为text对象分配内存的接口                 (保留)
   trackportinfo.iMediaDataMemPool =mediadatamempool;//为mediaData分配的内存池
//   trackportinfo.iMediaDataGroupImplMemPool =mediadatagroupimplmempool;
//   trackportinfo.iMediaDataGroupAlloc = mediadatagroupalloc;
   trackportinfo.iNode = OSCL_STATIC_CAST(OsclTimerObject* , this);
   trackportinfo.iTimestamp = 0/*tsStartOffset*/;//时间戳
   trackportinfo.iSeqNum = 0;                   //流的数据序列,自增,每次发送数据后+1.
   trackportinfo.iSendBOS = true;             //标识是否需要发送BOS(BeginOf Stream),默认true
   trackportinfo.iLastTimestamp = 0;//标识上次采样的时间戳,以方便计算此次采样的持续时间(将本次       得到的时间戳-iLastTimestamp)即可.

完成各个node的DoRequestPort之后,engine就可以发命令播放了.在这里面还会在port连接(connect)过程中,会去设置Video/AudioMIO的一些必要参数,如果无法完成这一步,那么将会播放不成功(黑屏),在port类文件中会说明.

Start状态(播放),解析数据.发送数据

执行流程是接受start命令(是被PVPlayerDataPath驱动),将Node的状态改为started,然后线程开始工作,执行HandleTrackState,处理track的状态,首先在初始化的时候track的状态是UNINITIALIZED,然后将其改为GetState,如是第一次要发送BOS命令,GetState之后,如果出现异常或者流结束,退出.成功状态为SendState,
SendState成功后,继续将状态改为GetState,如此循环下去.知道流结束或者出现异常.

HandleTrackState(处理track的状态)
   基本各个媒体都是相同可直接参照.
  
    SendBeginOfMediaStreamCommand(发送BOS命令)
   基本各个媒体都是相同可直接参照.
  
    RetrieveTrackData(解析流的数据)
  这个功能主要是将流的数据解析出来.如果是视频流,需要解析出来的每个sample(采样)的数据(data),时间戳(timestamp),和每个sample的持续时间(duration).基本上是一个sample一个sample的解析.而音频流可能需要多个sample一起解析,对于字幕等,可以仿照mp4的写,本文并没有涉及.
Avi中的实现:
       PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG,iLogger, PVLOGMSG_STACK_TRACE, (0,"PVMFAVIFFParserNode::RetrieveTrackData() IN"));
   if (aTrackPortInfo.iState ==PVAVIFFNodeTrackPortInfo::TRACKSTATE_DOWNLOAD_AUTOPAUSE)
   {
        PVMF_AVIFFPARSERNODE_LOGDATATRAFFIC((0,"PVMFAVIFFParserNode::RetrieveTrackData() - Auto Pause"));
       return false;
    }

// Get the track ID
    uint32 trackid =aTrackPortInfo.iTrackId;
//
    // Create a databuffer from pool
    int errcode =OsclErrNoResources;
//
   OsclSharedPtr<PVMFMediaDataImpl> mediaDataImplOut;
   if (aTrackPortInfo.iFormatTypeInteger ==PVMF_AVI_PARSER_NODE_3GPP_TIMED_TEXT)
    {
       //just reserved
       mediaDataImplOut =aTrackPortInfo.iTextMediaDataImplAlloc->allocate(aTrackPortInfo.iTrackMaxDataSize);
   }
    else
    {
       mediaDataImplOut =aTrackPortInfo.iMediaDataImplAlloc->allocate(aTrackPortInfo.iTrackMaxDataSize);
   }

if (mediaDataImplOut.GetRep() !=NULL)
    {
        errcode= OsclErrNone;
    }
    else
   {
        PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG,iLogger, PVLOGMSG_INFO, (0, "PVMFAVIFFParserNode::RetrieveTrackData()No Resource Found"));
       aTrackPortInfo.iState =PVAVIFFNodeTrackPortInfo::TRACKSTATE_TRACKDATAPOOLEMPTY;
       aTrackPortInfo.iTrackDataMemoryPool->notifyfreeblockavailable(aTrackPortInfo,aTrackPortInfo.iTrackMaxDataSize);    // Enable flag toreceive event when next deallocate() is called on pool
       return false;
    }

// Now create a PVMF media data from pool
    errcode =OsclErrNoResources;
    PVMFSharedMediaDataPtrmediadataout;
    mediadataout =PVMFMediaData::createMediaData(mediaDataImplOut,aTrackPortInfo.iMediaDataMemPool);

if(mediadataout.GetRep() != NULL)
    {
       errcode = OsclErrNone;
    }

else
    {
       PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger,PVLOGMSG_INFO, (0, "PVMFAVIFFParserNode::RetrieveTrackData()Memory allocation for media data memory pool failed"));
       aTrackPortInfo.iState =PVAVIFFNodeTrackPortInfo::TRACKSTATE_MEDIADATAPOOLEMPTY;
       aTrackPortInfo.iMediaDataMemPool->notifyfreechunkavailable(aTrackPortInfo);    // Enable flag to receive event when nextdeallocate() is called on pool
        returnfalse;
    }
    // Retrieve memoryfragment to write to
    OsclRefCounterMemFragrefCtrMemFragOut;
    mediadataout->getMediaFragment(0,refCtrMemFragOut);

OpenCORE原理和核心代码破解--4

这段代码是从内存池中请求一块内存出来,和其它媒体几乎一致,用来储存解析出来的数据.Mediadataout是我们将要发送的数据.
    
     for(uint32 i =0 ; i <numSamples && nOutLength > 0 ;i++)
    {
       PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG,iLogger, PVLOGMSG_INFO, (0, "PVMFAVIFFParserNode::RetrieveTrackData()nOutLength=%d",nOutLength));
        error= iAVIFileHandle->GetNextStreamMediaSample(trackid,pOutBuffer,nOutLength, ts);
       PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger,PVLOGMSG_INFO, (0, "PVMFAVIFFParserNode::RetrieveTrackData() getnOutLength=%d,ts=%d",nOutLength,ts));
      if(error != PV_AVI_FILE_PARSER_SUCCESS && error !=PV_AVI_FILE_PARSER_EOS_REACHED)
       {
          PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG,iLogger, PVLOGMSG_INFO, (0, "PVMFAVIFFParserNode::RetrieveTrackData()for->break==0"));
            break;
       }
      if( error == PV_AVI_FILE_PARSER_EOS_REACHED)
      {
           nOutLength= ts==0?0:nOutLength;
       }
      if(nOutLength > 0)
        {
           iGau.numMediaSamples++;
           iGau.info[i].len =nOutLength;
           iGau.info[i].ts  = ts;
           if(aTrackPortInfo.iLastTimestamp == 0)
           {
              iGau.info[i].ts_delta =33;
            }
           else
              iGau.info[i].ts_delta =iGau.info[i].ts - aTrackPortInfo.iLastTimestamp;

pOutBuffer +=  nOutLength;
          aTrackPortInfo.iLastTimestamp= iGau.info[i].ts;

}
       else
        {
          PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_INFO,(0, "PVMFAVIFFParserNode::RetrieveTrackData()for->break==1"));
           break;
        }
      //reset
       nOutLength =nOutLengthTemp - nOutLength;
      nOutLengthTemp = nOutLength;
       ts = 0;
   }
这段代码就是根据numSamples(采样数,视频是1,音频可能多个),通过解析方法GetNextStreamMediaSample去取得sample数据和ts(时间戳).剩余部分就是将得到的数据去填充Mediadataout,和更新这个trackportinfo的一些信息了.
  
   SendTrackData 通过port将Retrieve的数据”发送”出去

所谓的发送就是将数据以消息的形式通知其它节点的port,decNode或者输出的port.这段代码各个媒体几乎相同,可仿照其它的来写.
  
    SendEndOfTrackCommand 发送流结束的命令
  基本上和其它媒体格式一样,稍微修改下就可以了.至此从处理流的状态到取数据,发送数据,结束数据就结束了

pvmf_aviffparser_outport.h
  
    主要有
   PVAVIFFNodeTrackPortInfo
   PVMFAVIFFParserOutPort
   PVAVIFFNodeTrackPortInfo这个类比较重要,在解析的时候经常用到trackportinfo这个类的对象.
  PVMFAVIFFParserOutPort,就是所谓的”port”类,继承2个类PvmfPortBaseImpl和PvmiCapabilityAndConfigPortFormatImpl.
  PVAVIFFNodeTrackPortInfo里面的变量或者方法基本各个媒体都相同,可以直接将其它的大部分移植过来,也可根据自己的需要添加其它变量.
  PVMFAVIFFParserOutPort继承了2个抽象类,必须去实现其中的虚方法,也可直接复用avi的.

pvmf_aviffparser_outport.cpp
  
  这里的代码基本上可以复制其它的.媒体文件如mp4方式来完成.
  主要介绍Connect函数

Connect 建立连接
  在这一过程中,会去配置mio的一些参数,视频和音频.如果有未设置的,或者有明显的错误,比如视频的高度设置为0,将导致不能播放.
  视频主要是
  MOUT_VIDEO_WIDTH_KEY                 //视频宽
  MOUT_VIDEO_DISPLAY_WIDTH_KEY         //视频显示的宽
  MOUT_VIDEO_HEIGHT_KEY                //视频的高
  MOUT_VIDEO_DISPLAY_HEIGHT_KEY        //显示的高
  MOUT_VIDEO_SUBFORMAT_KEY             //sub format
   音频是
  MOUT_AUDIO_FORMAT_KEY                //audio的mimetype  
   MOUT_AUDIO_SAMPLING_RATE_KEY         //采样率
  MOUT_AUDIO_NUM_CHANNELS_KEY          //声道数

这些值都可以通过解析来得到.实现过程几乎一样,只是要将取得的值设置即可.如avi的port中的实现.
  
    OsclAny* temp = NULL;
   aPort->QueryInterface(PVMI_CAPABILITY_AND_CONFIG_PVUUID, temp);
   PvmiCapabilityAndConfig *config =OSCL_STATIC_CAST(PvmiCapabilityAndConfig*, temp);
   
  if (!(pvmiSetPortFormatSpecificInfoSync(config,MOUT_VIDEO_WIDTH_KEY)))
    {
           PVMF_AVIFFPARSERNODE_LOGERROR((0,"PVMFAVIParserOutPort::Connect: Error - Unable To Decode WidthTo Peer"));
           return PVMFFailure;
   }
这个主要是调用pvmiSetPortFormatSpecificInfoSync方法
这个方法中的实现
   aFormatValType 就是 MOUT_VIDEO_WIDTH_KEY
       int32 width =iAVIFFParserNode->FindVideoWidth(trackInfoPtr->iTrackId);
       if (width > 0)
       {
           OsclMemAllocator alloc;
           PvmiKvp kvp;
           kvp.key = NULL;
           kvp.length = oscl_strlen(aFormatValType) + 1; // +1 for /0
           kvp.key =(PvmiKeyType)alloc.ALLOCATE(kvp.length);
           if (kvp.key == NULL)
           {
               return false;
           }
           oscl_strncpy(kvp.key, aFormatValType, kvp.length);

kvp.value.uint32_value =(uint32)width;
           PvmiKvp* retKvp = NULL; // for return value
           int32 err;
           OSCL_TRY(err, aPort->setParametersSync(NULL,&kvp, 1, retKvp););
           /* ignore the error for now */
           alloc.deallocate((OsclAny*)(kvp.key));
       }
        returntrue;
其它几个参数的设置过程基本如此.对于音频参数的设置直接调用了
iAVIFFParserNode->SetAudioConfigSettings(config)这个方法.

以上就是parsernode实现的一些关键地方了.

4>avi parserNode注册.

完成了parsernode我们需要将其注册进系统中
  /engines/player/config/core/pv_player_node_registry_populator.cpp在这个文件中
   RegisterAllNodes方法中
   添加

nodeinfo.iInputTypes.clear();
   nodeinfo.iInputTypes.push_back(PVMF_MIME_AVIFF);//这个parsernode支持的格式,PVMF_MIMIE_AVIFF
   nodeinfo.iNodeUUID = KPVMFAviFFParserNodeUuid;  //定义的uuid
   nodeinfo.iOutputType.clear();
   nodeinfo.iOutputType.push_back(PVMF_MIME_FORMAT_UNKNOWN);//输出unknown
   nodeinfo.iNodeCreateFunc =PVMFAVIFFParserNodeFactory::CreatePVMFAVIFFParserNode;
                                                //创建parserNode的方法地址
   nodeinfo.iNodeReleaseFunc =PVMFAVIFFParserNodeFactory::DeletePVMFAVIFFParserNode;
                                                //析购parserNode的方法地址
   aRegistry->RegisterNode(nodeinfo);            //注册到STL中

OpenCORE原理和核心代码破解--5

四.编译环境设置
  
  添加recognizer和parsernode目录后,每个目录下都需要有Android.mk文件,可以仿照其它的写,修改下即可.另外需要修改一些编译配置文件才能让系统去编译我们添加的文件.

/build_config/opencore_dynamic/Android_opencore_player.mk中
在20行左右添加  
   libpvaviffparsernode /
     libpvaviffrecognizer/
     
在50行左右添加
include  $(PV_TOP)/nodes/pvaviffparsernode/Android.mk
include  $(PV_TOP)/pvmi/recognizer/plugins/pvaviffrecognizer/Android.mk

五.启用PVLogger
  
  系统默认是没有启用pvlogger的,需要修改一些地方来启用这个log,log记录得很详细,作用很多,可以帮助查找错误或者去帮助阅读代码.
  
需要修改的地方.
  
/external/opencore/Config.mk

在第10行,

ifeq($(ENABLE_PV_LOGGING),1)
  PV_CFLAGS +=-DPVLOGGER_INST_LEVEL=5
endif

之前,添加一行

ENABLE_PV_LOGGING:= 1

/external/opencore/oscl/oscl/osclbase/src/pvlogger.h
在第138行左右添加
#definePVLOGGER_INST_LEVEL 5

完成这些之后,makesdk
启动模拟器,在sdcard中新建一个pvlogger.txt文件写入8即可
echo8 >/sdcard/pvlogger.txt
或者可以过滤一些logger,可以这么写,比如只看PVPlayerEngine的log
echo8,PVPlayerEngine >/sdcard/pvlogger.txt

***************************************************************************
在PVLoggerConfigFile可以看到一段话.

classPVLoggerConfigFile{
/*  
    To configurelogging at runtime, a file pvlogger.txt must be located in thesdcard.
    The format for log level and logger tag inthe file should be: "level,node".  Note thatthere should be no space between log level and logger tag.
   For example, pvlogger.txt can look like:
   1,PVPlayerEngine
    8,PVSocketNode
   Above example means log the message level PVLOGMSG_ALERT forPVPlayerEngine and PVLOGMSG_DEBUG for PVSocketNode.  Seepvlogger.h for log level values.
*/
public:
   PVLoggerConfigFile():iLogFileRead(false)

http://www.eoeandroid.com/thread-100945-1-1.html

OpenCORE原理和核心代码破解,增加AVI格式。相关推荐

  1. QQ消息群发器实现原理及核心代码

    春节前采用C#做了个<实用通讯录>的小软件,群发邮件能正常执行,群发QQ消息不是很理想,在采用进程打开qq消息界面后复制到QQ消息框的字符串消息被升级后的QQ给屏蔽掉了,真是郁闷~~~ 解 ...

  2. php avi格式播放,Linux_网页播放器代码全集,1.avi格式代码片断如下:obje - phpStudy...

    网页播放器代码全集 1.avi格式 代码片断如下: 2.mpg格式 代码片断如下: 3.smi格式 代码片断如下: 4.rm格式 代码片断如下: 5.wmv格式 代码片断如下: 6.wma格式 放在 ...

  3. 鱼眼图像自监督深度估计原理分析和Omnidet核心代码解读

    作者丨苹果姐@知乎 来源丨https://zhuanlan.zhihu.com/p/508090405 编辑丨3D视觉工坊 在自动驾驶实际应用中,对相机传感器的要求之一是拥有尽可能大的视野范围,鱼眼相 ...

  4. 移动Web体验月报(6月):MIP 核心代码升级,增加基于 Vue 开发能力

    原创: BrilliantOpenWeb OpenWeb开发者 7月6日 作者 | Brilliant Open Web 团队 编辑 | Daisy 升级与重要进展 历时2个月,MIP团队完成了核心代 ...

  5. 第13章 程序的动态加载和执行(三,核心代码)

    这个核心代码也是本书唯一的一个核心代码,把这个读懂了,本书基本上通了,这个核心代码不难,只是前面知识的综合应用而已,所以用一到两个星期把这个三个程序读熟再进行下面的四章. 怎么样才算是读通了一个代码: ...

  6. linux 内核 核心代码,8分钟掌握Linux内核分析的核心科技

    原标题:8分钟掌握Linux内核分析的核心科技 作者: OUYANG_LINUX007 来源: http://blog.csdn.net/ouyang_linux007/article/details ...

  7. SpringBoot原理-SpringBoot核心运行原理

    导语   Spring Boot最为核心的功能就是自动配置,所有功能的实现都是基于"约定优于配置"的原则,但是Spring Boot是如何实现自动配置的功能的,下面就通过源码学习S ...

  8. 从实现原理谈谈低代码

    点击上方"芋道源码",选择"设为星标" 管她前浪,还是后浪? 能浪的浪,才是好浪! 每天 10:33 更新文章,每天掉亿点点头发... 源码精品专栏 原创 | ...

  9. 现在大火的低代码是怎么回事?从实现原理谈谈低代码

    我们在低代码领域探索了很多年,从2015 开始研发低代码前端渲染(amis),从 2018 年开研发后端低代码数据模型,发布了爱速搭低代码平台,这些年调研过了几乎所有市面上的相关技术和产品,发现虽然每 ...

最新文章

  1. java代码中fastjson生成字符串和解析字符串的方法和javascript文件中字符串和json数组之间的转换方法...
  2. 压缩机html200a功率,汽车空调功率有多大?如果用电瓶充电器供电能行吗?
  3. java 统计单词个数和标点符号
  4. win7如何设置还原点
  5. Python 在线免费批量美颜,妈妈再也不用担心我 P 图两小时啦
  6. STM8学习笔记---外部中断实现
  7. Linux知识汇总 (二)
  8. 想多赚钱就要多培养自己脖子以上的能力
  9. linux编辑音频文件,Linux 上的最佳音频编辑工具推荐
  10. FISCO BCOS 日志分析 关键词 格式
  11. 设计模式---003代理模式---【巷子】
  12. Oracle服务端的tsnname,【监听】tnsname.ora文件理解
  13. python实现DDA算法
  14. mysql utf8 bom_UTF8的BOM
  15. 定时器控制一个ADC实现双通道采样(TIM+ADC+DMA)
  16. dvi接口引脚定义_为什么越来越多人用RS232接口,却还分不清DB9、DB25的引脚定义?...
  17. 线性组合(linear combinations), 生成空间(span), 基向量(basis vectors)——线性代数本质(二)
  18. How to Register/Update Ad Muncher using TOR
  19. 解读滴滴招股书:提供“移动“价值的全球共享经济企业潜力几何?
  20. docker安装gitea

热门文章

  1. Tkinter教程之Grid篇
  2. 百度翻译api错误码52003
  3. PMP项目管理-项目干系人管理(9)
  4. OV7670 摄像头模块介绍
  5. 前端系列三十五:跨域问题及解决方案
  6. 新一代区块链游戏:超越炒作的NFT和玩游戏盈利
  7. 如何反编译apk文件并解析.class文件查看Java源代码
  8. 『Java安全』XStream 1.4-1.4.61.4.10反序列化漏洞CVE-2013-7285复现与浅析
  9. lambda :: 和计算list中某个字段值的总和
  10. date获取时间某年某月一共有多少天以及格式化天数,月份,年