【内容导航】
本文主要介绍的是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.java
JAVA本地调用部分(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.h
mediaplayer.h
IMediaPlayer.h
IMediaPlayerService.h
MediaPlayerInterface.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 IInterface
{
public:
DECLARE_META_INTERFACE(MediaPlayerClient);
virtual void notify(int msg, int ext1, int ext2) = 0;
};
class BnMediaPlayerClient: public BnInterface<IMediaPlayerClient>
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
在定义中,IMediaPlayerClient类继承IInterface,并定义了一个MediaPlayer客户端的接口,BnMediaPlayerClient继承了BnInterface<IMediaPlayerClient>,这是为基于Android的基础类Binder机制实现在进程通讯而构建的。事实上,根据BnInterface类模版的定义BnInterface<IMediaPlayerClient>类相当于双继承了BnInterface和ImediaPlayerClient。这是Android一种常用的定义方式。
2.3 头文件mediaplayer.h
mediaplayer.h是对外的接口类,它最主要是定义了一个MediaPlayer类:
class MediaPlayer : public BnMediaPlayerClient
{
public:
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>& surface);
status_t setListener(const sp<MediaPlayerListener>& 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<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels);
static sp<IMemory> 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:: DeathRecipient
{
public:
DeathNotifier() {}
virtual ~DeathNotifier();
virtual void binderDied(const wp<IBinder>& who);
};
事实上,MediaPlayer类正是间接地继承了IBinder,而MediaPlayer::
DeathNotifier类继承了IBinder:: DeathRecipient,这都是为了实现进程间通讯而构建的。
2.4 头文件IMediaPlayer.h
IMediaPlayer.h主要的的内容是一个实现MediaPlayer功能的接口,它的主要定义如下所示:
class IMediaPlayer: public IInterface
{
public:
DECLARE_META_INTERFACE(MediaPlayer);
virtual void disconnect() = 0;
virtual status_t setVideoSurface(const sp<ISurface>& 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 BnInterface<IMediaPlayer>
{
public:
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 IInterface
{
public:
DECLARE_META_INTERFACE(MediaPlayerService);
virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url) = 0;
virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length) = 0;
virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels) = 0;
virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels) = 0;
};
class BnMediaPlayerService: public BnInterface<IMediaPlayerService>
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
由于具有纯虚函数,IMediaPlayerService 以及BnMediaPlayerService必须被继承实现才能够使用,在IMediaPlayerService定义的create和decode等接口,事实上是必须被继承者实现的内容。注意,create的返回值的类型是sp<IMediaPlayer>,这个IMediaPlayer正是提供实现功能的接口。
第三部分 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 void android_media_MediaPlayer_reset(JNIEnv *env, jobject thiz)
{
sp<MediaPlayer> 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<IMediaPlayerService>& MediaPlayer::getMediaPlayerService()
{
Mutex::Autolock _l(mServiceLock);
if (mMediaPlayerService.get() == 0) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> 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<IMediaPlayerService>(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<IMediaPlayerService>& service(getMediaPlayerService());
if (service != 0) {
sp<IMediaPlayer> player(service->create(getpid(), this, url));
err = setDataSource(player);
}
}
return err;
}
在函数setDataSource函数中,调用getMediaPlayerService得到了一个IMediaPlayerService,又从IMediaPlayerService中得到了IMediaPlayer类型的指针,通过这个指针进行着具体的操作。
其他一些函数的实现也与setDataSource类似。
libmedia.so中的其他一些文件与头文件的名称相同,它们是:
libs/media/IMediaPlayerClient.cpp
libs/media/IMediaPlayer.cpp
libs/media/IMediaPlayerService.cpp
为了实现Binder的具体功能,在这些类中还需要实现一个BpXXX的类,例如IMediaPlayerClient.cpp的实现如下所示:l
class BpMediaPlayerClient: public BpInterface<IMediaPlayerClient>
{
public:
BpMediaPlayerClient(const sp<IBinder>& impl)
: BpInterface<IMediaPlayerClient>(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/media\libmediaplayerservice目录中的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<MediaPlayerBase> createPlayer(player_type playerType, void* cookie,
notify_callback_f notifyFunc)
{
sp<MediaPlayerBase> 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.5 OpenCorePlayer的实现libopencoreplayer.so
OpenCore Player在external/opencore/中实现,这个实现是一个基于OpenCore的Player的实现。具体实现的文件为playerdriver.cpp。其中实现了两个类:PlayerDriver和PVPlayer。PVPlayer通过调用PlayerDriver的函数实现具体的功能。
补充:
一 、类图扩充
图1
Binder代理,本地接口
类图中加入了Bp<Interface>的代理类:BpMediaPlayer,BpMediaPlayerService。这些类和相应的Bn<Interface>互相配对,Bp<Interface>客户端代理接口,而Bn<Interface>实现服务端本地接口。
拿BxMediaPlayer来举例:BpMediaPlayer继承BpInterface<IMediaPlayer>,并重载实现IMediaPlayer接口的所有方法,这些方法包括start(), stop(),pause()等等。在这些重载方法中,实现client<->server的服务协议,利用remote()->transact()发送请求ID(enum结构),请求附带数据并获得调用结果(reply)。然后由宏IMPLEMENT_META_INTERFACE(MediaPlayer,"android.hardware.IMediaPlayer”)
将BpMediaPlayer和静态方法IMediaPlayer::asInterface(constsp<IBinder>& obj)相关联。你在代码里看不到有代码去实例化BpMediaPlayer,其实是在asInterface里做的,并且由interface_cast模板函数实现:
该create函数,实际是mediaplayer client实例在请求MediaPlayerService获取一个MediaPlayer服务接口时,BpMediaPlayerService代理返回的interface_cast过后得到的BpMediaPlayer实例。
因此,如果某个service需要走IPC层(也就是服务提供者和请求者不在同一个进程里),那么这个服务需要暴露的IXXXXInterface必然被BpXX/BnXX两个类实现(继承),Bp用在客户端,Bn用在服务端。客户端获取服务接口类的类型虽然是IXXXX,但实际上利用C++的多态机制,得到的是BpXX的类型。
各个类之间关系
MediaPlayer(MediaPlayerClient)对象从MediaPlayerService获取的player,实际上是BpMediaPlayer实例,并保存在它的mPlayer成员变量中。对应的在MediaPlayerService实例中,MediaPlayerClient申请,并创建的player对象会被放入clients成员向量中,该向量实际上是BnMediaPlayer的对象容器。每个client对象都有自己的mPlayer成员变量,保存的则是OpenCORE创建的PVPlayer对象。这里有两个mPlayer成员变量,各自代表不同类型,不要混淆。
二、通过MediaPlayer获得service player时序图
以setDataSource接口为例:
使用MediaPlayer的App,可以通过setDataSource来获得(创建)一个player对象,并保存在mPlayer成员中。大概的时序:MediaPlayer首先创建一个service代理对象BpMediaPlayerService,通过该代理对象的create()方法去call IPC,对MediaPlayerService发出创建player的请求。BnMediaPlayerService则在获取IPC的调用请求后,直接调用MediaPlayerService的create方法,分配一个Client对象,然后通过IPC返回该client对象的asBinder()的返回结果。BpMediaPlayerService在得到BnMediaPlayerService返回的client->asBinder()的reply后,会利用readStrongBinder()去读出IBinder对象,将其作为参数调用interface_cast创建一个BpMediaPlayer。(IBinder对象内部到底记录了些什么,目前还没研究:))得到了BpMediaPlayer的MediaPlayer对象,将其保存入mPlayer,之后App就可以调用其完成一些列操作。
三、使用serviceplayer时序图
以上的时序图,描述的是之前获得的mPlayer的使用时序。这个时序比较简单,这里不做详细说明,需要注意的是图最右侧,Client对象实际要通过创建和调用OpenCORE里的PVPlayer来最终实现多媒体播放功能。
Android
Mediaplayer详细文字解读
1、 Gallery应用端表现
Gallery仅仅提供一个呈现框架,Gallery用来管理所有的视频和图片文件,具有播放、查看、删除等功能。自动搜索本地sdcard存有的picture和video,并分类将同性质文件picture和video集中在一起,播放时呈现。Gallery内部实现的播放主用是同MediaPlayer,主要包含了Audio和video的播放功能。
Gallery中增加从指定目录选择播放文件的功能:
方法:首先遍历sdcard下的目录,然后通过选择某个目录,再遍历文件,点击文件播放。
说明:
定义了两个List表:
videoPathList:遍历/sdcard下目录,并存于此List。
videlFileList:遍历相应目录下的文件,并存于此List。
定义了两个Activity:
VideoList2Play:实现/sdcard下目录和文件遍历。
VideoPlayer:利用VideoView类实现视频播放。
从Gallery的按钮和菜单项着手,将该功能植入Gallery。
Gallery首次加载的图框上“拍照按钮”,能够找到内部功能实现的类和方法,并可以将该功能加入。
利用more菜单,增加“选择其它视频”功能,涉及内部类复杂,需进一步研究,暂没有实现。
利用视频播放是的MovieView中增加“选择其它视频”按钮,当有视频真正在播放时,点击按钮选择其它视频,会有冲突。
需改进:研究通过弹出选择对话框方式,可以自由选择/sdcard下的目录和文件,并实现播放。
Gallery中数据缓存及处理流程
在应用程序中有三个线程存在:主线程(随activity的声明周期启动销毁)、feed初始化线程(进入程序时只运行一次,用于加载相册初始信息)、feed监听线程(监听相册和相片的变更)。主要流程归纳如下:
1、首次进入程序Gallery调用onCreate,此时发送初始化消息进入消息队列;然后Gallery调用onResume,向下进入GridLayer的onResume。MediaFeed对象需要进行初始化,然后才可调用MediaFeed 的onResume;
2、处理消息队列中的HANDLE_INTENT消息,Gallery处理这个消息会初始化数据源,从而调用GridLayer的setDataSource方法,这个方法会触发底层MediaFeed的启动方法start,执行完后启动feed监听线程继续执行MediaFeed的run方法。
start方法会作两件事:调用自己底层的重新开始方法onResume,onResume中会为图像和视频这两个媒体源分别增加“内容变化监听器”,并请求刷新这两个媒体源(加入全局的刷新请求列表);启动feed初始化线程mAlbumSourceThread。
3、其中MediaFeed初始化线程的工作是:调用MediaFeed的loadMediaSets加载相册,它又调用了下层 LocalDataSource中的refresh方法(查询数据库是否有相册变化,新增或修改feed中相应的MediaSet相册的名字)和 loadMediaSets方法(调用下层CacheService.loadMediaSets方法)加载所有相册和相册中的所有相片信息。
4、MediaFeed监听线程MediaFeed.run()的工作是:根据“内容变化监听器”返回的媒体变动消息(增删改),持续不断的更新MediaFeed中的相册和相片变量。具体机制如下:如果全局的刷新请求列表中有内容,则调用LocalDataSource.refresh进行相册信息的更新(其中 LocalDataSource.refresh调用了CacheService的computeDirtySets),然后run遍历每个相册并调用dataSource.loadItemsForSet()方法为相册加载相片记录。
2、 功能模块说明
1》 层次结构
分为三层:上层Java应用程、中层Framework层、下层是底层libaries层。整个MediaPlayer在运行的时候,可以大致上分成Client和Server两个部分,它们分别在两个进程中运行,它们之间使用Binder机制实现IPC通讯。
2》 说明
2.1》Gallery.java中通过Handler实现实现进程间通信,涉及sendInitialMessage()方法;检查存储的媒体文件,涉及checkStorage()方法;并初始化Data Source,涉及initializeDatasource()方法;判定数据类型后,通过onActivityResult()执行相应的活动。涉及到图层,GridLayer主图层,同GridDrawManager管理媒体的呈现。
2.2》MediaPlayer的JAVA本地调用部分
MediaPlayer的JAVA本地调用部分在目录frameworks/base/media/jni/的 android_media_MediaPlayer.cpp中的文件中实现。android.media.MediaPlayer中有2部分,一部分供java上层如VideoView调用,一部分为native方法,调用jni。
通过MediaPlayerService实现client端和MediaPlayer进行交互和数据通信,其中涉及通过MediaProvider(多媒体内容提供者)调用数据源,MediaScannerService(多媒体扫描服务)和MediaScannerReceiver检查数据类型(这两个类之间通过Server和BroadcasterRevceiver,主要的方法scan()、scanFlle()),并将统一类型的文件归类用MediaStore(多媒体存储)进行数据存储;MediaPlayer.java调用jni_android_media_MediaPlayer.jni进行同MediaPalyer.cpp实现通信。
说明:
• MediaStore这个类是android系统提供的一个多媒体数据库,android中多媒体信息都可以从这里提取。这个MediaStore包括了多媒体数据库的所有信息,包括音频、视频和图像。
• MediaScannerReceiver在任何的ACTION_BOOT_COMPLETED, ACTION_MEDIA_MOUNTED或 ACTION_MEDIA_SCANNER_SCAN_FILE 意图(intent)发出的时候启动。因为解析媒体文件的元数据或许会需要很长时间,所以MediaScannerReceiver会启动MediaScannerService。
• MediaScannerService调用一个公用类MediaScanner进行媒体扫描工作。MediaScannerReceiver维持两种扫描目录:一种是内部卷(internal volume)指向$(ANDROID_ROOT)/media. 另一种是外部卷(external volume)指向$(EXTERNAL_STORAGE).
3》MediaPlayer
Android的MediaPlayer包含了Audio和video的播放功能,在Android的界面上,Music和Video两个应用程序都是调用MediaPlayer实现的,上层还包含了进程间通讯等内容,这种进程间通讯的基础是Android基本库中的Binder机制。Android的媒体播放功能分成两部分,一部分是媒体播放应用,一部分是媒体播放服务。这两部分分别跑在不同的进程中。媒体播放应用包括Java程序和部分C++代码,媒体播放服务是C++代码。媒体播放应用和媒体播放服务之间需要通过binder机制来进行相互调用,这些调用包括:
(1)媒体播放应用向媒体播放服务发控制指令;
(2)媒体播放服务向媒体播放应用发事件通知(notify)。
• 媒体播放服务对外提供多个接口,其中有2个重要的接口:IMediaPlayerService和IMediaPlayer;IMediaPlayerServer用于创建和管理播放实例,而IMediaPlayer接口则是播放接口,用于实现指定媒体文件的播放以及播放过程的控制。
• 媒体播放应用向媒体播放服务提供的1个接口:IMediaPlayerClient,用于接收notify()。这些接口需要跨进程调用,涉及到binder机制(就是让这两部分之间建立联系)。每个接口包括两部分实现,一部分是接口功能的真正实现(BnInterface),这部分运行在接口提供进程中;另一部分是接口的proxy(BpInterface),这部分运行在调用接口的进程中。
3、 代码框架
1》JAVA程序的路径:
packages/apps/Camera/
编译后生成Camera.apk,对应于Camera、Gallery、Camcorder三个应用。
packages/apps/Gallery/src/com/android/camera/gallery、
packages/apps/Gallery3D/src/com/cooliris/app
packages/providers/MediaProvider/
含有类MediaProvider.java、MediaScannerService.java、MediaScannerReceiver.java,
编译后生成MediaProvider.apk。会在开机时扫描本机和sdcard上的媒体文件(图片、视频、音频),并在/data/data/com.android.providers.media/databases 目录下生成internal.db(/system/meida)和external-?.db(/sdcard)两个数据库文件。此后所有的多媒体信息都从这两个数据库中获取。
2》JAVA Framework的路径:
frameworks/base/core/java/android/provider/MediaStore.java
提供的多媒体数据库,所有多媒体数据信息都可以从这里提取。数据库的操作通过利用ContentResolver调用相关的接口实现。
frameworks/base/media/java/android/media/
提供了android上 多媒体应用层的操作接口。主要说明:
• MediaPlayer.java:提供了视频、音频、数据流的播放控制等操作的接口。
• MediaScanner*.java:提供了媒体扫描接口的支持,媒体扫描后加入数据库中,涉及MediaScannerConnection.java和MediaScannerConnectionClient.java。
3》JAVA本地调用部分(JNI):
frameworks/base/media/jni
JAVA本地调用部分。编译后生成的目标是libmedia_jni.so。
• android_media_MediaPlayer.cpp:JAVA本地调用部分,它定义了一个JNINativeMethod(JAVA本地调用方法)类型的数据gMethods用来描述接口的关联信息;定义了JNIMediaPlayerListener:MediaPlayerListener的notify()方法(该方法是调用c++层次的mediaplayer中,实现播放管制)。
• android_media_MediaScanner.cpp: 媒体扫描相关的本地调用实现。处理路径、文件和Ablum相册内容释放。
• soundpool/android_media_SoundPool.cpp:定义了音频系统的本地调用实现、MediaPlayer回调方法android_media_callback()。
4》多媒体底层库:
frameworks/base/include/media/、frameworks/base/media/libmedia/
这里为多媒体的的底层库,编译生成libmedia.so。这个库处于android多媒体架构的核心位置,它对上层提供的接口主要有MediaPlayer、MediaScanner等类。
android.meida.* 就是通过libmedia_jni.so调用libmedia.so实现的接口实现的。 A》MediaPlayerInterface.h头文件定义了MediaPlayer的底层接口,定义了以下类:
• MediaPlayerBase:MediaPlayerInterface的抽象基础类,里面包含了音频输出、视频输出、播放控制等的基本接口。
• MediaPlayerInterface、MediaPlayerHWInterface 继承自MediaPlayerBase针对不同输出作出的扩展。
• MediaPlayerInterface得到具有相同的播放接口,可以通过继承MediaPlayerInterface的方法,实现增加新的播放器实现。
B》IMediaPlayer.h定义了BnMediaPlayer本地播放类;IMediaPlayer.cpp定义了BpMediaPlayer代理类(其中通过remote()->transact()方法发送消息)和实现了BnMediaPlayer:onTransact()的具体方法。
C》IMediaPlayerClient.h定义了BnMediaPlayerClient本地客户端类;IMediaPlayerClient.cpp定义了BpMediaPlayerClient代理类(其中通过notify()中的remote()->transact()方法发送消息)和实现了BnMediaPlayerClient:onTransact()方法。
D》IMediaPlayerService.h定义了BnMediaPlayerService本地服务端类;IMediaPlayerService.cpp定义了BpMediaPlayerService代理类(其中通过remote()->transact()方法发送消息)和实现了BnMediaPlayerService:onTransact()方法。
E》mediaplayer.h定义了MediaPlayerListener类的notify()方法和类MediaPlayer:BnMediaPlayerClient;mediaplayer.cpp主要实现了MediaPlayer的数据设置播放和实现了MediaPlayerListener类的notify()具体方法。
5》多媒体服务部分:
frameworks/base/media/libmediaplayerservice/
文件为mediaplayerservice.h和mediaplayerservice.cpp
这是多媒体的服务部分(提供Media Player执行的Proxy,同Client端建立连接、设置数据源、根据不同类型创建播放),编译生成libmediaplayerservice.so。
• MediaPlayerService.cpp 通过instantiate()方法实现了一个名字为media.player的服务,MediaPlayer通过IPC同其实现通讯;
• 根据playerType的类型来决定创建不同的播放器;
• 实现了notify()通知Client端、callbackThread()回调机制、decode解码。
frameworks/base/media/mediaserver/
文件为main_mediaserver.cpp是Mediaplayer Server启动的主程序,涉及AudioFlinger()、AudioPolicyService()、MediaPlayerService()的加载。
6》MediaPlayer生命周期:
4、 Audio概念
Audio系统在Android中负责音频方面输入/输出和管理层次,一般负责播放PCM声音输出和从外部获取PCM声音,以及管理声音设备和设置。主要涉及到AudioManager、AudioTrack、AudioServiece、AudioRecord。主要分成如下几个层次:
(1)media库提供的Audio系统本地部分接口;
(2)AudioFlinger作为Audio系统的中间层;
(3)Audio的硬件抽象层提供底层支持;
(4)Audio接口通过JNI和Java框架提供给上层。
Audio管理环节 Audio输出 Audio输入
Java层 android.media.
AudioSystem android.media
AudioTrack android.media.
AudioRecorder
本地框架层 AudioSystem AudioTrack AudioRecorder
AudioFlinger IAudioFlinger IAudioTrack IAudioRecorder
硬件抽象层 AudioHardwareInterface AudioStreamOut AudioStreamIn
AudioTrack.java:SoundPool.java 播放android application的生音资源。
AudioRecord.java: 为android applicatio 提供录音设置(sample、chanel等)的接口;
AudioManager.java: 提供了音频音量,以及播放模式(静音、震动等)的控制。
说明:
1》Audio驱动程序(Linux系统,因不同平台而已)
2》Audio硬件抽象层:hardware/libhardware_legacy/include/hardware/
AudioHardwareInterface.h(定义Audio硬件抽象层的接口),其中三个主要类AuidoStreamOut/AudioStreamIn/AuidoHardwareInterface实现Audio的输出/输入/管理。
2.1》AudioStreamOut关键接口write(const void* buffer, size_t bytes)/AudioStreamIn关键接口read(void* buffer, size_t bytes),通过定义内存的指针和长度音频数据的输出和输入。
2.2》AudioHardwareInterface使用openOutputStream()和openInputStream()函数来获取AudioStreamOut和AudioStreamIn。
2.3》AudioHardwareInterface中所涉及的参数是在AudioSystem.h中定义,通过setParameters和getParameters接口设置和获取参数,通过setMode()设置系统模式。
2.4》Audio中引进了策略管理AudioPolicyInterface,目的是将Audio核心部分和辅助性功能分离。
3》AudioFlinger的实现方式
3.1》通用方式AndroidHardwareGeneric实现基于特定驱动的通用Audio硬件抽象层。
3.2》桩实现方式AndroidHardwareStub,实现Audio硬件抽象层的一个桩,是个空操作,保证没有Audio设备时系统正常工作。
3.3》AudioDumpInterface实现以文件为输入输出的Audio硬件抽象层,以文件模拟Audio硬件流的输入输出环节。
Audio代码分布:
(1) Java部分:frameworks/base/media/java/android/media
与audio相关的java package是android.media,主要包含audio manager和audio系统的几个类,这部分主要给上层的AP部分提供audio相关的接口。
(2) JNI部分: frameworks/base/core/jni
Android系统会生成一个libandroid_runtime.so,audio的JNI是其中的一个部分。
(3) audio frameworks
头文件路径:frameworks/base/include/media/
代码路径:frameworks/base/media/libmedia/
Audio本地框架是media库的一部分,本部分的内容被编译成库libmedia.so,提供audio部分的接口(其中包括基于binder的IPC机制)。
(4) Audio Flinger:frameworks/base/libs/audioflinger
这部分内容被编译成库libaudioflinger.so,它是audio系统的本地服务部分。
http://www.xuebuyuan.com/2078166.html