热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

OpenCores框架

媒体播放引擎v在opencore中由PVPlayerEngine负责媒体播放功能的实现;v在PVPlayerEngine中负责创建各个节点来完成媒体文件格式解析&

媒体播放引擎

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

    {

        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

 

附录:

具体的代码阅读:



目录结构


OpenCore的代码在以下目录中:external/opencore/。这个目录是OpenCore的根目录,其中包含的子目录如下所示:

    * 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 类是通过PVMFWAVFFParserNodeFactoryCreatePVMFWAVFFParserNode() 成员函数生成的。而CreatePVMFWAVFFParserNode() 函数是在PVPlayerNodeRegistry::PVPlayerNodeRegistry() 类构造函数中通过PVPlayerNodeInfo 类被注册到Oscl_Vector 的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 iType 成员变量中。

当前Splitter的匹配 过程
PVMFStatus PVPlayerNodeRegistry::QueryRegistry(PVMFFormatType& aInputType, PVMFFormatType& aOutputType, Oscl_Vector& aUuids) 函数的功能是根据输入类型和输出类型,在已注册的node vector中寻找是否有匹配的node,有的话传回其唯一识别标识PVUuid。
QueryRegistry 这个函数至底向上搜索可得到,在android 中splitter的匹配过程如下:
  • android_media_MediaPlayer.cpp 之中定义了一个 JNINativeMethod ( JAVA 本地调用方法)类型的数组 gMethods ,供java代码中调用 MultiPlayer类的 setDataSource成员函数时找到对应的c++函数  
    {"setDataSource",       "(Ljava/lang/String;)V",            (void *)android_media_MediaPlayer_setDataSource},


  • 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& service(getMediaPlayerService());
     然后新建一个IMediaPlayer变量,  sp player(service->create(getpid(), this, fd, offset, length));
     在sp MediaPlayerService::create(pid_t pid, const sp& client, const char* url)
     调status_t MediaPlayerService::Client::setDataSource(const char *url) 函数,ClientMediaPlayerService 的一个内部类。
     在MediaPlayerService::Client::setDataSource中,调sp 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


推荐阅读
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • LeetCode笔记:剑指Offer 41. 数据流中的中位数(Java、堆、优先队列、知识点)
    本文介绍了LeetCode剑指Offer 41题的解题思路和代码实现,主要涉及了Java中的优先队列和堆排序的知识点。优先队列是Queue接口的实现,可以对其中的元素进行排序,采用小顶堆的方式进行排序。本文还介绍了Java中queue的offer、poll、add、remove、element、peek等方法的区别和用法。 ... [详细]
  • 本文讨论了在Spring 3.1中,数据源未能自动连接到@Configuration类的错误原因,并提供了解决方法。作者发现了错误的原因,并在代码中手动定义了PersistenceAnnotationBeanPostProcessor。作者删除了该定义后,问题得到解决。此外,作者还指出了默认的PersistenceAnnotationBeanPostProcessor的注册方式,并提供了自定义该bean定义的方法。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • JVM 学习总结(三)——对象存活判定算法的两种实现
    本文介绍了垃圾收集器在回收堆内存前确定对象存活的两种算法:引用计数算法和可达性分析算法。引用计数算法通过计数器判定对象是否存活,虽然简单高效,但无法解决循环引用的问题;可达性分析算法通过判断对象是否可达来确定存活对象,是主流的Java虚拟机内存管理算法。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • imx6ull开发板驱动MT7601U无线网卡的方法和步骤详解
    本文详细介绍了在imx6ull开发板上驱动MT7601U无线网卡的方法和步骤。首先介绍了开发环境和硬件平台,然后说明了MT7601U驱动已经集成在linux内核的linux-4.x.x/drivers/net/wireless/mediatek/mt7601u文件中。接着介绍了移植mt7601u驱动的过程,包括编译内核和配置设备驱动。最后,列举了关键词和相关信息供读者参考。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • 本文记录了在vue cli 3.x中移除console的一些采坑经验,通过使用uglifyjs-webpack-plugin插件,在vue.config.js中进行相关配置,包括设置minimizer、UglifyJsPlugin和compress等参数,最终成功移除了console。同时,还包括了一些可能出现的报错情况和解决方法。 ... [详细]
  • Linux的uucico命令使用方法及工作模式介绍
    本文介绍了Linux的uucico命令的使用方法和工作模式,包括主动模式和附属模式。uucico是用来处理uucp或uux送到队列的文件传输工具,具有操作简单快捷、实用性强的特点。文章还介绍了uucico命令的参数及其说明,包括-c或--quiet、-C或--ifwork、-D或--nodetach、-e或--loop、-f或--force、-i或--stdin、-I--config、-l或--prompt等。通过本文的学习,读者可以更好地掌握Linux的uucico命令的使用方法。 ... [详细]
  • 本文介绍了在Android开发中使用软引用和弱引用的应用。如果一个对象只具有软引用,那么只有在内存不够的情况下才会被回收,可以用来实现内存敏感的高速缓存;而如果一个对象只具有弱引用,不管内存是否足够,都会被垃圾回收器回收。软引用和弱引用还可以与引用队列联合使用,当被引用的对象被回收时,会将引用加入到关联的引用队列中。软引用和弱引用的根本区别在于生命周期的长短,弱引用的对象可能随时被回收,而软引用的对象只有在内存不够时才会被回收。 ... [详细]
  • MybatisPlus入门系列(13) MybatisPlus之自定义ID生成器
    数据库ID生成策略在数据库表设计时,主键ID是必不可少的字段,如何优雅的设计数据库ID,适应当前业务场景,需要根据需求选取 ... [详细]
author-avatar
韵公举_R
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有