本文 主要介绍的是 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 OpenCore Player的实现libopencoreplayer.so
OpenCore Player 在 external/opencore/ 中实现,这个实现是一个基于 OpenCore 的 Player 的实现。具体实现的文件为 playerdriver.cpp 。其中实现了两个类: PlayerDriver 和 PVPlayer 。 PVPlayer 通过调用PlayerDriver 的函数实现具体的功能。
http://www.cublog.cn/u3/112227/showart_2441125.html