版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第十一章多媒体服务
1第十一章多媒体服务1完成本章内容之后我们将能够:了解多媒体框架(MMF)使用MMF音频API了解MMF视频概念,框架和相关API本章目标2本章目标2概述本章先讲述多媒体架构,包括它的发展简史。然后,描述多媒体子系统的各个单独部分。3概述本章先讲述多媒体架构,包括它的发展简史。然后,描多媒体组件架构
在讨论多媒体的当前状态之前,先回顾SymbianOS中多媒体的发展历史。从历史上看,多媒体子系统包括用于处理下面多媒体领域的单独API:音频——回放、录音和操作。静态图像——解码、编码和操作。直到SymbianOS7.0,这一切都没有发生变化,不过,对应的子系统却发生了相当大的变化。SymbianOS7.0中的多媒体API主要是以前版本中出现的API的超集,在某些地方,同时存在新方法和遗留的方法。
4多媒体组件架构在讨论多媒体的当前状态之前,先回顾S多媒体组件架构
为了解释在SymbianOS7.0中对多媒体子系统进行许多基本改进的原因,有必要回顾这—操作系统以前版本中的子系统以及它们所面临的问题。这一节简要概述6.1和7.0中的Media服务器及进行更改的原因。5多媒体组件架构为了解释在SymbianOS7.0媒体服务器
在SymbianOS6.1和7.0中,所有多媒体处理都通过媒体服务器(mediaserver)进行。这是一个标准SymbianOS服务器,是提供全部多媒体功能的单个进程。该服务器支持音频回放和录音,还支持对静止图像的编码/解码和处理。操作系统支持大量音频和图像格式,可以通过编写插件扩展这些格式。为了使用该服务器,用户可以显示实例化一个与服务器的连接,或允许客户端API自动提供一个连接。每个客户端用户将提供一个服务器的观察器类,使该服务器能够与用户应用程序或库通信传递消息。
6媒体服务器在SymbianOS6.1和7.0中媒体服务器
服务器保持一个客户端对象列表,并且并发循环处理多媒体请求。这意味着,许多不同的客户端用户可以同时使用该服务器,例如,让一个用户应用程序播放音频,同时解码用于显示的图像。尽管这听起来很理想,但产生这种行为的实际问题很复杂并且困难重重。例如,如果用户实际上希望同时使用服务器的两部分功能,让一个进程控制这两部分造成的潜伏期可能使系统实际上不可用。例如,一项处理器密集型任务(如解码图像)将阻止使用任何实时多媒体任务(如音频流式处理),音频流在设备驱动器中将非常迅速地下溢。当使用编写得很差的第三方插件(常常转换自非Symbian代码,并包含消耗大量处理器时间的冗长程序)时,情况更糟。插件框架本身就使得为之编程非常复杂,这并不会改善上述状况。7媒体服务器服务器保持一个客户端对象列表,并媒体服务器
上述各种问题,加上连接到服务器在最坏的情况下可能花费几秒钟的事实,意味着必须对此作出改进。8媒体服务器上述各种问题,加上连接到服务器在最新时代的开始
Symbian开始编写全新的多媒体子系统,它将成功允许该子系统的不同功能同时使用,也提供手机制造商和第三方可以轻松扩展的轻量级框架。新系统不是只基于一个服务器,而是拆分不同的多媒体部分,以使用单独的服务器和进程。新的子系统使用多个并发多媒体线程,没有在媒体服务器中看到的任何副作用。它使用许多与媒体服务器相同的客户端API,但是使用了一个新的插件解决方案,称作ECom。这使得在电话制造过程中或出产以后,手机生产商和第三方能够编写和集成插件。
9新时代的开始Symbian开始编写全新的多媒体子系多媒体框架(MMF)由于这个新的子系统如此成功,以至于很快被集成到SymbianOS7.0中,SymbianOS7.0只是刚刚开始它的开发生命周期。随着SymbianOS7.0的发展进步,这个子系统演变成现在所谓的多媒体框架(MMF,MultimediaFramework)。MMF提供了在SymbianOS7.0中能够处理多媒体的框架。这个框架本身十分轻量化,并且提供了一个基于ECom的多线程系统,它保留了从6.1到7.0中原始API的子集,还进行了很多改进。MMF的基本结构包括一个客户端API层、一个控制器框架、控制器插件和低层次子系统。图7.1显示了这个架构的概观。10多媒体框架(MMF)由于这个新的子系统如此成功,多媒体框架(MMF)图7.1MMF架构11多媒体框架(MMF)图7.1MMF架构11多媒体框架(MMF)在图7.1中,控制器框架可以看作是客户端/服务器层,它为MMF端控制器插件提供接口。子系统的每一部分的描述如下。12多媒体框架(MMF)在图7.1中,控制器框架可以看作是客客户端API客户端API(有时也称为“应用程序API”)位于子系统中的最高抽象层。这些API为开发人员提供了MMF的基本功能。在SymbianOS7.0中,存在用来生成正弦波音频音、音频和视频剪辑操作以及音频流的客户端API。除两种API以外,其余所有控制器框架都用来获取并控制相关的控制器插件,以执行多媒体任务。上述两种例外是音频流API和音频声音生成API,二者都不使用控制器框架,而是直接与DevSound层接口。DevSound层是一个低层API,这将在本章后续部分简短讨论。13客户端API客户端API(有时也称为“应用程序API”)控制器框架
控制器框架提供了支持MMF内部多媒体插件的框架。它提供了MMF的客户API层与实际MMF控制器插件间的客户端服务器通信。它由以下几个逻辑模块构成。控制器插件解析器对于特定的应用程序,控制器插件解析器允许系统选择最佳插件。它提供了—个API集,客户可以使用它们来获取插件(MMF客户端API就是其中之一)。2.控制器代理控制器代理用来管理控制器框架之内的所有线程处理和线程间通信。这是标准SymbianOS客户端/服务器机制之上的一个简单层,用来提供控制器API的客户端与服务器端之间通信的方式。它不能直接使用,我们只是为了完整性而在此提及。
14控制器框架控制器框架提供了支持MMF内部多媒体控制器框架
3.控制器API层控制器API层由控制器代理的客户端和服务器端的匹配类构成。应用程序(或MMF)使用客户端类(即RMMFController)来运用控制器插件的功能。服务器端类(CMMFController)是控制器插件编写者为了实现他们的插件而必须派生的类。这个类也把所有发送给控制器插件的信息解包。
15控制器框架3.控制器API层15控制器框架
4.自定义命令根据设计,控制器插件API局限于非常基础的数据流操作。为了扩展该API,可以使用—种自定义命令框架,它允许客户访问控制器特定的扩展。在一般情况下,MMF使用该框架为常规命令(称做“标准自定义命令”,如设置音量和获取平衡)的处理定义API。控制器插件的生产商特定API也可以使用该技术进行暴露。16控制器框架4.自定义命令16控制器插件
控制器插件为MMF提供特定的多媒体功能。控制器的基本任务是从一个或者多个源中引导数据,把数据转变成不同的格式,然后把数据传输到一个或者多个接收器。数据源一般是文件、麦克风或摄像头,而接收器一般为文件,扬声器或屏幕。控制器插件一般都支持播放和录制一种或多种多媒体格式,比如mp3或avi格式。它能够解读相关源或接收器中的原始数据,并从源中读取数据,进行必要的数据转换,然后写入接收器。控制器插件的架构略为复杂,超出了本书的讨论范围。不过,我们将简要介绍实现控制器插件的步骤,如下所述:17控制器插件控制器插件为MMF提供特定的多媒体功能。控制器插件
实现MMF控制器插件的API。如上面“控制器框架”所述,所有MMF控制器插件都是由CMMFController基类派生而来。该基类提供控制器API,而且还提供诸如通过ECom插件框架实例化控制器插件的功能。在该基类中,需要重载的最基本函数集声明为纯虚函数,包括PlayL()和StopL()等函数。必须实现所有标准自定义命令集所需要的函数。这可以通过以下方式实现,先用适当的“自定义命令实现器”类派生控制器程序,然后使用CMMFController()和AddCustomCommandParserL()函数来注册这个该控制器,使其能够处理标准命令集。18控制器插件实现MMF控制器插件的API。如上面“控制器框控制器插件
MMF提供了一组基类来协助编写控制器插件。这些基类包括数据源和接收器类,以及缓冲类。数据源和接收器类本身就是ECom插件,并且封装了文件、描述符、音频输入和输出的使用功能。当把数据由源传送到接收器的时候,缓冲用来存储这些数据。缓冲类的类型有很多,但它们都是从CMMBuffer类派生而来。不同缓冲类的主要区别在如何存储数据,例如,数据是存储于描述符中,还是存储在内核端缓冲中。19控制器插件MMF提供了一组基类来协助编写控制器插件。这些控制器插件
为了与控制器框架插件解析器一起工作,每个控制器插件(实际是每个ECom插件)均要求一个ECom插件资源文件。控制器框架插件解析器使用它标识该插件的多媒体功能,比如是否能够播放和录音,支持什么格式。20控制器插件为了与控制器框架插件解析器一起工作,每个控制器插控制器插件
可以在SDK中找到有关ECom插件的更多信息。MMF控制器插件可以具有硬件特定的源和接收器。对于所涉及的硬件,它通常是一种设计驱动器或一个自定义API,它由授权商或硬件商来实现。在SymbianOS7.0中,惟一的例外是DevSoundAPl,它具有标准的实现,供参考音频控制器使用。
21控制器插件可以在SDK中找到有关ECom插件的更多底层子系统
在MMF架构的底层,存在许多子系统。授权商在生产新手机时可以实现这些子系统,它们一般与硬件相对应。子系统包含的内容主要取决于授权商的要求,这些层规定了手机的大部分多媒体功能。这些底层一般与第三方用户无关,不过,下面会简短介绍其中一个主要子系统DevSound。DevSound将SymbianOS移植到新平台时,授权商或硬件商实现CMMFDevSound类。它的API提供了MMF中音频回放和录音的最底层抽象,在音频设备驱动器之上提供了独立于硬件的层。DevSoundAPI提供了对音频数据播放和录音的支持、对播放音频音的支持以及对音量、对平衡等音频硬件属性修改的支持。22底层子系统在MMF架构的底层,存在许多子系统。授权底层子系统
另外,DevSound包括一种在同时使用声音硬件的不同客户间进行仲裁的方式。这种仲裁层处理声音的优先级(在客户API中设置),它的单个优先级层由授权商定义。典型用法是在中断低优先级客户(如MP3播放器)时,决定DevSound处理高优先级客户(如电话铃声)的方式。也可以包含“优先级首选”的使用,它可以按请求优先级的同样方式来请求,用于决定高优先级声音挂起时,对低优先级声音如何处理。例如,可以选择终止低优先级的声音,或与高优先级声音进行混音。23底层子系统另外,DevSound包括一种在同时使用使用MMF
观察器模式在开始前,有必要提一下整个MMF的通用设计模式,即所谓的“观察器模式(observerpattern)”。使用观察器类可以为API用户提供反馈。这些类是作为“混合”类而实现,并定义了—组观察器纯虚函数。该模式不局限于MMF,而广泛应用于SymbianOS。在第4章可以看到利用观察器类向控件报告重要事件的例子。为使用MMF客户API,你需要创建一个派生自相关观察器类的对象,提供其虚函数的实现,并将该对象的一个引用传递给MMF客户实用程序。正如下述实例所示,常常从观察器类派生MMF客户本身。24使用MMF观察器模式24使用MMF
在MMF中,观察器函数的通常用途是通知实用程序的创建、或者特定范围内操作的开始或结束,并提供错误码和状态信息。值得注意的是,观察器模式依赖于活动对象的使用,也就是说活动规划器必须一直运行,以便客户API能够正常工作,因为它处于标准GUI应用程序中。25使用MMF在MMF中,观察器函数的通常用途是通使用音频
MMF音频API可用于处理简单的音调产生、音频剪辑处理(播放、录制和转换)以及音频流化(播放和录音)。当初次在软件中使用音频时,可能难以理解实际发生的事情以及声音是如何保存的。图7.3说明了一些基本概念。图7.3模数转换和数模转换26使用音频MMF音频API可用于处理简单的音调产生、使用音频
模数转换将音频转换为数字形式的第一步,通常是模拟到数字的转换。这是通过所谓的“模数转换器”或“ADC”的微型芯片实现的。例如,在用麦克风录音时,麦克风将声音波形转换为电子信号,然后在ADC阶段将电子信号转换为二进制数据?二进制数据就可以被处理,并存入内存或保存到文件中。2.数模转换相反的处理称为数模转换。在这一步,音频数据的二进制序列通过所谓的“数模转换器”或“DAC”的微型芯片进行转换。这些芯片输出模拟电子信号,然后电子信号再通过扬声器等硬件转变为声音波形。27使用音频模数转换27使用音频
3.数字音频格式所有数字音频均以特定格式存储,该格式描述音频的各个细节。此信息包括音频录制的取样速率(ADC的每秒取样个数)、通道个数以及是否将音频编码为要求分离解码阶段的状态。未压缩的音频称为脉冲编码调制(PCM,PulseCodeModulation),在任一给定时刻,将音频取样直接度量为已知音频信号的振幅。例如,一个50Hz正弦波的数字PCM取样看起来如图7.4所示。在此图中,使用了16位PCM编码格式。这也就意味着,数字音频取样将以16位存储,该值的范围为—32768~+32767。这些值对应正弦波的波谷和波峰。图上的每个点对应需要保存的取样。28使用音频3.数字音频格式28使用音频
图7.4正弦波的数字取样以PCM音频格式存储,这些取样不需要再进行任何处理,因此每个取样将占用16位或两个字节。音频数据如何以其他格式存储,依赖于应用到信号上的附加处理。比如,压缩信号的音频数据可能包含所使用的转换系数。29使用音频图7.4正弦波的数字取样以PCM音频格式存使用音频
4.音频剪辑剪辑可以看做是音频或视频的一个“包”。它有明确定义的开始和结束,并且通常将描述其格式的信息一起打包,如取样速率和编码方式。剪辑通常包含在文件或描述符中,尽管某些客户API也允许将URL看做剪辑。5.音频流音频流由音频数据的连续流组成,它没有明确定义的开始和结束。音频数据流可以是大小不等的间断块。例如,从因特网上获取流的RealAudio。30使用音频4.音频剪辑30播放音调音调播放器实用工具类CMdaAudioToneUtility在mdaaudiotoneplayer.h中定义。它提供播放单独的单声道正弦波音调、用户自定义双音(Nokia6600手机不提供)、双音多频(DTMF)或音调序列的能力。在典型应用程序中,DTMF用于在打电话期间模仿电话按键,音调序列文件用于产生铃声。希望使用音调播放器实用工具的客户,必须先从MMdaAudioToneObserver派生一个观察器类,MMdaAudioToneObserver的类定义显示如下:31播放音调音调播放器实用工具类CMdaAudioTon播放音调classMMdaAudioToneObserver{ Public: virtualvoidMatoPrepareComplete(TintaError)=0; virtualvoidMatoPlayComplete(TIntaError)=0;};通过调用NewL()函数创建的音调播放器实用工具,向观察器类提供一个引用作为参数。然后需要以下两个步骤来播放音调。32播放音调classMMdaAudioToneObs播放音调(1)调用适当的“预备”函数预备待播放的音调。客户通过观察器的MatoPrepareComplete()函数获知预备工作已完成。如果预备工作成功完成,则以错误值KEn'None调用该函数,否则将报告相应的错误码,例如,当设备不支持音调回放时,错误码为KErrNotSupported。(2)如果预备成功,客户就可以开始播放音调,或者针对该音调执行其他处理,比如设置音量。如果发生错误,则决不可以调用这些函数,因为底层子系统没有正确初始化。通过调用观察器的MatoPlayComplete()函数,客户可以知道何时音调播放完成。这组事件对于三种音调播放API是通用的。惟一不同的是,作为参数传递给相关“预备”函数的数据。33播放音调(1)调用适当的“预备”函数预备待播放的音调播放音调audi04例程演示了如何使用音调播放实用工具,并播放3秒2600Hz的音调。按照前面所描述的观察器模式,应用程序的引擎类实现了音频音调观察器的函数。如下所示:classCAudio4Engine:publicCBase,publicMMdaAudioToneObserver{… public://从MMdaAudioToneObservervoidMatoPrepareComplete(TIntaError);voidMatoPlayComplete(TIntaError);…};34播放音调audi04例程演示了如何使用音调播放实用工播放音调应用程序引擎的ConstructL()很简单:voidCAudio4Engine::ConstructL(){ iUtility=CMdaAudioToneUtility::NewL (*this); iUtility->PrepareToPlayTone(KFrequency, TTimeIntervalMicroSeconds(KDuration));}35播放音调应用程序引擎的ConstructL()很简单:35播放音调它首先通过调用NewL()函数创建CMdaAudioToneUtility的一个实例,并传递自身的一个引月作为观察器。然后调用实用工具的PrepareToPlayTone()函数预备音调,并传递以赫兹为单位的音调频率和以微秒为单位的音调持续时间。在该例中,KFrequency和KDuration的值为2600和3000000。需要注意的是,在任一给定时刻,只能存在一个预备的音调。如果再次调用PrepareToPlayTone(),则新的音调将取代原先的那个音调。当音调实用工具预备好音调以后,它调用MatoPrepareComplete():36播放音调它首先通过调用NewL()函数创建CMdaA播放音调voidCAudio4Engine::MatoPrepareComplete(TintaError){ if(aError==KErrNone) { iUtility->SetVolume(iUtility->MaxVolume ()); iState=EReady; }}如果预备成功,传递给该函数的错误码为KErrNone。任何其他值都表明—个错误。例如,如果声音设备已经被一个更高优先级的进程使用,则该值为KErrlnUse。37播放音调voidCAudio4Engine::MatoPr播放音调有重要的一点需要注意,音调实用工具预备音调和调用MatoPrepareComplete()的时间没有定义。在有些设备上可能立即发生,而在另外一些设备上则可能存在非常明显的延迟,因此,不要使用单个音调播放API来播放由一系列音调组成的曲子。这种情况下,应该使用音调序列API来代替。现在音调已准备好,客户可以调用Play()来播放它了:voidCAudio4Engine::PlayL(){ iUtility->Play(); iState=EPlaying;}38播放音调有重要的一点需要注意,音调实用工具预备音调和播放音调当音调播放完以后,将调用MatoPlayComplete()函数:voidCAudio4Engine::MatoPlayComplete(TInt/*aError*/){ iState=EReady;}回放成功的话,会传递错误码KErrNone给该函数。在该例中,忽略了错误码,但在真正的应用程序中,却要注意可能由于试图再次开始播放音调所造成的错误。通过多次调用P1ay()函数而不必调用PrepareToPlayTone(),可以重复播放音调。然而,如果想播放不同类型的音调,则必须另外再调用PrepareToPlayTone()。39播放音调当音调播放完以后,将调用MatoPlayCo播放音调注意一个有趣的地方,音频实用工具不像后面将看到的其他许多实用工具,它没有Stop()函数。作为代替,可以使用CancelPlay()函数停止一个已开始播放的音调:voidCAudio4Engine::StopL(){ iUtility->CancelPlay();…}在完成播放以前取消音调,会阻止对观察器的MatoPlayComplete()函数的调用。使用完音调实用工具之后,通过简单地销毁音调发生器对象,就可以完成音频设备的所有必要的清除工作。40播放音调注意一个有趣的地方,音频实用工具不像后面将看播放音调该实例演示了如何播放单个正弦波音调音调,不过播放双音、DTMF音和音调序列的原理相同。对于每种音调,都有一个对应的“预备”函数,一旦预备好,就可以利用Play()和CancelPlay()来播放音调和停止音调。在每一种情形中,都会调用上述的相同的观察器函数。作为导引,下面列出用于其他音调类型的“预备”函数的信息:双音——具有同一持续时间的两个音调频率。DTMF——对包含DTMF音调列表的描述符的引用。来自文件的序列——对音调序列文件的文件名的引用。来自描述符的序列——对包含音调序列的描述符的引用。固定序列——引用通过硬编码预定义音调序列的整数。41播放音调该实例演示了如何播放单个正弦波音调音调,不过播放音调SymbianOS不指定用于定义音调序列的格式,因此,通常手机与手机之间的格式会不同。DTMF音调序列通常由包含0123456789#*字符集中字符的文本串指定,但该格式也可以由设备指定。请参考特定手机的SDK,获得特定手机所使用的准确格式的详细信息。42播放音调SymbianOS不指定用于定义音调序列的播放剪辑
音频播放器实用工具CMdaAudioPlayerUtility定义在mdaaudiosampleplayer.h中,它提供播放取样音频数据的能力。取样音频数据可由文件(如WAV文件)或描述符(TDes8或TDesC8)提供,或者也可以定位于特定的URL地址。音频播放器实用工具提供标准的功能,用于播放和停止播放音频数据,以及设置音量和平衡等等。它还提供许多更高级的操作,如音量倾斜、重复设置、元数据处理和传递定制命令到当前MMF控制器。元数据是包含在音频剪辑中的非音频数据。例如,WAV文件可以包含该文件何时创建、以及由谁创建的信息。43播放剪辑音频播放器实用工具CMdaAudioPla播放剪辑
在决定使用哪个实用工具来播放剪辑时,可以选择音频播放器或音频记录器(描述见下一节),记录器包含播放和录制剪辑两种功能。通常会使用音频记录器实用工具,它提供最为灵活的选择,不过使用音频播放器实用工具是播放剪辑的最简单方法。希望使用音频播放器实用工具的客户,必须首先从MMdaAudioPlayerCa!lback类中派生一个观察器类,MMdaAudioPlayerCallback类的定义如下:
44播放剪辑在决定使用哪个实用工具来播放剪辑时,可以选播放剪辑
classMMdaAudioPlayerCallback{ public: virtualvoidMapcInitComplete(TIntaError, constTTimeIntervalMicroSeconds&aDuration)=0; virtualvoidMapcPlayComplete(TIntaError)=0;};创建音频播放器实用工具的标准方式是使用NewL()函数,该函数同样将一个引用传递给观察器类。与音调播放实用工具一样,播放剪辑也分为两个不连续的阶段。45播放剪辑classMMdaAudioPlayerCall播放剪辑
(1)剪辑必须使用下面介绍的相应“打开”函数来打开。调用观察器的MapclnitComplete()函数表示这一阶段完成。如果剪辑成功打开,则以错误码KErrNone调用该函数,但是如果剪辑格式不可识别,则错误码为KErrNotSupported。(2)如果剪辑成功打开,则客户既可以开始播放剪辑,也可以处理设置,如音量。当剪辑完成播放时,调用观察器的MapcPlayComplete()函数。对于播放三种可能来源中的每一种音频,这组事件都是通用的,惟一的不同是传递给相应“打开”函数的数据——传递给OpenFileL()的是一个文件名,传递给OpenDesL()的是一个描述符,而传递给OpenUrlL()的是一个URL。46播放剪辑(1)剪辑必须使用下面介绍的相应“打开”函播放剪辑
使用OpenUrlL()函数,也可以指定一个因特网接入点ID。这将指示控制器使用特定的接入点代替其默认接入点。有关此参数类型的更多信息,请参考mmfurl.h中的CMMFUrlSink类。如果知道了音频实用工具只是用于处理某一特定类型的剪辑时,就可以通过使用NewDesPlayerL()、NewDesPlayerReadOnlyL()或NewFilePlayerL()函数中的一个来创建一个实例,代替调用NewL()。另外要为这些函数提供包含音频数据本身或者音频剪辑文件名的描述符。这些函数封装了播放器实用工具的实例化和打开阶段,所以在完成时,它们会尝试打开提供的剪辑,并以相应的错误码调用观察器的MapclnitComplete()函数。47播放剪辑使用OpenUrlL()函数,也可以指定一播放剪辑
实例audi01说明了如何打开一个WAV文件并播放它。利用实用工具的NewFilePlayerL()函数,应用程序引擎的SetFileL()函数创建音频播放器实用工具类的一个实例,创建它的明确目的就是播放一个单独的文件:voidCAudiolEngine::SetFileL(constTDesC&aFileName){ if(iUtility) { deleteiUtility; }iUtility=CMdaAudioPlayerUtility::NewFilePlayerL(aFileName,*this);}48播放剪辑实例audi01说明了如何打开一个WAV文播放剪辑
引擎本身派生于观察器类。NewFilePiayerL()不仅创建实用工具,而且尝试打开剪辑、导致调用观察器的MapclnitComplete()函数、传递错误码和音频剪辑的持续时间。voidCAudiolEngine::MapcInitComplete(TIntaError,constTTimeIntervalMicroSeconds&/*aDuration*/){ if(aError==KErrNone) { iState=EReady; iAppUi.UpdateViewL(); }}49播放剪辑引擎本身派生于观察器类。NewFilePi播放剪辑
在该例中,持续时间被忽略,但在真正的应用程序中可能会用到,比如显示剪辑的剩余播放时间。有一点很重要,在没有调用错误码为KErrNone的MapclnitComplete()之前,不要调用Play()、SetVolume()或SetRepeats()之类的函数,否则会发生严重错误。在使用这些函数时,这种错误是最常犯的错误。确保不发生这种错误的一个有效方法,是从MapclnitComplete()函数中直接调用Play()。如果剪辑已经成功打开,就可以使用Play()函数播放它。voidCAudiolEngine::PlayL(){ iUtility->Play(); iState=EPlaying;}50播放剪辑在该例中,持续时间被忽略,但在真正的应用程播放剪辑
当剪辑播放完时,调用MapcPlayComplete()函数。voidCAudiolEngine::MapcPlayComplete(Tint/*aError*/){ iState=EReady;}成功的回放会导致调用错误码为KErrNone的MapcPlayComplete()函数。如同前面的例子,忽略错误码。例如,更实际的应用程序对KErrlnUse错误的响应,可能会是再次尝试播放剪辑。
51播放剪辑当剪辑播放完时,调用MapcPlayCom播放剪辑
播放完成之后,可以通过再次调用Play()重放剪辑,而不必调用“打开”函数。在播放新剪辑之前,必须通过调用实用工具的Close()函数关闭当前剪辑,并利用相应的“打开”函数打开新的剪辑。如果像这个例子中一样,创建了用于播放指定剪辑类型的实用工具,那么为了播放不同类型的数据,必须要创建一个新实例。播放剪辑以前,可以通过SetPlayWindow()函数设置—个“播放窗口”,并以微秒为单位提供窗口的开始和结束时间。完成后,播放就要受限于窗口中的剪辑部分。默认情况下,播放窗口为剪辑的整个长度。在播放剪辑时,可以利用实用工具的Stop()和Pause()函数停止播放。Stop()函数把剪辑的位置复位到当前播放窗口的开始,而不是复位到整个剪辑的开始。52播放剪辑播放完成之后,可以通过再次调用Play()录制剪辑
定义在mdaaudiosampleeditor.h中的音频记录器实用工具CMdaAudioRecorderUtility派生自CMdaAudioClipUtility。它包含CMdaAudioPlayerUtility所提供的功能超集,增加了录制和播放剪辑的功能。与播放器实用工具一样,音频数据可以在文件(如WAV文件)、描述符(TDes8或TDesC8)或远程URL中进行处理。如前所述,当播放或录制剪辑时,通常会选择使用这个类。除了提供播放、录制、停止、设置音量、设置平衡等基本功能外,该类还允许从当前剪辑中裁剪数据,从当前读/写位置到数据的开头或结尾进行处理。与播放器实用工具一样,该类还提供许多更高级的操作,如音量倾斜、重复设置、元数据处理和传递定制命令给当前MMF控制器。53录制剪辑定义在mdaaudiosampleedit录制剪辑
除有一个例外(后面会解释),音频记录器实用工具的播放功能与播放器实用工具的功能—样,所以这里将着重介绍录制功能。与使用其他实用工具一样,希望使用音频记录器实用工具的客户,必须首先从MmdaObjectStateChangeObserver派生一个观察器类,以下是MMdaObjectStateChangeObserver的类定义:classMMdaObjectStateChangeObserver{public: virtualvoidMoscoStateChangeEvent(CBase*aObject, TIntaPreviousState, TIntaCurrentState, TIntaErrorCode)=0;};54录制剪辑除有一个例外(后面会解释),音频记录器实用录制剪辑
使用这个观察器类比使用播放器和音调实用工具的观察器类稍微复杂一点。只要记录器实用工具对象的状态发生变化,就会调用MoscoStateChangeEvent(),并传递一个指向实用工具对象本身的指针、以前状态和当前状态以及一个错误码。因为提供了对象指针,观察器就能够观察不止一个实用工具对象。记录器实用工具可能的操作状态在CMdaAudioClipUtility父类中枚举,如下所示:55录制剪辑使用这个观察器类比使用播放器和音调实用工具录制剪辑
使用这个观察器类比使用播放器和音调实用工具的观察器类稍微复杂一点。只要记录器实用工具对象的状态发生变化,就会调用MoscoStateChangeEvent(),并传递一个指向实用工具对象本身的指针、以前状态和当前状态以及一个错误码。因为提供了对象指针,观察器就能够观察不止一个实用工具对象。记录器实用工具可能的操作状态在CMdaAudioClipUtility父类中枚举,如下所示:enumTState{ ENotReady=0 EOpen, EPlaying, ERecording};56录制剪辑使用这个观察器类比使用播放器和音调实用工具录制剪辑
音频记录器实用工具利用NewL()函数创建,通常,该函数以观察器的引用为参数。然后,音频的录制按照与使用播放器实用工具进行播放的相似步骤进行。(1)必须使用相应的“打开”函数打开剪辑。在完成时,调用观察器的MoscoStateChangeEvent()函数,以EOpen的当前状态和一个错误码为参数,如果剪辑打开成功,该错误码是KErrNone。(2)如果成功打开,则客户既可以开始录制剪辑,也可以处理设置。与播放器实用工具—样,当发生错误的状态时,不能调用这些函数。在开始录制剪辑时,调用观察器的MoscoStateChangeEvent()函数,以当前状态ERecording为参数进行调用。当完成录制剪辑时,再次调用观察器的MoscoStateChangeEvent()函数。如果录制被一个Stop()调用中断,则错误码为KErrNone;如果剪辑录制达到用户定义的限定长度,则错误码为KErrEof;如果发生其他错误情况,则会是其他错误码。57录制剪辑音频记录器实用工具利用NewL()函数创建录制剪辑
与使用播放器实用工具一样,对于三种音频记录剪辑类型,这组事件都是通用的,惟一的不同也是传递给相应“打开”函数的数据。与剪辑播放实用工具相比,这里有更多种类的“打开”函数。其中有些函数采用一个TMdaClipFormat参数(该数据结构将在后面音频转换的章节中作简要介绍),有些允许调用者选择特定的控制器插件,而不是由MMF架构选择它认为适用的插件。最后,记录器实用工具提供了一些接受媒体服务器遗留参数的“打开’’函数。通常,如果作为参数提供给“打开”函数的目标文件并不存在,则创建该文件,并使用特定的文件扩展名作为使用格式的线索。
58录制剪辑与使用播放器实用工具一样,对于三种音频记录录制剪辑
audi02例程演示了记录器和转换实用工具的使用,并且直接使用了7.6节所描述的控制器框架。本节着眼于使用记录器实用工具打开一个WAV文件并进行录制。59录制剪辑audi02例程演示了记录器和转换实用工录制剪辑
在实例audi02的引擎中,ConstructL()函数创建音频记录器实用工具类的—个实例,使用的是现在已经变得熟悉的模式,表明引擎本身就是实用工具的观察器:voidCAudio2Engine::ConstructL(){ deleteiUtility;iUtility=NULL;iUtility= CMdaAudioRecorderUtility::NewL(*this);iUtility->OpenFileL(KFileName);}
60录制剪辑在实例audi02的引擎中,Constru录制剪辑
创建了实用工具之后,ConstructL()打开用于录制的文件,这会引起观察器的MoscoStateChangeEvent()函数被调用,并以EOpen的当前状态及一个错误码KErrNone为参数。voidCAudio2Engine::MoscoStateChangeEvent(CBase*aObject,TIntaPreviousState,TIntaCurrentState,TIntaErrorCode){ if(aErrorCode!=KErrNone) {//消息 iState=ENotReady; return;}61录制剪辑创建了实用工具之后,ConstructL(录制剪辑
if(aObject==iUtility) { switch(aCurrentState) { caseCMdaAudioClipUtility::EOpen: iState=EReady; break; caseCMdaAudioClipUtility::ERecording: iState=ERecording; break; caseCMdaAudioClipUtility::EPlaying: iState=EPlaying; break; default:; } }
62录制剪辑if(aObject==iUtili录制剪辑
else//必须为转换器 { … }}在该例中,MoscoStateChangeEvent()所报告的状态用于维护一个内部状态机。任何错误都会使内部状态置为ENotReady,该状态用于表示实用工具类不可用。63录制剪辑else//必须为转换器63录制剪辑
在开始录制之前,可能会使用如下函数设置一或多个录制参数:SetDestinationDataTypeL0。SetDestinationFormat()。SetDestinationBitRate()。SetDestinationSampleRateL()。SetDestinationNumberOfChannels()。SetMaxWriteLength()。目标数据类型是作为TFourCC提供的,它规定使用MmfFourCC.h中所定义的正确编码方法。
64录制剪辑在开始录
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 六上英语教学计划
- 成都市青羊区2023年八年级下学期《数学》期中试题与参考答案
- 五年级数学(小数乘法)计算题专项练习及答案汇编
- 有关四年级上册数学教学计划模板集合
- 《海淘研究报告》课件
- 《AOPA飞行原理》课件
- 《文化旅游产业趋势》课件
- 《室内设计施工》课件
- 电路连接的基本方式教学设计
- 安全出行诈骗案例分享
- 人防工程给排水管道隐蔽工程检查验收记录表(隐蔽)
- 人教版高一英语必修第一册-Unit-4-Reading-and-thinking(公开课)课件
- 施工过程中淤泥质土层分级放坡开挖土坡稳定性计算书
- 上线切换12系统报告
- (新教材)广东粤教粤科版六年级上册科学 4.21 自然的选择 课时练(同步练习)
- 实验室生物安全讲(一)课件
- 北京广播电视大学组织行为学形考作业二-形考作业二(20分)0答案
- DB37T 3366-2018 山东省涉路工程技术规范
- ICD-10恶性肿瘤编码整理版
- 丙二醇化学品安全技术说明书
- 黑布林名著阅读-Black Beauty 黑骏马 学案及阅读训练(含答案)
评论
0/150
提交评论