Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> Android開發——MediaPlayer源碼不完整分析

Android開發——MediaPlayer源碼不完整分析

編輯:Android開發實例

因為MediaPlayer的源碼比較多,所以只能來一個不完整分析了。

前段時間在工作中遇到一個問題就是MediaPlayer的播放網絡流媒體的時候,當沒有完全下載完畢的時,我們調用seekTo的時候會觸發OnCompletionListener,你們懂的咯,這樣就直接播放下一首了。挺糾結的,所以就決定看看mediaplayer的源碼咯,希望能從中找到解決的方法。

seekTo在MediaPlayer的調用流程如下圖:

seekTo 點擊上圖看大圖

在MediaPlayer.java中的seekTo是一個native修飾的方法

   1: /**
   2:  * Seeks to specified time position.
   3:  *
   4:  * @param msec the offset in milliseconds from the start to seek to
   5:  * @throws IllegalStateException if the internal player engine has not been
   6:  * initialized
   7:  */
   8: public native void seekTo(int msec) throws IllegalStateException;

好,我們來看看此方法的JNI是如何實現的。

   1: static void android_media_MediaPlayer_seekTo(JNIEnv *env, jobject thiz, int msec)
   2: {
   3:     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);//獲取MediaPlayer實例
   4:     if (mp == NULL ) {
   5:         jniThrowException(env, "java/lang/IllegalStateException", NULL);
   6:         return;
   7:     }
   8:     LOGV("seekTo: %d(msec)", msec);
   9:     status_t result = mp->seekTo(msec);//1,調用MediaPlayer的seekTo方法
  10:     process_media_player_call( env, thiz, result, NULL, NULL );//2,處理MediaPlayer方法調用的返回結果
  11: }

1,調用MediaPlayer的seekTo方法

   1: status_t MediaPlayer::seekTo_l(int msec)
   2: {
   3:     LOGV("seekTo %d", msec);
   4:     if ((mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_PAUSED |  MEDIA_PLAYER_PLAYBACK_COMPLETE) ) ) {
   5:         if ( msec < 0 ) {
   6:             LOGW("Attempt to seek to invalid position: %d", msec);
   7:             msec = 0;
   8:         } else if ((mDuration > 0) && (msec > mDuration)) {
   9:             LOGW("Attempt to seek to past end of file: request = %d, EOF = %d", msec, mDuration);
  10:             msec = mDuration;
  11:         }
  12:         // cache duration
  13:         mCurrentPosition = msec;
  14:         if (mSeekPosition < 0) {
  15:             getDuration_l(NULL);
  16:             mSeekPosition = msec;
  17:             //調用seekTo了
  18:             return mPlayer->seekTo(msec);
  19:         }
  20:         else {
  21:             LOGV("Seek in progress - queue up seekTo[%d]", msec);
  22:             return NO_ERROR;
  23:         }
  24:     }
  25:     LOGE("Attempt to perform seekTo in wrong state: mPlayer=%p, mCurrentState=%u", mPlayer.get(), mCurrentState);
  26:     return INVALID_OPERATION;
  27: }
  28:  
  29: status_t MediaPlayer::seekTo(int msec)
  30: {
  31:     mLockThreadId = getThreadId();
  32:     Mutex::Autolock _l(mLock);
  33:     status_t result = seekTo_l(msec);
  34:     mLockThreadId = 0;
  35:     return result;
  36: }

2,處理MediaPlayer方法調用的返回結果

   1: static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
   2: {
   3:     if (exception == NULL) {  // Don't throw exception. Instead, send an event.
   4:         if (opStatus != (status_t) OK) {
   5:             sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
   6:             if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0);//調用MediaPlayer的notify
   7:         }
   8:     } else {  // Throw exception!
   9:         if ( opStatus == (status_t) INVALID_OPERATION ) {
  10:             jniThrowException(env, "java/lang/IllegalStateException", NULL);
  11:         } else if ( opStatus != (status_t) OK ) {
  12:             if (strlen(message) > 230) {
  13:                // if the message is too long, don't bother displaying the status code
  14:                jniThrowException( env, exception, message);
  15:             } else {
  16:                char msg[256];
  17:                 // append the status code to the message
  18:                sprintf(msg, "%s: status=0x%X", message, opStatus);
  19:                jniThrowException( env, exception, msg);
  20:             }
  21:         }
  22:     }
  23: }

接下來看看MediaPlayer的notify方法,這個方法主要是通過判斷MediaPlayer的狀態向我們的app發送回調:

   1: void MediaPlayer::notify(int msg, int ext1, int ext2)
   2: {
   3:     LOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
   4:     bool send = true;
   5:     bool locked = false;
   6:  
   7:     // TODO: In the future, we might be on the same thread if the app is
   8:     // running in the same process as the media server. In that case,
   9:     // this will deadlock.
  10:     //
  11:     // The threadId hack below works around this for the care of prepare
  12:     // and seekTo within the same process.
  13:     // FIXME: Remember, this is a hack, it's not even a hack that is applied
  14:     // consistently for all use-cases, this needs to be revisited.
  15:      if (mLockThreadId != getThreadId()) {
  16:         mLock.lock();
  17:         locked = true;
  18:     }
  19:  
  20:     if (mPlayer == 0) {
  21:         LOGV("notify(%d, %d, %d) callback on disconnected mediaplayer", msg, ext1, ext2);
  22:         if (locked) mLock.unlock();   // release the lock when done.
  23:         return;
  24:     }
  25:  
  26:     switch (msg) {
  27:     case MEDIA_NOP: // interface test message
  28:         break;
  29:     case MEDIA_PREPARED://prepared結束
  30:         LOGV("prepared");
  31:         mCurrentState = MEDIA_PLAYER_PREPARED;
  32:         if (mPrepareSync) {
  33:             LOGV("signal application thread");
  34:             mPrepareSync = false;
  35:             mPrepareStatus = NO_ERROR;
  36:             mSignal.signal();
  37:         }
  38:         break;
  39:     case MEDIA_PLAYBACK_COMPLETE://播放完畢
  40:         LOGV("playback complete");
  41:         if (!mLoop) {
  42:             mCurrentState = MEDIA_PLAYER_PLAYBACK_COMPLETE;
  43:         }
  44:         break;
  45:     case MEDIA_ERROR://出錯
  46:         // Always log errors.
  47:         // ext1: Media framework error code.
  48:         // ext2: Implementation dependant error code.
  49:         LOGE("error (%d, %d)", ext1, ext2);
  50:         mCurrentState = MEDIA_PLAYER_STATE_ERROR;
  51:         if (mPrepareSync)
  52:         {
  53:             LOGV("signal application thread");
  54:             mPrepareSync = false;
  55:             mPrepareStatus = ext1;
  56:             mSignal.signal();
  57:             send = false;
  58:         }
  59:         break;
  60:     case MEDIA_INFO://logcat經常可以看到
  61:         // ext1: Media framework error code.
  62:         // ext2: Implementation dependant error code.
  63:         LOGW("info/warning (%d, %d)", ext1, ext2);
  64:         break;
  65:     case MEDIA_SEEK_COMPLETE://seek完畢
  66:         LOGV("Received seek complete");
  67:         if (mSeekPosition != mCurrentPosition) {
  68:             LOGV("Executing queued seekTo(%d)", mSeekPosition);
  69:             mSeekPosition = -1;
  70:             seekTo_l(mCurrentPosition);
  71:         }
  72:         else {
  73:             LOGV("All seeks complete - return to regularly scheduled program");
  74:             mCurrentPosition = mSeekPosition = -1;
  75:         }
  76:         break;
  77:     case MEDIA_BUFFERING_UPDATE://緩沖
  78:         LOGV("buffering %d", ext1);
  79:         break;
  80:     case MEDIA_SET_VIDEO_SIZE://設置視頻大小
  81:         LOGV("New video size %d x %d", ext1, ext2);
  82:         mVideoWidth = ext1;
  83:         mVideoHeight = ext2;
  84:         break;
  85:     default:
  86:         LOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2);
  87:         break;
  88:     }
  89:  
  90:     sp<MediaPlayerListener> listener = mListener;
  91:     if (locked) mLock.unlock();
  92:  
  93:     // this prevents re-entrant calls into client code
  94:     if ((listener != 0) && send) {
  95:         Mutex::Autolock _l(mNotifyLock);
  96:         LOGV("callback application");
  97:         //調用監聽器,回調應用的監聽器
  98:         listener->notify(msg, ext1, ext2);
  99:         LOGV("back from callback");
 100:     }
 101: }

在監聽器的notify方法中,是通過jni“反向調用”MediaPlayer.java中的postEventFromNative,在通過mEventHandler根據不同的消息類型調用不同的監聽器。

   1: private static void postEventFromNative(Object mediaplayer_ref,
   2:                                           int what, int arg1, int arg2, Object obj)
   3:   {
   4:       MediaPlayer mp = (MediaPlayer)((WeakReference)mediaplayer_ref).get();
   5:       if (mp == null) {
   6:           return;
   7:       }
   8:  
   9:       if (mp.mEventHandler != null) {
  10:           Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
  11:           mp.mEventHandler.sendMessage(m);
  12:       }
  13:   }

OK,至此我們分析了seekTo的整個流程。其他方法的流程是很相似的,大家不妨親自去看看。

回到本文開頭的問題,通過觀察logcat得知是發生錯誤導致的,暫時未找到解決的辦法。如果你有解決方法不妨告訴我哦。

------------------------

03-20 03:08:13.302: ERROR/MediaPlayer(419): Attempt to call getDuration without a valid mediaplayer
03-20 03:08:13.365: ERROR/MediaPlayer(419): error (-38, 0)
03-20 03:08:13.372: ERROR/MediaPlayer(419): Attempt to call getDuration without a valid mediaplayer
03-20 03:08:13.512: ERROR/MediaPlayer(419): error (-38, 0)
03-20 03:08:13.522: DEBUG/dalvikvm(419): GC freed 3405 objects / 831072 bytes in 122ms
03-20 03:08:13.752: ERROR/MediaPlayer(419): Error (-38,0)
03-20 03:08:13.762: DEBUG/MultiPlayer(419): Error: -38,0
03-20 03:08:13.772: ERROR/MediaPlayer(419): Error (-38,0)
03-20 03:08:13.781: ERROR/MediaPlayer(419): pause called in state 0
03-20 03:08:13.781: ERROR/MediaPlayer(419): error (-38, 0)

-------------------------

PS:“反向調用”就是在c/c++中通過jni來調用java中的方法或屬性。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved