Android MediaPlayer 分析- MediaPlayerService.cpp

來源:互聯網
上載者:User

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類型,選擇播放外掛程式。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.