咱們下面進入正題研究多媒體文件的本地播放(video playback),具體用到的工具備source insight,astah(免費的畫流程圖工具),android 4.1代碼。代碼如何獲取能夠到google source下下載:http://source.android.com/source/downloading.html。html
通常上層應用要本地播放播放一個媒體文件,須要通過以下過程: java
MediaPlayer mMediaPlayer = new MediaPlayer( ); mMediaPlayer.setDataSource(mContext, mUri);- mMediaPlayer.setDisplay(mSurfaceHolder); mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mMediaPlayer.prepareAsync(); mMediaPlayer.start();
首先咱們先來分析setDataSource方法,這個方法有兩個功能:一個是根據文件類型得到相應的player,一個是建立相應文件類型的mediaExtractor,解析媒體文件,記錄metadata的主要信息。android
代碼以下:cookie
framework/av/media/libmedia/ MediaPlayer.cpp架構
status_t MediaPlayer::setDataSource( const char *url, const KeyedVector<String8, String8> *headers) { ALOGV("setDataSource(%s)", url); status_t err = BAD_VALUE; if (url != NULL) { const sp<IMediaPlayerService>& service(getMediaPlayerService()); if (service != 0) { sp<IMediaPlayer> player(service->create(getpid(), this, mAudioSessionId)); if ((NO_ERROR != doSetRetransmitEndpoint(player)) || (NO_ERROR != player->setDataSource(url, headers))) { player.clear(); } err = attachNewPlayer(player); } } return err; }
咱們先來看下setDataSource方法中如何獲取player。大致的流程圖以下圖: 框架
咱們知道MediaplayerService是負責外部請求,針對每一個APP player ,mediaplayerservice都會開闢一個client端來專門處理。ide
client 定義以下:函數
framework/av/media/libmediaplayerservice/ MediaPlayerService.h工具
class Client : public BnMediaPlayer {... private: friend class MediaPlayerService; Client( const sp<MediaPlayerService>& service, pid_t pid, int32_t connId, const sp<IMediaPlayerClient>& client, int audioSessionId, uid_t uid); } }
從代碼看就是一個BnMediaplayer的子類(即local binder)。既然有了BnMediaplayer,客戶端也應該有相應的BpMediaplayer。獲取這個BpMediaplayer要分兩步驟走:第一,獲取BpMediaplayerService;第二就是在setDataSource方法中的:ui
sp<IMediaPlayer> player(service->create(getpid(), this, mAudioSessionId)); 這個函數會返回一個BpMediaplayer。
獲取BpMediaplayerService,首先要去ServiceManager獲取相應的服務Mediaplayer,裏面的流程是這樣檢查是否存在要找的service,沒有就建立,有就返回BpXX。
有了BpMediaplayerService,咱們就能夠跟MediaplayerService通訊了,天然就能夠建立對應的client端來服務對應的BpMediaplayer(客戶端):
framework/av/media/libmediaplayerservice/ MediaPlayerService.cpp
sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClient>& client, int audioSessionId) { int32_t connId = android_atomic_inc(&mNextConnId); sp<Client> c = new Client( this, pid, connId, client, audioSessionId, IPCThreadState::self()->getCallingUid()); ALOGV("Create new client(%d) from pid %d, uid %d, ", connId, pid, IPCThreadState::self()->getCallingUid()); wp<Client> w = c; { Mutex::Autolock lock(mLock); mClients.add(w); } return c; }
到此咱們的 sp<IMediaPlayer> player(service->create(getpid(), this, mAudioSessionId));
變成了:sp<IMediaPlayer> player(Client); 那它是如何變成咱們須要的BpMediaplayer呢,請看下面的定義原型,INTERFACE就是mediaplayer,大夥把宏取代下就知道了:
frameworks/av/media/include/IMediapalyer.h
class IMediaPlayer: public IInterface { public: DECLARE_META_INTERFACE(MediaPlayer); DECLARE_META_INTERFACE宏定義以下: #define DECLARE_META_INTERFACE(INTERFACE) \ static const android::String16 descriptor; \ static android::sp<I##INTERFACE> asInterface( \ const android::sp<android::IBinder>& obj); \ virtual const android::String16& getInterfaceDescriptor() const; \ I##INTERFACE(); \ virtual ~I##INTERFACE(); 有了DECLARE IMediaplayer.cpp 必有 IMPLEMENT #define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \ const android::String16 I##INTERFACE::descriptor(NAME); \ const android::String16& \ I##INTERFACE::getInterfaceDescriptor() const { \ return I##INTERFACE::descriptor; \ } \ 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; \ } \ I##INTERFACE::I##INTERFACE() { } \ I##INTERFACE::~I##INTERFACE() { } \
經過如上方法 ,咱們得到了BpMediaplayer(remoteBinder),咱們就能夠經過BpMediaplayer 跟BnMediaplayer通訊了。二者的交互是IBinder。
BpMediaplayer具體實如今哪呢?
frameworks/av/media/libmedia/IMediaplayer.cpp:
class BpMediaPlayer: public BpInterface<IMediaPlayer> { public: BpMediaPlayer(const sp<IBinder>& impl) : BpInterface<IMediaPlayer>(impl) { } // disconnect from media player service void disconnect() { Parcel data, reply; data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); remote()->transact(DISCONNECT, data, &reply); } status_t setDataSource(const char* url, const KeyedVector<String8, String8>* headers) { Parcel data, reply; data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); data.writeCString(url); if (headers == NULL) { data.writeInt32(0); } else { // serialize the headers data.writeInt32(headers->size()); for (size_t i = 0; i < headers->size(); ++i) { data.writeString8(headers->keyAt(i)); data.writeString8(headers->valueAt(i)); } } remote()->transact(SET_DATA_SOURCE_URL, data, &reply); return reply.readInt32(); } remote 就是一個IBinder, IBinder 經過transact 方法中的 IPCThreadState::self()->transact( mHandle, code, data, reply, flags);
通知相應BnMediaplayer(client)進行相應的處理。裏面的如何打開binder,如何傳到MediaplayerService::client就不具體說了,有興趣能夠跟下去看看。
以上咱們運用到了Binder的通訊機制,若是你們對此不太瞭解能夠看:
Android系統進程間通訊(IPC)機制Binder中的Server和Client得到Service Manager接口之路 .
得到了BpMediaplayer ,咱們就能夠經過調用client端的setDataSource建立 player了:
status_t MediaPlayerService::Client::setDataSource( const char *url, const KeyedVector<String8, String8> *headers) {….
若是是url 以content://開頭要轉換爲file descriptor if (strncmp(url, "content://", 10) == 0) {… int fd = android::openContentProviderFile(url16); ………. setDataSource(fd, 0, 0x7fffffffffLL); // this sets mStatus close(fd); return mStatus; } else { player_type playerType = getPlayerType(url);…. createplayer前要判斷是哪一種類型 LOGV("player type = %d", playerType); // create the right type of player sp<MediaPlayerBase> p = createPlayer(playerType); mStatus = p->setDataSource(url, headers); … return mStatus; } } player_type getPlayerType(const char* url) ……………. 根據url的後綴名判斷屬於哪一種playerType,默認是stagefright,咱們如今研究的是本地播放,天然是stagefrightPlayer了 { if (TestPlayerStub::canBeUsed(url)) { return TEST_PLAYER; } // use MidiFile for MIDI extensions int lenURL = strlen(url); for (int i = 0; i < NELEM(FILE_EXTS); ++i) { int len = strlen(FILE_EXTS[i].extension); int start = lenURL - len; if (start > 0) { if (!strncasecmp(url + start, FILE_EXTS[i].extension, len)) { return FILE_EXTS[i].playertype; } } } ………………. return getDefaultPlayerType(); }
自此咱們得到了想要的player了。這裏最主要的知識點就是Binder的通訊了,Binder的流程咱們能夠用下圖來解釋,你們能夠好好琢磨:
player已經取得,接下來就是setDataSource的第二步:獲取相應的MediaExtractor並儲存相應的數據。
關於這一步,我也畫了個時序圖:
緊接剛纔咱們得到player的步驟,咱們實例話一個stagefrightPlayer的同時也實例話了一個AwesomePlayer,其實真正幹實事的AwesomePlayer,stagefrightPlayer只是個對外的接口,
代碼以下:framework/av/media/libmediaplayerservice/ StagefrightPlayer.cpp
static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie, notify_callback_f notifyFunc) { ….. case STAGEFRIGHT_PLAYER: ALOGV(" create StagefrightPlayer"); p = new StagefrightPlayer; break; ……. } 建立stagefrightplayer實例也new了個AwesomePlayer(mPlayer) StagefrightPlayer::StagefrightPlayer() : mPlayer(new AwesomePlayer) { LOGV("StagefrightPlayer"); mPlayer->setListener(this); } 既然Awesomeplayer是幹實事的,咱們直接進去看看吧: frameworks/av/media/libstagefright/AwesomePlayer.cpp status_t AwesomePlayer::setDataSource_l( const sp<DataSource> &dataSource) { sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);…….建立對應的extractor ….. return setDataSource_l(extractor); } status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) { … for (size_t i = 0; i < extractor->countTracks(); ++i) { sp<MetaData> meta = extractor->getTrackMetaData(i);.......獲取相應track的元數據 int32_t bitrate; if (!meta->findInt32(kKeyBitRate, &bitrate)) { const char *mime; CHECK(meta->findCString(kKeyMIMEType, &mime)); ALOGV("track of type '%s' does not publish bitrate", mime); totalBitRate = -1; break; } totalBitRate += bitrate; } ......... if (!haveVideo && !strncasecmp(mime, "video/", 6)) { setVideoSource(extractor->getTrack(i)); ………>mVideoTrack haveVideo = true; } else if (!haveAudio && !strncasecmp(mime, "audio/", 6)) { setAudioSource(extractor->getTrack(i));……….>mAudioTrack haveAudio = true; return OK; }
關於MediaExtractor裏面涉及到媒體文件格式的不少內容,好比track的構成,有幾種track等等,咱們未來在videoRecorder中再詳細講解。這裏只有知道提取相關信息就好了。
此方法調用完成意味着player進入了MEDIA_PLAYER_INITIALIZED狀態。Player的狀態有以下幾種:
MEDIA_PLAYER_STATE_ERROR
MEDIA_PLAYER_IDLE
MEDIA_PLAYER_INITIALIZED
MEDIA_PLAYER_PREPARING
MEDIA_PLAYER_PREPARED
MEDIA_PLAYER_STARTED
MEDIA_PLAYER_PAUSED
MEDIA_PLAYER_STOPPED
MEDIA_PLAYER_PLAYBACK_COMPLETE
setDataSource咱們已經講完了,講流程咱們的目的是熟悉它的架構,但願你們很好好熟悉熟悉,在項目須要的時候根據咱們本身的媒體格式進行,依葫蘆畫瓢進行改造,好比說支持多track,切換track,以達到KTV的功能等等。。。
下一篇咱們將講解prepare的過程,這個工程主要是匹配codec,初始化codec等。