
最近項目遇到一個bug,我先描述下bug現象。耳機模式下,播放音樂,手動背光滅屏(或者自動背光滅屏),音樂暫停;手動點亮屏音樂又可以繼續播放,從bug描述上猜測應該是滅屏的時候把音樂暫停了,然而并不是。因為亮屏時發現播放的時間并不是滅屏的時間,也就說滅屏狀态下音樂依舊在播放,隻是耳機沒有輸出罷了。
既然找到問題原因了,我們接着來分析下聲音播放流程。
上圖是使用MediaPlayer播放mp3檔案的一個簡單流程。調用start函數開始播放。
調用了JNI函數_start(),繼續跟蹤_start()函數
可以看下mp,熟悉android C++開發都知道這個是一個強指針,使用強指針開發者不用過分關注指針釋放的問題,為了防止記憶體洩漏。跟蹤代碼可以發現MediaPlayer對應的是mediaplayer.cpp檔案,看下該檔案中start是如何實作的?
重點看下mPlayer,是sp<IMediaPlayer> 指針類型,IMediaPlayer是一個Interface類型,也就是說可以用于IPC通信,來看下他的Bn和Bp端,
通過跟蹤源碼發現真正實作它的是
他是一個Client,也就是說在在建立一個MediaPlayer的時候都會去建立一個Client端,繼續跟蹤源碼看下是不是和我們猜測的一樣。在mediaplayer.cpp檔案中的setDataSource函數裡有IMediaPlayer引起了我們的注意。
看下這個create函數,IMediaPlayerService也是一個Interface,從命名上看應該是個服務類,看下getMediaPlayerService函數,他是在IMediaDeathNotifier類中的
其中linkToDeath函數,從字面上了解就是“綁到死”,可以了解當服務出現異常died了,這個類是幫助這個服務處理後事的,主要是釋出訃告,notify使用者。
再看IMediaPlayerService它隻是一個Proxy類,找到實作Bn的服務類。
原來是MediaPlayerService,這是個系統服務類,在mediaserver程序中啟動。接着看下create函數。
果然和我們之前猜想的一緻,create的時候執行個體了一個Client,并将Client放在了mClients裡,也就說ap端是可以實作多個MediaPlayer的。分析到這可以知道前面start函數中的mPlayer實際上是一個Client,真正實作它的接口是MediaPlayerService::Client,看下Client端的start函數:
有一個MediaPlayerBase,這又是個什麼鬼,繼續分析MediaPlayerBase,MediaPlayerBase是一個abstract類,找到他的實作類MediaPlayerInterface,MediaPlayerInterface主要是和AudioFlinger打交道的,主要是用于audio mixer。繼續分析發現真正實作它的是
Android4.0以後就用NuPlayer架構代替了之前的AwesomePlayer,因為NuPlayer采用的是異步消息機制,這樣加載大的檔案,不會導緻UI主線程阻塞了。有時間給大家分析下NuPlayer播放架構。今天主要還是要通過分析來解決這個bug的。由上面可知,我們從Java層、JNI、Native貌似都沒有發現播放異常情況。猜測可能就是Hal層聲音通道切換出了問題。下面我們繼續分下Hal層代碼。
這個是Hal層的一個入口接口。看下adev_open函數,
這個是adev——open函數中的部分代碼,重點看下adev_open_input_stream打打開一個輸入流,
看下in_read函數
進入start_input_stream
select_devices根據目前裝置狀态選擇通道裝置,
擷取到out_snd_device,進入platform_get_output_snd_device,根據傳入的devices,選擇聲音輸出的通道為
分析到這發現聲音的輸出通道也沒有問題。
從log中看,雖然可以看見screen on和screen off資訊,但是這塊并沒有針對處理過這2個狀态資訊。
是以懷疑可能是驅動問題,找到驅動同僚去查,分析log發現在通道切換的時候,左右耳機聲道的gpio都是拉高的,也就是說聲道已經切換過去,最後看原理圖才知耳機部分用到了TP的電源,滅屏的時候,沒有關閉該電源導緻通路不通。至此問題解決。
其實上面說了這麼多,但是還有涉及到的一些沒有分析到,這裡隻是給大家一個分析問題的思路,及如何跟蹤源碼的方法,從Java,JNI,Native到Hal層,中間有關于查找IPC通信的流程。看源碼你會發現有很多Interface、Bn,Bp的東西,隻要了解其原理,分析起來就很得心應手。還有要有懷疑精神,大膽的懷疑提出假設,然後根據你的假設,結合源碼去分析解決。不過這些假設是建立在你有一定的定位問題的經驗上。如果你經驗很豐富就不用再去從Java、JNI、Native跟蹤了,可以直接定位到Hal層,從Hal層在去進一步分析。
上次分析的源碼是截圖發的,有熱心朋友回報說,不方面查找,這次保證不用截圖:
Hal:
hardware/qcom/audio/hal/msm8916/platform.c
hardware/qcom/audio/hal/audio_hw.c
framework:
frameworks/av/media/libmediaplayerservice/MediaPlayerFactory.cpp
frameworks/av/media/libmediaplayerservice/MediaPlayerFactory.h
frameworks/av/media/libmedia/IMediaDeathNotifier.cpp
frameworks/av/include/media/IMediaDeathNotifier.h
frameworks/av/media/mediaserver/main_mediaserver.cpp
frameworks/av/include/media/mediaplayer.h
frameworks/av/media/libmedia/mediaplayer.cpp
frameworks/av/media/libmediaplayerservice/MediaPlayerService.h
frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp
frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
frameworks/av/include/media/MediaPlayerInterface.h
frameworks/av/include/media/IMediaPlayerService.h
frameworks/av/media/libmedia/IMediaPlayer.cpp
frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
frameworks/base/media/jni/android_media_MediaPlayer.cpp
frameworks/av/include/media/IMediaPlayerClient.h
frameworks/av/services/audioflinger/AudioFlinger.cpp
frameworks/base/media/java/android/media/MediaPlayer.java
原創不易,如果您覺得好,可以分享此公衆号給你更多的人。