Android的MediaPlayer架构介绍(自己总计的)_第1页
Android的MediaPlayer架构介绍(自己总计的)_第2页
Android的MediaPlayer架构介绍(自己总计的)_第3页
Android的MediaPlayer架构介绍(自己总计的)_第4页
Android的MediaPlayer架构介绍(自己总计的)_第5页
免费预览已结束,剩余12页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Android的MediaPlayer架构介绍【IT168 技术文档】本文主要介绍的是Android中很重要也最为复杂的媒体播放器(MediaPlayer)部分的架构。对于Android这样一个完整又相对复杂的系统,一个MediaPlayer功能的实现不在其具体的功能,而是具体功能如何适应Android系统Android MediaPlayer的主要具体实现在OpenCore的Player中,这部分不是本文的关注点。本文关注的是MediaPlayer系统的架构,其他的一些Android的应用程序也使用类似的架构。 对于开源事业在中国的发展,hanchao3c认为应该共享的不仅仅是代码,文档、设计思想、理念甚至对于技术的理解都应该得到充分的共享。Android为中国人进入大规模的开源项目提供了很好的机遇,对于走在技术前沿的人们,不应将技术视为私有财产,而应该将自己理解更好地奉献给大众,提高大众的学习速度,从中也可以得到反馈,从而促进自己的进步。仅以此文奉献给所有关系技术的朋友,希望可以抛砖引玉,促进我们共同的技术进步!第一部分 MediaPlayer概述 Android的MediaPlayer包含了Audio和video的播放功能,在Android的界面上,Music和Video两个应用程序都是调用MediaPlayer实现的。 MediaPlayer在底层是基于OpenCore(PacketVideo)的库实现的,为了构建一个MediaPlayer程序,上层还包含了进程间通讯等内容,这种进程间通讯的基础是Android基本库中的Binder机制。以开源的Android为例MediaPlayer的代码主要在以下的目录中:JAVA程序的路径:packages/apps/Music/src/com/android/music/JAVA类的路径:frameworks/base/media/java/android/media/MediaPlayer.javaJAVA本地调用部分(JNI):frameworks/base/media/jni/android_media_MediaPlayer.cpp这部分内容编译成为目标是libmedia_jni.so。主要的头文件在以下的目录中:frameworks/base/include/media/多媒体底层库在以下的目录中:frameworks/base/media/libmedia/这部分的内容被编译成库libmedia.so。多媒体服务部分:frameworks/base/media/libmediaplayerservice/文件为mediaplayerservice.h和mediaplayerservice.cpp这部分内容被编译成库libmediaplayerservice.so。基于OpenCore的多媒体播放器部分external/opencore/这部分内容被编译成库libopencoreplayer.so。 从程序规模上来看,libopencoreplayer.so是主要的实现部分,而其他的库基本上都是在其上建立的封装和为建立进程间通讯的机制。第二部分 MediaPlayer的接口与架构 2.1 整体框架图 MediaPlayer的各个库之间的结构比较复杂,可以用下图的表示 在各个库中,libmedia.so位于核心的位置,它对上层的提供的接口主要是MediaPlayer类,类libmedia_jni.so通过调用MediaPlayer类提供对JAVA的接口,并且实现了android.media.MediaPlayer类。libmediaplayerservice.so是Media的服务器,它通过继承libmedia.so的类实现服务器的功能,而libmedia.so中的另外一部分内容则通过进程间通讯和libmediaplayerservice.so进行通讯。libmediaplayerservice.so的真正功能通过调用OpenCore Player来完成。 MediaPlayer部分的头文件在frameworks/base/include/media/目录中,这个目录是和libmedia.so库源文件的目录frameworks/base/media/libmedia/相对应的。主要的头文件有以下几个:IMediaPlayerClient.hmediaplayer.h IMediaPlayer.hIMediaPlayerService.hMediaPlayerInterface.h 在这些头文件mediaplayer.h提供了对上层的接口,而其他的几个头文件都是提供一些接口类(即包含了纯虚函数的类),这些接口类必须被实现类继承才能够使用。 整个MediaPlayer库和调用的关系如下图所示: 整个MediaPlayer在运行的时候,可以大致上分成Client和Server两个部分,它们分别在两个进程中运行,它们之间使用Binder机制实现IPC通讯。从框架结构上来看,IMediaPlayerService.h、IMediaPlayerClient.h和MediaPlayer.h三个类定义了MeidaPlayer的接口和架构,MediaPlayerService.cpp和mediaplayer.coo两个文件用于MeidaPlayer架构的实现,MeidaPlayer的具体功能在PVPlayer(库libopencoreplayer.so)中的实现。2.2 头文件IMediaPlayerClient.h IMediaPlayerClient.h用于描述一个MediaPlayer客户端的接口,描述如下所示:class IMediaPlayerClient: public IInterfacepublic: DECLARE_META_INTERFACE(MediaPlayerClient); virtual void notify(int msg, int ext1, int ext2) = 0;class BnMediaPlayerClient: public BnInterfacepublic: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); 在定义中,IMediaPlayerClient类继承IInterface,并定义了一个MediaPlayer客户端的接口,BnMediaPlayerClient继承了BnInterface,这是为基于Android的基础类Binder机制实现在进程通讯而构建的。事实上,根据BnInterface类模版的定义BnInterface类相当于双继承了BnInterface和ImediaPlayerClient。这是Android一种常用的定义方式。2.3 头文件mediaplayer.h mediaplayer.h是对外的接口类,它最主要是定义了一个MediaPlayer类:class MediaPlayer : public BnMediaPlayerClientpublic: MediaPlayer(); MediaPlayer(); void onFirstRef(); void disconnect(); status_t setDataSource(const char *url); status_t setDataSource(int fd, int64_t offset, int64_t length); status_t setVideoSurface(const sp& surface); status_t setListener(const sp& listener); status_t prepare(); status_t prepareAsync(); status_t start(); status_t stop(); status_t pause(); bool isPlaying(); status_t getVideoWidth(int *w); status_t getVideoHeight(int *h); status_t seekTo(int msec); status_t getCurrentPosition(int *msec); status_t getDuration(int *msec); status_t reset(); status_t setAudioStreamType(int type); status_t setLooping(int loop); status_t setVolume(float leftVolume, float rightVolume); void notify(int msg, int ext1, int ext2); static sp decode(const char* url, uint32_t *pSampleRate, int* pNumChannels); static sp decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels);/ 从接口中可以看出MediaPlayer类刚好实现了一个MediaPlayer的基本操作,例如播放(start)、停止(stop)、暂停(pause)等。 另外的一个类DeathNotifier在MediaPlayer类中定义,它继承了IBinder类中的DeathRecipient类:class DeathNotifier: public IBinder: DeathRecipientpublic: DeathNotifier() virtual DeathNotifier(); virtual void binderDied(const wp& who); 事实上,MediaPlayer类正是间接地继承了IBinder,而MediaPlayer: DeathNotifier类继承了IBinder: DeathRecipient,这都是为了实现进程间通讯而构建的。2.4 头文件IMediaPlayer.h IMediaPlayer.h主要的的内容是一个实现MediaPlayer功能的接口,它的主要定义如下所示:class IMediaPlayer: public IInterfacepublic: DECLARE_META_INTERFACE(MediaPlayer); virtual void disconnect() = 0; virtual status_t setVideoSurface(const sp& surface) = 0; virtual status_t prepareAsync() = 0; virtual status_t start() = 0; virtual status_t stop() = 0; virtual status_t pause() = 0; virtual status_t isPlaying(bool* state) = 0; virtual status_t getVideoSize(int* w, int* h) = 0; virtual status_t seekTo(int msec) = 0; virtual status_t getCurrentPosition(int* msec) = 0; virtual status_t getDuration(int* msec) = 0; virtual status_t reset() = 0; virtual status_t setAudioStreamType(int type) = 0; virtual status_t setLooping(int loop) = 0; virtual status_t setVolume(float leftVolume, float rightVolume) = 0;class BnMediaPlayer: public BnInterfacepublic: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); 在IMediaPlayer类中,主要定义MediaPlayer的功能接口,这个类必须被继承才能够使用。值得注意的是,这些接口和MediaPlayer类的接口有些类似,但是它们并没有直接的关系。事实上,在MediaPlayer类的各种实现中,一般都会通过调用IMediaPlayer类的实现类来完成。2.5 头文件IMediaPlayerService.h IMediaPlayerService.h用于描述一个MediaPlayer的服务,定义方式如下所示:class IMediaPlayerService: public IInterfacepublic: DECLARE_META_INTERFACE(MediaPlayerService); virtual sp create(pid_t pid, const sp& client, const char* url) = 0; virtual sp create(pid_t pid, const sp& client, int fd, int64_t offset, int64_t length) = 0; virtual sp decode(const char* url, uint32_t *pSampleRate, int* pNumChannels) = 0; virtual sp decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels) = 0;class BnMediaPlayerService: public BnInterfacepublic: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); 由于具有纯虚函数,IMediaPlayerService以及BnMediaPlayerService必须被继承实现才能够使用,在IMediaPlayerService定义的create和decode等接口,事实上是必须被继承者实现的第三部分 MediaPlayer的主要实现分析3.1 JAVA程序部分 在packages/apps/Music/src/com/android/music/目录的MediaPlaybackService.java文件中,包含了对MediaPlayer的调用。 在MediaPlaybackService.java中包含对包的引用:import android.media.MediaPlayer; 在MediaPlaybackService类的内部,定义了MultiPlayer类:private class MultiPlayer private MediaPlayer mMediaPlayer = new MediaPlayer(); MultiPlayer类中使用了MediaPlayer类,其中有一些对这个MediaPlayer的调用,调用的过程如下所示: mMediaPlayer.reset(); mMediaPlayer.setDataSource(path); mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); reset、setDataSource和setAudioStreamType等接口就是通过JAVA本地调用(JNI)来实现的。3.2 MediaPlayer的JAVA本地调用部分 MediaPlayer的JAVA本地调用部分在目录frameworks/base/media/jni/的android_media_MediaPlayer.cpp中的文件中实现。 android_media_MediaPlayer.cpp之中定义了一个JNINativeMethod(JAVA本地调用方法)类型的数组gMethods,如下所示:static JNINativeMethod gMethods = setDataSource, (Ljava/lang/String;)V, (void *)android_media_MediaPlayer_setDataSource,setDataSource, (Ljava/io/FileDescriptor;JJ)V, (void *)android_media_MediaPlayer_setDataSourceFD, prepare, ()V, (void *)android_media_MediaPlayer_prepare, prepareAsync, ()V, (void *)android_media_MediaPlayer_prepareAsync, _start, ()V, (void *)android_media_MediaPlayer_start, _stop, ()V, (void *)android_media_MediaPlayer_stop, getVideoWidth, ()I, (void *)android_media_MediaPlayer_getVideoWidth, getVideoHeight, ()I, (void *)android_media_MediaPlayer_getVideoHeight, seekTo, (I)V, (void *)android_media_MediaPlayer_seekTo, _pause, ()V, (void *)android_media_MediaPlayer_pause, isPlaying, ()Z, (void *)android_media_MediaPlayer_isPlaying, getCurrentPosition, ()I, (void *)android_media_MediaPlayer_getCurrentPosition, getDuration, ()I, (void *)android_media_MediaPlayer_getDuration, _release, ()V, (void *)android_media_MediaPlayer_release, _reset, ()V, (void *)android_media_MediaPlayer_reset, setAudioStreamType, (I)V, (void *)android_media_MediaPlayer_setAudioStreamType, setLooping, (Z)V, (void *)android_media_MediaPlayer_setLooping, setVolume, (FF)V, (void *)android_media_MediaPlayer_setVolume, getFrameAt, (I)Landroid/graphics/Bitmap;, (void *)android_media_MediaPlayer_getFrameAt, native_setup, (Ljava/lang/Object;)V, (void *)android_media_MediaPlayer_native_setup, native_finalize, ()V, (void *)android_media_MediaPlayer_native_finalize, JNINativeMethod的第一个成员是一个字符串,表示了JAVA本地调用方法的名称,这个名称是在JAVA程序中调用的名称;第二个成员也是一个字符串,表示JAVA本地调用方法的参数和返回值;第三个成员是JAVA本地调用方法对应的C语言函数。 其中android_media_MediaPlayer_reset函数的实现如下所示:static voidandroid_media_MediaPlayer_reset(JNIEnv *env, jobject thiz) sp mp = getMediaPlayer(env, thiz); if (mp = NULL ) jniThrowException(env, java/lang/IllegalStateException, NULL); return; process_media_player_call( env, thiz, mp-reset(), NULL, NULL ); 在android_media_MediaPlayer_reset的调用中,得到一个MediaPlayer指针,通过对它的调用实现实际的功能。 register_android_media_MediaPlayer用于将gMethods注册为的类android/media/MediaPlayer,其实现如下所示。static int register_android_media_MediaPlayer(JNIEnv *env) jclass clazz; clazz = env-FindClass(android/media/MediaPlayer);/ . return AndroidRuntime:registerNativeMethods(env, android/media/MediaPlayer, gMethods, NELEM(gMethods); android/media/MediaPlayer对应JAVA的类android.media.MediaPlayer。 3.3 mediaplayer的核心库libmedia.so libs/media/mediaplayer.cpp文件用于实现mediaplayer.h提供的接口,其中一个重要的片段如下所示:const sp& MediaPlayer:getMediaPlayerService() Mutex:Autolock _l(mServiceLock); if (mMediaPlayerService.get() = 0) sp sm = defaultServiceManager(); sp binder; do binder = sm-getService(String16(media.player); if (binder != 0) break; LOGW(MediaPlayerService not published, waiting.); usleep(500000); / 0.5 s while(true); if (mDeathNotifier = NULL) mDeathNotifier = new DeathNotifier(); binder-linkToDeath(mDeathNotifier); mMediaPlayerService = interface_cast(binder); LOGE_IF(mMediaPlayerService=0, no MediaPlayerService!?); return mMediaPlayerService; 其中最重要的一点是binder = sm-getService(String16(media.player);这个调用用来得到一个名称为media.player的服务,这个调用返回值的类型为IBinder,根据实现将其转换成类型IMediaPlayerService使用。 一个具体的函数setDataSource如下所示:status_t MediaPlayer:setDataSource(const char *url) LOGV(setDataSource(%s), url); status_t err = UNKNOWN_ERROR; if (url != NULL) const sp& service(getMediaPlayerService(); if (service != 0) sp player(service-create(getpid(), this, url); err = setDataSource(player); return err; 在函数setDataSource函数中,调用getMediaPlayerService得到了一个IMediaPlayerService,又从IMediaPlayerService中得到了IMediaPlayer类型的指针,通过这个指针进行着具体的操作。 其他一些函数的实现也与setDataSource类似。 libmedia.so中的其他一些文件与头文件的名称相同,它们是:libs/media/IMediaPlayerClient.cpplibs/media/IMediaPlayer.cpplibs/media/IMediaPlayerService.cpp 为了实现Binder的具体功能,在这些类中还需要实现一个BpXXX的类,例如IMediaPlayerClient.cpp的实现如下所示:lclass BpMediaPlayerClient: public BpInterfacepublic: BpMediaPlayerClient(const sp& impl) : BpInterface(impl) virtual void notify(int msg, int ext1, int ext2) Parcel data, reply; data.writeInterfaceToken(IMediaPlayerClient:getInterfaceDescriptor(); data.writeInt32(msg); data.writeInt32(ext1); data.writeInt32(ext2); remote()-transact(NOTIFY, data, &reply, IBinder:FLAG_ONEWAY); ; 还需要实现定义宏IMPLEMENT_META_INTERFACE,这个宏将被展开,生成几个函数:IMPLEMENT_META_INTERFACE(MediaPlayerClient, android.hardware.IMediaPlayerClient); 以上的实现都是基于Binder框架的实现方式,只需要按照模版实现即可。其中BpXXX的类为代理类(proxy),BnXXX的类为本地类(native)。代理类的transact函数和本地类的onTransact函数实现对应的通讯。3.4 media服务libmediaservice.so frameworks/base/medialibmediaplayerservice目录中的MediaPlayerService.h和MediaPlayerService.cpp用于实现一个servers/media/的服务,MediaPlayerService是继承BnMediaPlayerService的实现,在这个类的内部又定义了类Client,MediaPlayerService:Client继承了BnMediaPlayer。class MediaPlayerService : public BnMediaPlayerService class Client : public BnMediaPlayer 在MediaPlayerService中具有如下一个静态函数instantiate:void MediaPlayerService:instantiate() defaultServiceManager()-addService( String16(media.player), new MediaPlayerService(); 在instantiate函数中,调用IServiceManager的一个函数addService,向其中增加了一个名为media.player的服务。 这个名为media.player的服务和mediaplayer.cpp中调用getService中得到的使用一样名称。因此,在这里调用addService增加服务在mediaplayer.cpp中可以按照名称media.player来使用。这就是使用Binder实现进程间通讯的(IPC)的作用,事实上这个MediaPlayerService类是在服务中运行的,而mediaplayer.cpp调用的功能在应用中运行,二者并不是一个进程。但是在mediaplayer.cpp却像一个进程的调用一样调用MediaPlayerService的功能。 在MediaPlayerService.cpp中的createPlayer函数如下所示:static sp createPlayer(player_type playerType, void* cookie, notify_callback_f notifyFunc) sp p; switch (playerType) case PV_PLAYER: LOGV( create PVPlayer); p = new PVPlayer(); break; case SONIVOX_PLAYER: LOGV( create MidiFile); p = new MidiFile(); break; case VORBIS_PLAYER: LOGV( create VorbisPlayer); p = new VorbisPlayer(); break; / return p; 在这里根据playerType的类型建立不同的播放器:对于大多数情况,类型将是PV_PLAYER,这时会调用了new PVPlayer()建立一个PVPlayer,然后将其指针转换成MediaPlayerBase来使用;对于Mini文件的情况,类型为SONIVOX_PLAYER,将会建立一个MidiFile;对于Ogg Vorbis格式的情况,将会建立一个VorbisPlayer。 (OGG Vobis是一种音频压缩格式,与MP3等的音乐格式类似,它具有完全免费、开放和没有专利限制的特点。) 值得注意的是PVPlayer、MidiFile和VorbisPlayer三个类都是继承MediaPlayerInterface得到的,而MediaPlayerInterface又是继承MediaPlayerBase得到的,因此三者具有相同接口类型。只有建立的时候会调用各自的构造函数,在建立之后,将只通过MediaPlayerBase接口来MediaPlayerBase控制它们。 在frameworks/base/media/libmediaplayerservice目录中,MidiFile.h和MidiFile.cpp的实现MidiFile,VorbisPlayer.h和VorbisPlayer.cpp实现一个VorbisPlayer。3.5OpenCorePlayer的实现libopencoreplayer.so OpenCorePlayer在external/opencore/中实现,这个实现是一个基于OpenCore的Player的实现。具体实现的文件为playerdriver.cpp。其中实现了两个类:PlayerDriver和PVPlayer。PVPlayer通过调用PlayerDriver的函数实现具体的功能。 Dalvik 虚拟机简介Java 语言的一个非常重要的特点就是与平台的无关性。而使用 Java 虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行, 至少需要编译成不同的目标代码。而引入 Java 语言虚拟机后,Java 语言在不同平台上运行时不需要重新编译。Java 虚拟机屏蔽了与具体平台相关的信息,使得 Java 语言编译程序只需生成在 Java 虚拟机上运行的目标代码(字节码) ,就可以在多种平台上不加修改地运行。Java 虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。Dalvik 的出现是为了躲避 Sun 公司 Java ME 的版权以及授权问题, Google 公司自己设计用于 Android 平台的 Java 虚拟机. Dalvik 虚拟机的主要特征 专有的 DEX 文件格式一个应用中会定义很多类,编译完成后即会有很多相应的 CLASS 文件,CLASS 文件间会有不少冗余的信息;而 DEX 文件格式会把所有的 CLASS 文件内容整合到一个文件中。这样,除了减少整体的文件尺寸,I/O 操作,也提高了类的查找速度。 原来每个类文件中的常量池,在 DEX 文件中由一个常量池来管理。 一个应用,一个虚拟机实例,一个进程每一个 Android 应用都运行在一个 Dalvik 虚拟机实例里,而每一个虚拟机实例都是一个独立的进程空间。虚拟机的线程机制,内存分配和管理,Mutex 等等都是依赖底层操作系统实现的。所有 Android 应用的线程都对应一个 Linux 线程,虚拟机因而可以更多的依赖操作系统的线程调度和管理机制。不同的应用在不同的进程空间里运行,加之对不同来源的应用都使用不同的 Linux 用户来运行,可以最大程度的保护应用的安全和独立运行。Zygote 是一个虚拟机进程,同时也是一个虚拟机实例的孵化器,每当系统要求执行一个Android 应用程序,Zygote 就会 FORK 出一个子进程来执行该应用程序。这样做的好处显而易见:Zygote 进程是在系统启动时产生的,它会完成虚拟

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论