MediaPlayer代码分析(2)-处理返回机制Notify

在各层处理消息时都是使用notify将处理的信息返回的。各层都对下一层注册了notify函数。

Java层是处理返回给应用层的消息,postEventFromNative

    private static void postEventFromNative(Object mediaplayer_ref,
                                            int what, int arg1, int arg2, Object obj)
    {
        MediaPlayer mp = (MediaPlayer)((WeakReference)mediaplayer_ref).get();
        if (mp == null) {
            return;
        }

        if (what == MEDIA_INFO && arg1 == MEDIA_INFO_STARTED_AS_NEXT) {
            // this acquires the wakelock if needed, and sets the client side state
            mp.start();
        }
        if (mp.mEventHandler != null) {
            Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
            mp.mEventHandler.sendMessage(m);
        }
    }
之后将一些消息扔给EventHandler处理。有些是调用app注册的回调函数,如onPrepared,onCompletion,onBufferingUpdate,onSeekComplete等。

JNI层首先定义了各种Java层的本地变量和回调函数,MediaPlayer的Java层回调函数使用post_event:

struct fields_t {
    jfieldID    context;
    jfieldID    surface_texture;

    jmethodID   post_event;

    jmethodID   proxyConfigGetHost;
    jmethodID   proxyConfigGetPort;
    jmethodID   proxyConfigGetExclusionList;
};
static fields_t fields;
获取post_event的代码:

    jclass clazz;

    clazz = env->FindClass("android/media/MediaPlayer");
    if (clazz == NULL) {
        return;
    }

    。。。。。。

    fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
                                               "(Ljava/lang/Object;IIILjava/lang/Object;)V");
    if (fields.post_event == NULL) {
        return;
    }
那么在什么地方调用它呢?当然是在jni层提供给下层的notify中了

void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
    JNIEnv *env = AndroidRuntime::getJNIEnv();
    if (obj && obj->dataSize() > 0) {
        jobject jParcel = createJavaParcelObject(env);
        if (jParcel != NULL) {
            Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
            nativeParcel->setData(obj->data(), obj->dataSize());
            env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
                    msg, ext1, ext2, jParcel);
            env->DeleteLocalRef(jParcel);
        }
    } else {
        env->CallStaticVoidMethod(mClass, <strong>fields.post_event</strong>, mObject,
                msg, ext1, ext2, NULL);
    }
    if (env->ExceptionCheck()) {
        ALOGW("An exception occurred while notifying an event.");
        LOGW_EX(env);
        env->ExceptionClear();
    }
}
下层注册回调的地方是

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);
    <strong>mp->setListener(listener)</strong>;

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

status_t MediaPlayer::setListener(const sp<MediaPlayerListener>& listener)
{
    ALOGV("setListener");
    Mutex::Autolock _l(mLock);
    mListener = listener;
    return NO_ERROR;
}
这层就是将JNI层的JNIMediaPlayerListener赋值给mListener。那么何时调用notify呢?

在MediaPlayer给下层的notify中找到了它,同时我们也找到了MediaPlayer给Stagefright层注册的notify回调

void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
    ALOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
    bool send = true;
    bool locked = false;

    // TODO: In the future, we might be on the same thread if the app is
    // running in the same process as the media server. In that case,
    // this will deadlock.
    //
    // The threadId hack below works around this for the care of prepare
    // and seekTo within the same process.
    // FIXME: Remember, this is a hack, it's not even a hack that is applied
    // consistently for all use-cases, this needs to be revisited.
    if (mLockThreadId != getThreadId()) {
        mLock.lock();
        locked = true;
    }

    // Allows calls from JNI in idle state to notify errors
    if (!(msg == MEDIA_ERROR && mCurrentState == MEDIA_PLAYER_IDLE) && mPlayer == 0) {
        ALOGV("notify(%d, %d, %d) callback on disconnected mediaplayer", msg, ext1, ext2);
        if (locked) mLock.unlock();   // release the lock when done.
        return;
    }

    switch (msg) {
    case MEDIA_NOP: // interface test message
        break;
    case MEDIA_PREPARED:
        ALOGV("prepared");
        mCurrentState = MEDIA_PLAYER_PREPARED;
        if (mPrepareSync) {
            ALOGV("signal application thread");
            mPrepareSync = false;
            mPrepareStatus = NO_ERROR;
            mSignal.signal();
        }
        break;
    case MEDIA_PLAYBACK_COMPLETE:
        ALOGV("playback complete");
        if (mCurrentState == MEDIA_PLAYER_IDLE) {
            ALOGE("playback complete in idle state");
        }
        if (!mLoop) {
            mCurrentState = MEDIA_PLAYER_PLAYBACK_COMPLETE;
        }
        break;
    case MEDIA_ERROR:
        // Always log errors.
        // ext1: Media framework error code.
        // ext2: Implementation dependant error code.
        ALOGE("error (%d, %d)", ext1, ext2);
        mCurrentState = MEDIA_PLAYER_STATE_ERROR;
        if (mPrepareSync)
        {
            ALOGV("signal application thread");
            mPrepareSync = false;
            mPrepareStatus = ext1;
            mSignal.signal();
            send = false;
        }
        break;
    case MEDIA_INFO:
        // ext1: Media framework error code.
        // ext2: Implementation dependant error code.
        if (ext1 != MEDIA_INFO_VIDEO_TRACK_LAGGING) {
            ALOGW("info/warning (%d, %d)", ext1, ext2);
        }
        break;
    case MEDIA_SEEK_COMPLETE:
        ALOGV("Received seek complete");
        if (mSeekPosition != mCurrentPosition) {
            ALOGV("Executing queued seekTo(%d)", mSeekPosition);
            mSeekPosition = -1;
            seekTo_l(mCurrentPosition);
        }
        else {
            ALOGV("All seeks complete - return to regularly scheduled program");
            mCurrentPosition = mSeekPosition = -1;
        }
        break;
    case MEDIA_BUFFERING_UPDATE:
        ALOGV("buffering %d", ext1);
        break;
    case MEDIA_SET_VIDEO_SIZE:
        ALOGV("New video size %d x %d", ext1, ext2);
        mVideoWidth = ext1;
        mVideoHeight = ext2;
        break;
    case MEDIA_TIMED_TEXT:
        ALOGV("Received timed text message");
        break;
    case MEDIA_SUBTITLE_DATA:
        ALOGV("Received subtitle data message");
        break;
    default:
        ALOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2);
        break;
    }

    sp<MediaPlayerListener> <strong>listener = mListene</strong>r;
    if (locked) mLock.unlock();

    // this prevents re-entrant calls into client code
    if ((listener != 0) && send) {
        Mutex::Autolock _l(mNotifyLock);
        ALOGV("callback application");
        <strong>listener->notify</strong>(msg, ext1, ext2, obj);
        ALOGV("back from callback");
    }
}
根据前一篇的分析,MediaPlayer是一个BpMediaPlayer,而调用它的notify的函数肯定在BnMediaPlayer里。BnMediaPlayer是MediaPlayer::Client

那么BnMediaPlayer的notify函数里肯定有BpMediaPlayer的notify,继续寻找:

void MediaPlayerService::Client::notify(
        void* cookie, int msg, int ext1, int ext2, const Parcel *obj)
{
    Client* client = static_cast<Client*>(cookie);
    if (client == NULL) {
        return;
    }

    sp<IMediaPlayerClient> c;
    {
        Mutex::Autolock l(client->mLock);
        <strong>c = client->mClient</strong>;
        if (msg == MEDIA_PLAYBACK_COMPLETE && client->mNextClient != NULL) {
            if (client->mAudioOutput != NULL)
                client->mAudioOutput->switchToNextOutput();
            client->mNextClient->start();
            client->mNextClient->mClient->notify(MEDIA_INFO, MEDIA_INFO_STARTED_AS_NEXT, 0, obj);
        }
    }

    if (MEDIA_INFO == msg &&
        MEDIA_INFO_METADATA_UPDATE == ext1) {
        const media::Metadata::Type metadata_type = ext2;

        if(client->shouldDropMetadata(metadata_type)) {
            return;
        }

        // Update the list of metadata that have changed. getMetadata
        // also access mMetadataUpdated and clears it.
        client->addNewMetadataUpdate(metadata_type);
    }

    if (c != NULL) {
        ALOGV("[%d] notify (%p, %d, %d, %d)", client->mConnId, cookie, msg, ext1, ext2);
        <strong>c->notify</strong>(msg, ext1, ext2, obj);
    }
}

上面的c代表BpMediaPlayer对象。

这样,MediaPlayerService::Client::notify就是BnMediaPlayer的通知函数了。接着找调用它的位置。

再往下找根据前一篇的分析,肯定是要到AwesomePlayer里找,但是我们只在AwesomePlayer里找到了AwesomePlayer::notifyListener_l。

那么究竟是从什么地方注册了MediaPlayerService::Client::notify,又是从什么地方调用它的呢?继续找

我们按照前一篇的思路,从create函数开始找。

sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerType)
{
    // determine if we have the right player type
    sp<MediaPlayerBase> p = mPlayer;
    if ((p != NULL) && (p->playerType() != playerType)) {
        ALOGV("delete player");
        p.clear();
    }
    if (p == NULL) {
        p = MediaPlayerFactory::createPlayer(playerType, this, <strong><span style="color:#ff0000;">notify</span></strong>);
    }

    if (p != NULL) {
        p->setUID(mUID);
    }

    return p;
}
一下就找到了MediaPlayerService::Client::notify,继续往下找

sp<MediaPlayerBase> MediaPlayerFactory::createPlayer(
        player_type playerType,
        void* cookie,
        notify_callback_f <strong>notifyFunc</strong>) {
    sp<MediaPlayerBase> p;
    IFactory* factory;
    status_t init_result;
    Mutex::Autolock lock_(&sLock);

    if (sFactoryMap.indexOfKey(playerType) < 0) {
        ALOGE("Failed to create player object of type %d, no registered"
              " factory", playerType);
        return p;
    }

    factory = sFactoryMap.valueFor(playerType);
    CHECK(NULL != factory);
    p = factory->createPlayer();

    if (p == NULL) {
        ALOGE("Failed to create player object of type %d, create failed",
               playerType);
        return p;
    }

    init_result = p->initCheck();
    if (init_result == NO_ERROR) {
        <strong><span style="color:#ff0000;">p->setNotifyCallback(cookie, notifyFunc)</span></strong>;
    } else {
        ALOGE("Failed to create player object of type %d, initCheck failed"
              " (res = %d)", playerType, init_result);
        p.clear();
    }

    return p;
}
变量p是MediaPlayerBase类的对象,那么setNotifyCallback就是将MediaPlayerService::Client::notify注册给了它。

我们来看看setNotifyCallback函数,很简单,就是赋值

    void        setNotifyCallback(
            void* cookie, notify_callback_f notifyFunc) {
        Mutex::Autolock autoLock(mNotifyLock);
        mCookie = cookie; mNotify = notifyFunc;
    }
终于在MediaPlayerBase类里找到了notify赋值的地方,那么对应的就应该有调用的地方。

没错,就在下面的sendEvent

    void        sendEvent(int msg, int ext1=0, int ext2=0,
                          const Parcel *obj=NULL) {
        Mutex::Autolock autoLock(mNotifyLock);
        if (mNotify) mNotify(mCookie, msg, ext1, ext2, obj);
    }
而这个sendEvent就是在AwesomePlayer::notifyListener_l里调用的:

void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) {
    if ((mListener != NULL) && !mAudioTearDown) {
        sp<MediaPlayerBase> listener = mListener.promote();

        if (listener != NULL) {
            listener->sendEvent(msg, ext1, ext2);
        }
    }
}
看来帅哥为了通知上面的app,也是费尽周折啊。
AwesomePlayer.cpp:            notifyListener_l(MEDIA_PAUSED);
AwesomePlayer.cpp:    notifyListener_l(MEDIA_PAUSED);
AwesomePlayer.cpp:        notifyListener_l(MEDIA_PAUSED);
AwesomePlayer.cpp:        notifyListener_l(MEDIA_SEEK_COMPLETE);
AwesomePlayer.cpp:        notifyListener_l(MEDIA_SKIPPED);
AwesomePlayer.cpp:        notifyListener_l(MEDIA_SEEK_COMPLETE);
AwesomePlayer.cpp:            notifyListener_l(MEDIA_INFO, MEDIA_INFO_RENDERING_START);
AwesomePlayer.cpp:            notifyListener_l(MEDIA_SEEK_COMPLETE);
AwesomePlayer.cpp:        notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
AwesomePlayer.cpp:            notifyListener_l(MEDIA_SET_VIDEO_SIZE, 0, 0);
AwesomePlayer.cpp:        notifyListener_l(MEDIA_PREPARED);
AwesomePlayer.cpp:                notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END);
这里仅列出一部分通知的调用。
发布了163 篇原创文章 · 获赞 110 · 访问量 100万+
展开阅读全文

MediaPlayer info/warning (703, 0),播放没声

04-07

04-07 09:15:53.501 3820-3820/pc.dialog.com.wutils I/MediaPlayActivity: onStart=http://jiayin-10076642.video.myqcloud.com/jsmy02/W38833d49-f783-4d27-919a-c198de8678ef.mp3 04-07 09:15:53.512 3820-3820/pc.dialog.com.wutils E/ExtMediaPlayer-JNI: env->IsInstanceOf fails 04-07 09:15:53.512 3820-3820/pc.dialog.com.wutils E/MediaPlayer-JNI: JNIMediaPlayerFactory: bIsQCMediaPlayerPresent 0 04-07 09:15:53.512 3820-3820/pc.dialog.com.wutils E/ExtMediaPlayer-JNI: env->IsInstanceOf fails 04-07 09:15:53.512 3820-3820/pc.dialog.com.wutils E/MediaPlayer-JNI: JNIMediaPlayerFactory: bIsQCMediaPlayerPresent 0 04-07 09:15:53.513 3820-3820/pc.dialog.com.wutils W/MediaPlayer: Couldn't open file on client side; trying server side: java.io.FileNotFoundException: No content provider: http://jiayin-10076642.video.myqcloud.com/jsmy02/W38833d49-f783-4d27-919a-c198de8678ef.mp3 04-07 09:15:53.517 3820-4027/pc.dialog.com.wutils D/MediaHTTPConnection: filterOutInternalHeaders: key=User-Agent, val= stagefright/1.2 (Linux;Android 6.0.1) 04-07 09:15:53.518 3820-3851/pc.dialog.com.wutils D/MediaHTTPConnection: proxy null port 0 04-07 09:15:54.746 3820-3820/pc.dialog.com.wutils D/MediaPlayer: setSubtitleAnchor in MediaPlayer 04-07 09:15:54.752 3820-3820/pc.dialog.com.wutils I/Choreographer: Skipped 77 frames! The application may be doing too much work on its main thread. 04-07 09:15:54.822 3820-3820/pc.dialog.com.wutils I/MediaPlayActivity: onPrepared 04-07 09:15:59.782 3820-3820/pc.dialog.com.wutils I/MediaPlayActivity: onStart=http://jiayin-10076642.video.myqcloud.com/jsmy02/W38833d49-f783-4d27-919a-c198de8678ef.mp3 04-07 09:15:59.783 3820-3820/pc.dialog.com.wutils E/ExtMediaPlayer-JNI: env->IsInstanceOf fails 04-07 09:15:59.783 3820-3820/pc.dialog.com.wutils E/MediaPlayer-JNI: JNIMediaPlayerFactory: bIsQCMediaPlayerPresent 0 04-07 09:15:59.783 3820-3820/pc.dialog.com.wutils E/ExtMediaPlayer-JNI: env->IsInstanceOf fails 04-07 09:15:59.783 3820-3820/pc.dialog.com.wutils E/MediaPlayer-JNI: JNIMediaPlayerFactory: bIsQCMediaPlayerPresent 0 04-07 09:15:59.789 3820-3820/pc.dialog.com.wutils W/MediaPlayer: Couldn't open file on client side; trying server side: java.io.FileNotFoundException: No content provider: http://jiayin-10076642.video.myqcloud.com/jsmy02/W38833d49-f783-4d27-919a-c198de8678ef.mp3 04-07 09:15:59.800 3820-3848/pc.dialog.com.wutils D/MediaHTTPConnection: filterOutInternalHeaders: key=User-Agent, val= stagefright/1.2 (Linux;Android 6.0.1) 04-07 09:15:59.801 3820-3820/pc.dialog.com.wutils D/MediaPlayer: setSubtitleAnchor in MediaPlayer 04-07 09:15:59.804 3820-7079/pc.dialog.com.wutils D/MediaHTTPConnection: proxy null port 0 04-07 09:15:59.851 3820-3820/pc.dialog.com.wutils I/MediaPlayActivity: onPrepared 04-07 09:15:59.852 3820-7079/pc.dialog.com.wutils D/MediaHTTPConnection: proxy null port 0 04-07 09:15:59.863 3820-4027/pc.dialog.com.wutils W/MediaPlayer: info/warning (703, 0) 04-07 09:15:59.863 3820-4027/pc.dialog.com.wutils W/MediaPlayer: info/warning (701, 0) 04-07 09:16:02.832 3820-4027/pc.dialog.com.wutils D/MediaHTTPConnection: filterOutInternalHeaders: key=User-Agent, val= stagefright/1.2 (Linux;Android 6.0.1) 04-07 09:16:02.835 3820-3851/pc.dialog.com.wutils D/MediaHTTPConnection: proxy null port 0 04-07 09:16:05.885 3820-3851/pc.dialog.com.wutils D/MediaHTTPConnection: filterOutInternalHeaders: key=User-Agent, val= stagefright/1.2 (Linux;Android 6.0.1) 04-07 09:16:05.886 3820-4027/pc.dialog.com.wutils D/MediaHTTPConnection: proxy null port 0 04-07 09:16:06.867 3820-7079/pc.dialog.com.wutils D/MediaHTTPConnection: proxy null port 0 04-07 09:16:08.923 3820-7079/pc.dialog.com.wutils D/MediaHTTPConnection: filterOutInternalHeaders: key=User-Agent, val= stagefright/1.2 (Linux;Android 6.0.1)__![图片说明](https://img-ask.csdn.net/upload/201704/07/1491528189_523347.png) void init(String url){ mMediaPlayer =MediaPlayer.create(mContext,Uri.parse(url)); mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mMediaPlayer.setOnErrorListener(this); mMediaPlayer.setOnPreparedListener(this); mMediaPlayer.setOnCompletionListener(this); mMediaPlayer.setOnTimedMetaDataAvailableListener(this); mMediaPlayer.setOnTimedTextListener(this); mMediaPlayer.setOnSeekCompleteListener(this); mMediaPlayer.setOnBufferingUpdateListener(this); mMediaPlayer.setOnVideoSizeChangedListener(this); } public void playUrl(String url){ if (mOnMediaPlayerListener!=null){ mOnMediaPlayerListener.onStart(url); } if (mMediaPlayer!=null){ destroy(); } init(url); } public void stop(){ if (mMediaPlayer!=null){ mMediaPlayer.stop(); } } public void destroy(){ if (mMediaPlayer!=null){ stop(); mMediaPlayer.reset(); mMediaPlayer.release(); mMediaPlayer=null; } } public void pause(){ if (isPlaying()){ mMediaPlayer.pause(); } } ----------------------------------------- @Override public void onPrepared(MediaPlayer mp) { mp.start(); L.d(TAG,"onPrepared"); } 请教高人解答 问答

MediaPlayer播放资源音乐出现 start called in state 0 错误

11-22

package com.example.playaudio; import java.io.File; import java.io.IOException; import android.app.Activity; import android.media.MediaPlayer; import android.os.Bundle; import android.os.Environment; import android.view.View; import android.widget.Button; public class MainActivity extends Activity { private Button Play_btn,Pause_btn,Stop_btn; private MediaPlayer mediaplayer = new MediaPlayer(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Play_btn = (Button)findViewById(R.id.button1); Pause_btn = (Button)findViewById(R.id.button2); Stop_btn = (Button)findViewById(R.id.button3); initMediaPlayer(); //播放音乐; Play_btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!mediaplayer.isPlaying()) { mediaplayer.start(); } } }); //暂停音乐; Pause_btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(mediaplayer.isPlaying()){ mediaplayer.pause(); } } }); //停止音乐; Stop_btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(mediaplayer.isPlaying()){ mediaplayer.reset(); initMediaPlayer(); } } }); } //若APP停止,释放内存; private void initMediaPlayer() { try { File file = new File(Environment.getExternalStorageDirectory(),"music.mp3"); mediaplayer.setDataSource(file.getPath()); mediaplayer.prepare(); } catch (Exception e) { e.printStackTrace(); } } @Override protected void onDestroy() { super.onDestroy(); if(mediaplayer != null){ mediaplayer.stop(); mediaplayer.release(); } } } //已经在AndroidMainfest 添加了权限;根目录下已有music.mp3文件; 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览