媒体播放引擎
v 在 opencore 中由 PVPlayerEngine 负责媒体播放功能的实现;
v 在 PVPlayerEngine 中负责创建各个节点来完成媒体文件格式解析( SourceNode )、媒体数据编解码 (DecodeNode/EncNode) 以及媒体数据的输出 (MediaOutputNode) ;
v 由 PlayerDriver 负责 opencore 与 android 媒体框架的适配;
v 由 AndroidAudioOutput 负责音频数据输出到 android-audioflinger 服务输出,由 AndroidSurfaceOutput 来负责将视频数据输出到 android-surfaceflinger 服务输出;

媒体播放引擎 - 驱动层
v 驱动层包括: PVPlayer 和 PlayerDriver 组成;
v PVPlayer 负责将上层应用的命令转换成 PlayerDriver 可以内部命令加入到其命令队列中,由 PlayerDriver 负责执行,并负责将命令执行的结果回送给上层媒体框架以及 JAVA 程序;
v 在 PlayerDriver 中有一个消息循环负责处理 PVPlayer 发送过来的命令,调用 PVPlayerEngine 相应的接口来完成媒体控制;
v 在创建 PlayerDriver 对象时会生成 opencore 主线程,并调用 OsclExecScheduler.StartScheduler 来开始 opencore 主线程循环;
v 源码文件:
v android/playerdriver.cpp
v android/playerdriver.h
媒体播放引擎 - 文件格式识别
v 在 opencore 中由 PVPlayerRecognizerRegistry 负责文件格式识别,并将结果返回给 PVPlayerEngine ;
v PVPlayerEngine 根据类型来创建对应的文件解析节点 ParseNode ;
v opencore 中对文件格式的识别时通过读取文件头的方式进行的,而不是根据文件扩展名;
v opencore 中通过向系统中注册文件识别插件的方式来提供文件格式识别的功能,系统通过循环调用每个注册插件的 Recognize() 函数来进行格式识别;
v 在创建媒体播放引擎的时候会通过 PVPlayerRegistryPopulator::Populate() 函数来注册所有的文件识别插件到系统中

v 源码文件:
v pvmi/recognizer/src/pvmf_recognizer_registry.cpp
v pvmi/recognizer/src/pvmf_recognizer_registry_impl.cpp
v pvmi/recognizer/plugins/ 目录下所有的子目录中存放着文件识别插件的代码;
媒体播放引擎 - 文件格式解析节点
v 在 opencore 中通过文件解析节点( ParseNode )来完成音视频文件格式的解析,并将文件中的音频、视频数据送到对应的解码节点进行解码;
v PVPlayerEngine 通过 PVMFNodeInterface 接口来完成对 ParseNode 的操作; ParseNode 通过提供扩展接口来实现通用 Node 接口不能实现的功能;
v 常用的扩展接口如: PvmiCapabilityAndConfig ;
v opencore 中通过注册的方式向系统中注册文件格式解析节点

媒体播放引擎 - 节点注册
v 1 、每个节点都有一个对应的 PVMFXXXXFactrory 来负责创建;
v 2 、系统中通过 PVPlayerRegistryPopulator. RegisterAllNodes() 来注册所有支持的节点;
v 3 、 opencore 首先调用 PVPlayerNodeRegistry-> QueryRegistry() 函数查询对于指定的输入 & 输出格式的 Node 的 UUID ,然后通过 PVPlayerNodeRegistry-> CreateNode () 创建指定的 Node 。
v 4 、 PVPlayerEngine 就是通过文件格式识别插件返回的媒体类型来创建对应的文件解析节点的
void PVMFMP4FFParserNode::Run()
{
//Process commands.
if (!iInputCommands.empty())
{
ProcessCommand();
}
while (!iPortActivityQueue.empty() && (iInterfaceState == EPVMFNodeStarted || FlushPending()))
{
ProcessPortActivity();
}
if (iInterfaceState == EPVMFNodeStarted && !FlushPending())
{
HandleTrackState();
}
//Check for completion of a flush command...
if (FlushPending() && iPortActivityQueue.empty())
{
//Flush is complete.
CommandComplete(iCurrentCommand, iCurrentCommand.front(), PVMFSuccess);
}
}
void PVMFMP4FFParserNode::HandleTrackState()
{
for (uint32 i = 0; i < iNodeTrackPortList.size(); ++i)
{
switch (iNodeTrackPortList[i].iState)
{
case PVMP4FFNodeTrackPortInfo::TRACKSTATE_UNINITIALIZED:
……
case PVMP4FFNodeTrackPortInfo::TRACKSTATE_TRANSMITTING_GETDATA:
……
if (!RetrieveTrackData(iNodeTrackPortList[i]))
{
……
}
……
case PVMP4FFNodeTrackPortInfo::TRACKSTATE_TRANSMITTING_SENDDATA:
if (SendTrackData(iNodeTrackPortList[i]))
{
……
}
break;
……
}
}
}
媒体播放引擎 - 解码节点
v opencore 中由 DecNode 节点负责媒体数据的解码工作;
v DecNode 通过调用底层的 opencoreMAX 通用接口实现媒体数据的解码;
v 对于一个普通的视频文件,存在 2 个解码节点:音频解码节点和视频解码节点;

bool PVMFOMXBaseDecNode::SendInputBufferToOMXComponent()
{
……
OMX_EmptyThisBuffer(iOMXDecoder, input_buf->pBufHdr);
……
}
PVMFOMXBaseDecNode::SendOutputBufferToOMXComponent()
{
……
OMX_FillThisBuffer(iOMXDecoder, output_buf->pBufHdr);
……
}
OMX_ERRORTYPE PVMFOMXBaseDecNode::EmptyBufferDoneProcessing(OMX_OUT OMX_HANDLETYPE aComponent,
OMX_OUT OMX_PTR aAppData,
OMX_OUT OMX_BUFFERHEADERTYPE* aBuffer)
{
……
}
OMX_ERRORTYPE PVMFOMXBaseDecNode::FillBufferDoneProcessing(OMX_OUT OMX_HANDLETYPE aComponent,
OMX_OUT OMX_PTR aAppData,
OMX_OUT OMX_BUFFERHEADERTYPE* aBuffer)
{
……
}
v 源码文件:
v nodes/pvomxbasedecnode/
v nodes/pvomxvideodecnode/
v codecs_v2/omx/
媒体播放引擎 - 媒体输出节点
v MediaOutputNode 负责接收 DecNode 的解码后数据,并将数据输出到 MIO ;同时在输出前作视频的同步;
v MIO 负责将数据输出到 surfaceFlinger 和 AudioFlinger ;
v 视频数据在输出前需要做颜色空间的转换( YUV->RGB )和缩放,这部分的工作也在 MIO 中完成;
v 节点源码:
v nodes/pvmediaoutputnode/
v MIO 源码:
v android/android_surface_output.cpp
v android/android_audio_output.cpp
v android/android_audio_mio.cpp
v http://zhaixishan.cublog.cn
附录:
具体的代码阅读:
* android:这里面是一个上层的库,它基于PVPlayer和PVAuthor的SDK实现了一个为Android使用的Player和Author。
* baselibs:包含数据结构和线程安全等内容的底层库
* codecs_v2:这是一个内容较多的库,主要包含编解码的实现,以及一个OpenMAX的实现
* engines:包含PVPlayer和PVAuthor引擎的实现
* extern_libs_v2:包含了khronos的OpenMAX的头文件
* fileformats:文件格式的据具体解析(parser)类
* nodes:编解码和文件解析的各个node类。
* oscl:操作系统兼容库
* pvmi: 输入输出控制的抽象接口
* protocols:主要是与网络相关的RTSP、RTP、HTTP等协议的相关内容
* pvcommon:pvcommon库文件的Android.mk文件,没有源文件。
* pvplayer:pvplayer库文件的Android.mk文件,没有源文件。
* pvauthor:pvauthor库文件的Android.mk文件,没有源文件。
* tools_v2:编译工具以及一些可注册的模块。
Splitter的定义与初始化
以wav的splitter为例,在fileformats目录下有解析wav文件格式的pvwavfileparser.cpp文件,在nodes目录 下有pvmf_wavffparser_factory.cpp,pvmf_wavffparser_node.h, pvmf_wavffparser_port.h等文件。
我们由底往上看,vwavfileparser.cpp中的 PV_Wav_Parser 类有 InitWavParser () , GetPCMData () , RetrieveFileInfo () 等解析wav格式的成员函数,此类应该就是最终的解析类。我们搜索 PV_Wav_Parser 类被用到的地方可知,在 PVMFWAVFFParserNode 类中有 PV_Wav_Parser 的一个指针成员变量。再搜索可知, PVMFWAVFFParserNode 类是通过 PVMFWAVFFParserNodeFactory 的 CreatePVMFWAVFFParserNode() 成员函数生成的。而 CreatePVMFWAVFFParserNode() 函数是在 PVPlayerNodeRegistry::PVPlayerNodeRegistry() 类构造函数中通过 PVPlayerNodeInfo 类被注册到 Oscl_Vector<PVPlayerNodeInfo, OsclMemAllocator> 的vector中,在这个构造函数中,AMR,mp3等node也是同样被注册的。
由上可知,Opencore中对splitter的管理也是与ffmpeg等类似,都是在框架的初始化时注册的,只不过Opencore注册的是每个splitter的factory函数。
综述一下splitter的定义与初始化过程:
- 每个splitter都在fileformats目录下有个对应的子目录,其下有各自的解析类。
- 每个splitter都在nodes目录下有关对应的子目录,其下有各自的统一接口的node类和node factory类。
- 播放引擎PVPlayerEngine 类中有PVPlayerNodeRegistry iPlayerNodeRegistry 成员变量。
- 在PVPlayerNodeRegistry 的构造函数中,将 AMR, AAC, MP3等splitter的输入与输出类型标示和node factory类中的create node与release delete接口通过PVPlayerNodeInfo类 push到Oscl_Vector<PVPlayerNodeInfo, OsclMemAllocator> iType 成员变量中。
当前Splitter的匹配 过程
PVMFStatus PVPlayerNodeRegistry::QueryRegistry(PVMFFormatType& aInputType, PVMFFormatType& aOutputType, Oscl_Vector<PVUuid, OsclMemAllocator>& aUuids) 函数的功能是根据输入类型和输出类型,在已注册的node vector中寻找是否有匹配的node,有的话传回其唯一识别标识PVUuid。
从 QueryRegistry 这个函数至底向上搜索可得到,在 android 中splitter的匹配过程如下:
- android_media_MediaPlayer.cpp 之中定义了一个 JNINativeMethod ( JAVA 本地调用方法)类型的数组 gMethods ,供java代码中调用 MultiPlayer类的 setDataSource成员函数时找到对应的c++函数
- static void android_media_MediaPlayer_setDataSource(JNIEnv *env, jobject thiz, jstring path)
此函数中先得到当前的MediaPlayer实例,然后调用其setDataSource函数,传入路径
- status_t MediaPlayer::setDataSource(const char *url)
此函数通过调getMediaPlayerService()先得到当前的MediaPlayerService, const sp<IMediaPlayerService>& service(getMediaPlayerService());
然后新建一个IMediaPlayer变量, sp<IMediaPlayer> player(service->create(getpid(), this, fd, offset, length));
在sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url) 中
调status_t MediaPlayerService::Client::setDataSource(const char *url) 函数,Client 是MediaPlayerService 的一个内部类。
在MediaPlayerService::Client::setDataSource中,调sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerType)
生成一个继承自MediaPlayerBase的PVPlayer实例,
PVPlayer的继承关系如下:
PVPlayer -->MediaPlayerInterface -->MediaPlayerBase
最后调PVPlayer的setDataSource ()函数
- status_t PVPlayer::setDataSource(const char *url)
- status_t PVPlayer::prepare()
此函数开头执行ret = mPlayerDriver->enqueueCommand(new PlayerSetDataSource(mDataSourcePath,0,0));
将PlayerSetDataSource 的command类加入到PlayerDriver的command处理队列中,
在void PlayerDriver::Run()函数中处理此command,调用下面的handleSetDataSource函数。
- void PlayerDriver::handleSetDataSource(PlayerSetDataSource* ec)
- PVCommandId PVPlayerEngine::AddDataSource(PVPlayerDataSource& aDataSource, const OsclAny* aContextData)
This function allows a player data source to be specified for playback. This function must be called
- PVMFStatus PVPlayerEngine::DoAddDataSource(PVPlayerEngineCommand& aCmd)
- PVMFStatus PVPlayerEngine::DoSetupSourceNode(PVCommandId aCmdId, OsclAny* aCmdContext)
http://www.cublog.cn/u3/112227/showart_2494787.html