frameworks/base/libmediaplayerservice/MediaPlayerService.cpp
因為工作的平台是mx51,所以分析的是mx51 10.3的代碼,本文主要分析視頻播放部分的代碼,對於recorder和audio部分忽略掉。
Mediaplayer service是一個系統服務,Android 視頻播放,錄音錄影,中繼資料擷取等用戶端應用與Mediaplayer service互動,由MediaPlayer Service實現視頻的播放,錄影,中繼資料擷取等操作。
251 MediaPlayerService::MediaPlayerService() 252 { 253 LOGV("MediaPlayerService created"); 254 mNextConnId = 1; 255 }
MediaPlayerService的建構函式
mNextConnId記錄著client可用的connect id, 每建立一個client,mNextConnId都遞增1,已經用過的connect id不能複用
sp<IMediaMetadataRetriever> MediaPlayerService::createMetadataRetriever(pid_t pid){ sp<MetadataRetrieverClient> retriever = new MetadataRetrieverClient(pid); LOGV("Create new media retriever from pid %d", pid); return retriever;}建立一個Metadata retriever client,從類的名字我們可以猜測出這個函數是擷取media檔案的metadata。查看MetadataRetrieverClient的定義檔案libmediaplayerservice/MetadataRetrieverClient.cpp,可以看到這個類提供了如下幾種方法
- getFrameAtTime 擷取video檔案參數指定時間的幀內容,可用來擷取media檔案的thumbnail
- extractAlbumArt 擷取media檔案的AlbumArt, AlbumArt是media檔案的預覽封面,一般來說,如果不能抽取到這個,就用getFrameAtTime擷取第一幀來代替
- extractMetadata:擷取media檔案的中繼資料:Album, Artist, Author, Date, Genre, VIDEO_FORMAT, VIDEO_HEIGHT等等
286 sp<IMediaPlayer> MediaPlayerService::create( 287 pid_t pid, const sp<IMediaPlayerClient>& client, const char* url, 288 const KeyedVector<String8, String8> *headers, int audioSessionId) 289 { 290 int32_t connId = android_atomic_inc(&mNextConnId); 291 sp<Client> c = new Client(this, pid, connId, client, audioSessionId); 292 LOGV("Create new client(%d) from pid %d, url=%s, connId=%d, audioSessionId=%d", 293 connId, pid, url, connId, audioSessionId); 294 if (NO_ERROR != c->setDataSource(url, headers)) 295 { 296 c.clear(); 297 return c; 298 } 299 wp<Client> w = c; 300 Mutex::Autolock lock(mLock); 301 mClients.add(w); 302 return c; 303 }為MediaPlayer Client建立一個對應的實體
@pid: client所在進程的id
@client: MediaPlayer client
@url: media 檔案url
@headers: 未知
@audioSessionId:
301 mClients,是一個client對象數組,每一個建立的client都增加到這裡面
305 sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClient>& client, 306 int fd, int64_t offset, int64_t length, int audioSessionId) 307 { 308 int32_t connId = android_atomic_inc(&mNextConnId); 309 sp<Client> c = new Client(this, pid, connId, client, audioSessionId); 310 LOGV("Create new client(%d) from pid %d, fd=%d, offset=%lld, length=%lld, audioSessionId=%d", 311 connId, pid, fd, offset, length, audioSessionId); 312 if (NO_ERROR != c->setDataSource(fd, offset, length)) { 313 c.clear(); 314 } else { 315 wp<Client> w = c; 316 Mutex::Autolock lock(mLock); 317 mClients.add(w); 318 } 319 ::close(fd); 320 return c; 321 }create的另外一個重載形式是
305 sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClient>& client, 306 int fd, int64_t offset, int64_t length, int audioSessionId) 307 { 308 int32_t connId = android_atomic_inc(&mNextConnId); 309 sp<Client> c = new Client(this, pid, connId, client, audioSessionId); 310 LOGV("Create new client(%d) from pid %d, fd=%d, offset=%lld, length=%lld, audioSessionId=%d", 311 connId, pid, fd, offset, length, audioSessionId); 312 if (NO_ERROR != c->setDataSource(fd, offset, length)) { 313 c.clear(); 314 } else { 315 wp<Client> w = c; 316 Mutex::Autolock lock(mLock); 317 mClients.add(w); 318 } 319 ::close(fd); 320 return c; 321 }@fd: 已開啟的media檔案描述符
@offset: 對android引入offset和length參數很迷惑,難道對media檔案的操作不是從fd的起始位置開始嗎?猜測可能確實存在這樣的情況,media內容在另外一個檔案容器中,這樣就使用offset和length定義media內容的位置。
@length:
MediaPlayerService::Client::Client(const sp<MediaPlayerService>& service, pid_t pid, int32_t connId, const sp<IMediaPlayerClient>& client, int audioSessionId){ LOGV("Client(%d) constructor", connId); mPid = pid; mConnId = connId; mService = service; mClient = client; mLoop = false; mStatus = NO_INIT; mAudioSessionId = audioSessionId;#if CALLBACK_ANTAGONIZER LOGD("create Antagonizer"); mAntagonizer = new Antagonizer(notify, this);#endif}對於一個視頻播放,會建立兩個client,一個在使用者空間MediaPlayer執行個體(這個client負責在和MediaplayerService通訊);另外一個在Mediaplayer Service空間MediaplayerService::Client::Client執行個體,用於在Mediaplayer Service端維護client的狀態。
伺服器端client在MediaPlayerService::create中建立,新建立的Client對象加到mClients數組中
player_type getPlayerType(int fd, int64_t offset, int64_t length)........
該函數根據首先讀取檔案的頭部一些位元組,然後根據頭部標識的檔案類型,返回相應的播放器類型
player_type getPlayerType(const char* url)............
根據url來決定player類型
static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie, notify_callback_f notifyFunc){ sp<MediaPlayerBase> p; switch (playerType & 0xff) {#ifndef NO_OPENCORE case PV_PLAYER: LOGV(" create PVPlayer"); p = new PVPlayer(); break; #endif case SONIVOX_PLAYER: LOGV(" create MidiFile"); p = new MidiFile(); break; case STAGEFRIGHT_PLAYER: LOGV(" create StagefrightPlayer"); p = new StagefrightPlayer; break;#ifdef PREBUILT_FSL_IMX_OMX case OMX_PLAYER: LOGV(" Create OMXPlayer.\n"); p = new OMXPlayer(playerType >> 8); break;#endif case TEST_PLAYER: LOGV("Create Test Player stub"); p = new TestPlayerStub(); break; } if (p != NULL) { if (p->initCheck() == NO_ERROR) { p->setNotifyCallback(cookie, notifyFunc); } else { p.clear(); } } if (p == NULL) { LOGE("Failed to create player object"); } return p;}根據指定的不同播放器類型,建立相應的播放器並返回,可以看到,當前支援PVPlayer, MidiFIle, StagefrightPlayer, OMXPlayer
OMXPlayer是freescale專有的播放器
status_t MediaPlayerService::Client::setDataSource( const char *url, const KeyedVector<String8, String8> *headers){ LOGV("setDataSource(%s)", url); if (url == NULL) return UNKNOWN_ERROR; if (strncmp(url, "content://", 10) == 0) { // get a filedescriptor for the content Uri and // pass it to the setDataSource(fd) method String16 url16(url); int fd = android::openContentProviderFile(url16); if (fd < 0) { LOGE("Couldn't open fd for %s", url); return UNKNOWN_ERROR; } setDataSource(fd, 0, 0x7fffffffffLL); // this sets mStatus close(fd); return mStatus; } else { player_type playerType = getPlayerType(url); LOGV("player type = %d", playerType); // create the right type of player sp<MediaPlayerBase> p = createPlayer(playerType); if (p == NULL) return NO_INIT; if (!p->hardwareOutput()) { mAudioOutput = new AudioOutput(mAudioSessionId); static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput); } // now set data source LOGV(" setDataSource"); mStatus = p->setDataSource(url, headers); if (mStatus == NO_ERROR) { mPlayer = p; } else { LOGE(" error: %d", mStatus); } return mStatus; }}setDataSource 顧名思義就是把資料相關的內容設定給播放器,如果使用stagefright player最終會調用Awesome Player的setDataSource成員函數。AwesomePlayer::setDataSource有兩個重載函數:一個直接儲存@headers,另外一個調用相應的extractor從檔案中抽取需要的資訊 mflags, mBitrate等等。
status_t MediaPlayerService::Client::setVideoSurface(const sp<ISurface>& surface){ LOGV("[%d] setVideoSurface(%p)", mConnId, surface.get()); sp<MediaPlayerBase> p = getPlayer(); if (p == 0) return UNKNOWN_ERROR; return p->setVideoSurface(surface);} 為播放器設定surface對象
sp<IMemory> MediaPlayerService::Client::captureCurrentFrame(){ LOGV("captureCurrentFrame"); sp<MediaPlayerBase> p = getPlayer(); if (p == NULL) { LOGE("media player is not initialized"); return NULL; } Mutex::Autolock lock(mLock); mVideoFrame.clear(); mVideoFrameDealer.clear(); VideoFrame *frame = NULL; p->captureCurrentFrame(&frame); if (frame == NULL) { LOGE("failed to capture a video frame"); return NULL; } size_t size = sizeof(VideoFrame) + frame->mSize; mVideoFrameDealer = new MemoryDealer(size); if (mVideoFrameDealer == NULL) { LOGE("failed to create MemoryDealer"); return NULL; } mVideoFrame = mVideoFrameDealer->allocate(size); if (mVideoFrame == NULL) { LOGE("not enough memory for VideoFrame size=%u", size); mVideoFrameDealer.clear(); return NULL; } VideoFrame *frameCopy = static_cast<VideoFrame *>(mVideoFrame->pointer()); frameCopy->mWidth = frame->mWidth; frameCopy->mHeight = frame->mHeight; frameCopy->mDisplayWidth = frame->mDisplayWidth; frameCopy->mDisplayHeight = frame->mDisplayHeight; frameCopy->mSize = frame->mSize; frameCopy->mData = (uint8_t *)frameCopy + sizeof(VideoFrame); memcpy(frameCopy->mData, frame->mData, frame->mSize); return mVideoFrame;}擷取當前幀資料,返回一片記憶體空間,記憶體前面部分是幀的一些基本資料: width, height, displayWidth, displayHeight, size,後面是frame 資料。
MediaPlayerService.cpp中和video相關的代碼就分析完了。
MediaPlayerService主要的工作就是在客戶應用申請播放時,在Service建立對應的Client代理,處理用戶端應用發過來的請求,Service會根據開啟的檔案或者URI類型,選擇播放外掛程式。