前言
上一篇我们分析了MediaPlayer的创建过程和Native层的消息回调流程,本文将分析MediaPlayer的设置数据源的流程,并跟随源码逐步分析到MediaPlayer与MediaPlayerService建立关联的过程。
Android MediaPlayer支持网络和本地两种不同的数据源,本文挑选相对简单的本地数据源来做分析。在MediaPlayer的Client端,网络数据源的设置流程其实与本地数据源大同小异,这里不再另行分析。
setDataSource设置数据源
当APP中创建好MediaPlayer实例后,需要将视频数据源设置到MediaPlayer中,如下方式调用:
mMediaPlayer.setDataSource(mPath);
MediaPlayer中有多个setDataSource的重载方法,经过多重调用后,最终将本地数据源路径Path,转换成FileDescriptor文件描述符类型,并传入到native层。
// frameworks/base/media/java/android/media/MediaPlayer.java
//将path路径转换成FileDescriptor类型
public void setDataSource(...){
final File file = new File(path);
try (FileInputStream is = new FileInputStream(file)) {
setDataSource(is.getFD());
}
}
public void setDataSource(FileDescriptor fd, long offset, long length)
throws IOException, IllegalArgumentException, IllegalStateException{
_setDataSource(fd, offset, length);
}
//最终调用到native方法
private native void _setDataSource(FileDescriptor fd, long offset, long length)
throws IOException, IllegalArgumentException, IllegalStateException;
经过JNI动态注册方法,_setDataSource调用到android_media_MediaPlayer_setDataSourceFD方法中。
// frameworks/base/media/jni/android_media_MediaPlayer.cpp
static void android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
//获取之前创建好的C++层的MediaPlayer实例
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
...
//从Java层FileDescriptor对象中取出对应的文件描述符。在Native层,文件描述符是一个int整型
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
ALOGV("setDataSourceFD: fd %d", fd);
//mp->setDataSource 调用C++层的setDataSource方法
//process_media_player_call方法用于抛出异常给Java层
process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
}
将Java层FileDescriptor对象转换为Native层的文件描述符,并调用C++层的MediaPlayer的setDataSource方法
// frameworks/av/media/libmedia/mediaplayer.cpp
status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
{
ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);
status_t err = UNKNOWN_ERROR;
//通过Binder机制,获取MediaPlayerService的接口代理对象
const sp<IMediaPlayerService> service(getMediaPlayerService());
if (service != 0) {
//创建MediaPlayerService中的播放器对象,并返回播放器接口代理对象
sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
//设置重传端点,这里不做分析
if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
//将fd传递给MediaPlayerService中的播放器对象
(NO_ERROR != player->setDataSource(fd, offset, length))) {
player.clear();
}
//关联MediaPlayerService的播放器对象
err = attachNewPlayer(player);
}
return err;
}
MediaPlayer::setDataSource中的逻辑分为几个步骤:
- 获取IMediaPlayerService类型的服务端接口代理
- 获取IMediaPlayer类型的服务端播放器的接口代理
- 将fd文件描述符传递给服务端的播放器
- 关联服务端播放器代理对象
以上步骤可以看出,C++层的MediaPlayer对象最终通过binder通信机制,创建并关联到MediaPlayerService的服务端播放器,最终的播放器逻辑也是通过binder机制将数据传递到MediaPlayerService服务端完成。 本篇不分析MediaPlayerService的服务端逻辑,会另外再开一个系列分析MediaPlayerService和播放器的源码。 下面我们再来分析,MediaPlayer是如何与MediaPlayerService建立关联的。
与MediaPlayerService建立关联
首先来看如何获取服务端接口代理
// frameworks/av/media/libmedia/IMediaDeathNotifier.cpp
// establish binder interface to MediaPlayerService
/*static*/const sp<IMediaPlayerService>
IMediaDeathNotifier::getMediaPlayerService()
{
ALOGV("getMediaPlayerService");
Mutex::Autolock _l(sServiceLock);
if (sMediaPlayerService == 0) {
//获取ServiceManager的接口代理对象
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
//通过ServiceManager获取服务注册名称为"media.player"的Service,返回IBinder对象
binder = sm->getService(String16("media.player"));
if (binder != 0) {
break;
}
ALOGW("Media player service not published, waiting...");
usleep(500000); // 0.5 s
} while (true);
if (sDeathNotifier == NULL) {
sDeathNotifier = new DeathNotifier();
}
//设置IBinder的死亡监听
binder->linkToDeath(sDeathNotifier);
//将IBinder对象转换为IMediaPlayerService对象
sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
}
ALOGE_IF(sMediaPlayerService == 0, "no media player service!?");
return sMediaPlayerService;
}
通过ServiceManager获取服务注册名称为"media.player"的Service,再转换为IMediaPlayerService类型。
下面是通过MediaPlayerService服务端接口代理,创建IMediaPlayer播放器接口代理
// frameworks/av/media/libmedia/IMediaPlayerService.cpp
class BpMediaPlayerService: public BpInterface<IMediaPlayerService>
{
...
virtual sp<IMediaPlayer> create(
const sp<IMediaPlayerClient>& client, audio_session_t audioSessionId) {
//将远程调用参数封装到Parcel中
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
data.writeStrongBinder(IInterface::asBinder(client));
data.writeInt32(audioSessionId);
//调用远程transact方法
remote()->transact(CREATE, data, &reply);
return interface_cast<IMediaPlayer>(reply.readStrongBinder());
}
...
}
status_t BnMediaPlayerService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch (code) {
case CREATE: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
sp<IMediaPlayerClient> client =
interface_cast<IMediaPlayerClient>(data.readStrongBinder());
audio_session_t audioSessionId = (audio_session_t) data.readInt32();
//这里会调用到MediaPlayerService中的create方法,返回IMediaPlayer对象
sp<IMediaPlayer> player = create(client, audioSessionId);
reply->writeStrongBinder(IInterface::asBinder(player));
return NO_ERROR;
} break;
...
}
}
这里看起来比较复杂,其实是一段经典的binder机制的通信流程,简单来说就是,MediaPlayer Client端,通过IMediaPlayerService接口代理,调用MediaPlayerService的create方法,创建Player播放器对象,并返回IMediaPlayer的播放器接口代理对象。
那么获取到播放器接口代理以后,通过播放器接口代理,将fd等参数传给服务端播放器。
// frameworks/av/media/libmedia/IMediaPlayer.cpp
class BpMediaPlayer: public BpInterface<IMediaPlayer>
{
...
status_t setDataSource(int fd, int64_t offset, int64_t length) {
//将远程调用参数封装到Parcel中
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
data.writeFileDescriptor(fd);
data.writeInt64(offset);
data.writeInt64(length);
//调用远程transact方法
remote()->transact(SET_DATA_SOURCE_FD, data, &reply);
return reply.readInt32();
}
...
}
status_t BnMediaPlayer::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch (code) {
...
case SET_DATA_SOURCE_FD: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
int fd = data.readFileDescriptor();
int64_t offset = data.readInt64();
int64_t length = data.readInt64();
//这里会调用到BnMediaPlayer的setDataSource方法
reply->writeInt32(setDataSource(fd, offset, length));
return NO_ERROR;
}
...
}
}
同样,player->setDataSource也是通过binder机制,调用到服务端的播放器setDataSource方法,最终将fd等参数传递到服务端播放器中。
// frameworks/av/media/libmedia/mediaplayer.cpp
status_t MediaPlayer::attachNewPlayer(const sp<IMediaPlayer>& player)
{
status_t err = UNKNOWN_ERROR;
sp<IMediaPlayer> p;
{ // scope for the lock
Mutex::Autolock _l(mLock);
...
clear_l();
p = mPlayer;
//关联到mPlayer属性中
mPlayer = player;
...
}
if (p != 0) {
//断开旧的player对象
p->disconnect();
}
return err;
}
最后,使用sp mPlayer属性,关联服务端播放器代理对象,方便后续的播放器逻辑进行调用。
小结
本文分析了MediaPlayer的setDataSource方法流程,从Java层将数据传递到native层,再从native层通过binder机制,最终传递到MediaPlayerService中。
由于篇幅有限,我们只分析了MediaPlayer的Client端的调用流程。而MediaPlayerService源码和binder机制,需要另外再开一个系列分析。