MediaPlayer(四)--MediaPlayer()流程

基于Android8.1代码

#java MediaPlayer()
先从源头开始看
frameworks/base/media/java/android/media/MediaPlayer.java

    public MediaPlayer() {
        super(new AudioAttributes.Builder().build(),
                AudioPlaybackConfiguration.PLAYER_TYPE_JAM_MEDIAPLAYER);

        //1
        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;
        }

        //2
        mTimeProvider = new TimeProvider(this);
        //3
        mOpenSubtitleSources = new Vector<InputStream>();

       //4
        /* Native setup requires a weak reference to our object.
         * It's easier to create it here than in C++.
         */
        native_setup(new WeakReference<MediaPlayer>(this));
        //5
        baseRegisterPlayer();
    }

1 创建EventHandler(重点
后面jni回调java时会调用到postEventFromNative, postEventFromNative会将消息发给handler处理
2 mTimeProvider
对这个不太了解, 这是MediaPlayer的一个内部类,继承自MediaTimeProvider, 好像是一个用了提供Meida 播放,seek,buffer时间数据的类。
3 mOpenSubtitleSources
字幕源
4 native_setup(重点
是对 native层MediaPlayer和回调listener的创建和初始化
5 baseRegisterPlayer()
这个函数在MediaPlayer的父类PlayerBase.主要是获取AppOpsService, 即Application Operations Service,是关于系统应用权限管理的,这些API不对第三方应用开放。

这里主干创建流程主要关注1 和 4. 即创建handle和native层的MediaPlayer, 后续native层即可回调java层接口,java层将信息发送给handle处理

#native_setup(new WeakReference(this));
frameworks/base/media/jni/android_media_MediaPlayer.cpp

static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
    ALOGV("native_setup");
    sp<MediaPlayer> mp = new MediaPlayer();
    if (mp == NULL) {
        jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
        return;
    }

    // create new listener and give it to MediaPlayer
    sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
    mp->setListener(listener);

    // Stow our new C++ MediaPlayer in an opaque field in the Java object.
    setMediaPlayer(env, thiz, mp);
}

native_setup做了几件事情,
1 创建native层播放器实例MediaPlayer()
2 创建了JNIMediaPlayerListener,将java的MediaPlayer实例传递进去
3 将JNIMediaPlayerListener 设置给MediaPlayer()
4 将FFMediaPlayer 设置给Java层的mNativeContext

后面native层的MediaPlayer 就通过JNIMediaPlayerListener 回调java层接口。JNIMediaPlayerListener 能够访问全局变量 fields.post_event, 同时拥有 java的MediaPlayer实例,所以JNIMediaPlayerListener能够回调java层的postEventFromNative,并将MediaPlayer实例传递回去
关于这一部分listener的介绍可以参考
搭建ffmpeg player(一)–搭建上层框架

下面在分析一下setMediaPlayer,函数的目的是将将c++层MediaPlayer实例设置到java层的变量中。对于不熟悉智能指针的朋友可能有点绕

//假设引用计数为n, 这里传参不会增加计数
static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
{
    Mutex::Autolock l(sLock);
   //使用临时变量old,引用计数加1, n+1
    sp<MediaPlayer> old = (MediaPlayer*)env->GetLongField(thiz, fields.context);
    if (player.get()) {
        //主动增加新设置的player的引用计数, n+2
        player->incStrong((void*)setMediaPlayer);
    }
    if (old != 0) {
        //主动减少旧的player的引用计数, n+1
        old->decStrong((void*)setMediaPlayer);
    }
    env->SetLongField(thiz, fields.context, (jlong)player.get());
    return old;
}
//函数结束,如果外部没有使用函数的返回值,则引用计数会减1,变为n
  • 参数 sp& player
    这个参数传的是智能指针的引用, 引用不会增加智能指针的计数,这会导致什么问题呢。如果外部智能指针的计数为0了,MediaPlayer的实例会被释放掉的。如果参数是用传值,则会增加智能指针的计数,在函数执行结束后智能指针计数会减1。
  • sp old = (MediaPlayer*)env->GetLongField(thiz, fields.context);
    这里是先获取java层的mNativeContext变量,因为jni层均为static方法,是对应java层所有实例对象的,jni层本身不保存任何的实例,所以这里需要先获取java层对应实例对象的变量mNativeContext,即保存的c++层MediaPlayer实例。
  • env->SetLongField(thiz, fields.context, (jlong)player.get());
    函数最后将MediaPlayer的指针传递给java层。使用get()返回的指针,当最后一个对应的智能指针销毁后,指针就变为无效了。所以需要有办法保证MediaPlayer在销毁之前,player智能指针的引用计数不能为0.
    一种方法是使用全局的sp变量来引用MediaPlayer,不过Android并没有这么做,因为采用全局变量,则jni层只能给唯一一个播放器应用使用,这显然不符合要求。
    Android的做法是将sp的计数同 java层保存该指针的变量 private long mNativeContext 对应起来。首先参数采用引用sp&,不会增加计数,接着用临时变量获取java层实例会增加计数。接着主动增加sp的引用计数,在主动减少旧的sp的引用计数,再通过get()方法将指针传递给java层,这样保证java层变量不销毁时,sp的引用计数不为0。不过这样就需要在后续java层实例销毁的流程中,主动将引用计数减1

#c++ MediaPlayer()
frameworks/av/media/libmedia/mediaplayer.cpp

MediaPlayer::MediaPlayer()
{
    ALOGV("constructor");
    mListener = NULL;     // MediaPlayerListener, JNIMediaPlayerListener的父类,回调java层
    mCookie = NULL;
    mStreamType = AUDIO_STREAM_MUSIC;
    mAudioAttributesParcel = NULL;
    mCurrentPosition = -1;
    mCurrentSeekMode = MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC;
    mSeekPosition = -1;
    mSeekMode = MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC;
    mCurrentState = MEDIA_PLAYER_IDLE;    //播放器当前状态 IDLE
    mPrepareSync = false;
    mPrepareStatus = NO_ERROR;
    mLoop = false;
    mLeftVolume = mRightVolume = 1.0;
    mVideoWidth = mVideoHeight = 0;
    mLockThreadId = 0;
    mAudioSessionId = (audio_session_t) 
    AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
    AudioSystem::acquireAudioSessionId(mAudioSessionId, -1);
    mSendLevel = 0;
    mRetransmitEndpointValid = false;
}

在构造函数里将mCurrentState 设置成MEDIA_PLAYER_IDLE

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值