第二章:第三节:PVPlayerEngine的实现。

PlayerDriver类里有如下成员变量的声明:PVPlayerInterface *mPlayer;它的创建是在调用函数int PlayerDriver::playerThread();的时候。由如下一行代码来完成的:OSCL_TRY(error, mPlayer = PVPlayerFactory::CreatePlayer(this, this, this));

PVPlayerFactory是一个工厂类(不清楚工厂类的朋友可以去看设计模式里面的工厂模式)。最后它调用PVPlayerEngine::New(aCmdStatusObserver, aErrorEventObserver, aInfoEventObserver, aHwAccelerated);来得到PVPlayerEngine的一个实例。它在创建的时候同时PlayerDriver的this指针传给PVPlayerEngine作为参数,所以它同时也是PVPlayerEngine的观察者,这样PlayerEngine里发生了什么事情,可以通过观察者的接口传递上去。

这里有个疑问,为什么不用PVPlayerEngine的构造函数来产生新的实例,而要通过PVPlayerEngine的一个static函数来完成呢?我的理解是,为了保证PVPlayerEngine是封闭的,不可继承的。

在创建了PVPlayerInterface的实例以后,把它缴入到Scheduler中,然后一句sched->StartScheduler(mSyncSem),就完成了它在整个生命周期中应该干的事情(又见scheduler)。

PVPlayerEngine,是由若干PVMF nodes和对nodes进行管理的功能模块组成,每个node完成独特的工作,它是一个独立的功能块,若干个node一起组成了一个node graph,这个graph是进行媒体播放的核心部分。同事PVPlayerEngine还完成一些额外的工作,例如node的管理(包括搜索,注册,创建等),graph的创建。

在Opencore官方文档《pvplayer_developers_guide.pdf》中,有如下的状态转换图:

PVPlayerEngine状态图

为了理解这个状态图,我们可以从一个文件播放的流程来看它每个步骤干了些什么事情:

按上图的状态转换方式,一次媒体播放过程有如下步骤组成:

AddDataSoure->Init->AddDataSink->Prepare->Start  ->Pause->Resume ....... ->Stop->RemoveDataSink->RemoveDataSource

 

 

1,PVPlayerEngine::AddDataSource

从名字上就能理解,它是在一开始的时候给他提供要播放的数据来源,这里的数据来源可以是本地文件,也可以是流媒体文件。PVPlayerEngine的客户端是通过给他发送命令的方式来执行的,内部执行命令的函数是:PVMFStatus PVPlayerEngine::DoAddDataSource(PVPlayerEngineCommand& aCmd)。

在这个函数里面,首先进行差错检测,然后看Source的PVMFFormatType,在PlayerDriver接收到播放请求的URL以后它会判断URL是什么类型的,如果是"rtsp://"开头的,则设置类型为:PVMF_MIME_DATA_SOURCE_RTSP_URL,如果URL是以".sdp"结尾的,那么类型则被设置为:PVMF_MIME_DATA_SOURCE_SDP_FILE,如果是"http:"则这是一个无效的,其他的统一设置为:PVMF_MIME_FORMAT_UNKNOWN。

那么对于PVMF_MIME_FORMAT_UNKNOWN类型,PVPlayerEngine则要自己去判断是这个媒体文件时什么类型的,例如它是mp4文件,还是avi文件等。这些操作室通过PVPlayerEngine的子函数DoQuerySourceFormatType来执行的。这个函数很复杂,涉及到整个框架的内部子框架:FormatRecognizer。这个子模块又用到了那些耳熟能详的设计模式:工厂模式,命令模式,观察者模式,单件模式,等等等。具体怎么做我们不去暂时不是深究,因为这个内容比较庞杂。我们要记住的是,这个函数调用到了PVMFStatus PVPlayerRecognizerRegistry::QueryFormatType接口,并且把this指针传递下去作为FormatRecognizer执行文件格式检测以后的报告对象。总之,经过各种繁琐的类的构造和释放,命令的转换...最后终于识别成功了,于是,Recoginzer通知PVPlayerRecognizerRegistryObserver,格式识别出来了,由于PVPlayerRecognizerRegistryObserver是一个

接口类,PVPlayerEngine类从这个类中继承而来,所以,我们看PVPlayerEngine::RecognizeCompleted这个函数就行了。

关注这一行:PVMFStatus retval = DoSetupSourceNode(cmdid, cmdcontext);在文件格式被正确识别出来以后,就需要来创建SourceNode了。

在node被创建以后,并且它的接口也被正常配置了以后,SourceNode会报告PVMFInfoDataReady事件给PVPlayerEngine。(这中间经历了若干过程,一系列的Command 和Observer,过程类似,具体的可以看代码)。一直到最后调用到函数:DoSourceNodeInit(),sourcenode会受到Init的命令,然后告诉Observer即engine,这个过程通过函数PVPlayerEngine::HandleSourceNodeInit来实现。SourceNodeInit成功后,PVPlayerEngine的状态转变成PVP_ENGINE_STATE_INITIALIZED。

这里插一句,PVPlayerEngine的很多调用都是通过发送一个命令给他的一个成员类实例,然后这个成员类实例再调用PVPlayerEngine对应的Observer接口来完成操作,因为这些所有的操作都在一个线程里面实现,所有的调度都通过Scheduler实现,而它本身的所有的成员,也都由这个Schedule来进行调度,整个运行只有一个线程,所以不会出现同步的问题,因此我们也见到,在PVPlayerEngine实现里,没见到任何的mutex锁,佩服packervideo 公司的的程序设计设计人员的精巧框架。在PVPlayerEngine里面,很多形如:Doxxxxxx, Handlexxxxxx的函数,一般可以配对来看。这样就不至于被各种类,接口给绕进去。

 

2,PVPlayerEngine::Init

这个函数干的事情很简单,就是去调用SourceNode的Init函数。然后这个函数成功了以后调用HandleSourceNodeInit,然后调用DoSourceNodeGetDurationValue,然后就是HandleSourceNodeGetDurationValue。如果这一切没问题,OK,Engine已经是PVP_ENGINE_STATE_INITIALIZED了。

 

3,PVPlayerEngine::AddDataSink

这也是个相对简单的函数,没啥好说的。

 

4,PVPlayerEngine::DoPrepare

这个函数比较复杂,它要完成的事情很多,这个函数的注释指明:它有4个小步骤分别完成以下事情(这是原函数中的注释的内容,具体的实现得仔细去看):

a, When Engine is in PVP_ENGINE_STATE_INITIALIZED state, here engine will start the Track Selection, which will start with Sink Nodes。

b, After Init completes on Sink nodes Engine will be in PVP_ENGINE_STATE_TRACK_SELECTION_1_DONE and Engine will start creating Dec nodes and call Init on dec nodes, if needed。

这个步骤有必要说明一下,它会首先尝试看sink node能不能支持source node的多媒体流,如果能播放,则不必要进行到dec node的选择,如果不能播放,则必须要查询,看有没有合适的dec node,它的输入和source node匹配,输出和sink node匹配。

c, Init completion on Dec nodes will take Engine to PVP_ENGINE_STATE_TRACK_SELECTION_2_DONE and Engine will start populating the Playable List after verifying parameters of different tracks. Engine after selecting tracks will call Reset on Sink and Dec nodes。

d, Once Reset completes on Sink and Dec nodes, Engine will be in PVP_ENGINE_STATE_TRACK_SELECTION_3_DONE and then Engine will delete all the unused dec nodes.

上述的这些操作,很多都是异步方式执行的,也就是,等上述步骤执行完成以后,相应的命令才能执行完成。

 

5,PVPlayerEngine::DoStart

启动iPlaybackClock,开始播放。

 

6,PVPlayerEngine::DoPause

7,PVPlayerEngine::DoResume

暂停和继续。

 

至于剩下的操作,就是按照相反的操作进行,要相对简单的多,不用去完成如此多的检测和状态的转换。

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值