MediaPlayer源码分析(一)

对于创建MediaPlay我们可以使用create方式,也可以通过new MediaPlay的方式。下面以create方式来切入源码。

public static MediaPlayer create(Context context, Uri uri, SurfaceHolder holder,
        AudioAttributes audioAttributes, int audioSessionId) {

    try {
         //1、
        MediaPlayer mp = new MediaPlayer();
        //2、
        mp.setDataSource(context, uri);
        if (holder != null) {
          //3、
            mp.setDisplay(holder);
        }
        //4、
        mp.prepare();
        return mp;
    } 
//..
    return null;
}

首先重点分析1处MediaPlayer的创建过程:MediaPlayer构造方法如下。

public MediaPlayer() {
     //...   
    Looper looper;
    if ((looper = Looper.myLooper()) != null) {
        mEventHandler = new EventHandler(this, looper);
    } else if ((looper = Looper.getMainLooper()) != null) {
        mEventHandler = new EventHandler(this, looper);
    } else {
        mEventHandler = null;
    }
    //重点
    native_setup(new WeakReference<MediaPlayer>(this));

    baseRegisterPlayer();
}

首先根据当前线程的Looper创建一个EventHandler对象。这个EventHandler继承至Handler,和我们自己写的Handler差不多,用来接收消息,回调对应的方法。比如当我们给MediaPlayer设置了OnPreparedListener监听(setOnPreparedListener),当一切就绪后,C++层会通过JNI通知到JAVA层(postEventFromNative),之后postEventFromNative方法内就会发送一个MEDIA_PREPARED 消息给EventHandler,EventHandler收到就会调用onPreparedListener.onPrepared(mMediaPlayer)方法。
然后调用native_setup方法。这个方法很重要,它建立了Java层MediaPlayer和C++层MediaPlayer之间的关联,可以说Java层的MediaPlayer就是个皮包公司,它所作的工作包括setDataSource、prepare、start等,最后都是交给C++层的MediaPlayer。注意这个方法将Java层MediaPlayer的弱引用传给了JNI层。

private native final void native_setup(Object mediaplayer_this)

这是一个native方法,所以需要去扒一扒C/C++的底了。根据JNI的命名规则找到:
–》android_media_MediaPlayer.cpp

static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
    //创建一个C++层的MediaPlayer
    sp<MediaPlayer> mp = new MediaPlayer();
    //...
    //创建一个JNIMediaPlayerListener,以便C++层MediaPlayer通过这个listener 通知到JAVA层的//MediaPlayer去。
    sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
    mp->setListener(listener);

    //建立JAVA层MediaPlayer和C++层MediaPlayer之间的关联
    setMediaPlayer(env, thiz, mp);
}

可以看到 setMediaPlayer(env, thiz, mp)方法的后面两个参数thiz指代了Java层的那个MediaPlayer,mp指代了C++层的MediaPlayer。

static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
{
   //...
   //将C++层的MediaPlayer地址保存到Java层MediaPlayer的mNativeContext字段
    env->SetLongField(thiz, fields.context, (jlong)player.get());
    return old;
}

这样JAVA和C++层的MediaPlayer就建立了关联,那么fields.context什么时候被赋值的呢?在MediaPlayer.java的静态代码块可以看到如下:
–》MediaPlayer.java

static {
    //加载so
    System.loadLibrary("media_jni");
    //初始化
    native_init();
}

–》android_media_MediaPlayer.cpp

static void
android_media_MediaPlayer_native_init(JNIEnv *env)
{
    jclass clazz;

    clazz = env->FindClass("android/media/MediaPlayer");
    if (clazz == NULL) {
        return;
}
    //mNativeContext用来保存C++层MediaPlayer的地址,拿到它的FieldID
    fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
    if (fields.context == NULL) {
        return;
}
    //拿到postEventFromNative的MethodID,以后JNI就可以通过这个方法通知Java层啦
    fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
                                              "(Ljava/lang/Object;IIILjava/lang/Object;)V");
    if (fields.post_event == NULL) {
        return;
    }
    //这个是和显示相关的。
    fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "J");
    if (fields.surface_texture == NULL) {
        return;
    }

   env->DeleteLocalRef(clazz);
//...
}

这样MediaPlayer的构造方法就分析完了,总结一下:

  • 1、创建了EventHandler,以便处理JNI层通过postEventFromNative传递过来的消息。
  • 2、创建了C++层的MediaPlayer并和JAVA层的MediaPlayer建立关系。

MediaPlayer对象创建完成,紧接着调用了setDataSource方法(这篇文章就是以这条线分析的,其他方法类似):
–》MediaPlayer.java

private void setDataSource(String path, String[] keys, String[] values,
        List<HttpCookie> cookies) {
    final Uri uri = Uri.parse(path);
    final String scheme = uri.getScheme();
    if ("file".equals(scheme)) {
        path = uri.getPath();
    } else if (scheme != null) {
        //...
    }

    final File file = new File(path);
    try (FileInputStream is = new FileInputStream(file)) {
        setDataSource(is.getFD());
    }
}

这里只分析数据源是文件的情况,获取到数据源的FileDescriptor接着又调用了
–》setDataSource(fd, 0, 0x7ffffffffffffffL)–》_setDataSource(fd, offset, length)。_setDataSource是一个native 方法,因此去android_media_MediaPlayer.cpp看看其具体实现。

–》android_media_MediaPlayer.cpp

static void
android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
    //1、获取C++层的MediaPlayer指针
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
   //2、获取文件描述符。  
    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
   //3、调用C++层的MediaPlayer的setDataSource方法
    process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
}

那么如何获取C++层的MediaPlayer指针?

static sp<MediaPlayer> getMediaPlayer(JNIEnv* env, jobject thiz)
{
    Mutex::Autolock l(sLock);
    MediaPlayer* const p = (MediaPlayer*)env->GetLongField(thiz, fields.context);
    return sp<MediaPlayer>(p);
}

很简单,就是拿到Java层MediaPlayer中mNativeContext保存的值,这个值保存的就是C++层MediaPlayer的地址,然后赋给指针p 。
对于process_media_player_call这个函数,其实就是根据 mp->setDataSource(fd, offset, length)返回值,来决定是否抛出异常或者通知到Java层。

Ok,现在的执行流程已经从Java层MediaPlayer.setDataSource进入了C++层MediaPlayer.setDataSource,继续跟进。
–》mediaplayer.cpp

status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
{
     //给err 赋一个初值
    status_t err = UNKNOWN_ERROR;
    //1、获取BpMediaPlayerService,
    const sp<IMediaPlayerService> service(getMediaPlayerService());
    if (service != 0) {
     //2、
        sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
        if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
            (NO_ERROR != player->setDataSource(fd, offset, length))) {
            player.clear();
        }
        err = attachNewPlayer(player);
    }
    return err;
}

首先在第一步获取一个BpMediaPlayerService,这里说有必要多说几句:
只要看到BpXXX就不得不想到BpBinder和BBinder。Binder是Android系统C/S架构提供的一种IPC(进程间通信)机制。BpBinder就是客户端用来与Server交互的代理类,BBinder代表了通信的目的也就是服务端,每一个BpBinder都有一个BBinder与之一一对应(通过构造函数传入的handle对应)。

继续跟进getMediaPlayerService()方法。
–》IMediaDeathNotifier.cpp

const sp<IMediaPlayerService> IMediaDeathNotifier::getMediaPlayerService()
{   
if (sMediaPlayerService == 0) {//如果还没有创建就先创建
        //先获取BpServiceManager
        sp<IServiceManager> sm = defaultServiceManager();
        sp<IBinder> binder;
        do {
            //通过BpServiceManager去ServiceManager查询MediaPlayerService的Binder句柄
            binder = sm->getService(String16("media.player"));
            if (binder != 0) {
                break;
            }
          //如果MediaPlayerService还没注册到ServiceManager中,此时肯定获取不到,就等等。
           usleep(500000); // 0.5 s
        } while (true);
        //。。。
        //通过MediaPlayerService的Binder句柄创建BpMediaPlayerService
        sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
    }
    return sMediaPlayerService;
}

代码虽然很少,但是还是要好好捋捋的。首先要明白getMediaPlayerService这个函数返回的是BpMediaPlayerService。
对于系统服务来说,通常都是注册到ServiceManager中,由ServiceManager统一管理。如果某个应用需要获取系统服务,需要先去ServiceManager查询,如果这个服务还没被注册到ServiceManager,此时肯定获取不到,就等等在尝试获取。反之,则会返回这个系统服务的BBinder句柄对应的BpBinder给我们。
在这里插入图片描述
Client查询服务、使用服务;Service向ServiceManager注册服务都是IPC过程。对于SerciceManager来说,Client、Server都是C;对于Server来说Client是C。

在getMediaPlayerService中首先通过defaultServiceManager()获取BpServiceManager。
–》IServiceManager.cpp

sp<IServiceManager> defaultServiceManager()
{
    if (gDefaultServiceManager != NULL) return gDefaultServiceManager;

    {
        AutoMutex _l(gDefaultServiceManagerLock);
        while (gDefaultServiceManager == NULL) {
            gDefaultServiceManager = interface_cast<IServiceManager>(
                ProcessState::self()->getContextObject(NULL));
            if (gDefaultServiceManager == NULL)
                sleep(1);
        }
    }
    return gDefaultServiceManager;
}

这个方法返回的就是BpServiceManager,以单例的方式实现。首先看ProcessState::self()->getContextObject(NULL)这行代码。
Tip:文章的主题是分析MediaPlayer,关于binder机制不会细说,但为了更好的理解也会解释下。
ProcessState这个类是进程唯一的,在它的构造方法中会打开Binder驱动,映射一块内存供Binder接收数据。
–》ProcessState.cpp

ProcessState::ProcessState(const char *driver)
    : mDriverName(String8(driver))
, mDriverFD(open_driver(driver))//打开binder驱动
  //。。。。
{
    if (mDriverFD >= 0) {
        //映射一块内存       
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
    }
}

当然主要还是分析要ProcessState::self()->getContextObject(NULL))方法。注意这里传入的是一个参数NULL
–》ProcessState.cpp

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
    return getStrongProxyForHandle(0);
}

接着调用了getStrongProxyForHandle传入了0(这个0是有特殊含义的,待会就知道了)
–》ProcessState.cpp

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;

    AutoMutex _l(mLock);

    handle_entry* e = lookupHandleLocked(handle);

    if (e != NULL) {
       //省略了部分代码
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
           //直接通过这个handle创建了一个BpBinder
            b = new BpHwBinder(handle);
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        }
    }
    return result;
}

在调用getStrongProxyForHandle传入的是0,然后这个0在new BpHwBinder时又被传入了BpHwBinder的构造。那么这个0有什么含义呢?前面说过,客户端的每一个BpBinder都对应目的端的一个BBinder,那么怎么一一对应的呢?就是通过这个handle,0就对应ServiceManager端的BBinder句柄。
所以defaultServiceManager代码就相当于这样:

sp<IServiceManager> defaultServiceManager()
{
    if (gDefaultServiceManager != NULL) return gDefaultServiceManager;

    {
        AutoMutex _l(gDefaultServiceManagerLock);
        while (gDefaultServiceManager == NULL) {
            gDefaultServiceManager = interface_cast<IServiceManager>(
                new BpHwBinder(0));
            if (gDefaultServiceManager == NULL)
                sleep(1);
        }
    }

    return gDefaultServiceManager;
}

ok继续看看interface_cast又干了啥。
–》IInterface.h

template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
    return INTERFACE::asInterface(obj);
}

一个模板函数,相当于IServiceManager::asInterface(new BpHwBinder(0))
–》IServiceManager.h

class IServiceManager : public IInterface
{
public:
//这个宏声明在IInterface中
DECLARE_META_INTERFACE(ServiceManager)
//....省略代码专用逗号....
};

继续跟进IInterface.h文件
–》IInterface.h

//申明
#define DECLARE_META_INTERFACE(INTERFACE)                               \
    //看这里                                                            \
    static ::android::sp<I##INTERFACE> asInterface(                     \
            const ::android::sp<::android::IBinder>& obj);              \

//实现
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \
     //再看这里                                       \
    ::android::sp<I##INTERFACE> I##INTERFACE::asInterface(              \
            const ::android::sp<::android::IBinder>& obj)               \
    {                                                                   \
        ::android::sp<I##INTERFACE> intr;                               \
        if (obj != NULL) {                                              \
            intr = static_cast<I##INTERFACE*>(                          \
                obj->queryLocalInterface(                               \
                        I##INTERFACE::descriptor).get());               \
            if (intr == NULL) {     
                //快看这 快看这 快看这                               
                intr = new Bp##INTERFACE(obj);                          \
            }                                                           \
        }                                                               \
        return intr;                                                    \
}  

现在知道了,原来defaultServiceManager代码就相当于这样:

sp<IServiceManager> defaultServiceManager()
{
    if (gDefaultServiceManager != NULL) return gDefaultServiceManager;

   {
    //...
         gDefaultServiceManager = new BpServiceManager(new BpHwBinder(0))));
    }

    return gDefaultServiceManager;
}

BpServiceManager内部持有BpBinder,由BpServiceManager处理业务BpBinder和Binder打交道。

终于拿到了BpServiceManager,之后就可以通过它和ServiceManager打交道(IPC),
再来粘贴下getMediaPlayerService的方法

IMediaDeathNotifier::getMediaPlayerService()
{
if (sMediaPlayerService == 0) {
        //获取BpServiceManager
        sp<IServiceManager> sm = defaultServiceManager();
        sp<IBinder> binder;
        do {
           //去ServiceManager查询MediaPlayerService服务,获取其BpBinder
            binder = sm->getService(String16("media.player"));
           //创建BpMediaPlayerService
           sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
    }
    return sMediaPlayerService;
}

sm->getService(String16(“media.player”)),最终会跨进程去ServiceManager查询出MediaPlayerService的BpBinder句柄,这其中会经过将数据写入binder缓存(mOut),接收响应,就不再细说了。
接着又走到interface_cast(binder),又碰到这个了。
所以getMediaPlayerService就相当于:

IMediaDeathNotifier::getMediaPlayerService()
{
if (sMediaPlayerService == 0) {
    //获取BpServiceManager
        sp<IServiceManager> sm = defaultServiceManager();
        sp<IBinder> binder;
        do {
            //去ServiceManager查询MediaPlayerService服务,获取其BpBinder
            binder = sm->getService(String16("media.player"));
            //创建BpMediaPlayerService
            sMediaPlayerService = new BpMediaPlayerService(binder);
    }
    return sMediaPlayerService;
}

获取了BpMediaPlayerService之后就可以跨进程调用目的端BnMediaPlayerService的create方法了。

status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
{
  
    status_t err = UNKNOWN_ERROR;
    const sp<IMediaPlayerService> service(getMediaPlayerService());
    if (service != 0) {
        //IPC创建BpMediaPlayer
        sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
         //。。。
        //调用BpMediaPlayer的setDataSource方法
        player->setDataSource(fd, offset, length)
        //将player保存为mPlayer
         err = attachNewPlayer(player);
    }
    return err;
}

MediaPlayerService继承自BnMediaPlayerService,BnMediaPlayerService的create的具体业务应该去MediaPlayerService中找如下:

–》MediaPlayerService.cpp

sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client,
        audio_session_t audioSessionId)
{
    pid_t pid = IPCThreadState::self()->getCallingPid();
    int32_t connId = android_atomic_inc(&mNextConnId);
    //创建一个MediaPlayerService::Client
    sp<Client> c = new Client(
            this, pid, connId, client, audioSessionId,
            IPCThreadState::self()->getCallingUid());

    wp<Client> w = c;
    {
        Mutex::Autolock lock(mLock);
        mClients.add(w);
    }
    return c;
}

MediaPlayerService::Client继承自BnMediaPlayer。

所以现在搞清楚了,原来C++层的MediaPlayer也不干事,比如setDataSource,经过一顿猛如虎的操作调到了BpMediaPlayer的setDataSource方法,最后跨进程调用了 MediaPlayerService::Client对应的方法。分析到这里setDataSource在C端的调用其实就差不多了,后面就走到了S端(MediaPlayerService::Client)的setDataSource,对于其他的方法,调用过程都是类似的,关于音视频的处理过程是放在MediaPlayerService端的,因为篇幅原因将在下一篇记录。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值