| Symbian OS MMF architecture analysis
由来:
SymbianV6.0使用媒体服务器(media server)处理多媒体应用,这种模型基于单线程工作模型,即所有的多媒体服务处在同一个线程中。这种模型在视频流,CD音质,移动商务和位置服务等性能要求较高的服务处理时不能达到理想的效果;而且缺乏实时流支持,难以处理异步请求,在大量数据的装载过程中会遇到速度瓶颈。
因此在SymbianV7.0中,采用另外一种架构模型MMF(multimedia framework)。
特点:
1. 支持多线程处理 multiple thread
2. 媒体格式识别 format recognition
3. 流传输 streaming
4. 媒体插件库 plug-in media component library
5. 控制框架中基础类库 base media classes wrapped in controller framework
6. 方便证书和第三方多媒体应用开发
总体描述:
MMF 主要依赖于controller framework,controller framework能够支持多媒体插件程序。 MMF类似于一个多媒体处理插件程序的仓库,它为硬件设备提供一般的接口,其中的关键部分是controller framework,由其管理插件程序的选择,装载以及插件间的交互。MMF采用Client/Server架构模型。如图:
图1
MMF为每个插件创建一个客户端线程和服务器端线程,如果有多个MMF插件程序执行,那么就会有更多的线程。所有线程运行在一个进程中,能减少服务请求时的上下文交换,这样能提供MMF的效率。此外MMF的线程间通信用ITC(interthread communication)取代了IPC机制。
详细架构如图:


图2
总体模块结构
1. 客户端插件APIs为应用程序访问插件提供途径,具体的途径由Plug-in Utility对象提供。
2. Controller Framework 选择和启动相应的插件程序,即传递客户端消息到相应的插件程序。
3. 相应的controller plug-ins 负责处理从客户端来的数据。
4. Sound device 通过硬件设备接口为音频硬件提供通用接口。
5. Audio policy 解决客户端同时请求音频硬件使用的优先权问题。
6. Hardware device API提供一些底层的硬件接口。
详细分析:(按照大模块结构)
详细分析:(按照大模块结构)
1. Client-Side Plug-in API Layer
客户端插件API层由插件实用对象描述,插件实用对象提供插件框架的应用层接口。每个客户端实用类(client utility class)中含有一个非静态的成员,它是RMMFController类的一个实例的引用。当插件加载时,将会创建一个逻辑通信通道,来连接RMMFController类实例和在服务器端的CMMFController类实例。这就是symbian的Client/Server架构。
客户端到服务器端的请求通过RMMFControllerPorxy对象发送,该对象包含同步和异步发送请求方法SendSync(), SendAsync()。
标准的多媒体插件API根据其不同的功能划分为七部分:
1) 控制装载和卸载 Controller loading and unloading
2) 管理源和接收器数据 Managing sources and sinks
3) 改变控制状态 Changing control state
4) 调节播放头 位置 Adjusting play-head position
5) 查找元数据 Retrieving metadata
6) 处理自定义命令 Handling custom commands
7) 时间监控 Event monitoring
以下分别介绍这7个部分。(按照小模块结构)
1) Controller loading API
开启新线程开始插件装载:
TInt RMMFController::Open(Tuid aControllerUid,
Const TMMFPrioritySettings& aPrioritySettings);
应用通过提供UID来启动一个插件,如果应用不提供,那么controller framework来决定正确的UID。TMMFPrioritySettings参数用来为audio policy组件提供多个应用同时访问硬件设备时确定优先级。如果要修改优先级可以使用如下函数:
TInt RMMFController::SetPrioritySettings(
const TMMFPrioritySettings& aPrioritySettings) const;
卸载插件是使用方法RMMFController::Close();
2) Managing sources and sinks API
当插件启动后,controller就承担起在数据源和数据目标对象间传送数据的责任。以下方法用来添加和卸载数据源和接收器。
TInt RMMFController::AddDataSource(
Tuid aSourceUid,
const TDesC8& aSourceInitData);
TInt RMMFController::AddDataSource(
Tuid aSourceUid,
const TDesC8& aSourceInitData, TMMFMessageDestination & aHandleInfo);
TInt RMMFController::AddDataSink(
Tuid aSinkUid,
const TdesC8& aSinkInitData);
TInt RMMFController::AddDataSink(
Tuid aSinkUid,
const TdesC8& aSinkInitData,
TMMFMessageDestination& aHandleInfo);
TInt RMMFController::RemoveDataSource(
const TMMFMessageDestination& aSourceHandleInfo);
TInt RMMFController::RemoveDataSink(
const TMMFMessageDestination& aSinkHandleInfo);
3) Changing control state API
每个多媒体控制插件在任意时刻必然是下面几个状态之一:stopped, primed,或者playing。下面几个函数提供状态转移机制:
TInt RMMFController::Reset();
TInt RMMFController::Prime();
TInt RMMFController::Play();
TInt RMMFController::Pause();
TInt RMMFController::Stop();
Controller plug-in初始化为stopped状态,此时play-head位置置0并且没有缓冲以及资源的分配。当转变为primed状态时,controller已经申请了必要的资源并让数据源和接收器就绪接收和发送数据,该状态是playing状态的中间过渡状态。当转变为playing状态,controller plug-in就开始在源和接收器之间转移数据。Pause()方法使controller进入primed状态,而Reset()使controller plug-in回到stopped状态。
4) Adjusting play-head position API
播放时,在媒体源中调节play-head位置并返回当前位置由以下方法实现:
TInt RMMFController::GetPosition(
TTimeIntervalMicroSeconds& aPosition) const;
TInt RMMFController::SetPosition(
const TTimeIntervalMicroSeconds& aPosition) const;
类似的,返回媒体播放的持续时间由以下方法提供:
TInt RMMFController::GetDuration(
TTimeIntervalMicroSeconds& aDuration) const;
该功能只能在primed和playing状态中才能使用。
5) Retrieving metadata API
以下方法提供提供查找元数据特性:
TInt RMMFController::GetNumberOfMetaDataEntries(
TInt& aNumberOfEntries) const;
CMMFMetaDataEntry* RMMFController::GetMetaDataEntryL(
TInt aIndex) const;
6) Handling custom commands API
有了定制的命令,应用就可以向插件发送标准API不支持的特殊请求。特殊应用命令能够同步或者异步地发送给插件,在这两种情况下提供重载的方法通过数据源描述符返回来自插件的数据。
TInt RMMFController::CustomCommandSync(
const TMMFMessageDestinationPckg& aDestination,
TInt aFunction,
const TDesC8& aDataTo1,
const TDesC8& aDataTo2,
TDes8& aDataFrom);
TInt RMMFController::CustomCommandSync(
const MMFMessageDestinationPckg& aDestination,
TInt aFunction,
const TDesC8& aDataTo1,
const TDesC8& aDataTo2);
void RMMFController::CustomCommandAsync(
const TMMFMessageDestinationPckg& aDestination, TInt aFunction,
const TDesC8& aDataTo1,
const TDesC8& aDataTo2,
TDes8& aDataFrom,
TRequestStatus& aStatus);
void RMMFController::CustomCommandAsync(
const TMMFMessageDestinationPckg& aDestination, TInt aFunction,
const TDesC8& aDataTo1,
const TDesC8& aDataTo2,
TRequestStatus& aStatus);
7) Event-monitoring API
CMMFControllerEventMonitor类用来监听controller plug-in发生的事件。该类基于活动对象,当事件触发时通过MMMFControllerEventMonitorObserver接口向客户端发送信号。客户端必须注册才能接收来自controller plug-in的事件。也可以选择不通知controller-originated events,由CancleReceiveEvents()方法实现。
void RMMFController::ReceiveEvents(
TMMFEventPckg& aEventPckg,
TRequestStatus& aStatus);
TInt RMMFController::CancelReceiveEvents();
事件通过TMMFEvent容器返回,其中包含事件的ID和错误代码。由于返回值要跨越客户端和服务器端边界,因此被打包在TMMFEventPckg对象中。
2.MMF Controller Framework
Controller plug-ins指那些由controller framework提供支持的专门多媒体插件。Controller plug-in主要负责协调数据源和接收器之间的数据流,比如音频控制插件可能从文件获取数据,输出到扬声器;类似地,它也能从麦克风获取数据,然后保存到文件。Controller plug-in支持多种多媒体格式,如音频控制器可以支持WAV, AMR, MP3等格式。
Controller framework由以下几个部分组成:
1. Controller plug-in resolver,控制系统争对特殊的数据源能最大限度的使用可用插件。
2. Controller proxy, 通过symbian系统的客户/服务器机制处理插件线程启用,线程间通信。
3. Controller APIs,一系列具体的API用来处理插件通信。
4. Custom commands, 允许标准的controller API能够被扩展。
5. Miscellaneous helper classes(各种各样的帮助类),例如CMMFBuffer, 帮助处理数据缓冲,元数据分析,格式化。
下图描述音频处理插件的具体架构,其余插件架构也类似这种模型。
图3
1) Controller plug-in Resolver
Controller plug-in resolver 是一个 ECOM plug-in(ECOM接口维护symbian系统上所有支持的插件的注册信息)。它负责为一个特定的数据源格式查找一个最合适的插件并返回的UID。主要实现方法:从ECOM注册库中读取所有可用的插件描述信息,然后同数据源属性进行比较找到一个最合适的匹配。插件的属性包含有提供商,媒体MIME类型,文件扩展等。由于一些数据格式能被不止一个插件所支持,所以更加详尽的方案还需要分析媒体数据段头的一些字节信息来匹配最优的插件。
2) Controller Proxy
当合适的插件被找到后,controller proxy开始为插件运行分配足够的资源以便使插件启动和运行。Controller proxy的内部又是个很小的client/server层,它负责创建线程,启动服务以及client与server间的消息传递。
由上图可以看到其中的细节:
controller proxy中的client端由RMMFControllerProxy描述,该类继承于RMMFSessionBase,server端由CMMFControllerProxyServer对象描述;
client和server都运行在用户进程的独立线程中;server线程仅仅负责创建和销毁一个会话(CMMFControllerProxySession)从应用传递消息给插件。CMMFControllerPorxySession对象负责接收来client端的消息,一旦接收到消息,该会话对象就把消息传递给CMMFController基类。
每个MMF消息都指派了一个接口ID和目的句柄,CMMFController基类能判断该消息中的请求由自己处理还是继续传递到controller plug-in。该判断由如下方法实现
CMMFController::HandleRequestL()。
由于每个controller proxy服务维护一个单独的会话,这意味着只有一个应用能作为一个controller实例的client端;所有其他的的client端如果需要使用同样的插件,那么controller就产生一个插件的拷贝并启动新的线程为其他client服务。
3) Controller API
Controller plug-in API 由CMMFController接口描述。它负责接收来自客户请求(客户请求通过标识在RMMFController类中应用端API发送)并分发请求到更低的层次。当CMMFController实例化时,CMMFController产生其内部所有插件的工厂对象,他还建立一个音频设备的会话。每个MMF插件作为CMMFController类的派生类并实现所有功能的虚拟。
CMMFController的声明在MMFController.h中,其中的APIs看起来类似应用接口层的RMMFController类中的APIs。当然两者之间也有一些区别,比如RMMFController::Open()和RMMFController::Close(),在CMMFController类中没有着两个方法。原因在于RMMFController::Open()使用查询的插件UID导致CMMFController::NewL()的调用;而RMMFController::Close()引起CMMFController析构函数的执行。
另一个重要的区别在于状态变化的同步和异步,值得一提的是,状态改变的请求应该由controller异步处理并维护clientAPI端同步。例如Play请求在播放一开始时就必须立即返回,而不应该等到播放结束。
virtual void CMMFController::PlayL() = 0;
virtual void CMMFController::PlayL(
TMMFMessage& aMessage); //async
virtual void CMMFController::PauseL() = 0;
virtual void CMMFController::PauseL(
TMMFMessage& aMessage); //async
virtual void CMMFController::StopL() = 0;
virtual void CMMFController::StopL(
TMMFMessage& aMessage); //async
支持在controllerAPI层的Custom Command由CustomCommand()描述;另外还提供了
一个方法添加custom parser。Symbian提供了默认的custom command句柄的实现保证所有的client/server消息都能被处理,甚至不能识别的请求。
virtual void CMMFController::CustomCommand(
TMMFMessage& aMessage)
{aMessage.Complete(KErrNotSupported);};
//default implementation
void CMMFController::AddCustomCommandParserL(
CMMFCustomCommandParserBase& aParser);
4) Custom Commands
因为标准的插件APIs提供的功能毕竟有限,所以MMF提供了一系列custom command句柄允许插件实现添加controller-specific APIs的扩展。以下两图描述了custom command句柄如何在客户和服务端的实现过程。

图4 Custom command handlers on the client side
通过Custom command发送的消息可以是同步或者异步,他们不能和标准APIs相同对待。前面曾提到过framework有一个内建的机制来解释和管理通过标准APIs发送的消息;而通过custom command发送的消息需要发送到controller plug-in,而controller plug-in使用一个custom command parser解释和执行他们。
图5 Custom command handler for the audio controller plug-in with the sample parser on the server side
在服务器端,controller framework 拥有CMMFCustomCommandParserManager对象,该对象知道所有从CMMFCustomCommandParserBase继承的专门争对特定插件的custom command parser。
在服务器端,controller framework 拥有CMMFCustomCommandParserManager对象,该对象知道所有从CMMFCustomCommandParserBase继承的专门争对特定插件的custom command parser。
当一个custom command发送给controller plug-in时,CMMFController类首先检查CMMFCustomCommandParserManager对象看看它有没有处理这个command的parser,如果没有,那么这个custom command消息直接发送给controller plug-in去处理。在上图中负责处理的是CMMFAudioDeviceCustomCommandParser。
上图显示CMMFAudioController 插件使用一个custom command parser
(CMMFAudioPlayDeviceCustomCommandParser)处理普通音频格式的播放。CMMFAudioController继承于MMMFAudioPlayDeviceCustomCommandParser类并扩展了处理特殊音频请求的能力,比如设置和获取声音强度,平衡,最大可达音量等,然后发送给音频设备去执行。任何不能被识别的请求需要添加新的parser或者重载CMMFController::CustomCommand()才能被执行。 |