精华内容
下载资源
问答
  • opencore

    2019-10-07 07:00:26
    OpenCore是Android的多媒体核心,它是一个基于C++的实现,定义了全功能的操作系统移植层,各种基本的功能均被封装成类的形式,各层次之间的接口多使用继承等方式。 OpenCore是一个多媒体的框架,从宏观上来看,它...

    OpenCore是Android的多媒体核心,它是一个基于C++的实现,定义了全功能的操作系统移植层,各种基本的功能均被封装成类的形式,各层次之间的接口多使用继承等方式。

    OpenCore是一个多媒体的框架,从宏观上来看,它主要包含了两大方面的内容:
    PVPlayer:提供媒体播放器的功能,完成各种音频(Audio)、视频(Video)流的回放(Playback)功能
    PVAuthor:提供媒体流记录的功能,完成各种音频(Audio)、视频(Video)流的以及静态图像捕获功能
     
    临时设施(overlay)指在永久设施所能提供的基础设施条件上,为满足大型活动运行需要而在活动开始前加建、活动结束后拆除的临时性设施。如在农村,当某家有婚丧嫁娶等大事时,要请所有的亲朋好友来参加,家里的房屋面积不够,就需要临时搭建大棚或者向左邻右舍借房间,这就是临时建筑;家里的桌椅板凳不够,也需要购置或者外借,这就是家具物资;家里日常用的厨具也不够用,需要临时搭灶,这就是临时餐饮;场地上也需要临时拉个灯泡,这就是临时照明;等等。
    特点:①临时性、②灵活性、③安全性、④与活动运行密不可分;
     

    转载于:https://www.cnblogs.com/Ph-one/p/5049369.html

    展开全文
  • OpenCore

    2013-06-26 14:56:05
    OpenCore是一个基于C++的实现,定义了全功能的操作系统移植层,各种基本的功能均被封装成类的形式,各层次之间的接口多使用继承等方式。OpenCore是一个多媒体的框架,从宏观上来看,它主要包含了两大方面的内容: ...
    OpenCore是一个基于C++的实现,定义了全功能的操作系统移植层,各种基本的功能均被封装成类的形式,各层次之间的接口多使用继承等方式。OpenCore是一个多媒体的框架,从宏观上来看,它主要包含了两大方面的内容:
    PVPlayer:提供媒体播放器的功能,完成各种音频(Audio)、视频(Video)流的回放(Playback)功能
    PVAuthor:提供媒体流记录的功能,完成各种音频(Audio)、视频(Video)流的以及静态图像捕获功能
    PVPlayer和PVAuthor以SDK的形式提供给开发者,可以在这个SDK之上构建多种应用程序和服务。在移动终端中常常使用的多媒体应用程序,例如媒体播放器、照相机、录像机、录音机等等。
    为了更好的组织整体的架构,OpenCore在软件层次在宏观上分成几个层次:
    OSCL:Operating System Compatibility Library (操作系统兼容库),包含了一些操作系统底层的操作,为了更好地在不同操作系统移植。包含了基本数据类型、配置、字符串工具、IO、错误处理、线程等内容,类似一个基础的C++库。
    PVMF:PacketVideo Multimedia Framework(PV多媒体框架),在框架内实现一个文件解析(parser)和组成(composer)、编解码的NODE,也可以继承其通用的接口,在用户层实现一些NODE。
    PVPlayer Engine:PVPlayer引擎。
    PVAuthor Engine:PVAuthor引擎。
    事实上,OpenCore中包含的内容非常多:从播放的角度,PVPlayer的输入(Source)是文件或者网络媒体流,输出(Sink)是音频视频的输出设备,其基本功能包含了媒体流控制、文件解析、音频视频流的解码(Decode)等方面的内容。除了从文件中播放媒体文件之外,还包含了与网络相关的RTSP流(Real Time Stream Protocol,实时流协议)。在媒体流记录的方面,PVAuthor的输入(Source)是照相机、麦克风等设备,输出(Sink)是各种文件,包含了流的同步、音频视频流的编码(Encode)以及文件的写入等功能。
    在使用OpenCore的SDK的时候,有可能需要在应用程序层实现一个适配器(Adaptor),然后在适配器之上实现具体的功能,对于PVMF的NODE也可以基于通用的接口,在上层实现,以插件的形式使用。
    展开全文
  • OpenCORE

    千次阅读 2011-06-09 17:58:00
    OpenCORE 一.OpenCORE整体结构 逻辑层主要由PVPlayer,PlayerDriver,PVPlayerEngine来组成,其中PVPlayer主要接受上层的操作(JNI),PlayerDriver负责连接PVPlayer与PVPlayerEngine,主要是将从PVPlayer的指令交给engine...

    OpenCORE <AVI文件的识别和解析部分>

    一.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输出环境(Config MIO(Media Input/Output)).如Video需要配置视频的宽和高,视频显示的宽和高,subformattype.音频需要配置声道数,采样率(Hz),format type.

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

    三.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 as supported type
        OSCL_HeapString<OsclMemAllocator> supportedformat = PVMF_MIME_AVIFF;
        aSupportedFormatsList.push_back(supportedformat);
        return PVMFSuccess;
    }

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

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

    PVMFStatus PVAVIFFRecognizerPlugin::GetRequiredMinBytesForRecognition(uint32& aBytes)
    {
        aBytes = AVIFF_MIN_DATA_SIZE_FOR_RECOGNITION; //12
        return PVMFSuccess;
    }
    由上分析,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即可.如
    //uuidgen generate:64cf3733-a8bd-4f39-8a48-66951b54bbf2
    #define KPVMFAviFFParserNodeUuid PVUuid(0x64cf3733,0xa8bd,0x4f39,0x8a,0x48,0x66,0x95,0x1b,0x54,0xbb,0xf2)
        
    parserNode类

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

    pvmf_aviffparser_node.h

       在pvmf_aviffparser_node.h这个头文件中大约就有1千行的代码,有下面几个类
    PVMFAVIFFParserNode        //parsernode类定义                             必须
    PVMFAVIFFParserNodeCommand,//框架内实现avi的command类,可以直接copy mp4 .  必须
    PVMFAVIFFPortIter,         //                                             可以不要
    PVMFAVIParserNodeLoggerDestructDealloc //pvlogger用                       可以不要
      
    在源码中可以看到 继承关系
    class PVMFAVIFFParserNode:public OsclTimerObject,
        public PVMFNodeInterface,
        public PVMFDataSourceInitializationExtensionInterface,
        public PVMFTrackSelectionExtensionInterface,
        public PvmfDataSourcePlaybackControlInterface,
        public PVMFMetadataExtensionInterface,
        public PVMFTrackLevelInfoExtensionInterface,
        public PVMFCPMStatusObserver,
        public PvmiDataStreamObserver,
        public PVMIDatastreamuserInterface,
        public PVMFFormatProgDownloadSupportInterface,
        public OsclTimerObserver,
        public PVMFCPMPluginLicenseInterface,
        public PvmiCapabilityAndConfig,
        public PVMFMediaClockStateObserver, // For observing the playback clock states
        public PvmfDataSourceDirectionControlInterface
       
    其中 OsclTimerObject,
         OsclTimerObserver,
         PVMFNodeInterface,
         PVMFDataSourceInitializationExtensionInterface,
         PVMFTrackSelectionExtensionInterface,
         PvmfDataSourcePlaybackControlInterface
         PVMFMediaClockStateObserver

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

    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之中

     PVMFAVIFFParserNodeCommand cmd;
     cmd.PVMFAVIFFParserNodeCommandBase::Construct(aSessionId, PVMF_GENERIC_NODE_QUERYINTERFACE, aUuid, aInterfacePtr, aContext);
     return QueueCommandL(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来处理这个命令的队列
            return id;

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

    在ProcessCommand中
          
              case PVMF_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等大部分都是以这种形式,因此代码大部分都可以直接copy mp4的,然后修改适应avi.

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

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

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

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

    PVMFStatus PVMFAVIFFParserNode::DoInit(PVMFAVIFFParserNodeCommand& aCmd)
    {
       ...
    }
    在其它媒体格式中有去初始化CPM的(Content Policy Manager)的,这方面了解不多,但也不影响播放,所以没有在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使用.实现的几个方法
     uint32 GetNumMetadataKeys(char* aQueryKeyString = NULL);
     uint32 GetNumMetadataValues(PVMFMetadataList& aKeyList);
     PVMFCommandId GetNodeMetadataKeys(PVMFSessionId aSessionId, PVMFMetadataList& aKeyList, uint32 aStartingKeyIndex, int32 aMaxKeyEntries,
                                                  char* aQueryKeyString = NULL, const OsclAny* aContextData = NULL);
     PVMFCommandId GetNodeMetadataValues(PVMFSessionId aSessionId, PVMFMetadataList& aKeyList,
                                                    Oscl_Vector<PvmiKvp, OsclMemAllocator>& aValueList, uint32 aStartingValueIndex, int32 aMaxValueEntries, const OsclAny* aContextData = NULL);
     PVMFStatus ReleaseNodeMetadataKeys(PVMFMetadataList& aKeyList, uint32 aStartingKeyIndex, uint32 aEndKeyIndex);
     PVMFStatus ReleaseNodeMetadataValues(Oscl_Vector<PvmiKvp, OsclMemAllocator>& aValueList, uint32 aStartingValueIndex, uint32 aEndValueIndex);

    uint32 PVMFAVIFFParserNode::GetNumMetadataKeys(char* aQueryString)
    {
        uint32 num_entries = 0;
        if(aQueryString == NULL || iAVIFileHandle == NULL)
            return 0;
        if (oscl_strstr("duration", aQueryString) != NULL)
        {
            num_entries++;
        }
        return num_entries;
    }
    uint32 PVMFAVIFFParserNode::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 the counter for the number of values found so far
                 ++num_entries;
             }
        }
        return num_entries;
    }
    GetNodeMetadataKeys 和GetNodeMetadataValues都是以命令形式执行.

      PVMFStatus PVMFAVIFFParserNode::DoGetMetadataKeys(PVMFAVIFFParserNodeCommand& aCmd)
    {
       ...
    }
    这个函数主要作用是返回key的的列表,这个key的列表有parser类来决定.依靠解析出来的,我们这里只支持”duration”这个key
    PVMFStatus PVMFAVIFFParserNode::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 ff library.
            return PVMFFailure;
        }

        PvmiKvp KeyVal;
        KeyVal.key = NULL;
        uint32 KeyLen = 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 key string was created
        if (KeyVal.key != NULL)
        {
                int32 leavecode = OsclErrNone;
            leavecode = PushKVPValue(KeyVal, *valuelistptr);
            if (OsclErrNone != leavecode)
            {
                // push kvp failed
                return PVMFErrNoMemory;
            }
        }

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

     
    GetMediaPresentationInfo取得流的类型

         此方法是比较重要的一个方法,是必去实现的.它完成的主要功能是对文件进行解析,解析出各个流的格式,比如音频流,视频流,文字流...等,并且为每个流指定一个mimi type,这个mime type会对应decNode支持的mime Type,从而将parserNode解析出来的流指定decNode.
       Avi中的实现:
    PVMFStatus PVMFAVIFFParserNode::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);     //设置port tag    
         aTrack.setTrackDurationTimeScale(iAVIFileHandle->GetScale(i));
         aTrack.setTrackDurationValue(iAVIFileHandle->GetStreamDuration(i));
         aTrack.setTrackMimeType(mime_type);
    因为针对各个媒体格式,我们解析所获得的mime type的类型是不同的,所以这部分的实现过程不同.因此需要有一些对这个多媒体格式的一些认识...不需要非常专业.
    如在avi中如何获得video的mime type
          …
          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);
                uint32 temp = 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
                     {
                       return PVMFFailure;
                     }
                }
               else if (IsYUVFormat_Supported(temp))
                {
                      mime_type = _STRLIT_CHAR(PVMF_MIME_YUV420);
                }
               else
                {
                    return PVMFFailure;
                }

           }
    这段代码主要是获得视频流的mime type,主要是看parser类能提供些什么,这而avi的parser类提供了一个GetVideoFormatType这个方法,返回的是一个字符数组uint8 fmtType[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(Begin Of Stream),默认true
        trackportinfo.iLastTimestamp = 0; //标识上次采样的时间戳,以方便计算此次采样的持续时间(将本次         得到的时间戳-iLastTimestamp)即可.


    完成各个node的DoRequestPort之后,engine就可以发命令播放了.在这里面还会在port连接(connect)过程中,会去设置Video/Audio MIO的一些必要参数,如果无法完成这一步,那么将会播放不成功(黑屏),在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 data buffer 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 to receive event when next deallocate() is called on pool
            return false;
        }

        // Now create a PVMF media data from pool
        errcode = OsclErrNoResources;
        PVMFSharedMediaDataPtr mediadataout;
        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 next deallocate() is called on pool
            return false;
        }
        // Retrieve memory fragment to write to
        OsclRefCounterMemFrag refCtrMemFragOut;
        mediadataout->getMediaFragment(0, refCtrMemFragOut);

    这段代码是从内存池中请求一块内存出来,和其它媒体几乎一致,用来储存解析出来的数据. 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() get nOutLength=%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的mime type 
       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 Width To 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));
            }
            return true;
    其它几个参数的设置过程基本如此.对于音频参数的设置直接调用了
    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中
       
       
    四.编译环境设置
     
       添加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行左右添加
    #define PVLOGGER_INST_LEVEL 5


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



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

    class PVLoggerConfigFile{
    /* 
        To configure logging at runtime, a file pvlogger.txt must be located in the sdcard.
        The format for log level and logger tag in the file should be: "level,node".  Note that there 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 for PVPlayerEngine and PVLOGMSG_DEBUG for PVSocketNode.  See pvlogger.h for log level values.
    */
    public:
        PVLoggerConfigFile():iLogFileRead(false)

    展开全文
  • 图标主题OpenCore Opencore个人版本,Opencore版本,Opencore版本0.6.6以前
  • Opencore Wishbone

    2020-12-24 17:50:37
    Opencore Wishbone总线规范
  • opencore代码

    2019-09-06 16:14:42
    opencore代码
  • OpenCore主题 OpenCore v0.6.6的主题 感谢为Icnspack-Builder 感谢的OpenCore 主题 大苏尔 黑暗的 大苏尔-黑暗的图标 光 锐利(深色/浅色) 平滑(深色/浅色) 夏普-B 平滑-B 黑暗之锋-B DarkSmooth-B 包括...
  • opencore-amr-android, android中的opencore amr编解码器 opencore-amr-android一种带有解释功能的opencore amr编解码器及其封装amr音频文件的。中文文档请移步 README_CN QQ部落帮助: 453503476背景opencore 是...
  • OpenCore是我们所谓的“引导加载程序”,它是用于为macOS准备系统的复杂软件,特别是通过为macOS注入新数据(例如SMBIOS,ACPI表和kexts)。该工具与Clover等其他工具的不同之处在于,它在设计时就考虑到了安全性和...
  • OpenCore-0.5.8

    2020-05-05 22:45:18
    OpenCore是类似于Clover的UEFI的引导器,OpenCore提供了详细的日志系统,帮助黑苹果排错;其次OpenCore以更先进的方法注入第三方Kext,不破坏系统的SIP;再次,OpenCore支持读取NVRAM等一系列特性,可以让黑苹果变得...
  • Opencore-Vanilla-Desktop-Guide:《 OpenCore Vanilla桌面指南》文件的主机
  • OpenCore Configurator

    2020-08-28 17:04:46
    OpenCore Configurator,非常优秀的一款黑苹果OC配置工具,可以通过提供更加通用和模块化的系统来解决Clover带来的限制和问题,拦截内核加载以插入高级的rootkit,而该rootkit旨在替代Clover。
  • 前言众多黑苹果驱动的作者已经停止对Clover的兼容支持,改向Opencore的兼容。所以也许在未来的某一天,Clover就越来越难用了。提前了解了解OpenCore,还是很有必要的。但是OpenCore毕竟没有Clover成熟,同样也没有很...

    前言

    众多黑苹果驱动的作者已经停止对Clover的兼容支持,改向Opencore的兼容。所以也许在未来的某一天,Clover就越来越难用了。提前了解了解OpenCore,还是很有必要的。但是OpenCore毕竟没有Clover成熟,同样也没有很好用的可视化编辑器(OpenCore Configurator会导致config出错,慎用),fix的选项也没有,对hotpatch的要求就高了。经过一周的摸索,总结一些OC的坑,帮助以后的朋友少走一些弯路。

    系统10.15.1→10.15.3自动更新顺利。之后升级OpenCore版本出现问题,0.5.2→0.5.5,卡驱动。找到问题后在文章中增加版本更新方法,需要可以研究一下。

    我的配置

    雷 神 G170P 笔 记 本,蓝 天(CLEVO)P170SMA模 具

    CPU:Core i7-4710MQ 2.50GHz

    主 板:CLEVO P17SM-A

    硬 盘:威刚SSD 256GB

    内 存:16 GB ( 威 刚 DDR3L 1600MHz )

    显 卡:Nvidia GTX970M(DSDT屏蔽) Intel HD4600

    有 线 网 卡:瑞 昱 RTL8168/8111/8112

    无 线 网 卡:博通BCM94352HMB(原 装intel网 卡,无 解)

    声 卡:Realtek ALC892

    目录

    一.准备工作

    二.调试Config.plist

    三.调试驱动

    四.OpenCore更新

    一、准备工作

    准备工作分为以下几步:

    ①.在Clover引导下,正常运行macOS系统

    ②.BIOS设置

    ③.下载最新的OpenCore

    ④.安装Xcode或使用ProperTree

    ⑤.efi基础驱动

    ⑥.U盘或硬盘建立引导分区

    ⑦.确认电脑是否支持原生NVRAM

    ① 至少要先用Clover安装系统,并能够正常进入macOS系统,这样能测试OC能方便很多。如果零基础从OC直接安装系统,难度比较大。对于已经配置好Clover的,则需要精简EFI,把不必要的驱动,dsdt和ssdt删除。防止OC使用过程中不兼容导致的问题。

    ② BIOS设置

    禁用

    | Fast Boot |             | 快速启动 |

    | CFG Lock |             | CFG锁 |

    | VT-d |                     | Hyper Threading |

    | CSM |                     | 兼容性支持模块 |

    启用

    | VT-x |                                | VT-x |

    | Above 4G decoding |      | 大于4G地址空间解码 |

    | Hyper Threading |           | 超线程|

    | Execute Disable Bit |       | 执行禁止位 |

    | EHCI/XHCI Hand-off |     | 接手EHCI/XHCI控制 |

    |OS type:Windows 8.1/10|     |操作系统类型Windows8.1/10|

    ③ 下载最新的OpenCore

    OpenCore-Releases:https://github.com/williambj1/OpenCore-Factory/releases

    官方发布页下载的文件包里的Docs文件夹内的samplefull.plist改名为config.plist作为基础配置自己的OC。

    ④ 对于OC中Config.plist,Xcode是最好的选择。其次是ProperTree。

    Plist Editor Pro会破坏结构,不能转换Base64.

    OpenCore Configurator目前更新速度落后于OpenCore,因此会导致Config的损坏

    ⑤ efi基础驱动

    ApfsDriverLoader.efi 或 Apfs.efi      识别APFS分区

    FwRuntimeServices.efi     用来替换AptioMemoryFix.efi

    UsbKbDxe.efi     OC内置驱动不工作,可以提供Apple热键和FileVault的支持。卡OC引导界面,可以删除

    VirtualSMC.efi     配合VirtualSMC.kext使用,不能和FakeSMC.kext同时使用。

    VBoxHfs.efi 或 HFSPlus.efi     识别HFS分区,对于不能进入Recovery,需要使用HFSPlus.efi

    EmuVariableRuntimeDxe.efi     模拟NVRAM,对于没有原生NVRAM,需要此驱动(该驱动已和最新的FwRuntimeServices.efi合并,建议使用最新驱动)

    将需要的驱动,放置在EFI——OC——Drivers中,同时在Config.plist——UEFI——Drivers添加efi驱动名

    ⑥ 使用硬盘工具新建一个esp(Fat16)新分区,空间不低于200MB来存放。U盘或硬盘分区都可以。我是在Windows中使用DiskGenius分的区。

    ⑦确认电脑是否支持原生NVRAM

    打开终端输入:sudo nvram TestVar=HelloWorld然后输入密码

    重启系统后输入:sudo nvram -p | grep 'TestVar'

    如果显示:TestVar HelloWorld

    说明原生支持NVRAM

    二、调试Config.plist

    OC的Config.plist与Clover的Config.plist结构有很大的不同,因此不能通用。整体结构以及说明参考黑果小兵:精解OpenCore和XJN`S BLOG:使用OpenCore引导黑苹果,以下主要说明容易出现的问题。OC中的config.plist是由Docs文件夹内的samplefull.plist改名而来。

    ①Misc—security—requiresignature—NO

    防止出现报错:OC:configuration signed vault but no public key provided

    ②misc—security—requirevault—NO

    防止出现报错:OC:configuration requires vault but no vault procided

    ③可以删除 #WARNING - 1 和 #WARNING - 2等

    ④Booter—Quirks—DisableVariableWrite—NO

    禁止NVRAM写入,没有原生macOS支持NVRAM需要开启

    ⑤DeviceProperties—Add可以加入设备硬件地址以及补丁

    可以使用Hackintool应用补丁生成设备硬件代码驱动硬件

    例如:核显驱动、核显缓冲帧、EDID注入,声卡LayoutID注入、读卡器等等

    注意:这里使用的代码与Clover的Config.plist中的Devices—Properties的代码通用

    ⑥OC上所有的kext、.efi驱动、.aml补丁都需要填入config.plist,并且都有加载顺序,有依赖的驱动要放在所依赖的驱动之后。

    注意:Kernel—Add  按加载顺序加入kext名称

    ACPI—Add  按加载顺序加入.aml名称

    UEFI—Drivers  按加载顺序加入.efi驱动名称

    ⑦Kernel—Quirks—XhciPortLimit: YES

    USB的15个端口限制补丁,建议定制USB后关闭该选项

    ⑧Misc—Boot—ShowPicker: YES

    显示OpenCore的UI,可选引导项。

    ⑨NVRAM—LegacyEnable: NO

    允许将 NVRAM 存储在 nvram.plist 中, 没有原生 NVRAM 的设备必须开启

    ⑩NVRAM—7C436110-AB2A-4BBB-A880-FE41995C9F82—csr-active-config: bytes = 0xe7030000 - SIP 完全关闭

    ⑪设置默认语言为中文,在NVRAM—7C436110-AB2A-4BBB-A880-FE41995C9F82—prev-lang:kbd中填入<7a682d48 616e733a 323532>(需要NVRAM支持,不支持原生NVRAM或者没弄好模拟NVRAM不生效)

    ⑫Misc—Security—AllowNvramReset:YES

    在OC引导界面显示 Reset NVRAM选项

    注意:如果用Clover启动过系统后,再用OpenCore启动系统,需要执行Reset NVRAM来清除NVRAM。清除NVRAM,会清除BIOS自定义引导项名称。

    ⑬Misc—Security—ScanPolicy:0

    在OC引导界面显示所有可用硬盘

    定义:

    (01)0x00000001 — 限定为文件系统,由以下允许扫描文件系统子项开启

    (02)0x00000002 — 限定为设备类型,由以下允许扫描设备类型子项开启

    ​ 允许扫描文件系统子项:

    (03)0x00000100 — 允许扫描APFS文件系统

    (04)0x00000200 — 允许扫描HFS文件系统

    (05)0x00000400 — 允许扫描EFI系统分区文件系统

    ​ 允许扫描设备类型子项:

    (06)0x00010000 — 允许扫描SATA设备

    (07)0x00020000 — 允许扫描SAS和Mac NVMe设备

    (08)0x00040000 — 允许扫描SCSI设备

    (09)0x00080000 — 允许扫描NVMe设备

    (10)0x00100000 — 允许扫描CD / DVD设备

    (11)0x00200000 — 允许扫描USB设备

    (12)0x00400000 — 允许扫描FireWire设备

    (13)0x00800000 — 允许扫描读卡器设备

    扫描策略数值=(01)+(02)+1个或数个允许扫描文件系统子项+1个或数个允许扫描设备类型子项

    例如:希望扫描对象是APFS文件系统的USB设备,扫描策略数值=(01)+(02)+(03)+(11),经16进制加法计算得出,扫描策略数值=0x200103。

    注意,使用时需将16进制转换为10进制。示例最终扫描策略数值=2097411

    ⑭ACPI—Patch放入dsdt、ssdt补丁

    Kernel—Patch放入kext补丁

    ⑮PlatformInfo中直接删除Datahub,PlatfromNVRAM,SMBIOS这三项,无需填写。

    ⑯IgnoreInvalidFlexRatio: YES

    (在 BIOS 中无法禁用 MSR_FLEX_RATIO(0x194) ,或OpenCore卡引导项时开启)

    注意:部分OpenCore卡引导项,是由于UsbKbDxe.efi与键盘不兼容导致的,删除UsbKbDxe.efi即可

    三、调试驱动

    以我自身的驱动来说明

    驱动目录

    ①建议使用VirtualSMC.kext,因为需要与Drivers中的VirtualSMC.efi配合

    ②建议定制USB驱动,如自定义的USBInjectAll.kext、USBPorts.Kext、或加载SSDT-UIAC

    ③建议定制声卡驱动,虽然万能声卡驱动也支持OC

    ④VoodooPS2Controller.kext与ApplePS2SmartTouchPad.kext中存在插件kext,在Config.plist中Kernel—Add分别加入

    ⑤.kext中没有驱动,只有info.plist的,Kernel—Add加入info.plist的path,不必加入kext的path。

    ⑥建议核显驱动使用Hackintool应用补丁生成核显代码、相关补丁以及EDID的注入,防止添加过多kext影响兼容性,同时定义CPU接口,方便加载HDMI视频音频

    HD4600核显

    四、OpenCore版本更新

    写这篇文章时我的系统版本是10.15.1,OC是0.5.2。之后更新为10.15.3和0.5.5。

    更新OpenCore版本需要注意以下四点:

    ①做好原有EFI的备份,config.plist—NVRAM—Add—7C4361......—boot-args中添加-v,更新时出错,可以及时发现问题

    ②下载最新OpenCore,替换原有EFI中的Bootx64.efi和OpenCore.efi

    ③最新OpenCore—Drivers找到需要的驱动,对原有EFI中的Drivers驱动进行替换

    注:在0.5.5后Divers最新驱动已经合并到最新的OpenCore中

    ④查看最新OpenCore—Docs—SampleFull.plist中是否有更新,如有更新可以查看作者的说明进行修改,没有则直接使用之前的config.plist

    持续更新中

    展开全文
  • OpenCore简体中文参考手册 非官方维护的OpenCore参考手册的简体中文翻译 简介 这个仓库存放的是的源码。 由于OpenCore仍然在积极地开发和迭代,OpenCore参考手册仍然在高频率更新,我们需要更多黑苹果爱好者参与到...
  • OpenCore Configurator.zip

    2020-06-28 14:48:20
    支持OpenCore 0.5.9及以上版本.用于Hackintosh的配置.OpenCore Mac版是Mac电脑上的一款OpenCore(就是我们说的OC引导)的配置工具。就相当于Clover Configurator.app是Clover专用的配置工具一样,OpenCore ...
  • OpenCore更新器 可以更新kexts,EFI文件和OpenCore的Python脚本。 我仍然有很多事情要做,所以我不建议您现在就使用此脚本。 更新程序可以做什么,我想包括什么: 可以更新大多数Kexts 可以更新OpenCore和一些efi...
  • 这是在opencore0.54中加入的新功能,只要把MISC/Security/AllowSetDefault的值设置为true,然后在opencore启动项选择界面,光标移到要设置默认启动项处,按crtl+enter或者ctrl+index即可。 ...
  • 带有OpenCore的Lenovo ThinkPad P50 macOS安装 介绍 我已经能够通过OpenCore在Lenovo ThinkPad P50上安装macOS。 本指南将介绍如何使其正常工作的设置,但除ACPI和配置的某些部分外,将不包括任何文件,您必须自己...
  • Opencore-Aorus-Elite-AC OpenCore技嘉Z490 Aorus Elite AC 适用于技嘉z490 Aorus Elite AC的opencore 0.6.7引导程序。 此EFI的目的是,拥有同一块板的任何人都可以运行macOS BigSure,而不会出现任何问题,而不必...
  • Surface-Pro6-Opencore 前言 有一天开车回家的路上,听广播说“某通讯软件偷偷读取Chrome浏览记录”,突然发现是否可以更换苹果系统避免这个问题。 本来计划使用自己的MacBook Pro作为自己的办公机器,而满多软件不...
  • OpenCore Configurator 中文版是非常优秀的一款黑苹果OC配置工具,可以通过提供更加通用和模块化的系统来解决Clover带来的限制和问题,拦截内核加载以插入高级的rootkit,而该rootkit旨在替代Clover。OpenCore目前的...
  • OpenCore Gen-X 是mac上一款黑苹果OC引导配置制作小工具,可以一键制作黑苹果OpenCore EFI文件,不过该 app 需要 macOS 环境才能运行,没有的用户可以先使用虚拟机安装 macOS(macOS 10.15 即可),配置好 EFI ...
  • OpenCore引导配置说明第十四版-基于OpenCore-0.6.7 一、 OC配置的基本条件与工具软件 1.1、 准备Mac平台,包括实体机平台和虚拟机平台。 1.2、 下载最新镜像并核对MD5,制作安装U盘。 1.3、 工具软件: lover ...
  • 家 heroImage heroText 动作链接 元 真的 /dortania-logo-clear.png 多塔尼亚针对传统Mac的OpenCore 先决条件.md 姓名 内容 描述 由OpenCore Legacy Patcher取代 该指南已不再维护! 请改用
  • 华硕Prime-Z490-A-OpenCore-Hackintosh 华硕Prime Z490-A-OpenCore-Hackintosh
  • OpenCore引导配置说明第十三版-基于OpenCore-0.6.6正式版 一、 OC配置的基本条件与工具软件 1.1、 准备Mac平台,包括实体机平台和虚拟机平台。 1.2、 下载最新镜像并核对MD5,制作安装U盘。 1.3、 工具软件: lover...
  • OpenCore 0.6.0

    2020-12-08 21:43:39
    <div><p>Any plans for OpenCore 0.6.0? And on another unrelated topic, how likely would you guys start a repo for Big Sur?</p><p>该提问来源于开源项目:jaromeyer/XPS9570-Catalina</p></div>
  • Opencore EFI文件夹 我的台式机和PC的EFI文件夹。 学分 来自hackxtotitude,dartonia,r / hackintosh的Hackintosh社区的许多聪明善良的人以及编写所有kext的慷慨的人会预先编译所有ACPI补丁和hackintosh工具。 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 39,834
精华内容 15,933
关键字:

opencore