Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android4.2.2多媒體架構MediaPlay的創建過程分析(二):解析器的創建

Android4.2.2多媒體架構MediaPlay的創建過程分析(二):解析器的創建

編輯:關於Android編程

本文均屬自己閱讀源碼的點滴總結,轉賬請注明出處謝謝。

歡迎和大家交流。qq:1037701636 email: [email protected]

在上一文中,我們分析到setDataSource_pre()函數最終實際返回的是StagefrightPlayer類(class StagefrightPlayer : public MediaPlayerInterface).

1 .繼續分析setDataSource 函數:

    // now set data source
    setDataSource_post(p, p->setDataSource(fd, offset, length));

實際是多態下的StagefrightPlayer的setDataSource 函數的實現:

status_t StagefrightPlayer::setDataSource(
        const char *url, const KeyedVector *headers) {
    return mPlayer->setDataSource(url, headers);
}

mPlayer這個成員函數是new AwesomePlayer出來的對象,故最終是進入了stagefright中去做進一步的處理:

status_t AwesomePlayer::setDataSource(
        int fd, int64_t offset, int64_t length) {
    Mutex::Autolock autoLock(mLock);                                                                                                         ...                             .....                                                                                        return setDataSource_l(dataSource);
}
status_t AwesomePlayer::setDataSource_l(
        const sp &dataSource) {
    sp extractor = MediaExtractor::Create(dataSource);//創建一個解析器MPEG4Extractor,mime = NULL

    if (extractor == NULL) {
        return UNKNOWN_ERROR;
    }

    if (extractor->getDrmFlag()) {
        checkDrmStatus(dataSource);
    }

    return setDataSource_l(extractor);
}

MediaExtractor類,可以理解為音視頻數據源的解析器,我們來看其創建過程,傳入是默認參數mime= NULL:

sp MediaExtractor::Create(
        const sp &source, const char *mime) {
    sp meta;

    String8 tmp;
    if (mime == NULL) {
        float confidence;
        if (!source->sniff(&tmp, &confidence, &meta)) {//提取mime數值
            ALOGV("FAILED to autodetect media content.");

            return NULL;
        }

        mime = tmp.string();//獲取mime值
        ALOGV("Autodetected media content as '%s' with confidence %.2f",
             mime, confidence);
    }

//根據對文件解析的不同格式創建一個Extractor解析器
    MediaExtractor *ret = NULL;
    if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)
            || !strcasecmp(mime, "audio/mp4")) {
        int fragmented = 0;
        if (meta != NULL && meta->findInt32("fragmented", &fragmented) && fragmented) {
            ret = new FragmentedMP4Extractor(source);
        } else {
            ret = new MPEG4Extractor(source);
        }
.................
    return ret;
}

在這裡穿插解釋下MIME類型的概念,谷歌來的,應該是表示各種視頻格式的一個字符段:

video/x-ms-asf asf
video/mpeg mpeg mpg
video/x-msvideo avi
application/vnd.rn-realmedia rm
audio/x-pn-realaudio ram ra
audio/x-aiff aif aiff aifc
audio/mpeg mpga mp3
audio/midi mid midi
audio/wav wav
audio/x-ms-wma wma
video/x-ms-wmv wmv

2. 這裡和大家簡單分析source->sniff的實現過程,其目的很清楚就是獲取當前視頻源的MIME類型。

bool DataSource::sniff(
        String8 *mimeType, float *confidence, sp *meta) {
    *mimeType = "";
    *confidence = 0.0f;
    meta->clear();

    Mutex::Autolock autoLock(gSnifferMutex);
    for (List::iterator it = gSniffers.begin();
         it != gSniffers.end(); ++it) {
        String8 newMimeType;
        float newConfidence;
        sp newMeta;
        if ((*it)(this, &newMimeType, &newConfidence, &newMeta)) {
            if (newConfidence > *confidence) {
                *mimeType = newMimeType;
                *confidence = newConfidence;
                *meta = newMeta;
            }
        }
    }

    return *confidence > 0.0;
}

該函數中一個gSnifers的全局變量中查找注冊的函數it,函數指針類型為SnifferFunc。那麼這些函數是如何注冊的呢,我們回到AwesomePlay的構造函數中去:

  DataSource::RegisterDefaultSniffers();
void DataSource::RegisterDefaultSniffers() {
    RegisterSniffer(SniffMPEG4);
    RegisterSniffer(SniffFragmentedMP4);
..........

    char value[PROPERTY_VALUE_MAX];
    if (property_get("drm.service.enabled", value, NULL)
            && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
        RegisterSniffer(SniffDRM);
    }
}

我們看到其注冊了多個函數指針,而這都是針對不同的格式進行的注冊,將他維護在這個gSniffers迭代器中:

void DataSource::RegisterSniffer(SnifferFunc func) {
    Mutex::Autolock autoLock(gSnifferMutex);

    for (List::iterator it = gSniffers.begin();
         it != gSniffers.end(); ++it) {
        if (*it == func) {
            return;
        }
    }

    gSniffers.push_back(func);//保存函數指針
}


最終都回調到這個函數指針之中去:

bool SniffMPEG4(
        const sp &source, String8 *mimeType, float *confidence,
        sp *meta) {
    if (BetterSniffMPEG4(source, mimeType, confidence, meta)) {
        return true;
    }

    if (LegacySniffMPEG4(source, mimeType, confidence)) {
        ALOGW("Identified supported mpeg4 through LegacySniffMPEG4.");
        return true;
    }

    return false;
}

這裡的BetterSniffMPEG4()函數邏輯比較復雜,但總體思想是讀取出source的一些頭信息,如果好當前的MPEG4格式所需具備的信息一樣則返回。

最終返回一個屬於該source的MIEM類型,如這裡假設的是MEDIA_MIMETYPE_CONTAINER_MPEG4的格式。

3.解析器的創建

經歷過上述的過程,繼續回到sp MediaExtractor::Create()函數中去,根據提取到的MEME的類型,做如下操作。

  ret = new MPEG4Extractor(source);

創建好上述的解析器後,我們回到AwesomePlayer::setDataSource_l()中,繼續執行setDataSource_l(extractor),對新建的這個解析器做處理,其實質是顯示音視頻A/V的分離。

setVideoSource(extractor->getTrack(i));//設置視頻源mVideoTrack ;

setAudioSource(extractor->getTrack(i));//設置音頻源mAudioTrack;

mVideoTrack和mAudioTrack的做為創建的AwesomePlay的成員函數,其類型為MPEG4Source,繼承了MediaSource。

sp MPEG4Extractor::getTrack(size_t index) {
    status_t err;
    if ((err = readMetaData()) != OK) {
        return NULL;
    }

    Track *track = mFirstTrack;
    while (index > 0) {
        if (track == NULL) {
            return NULL;
        }

        track = track->next;
        --index;
    }

    if (track == NULL) {
        return NULL;
    }

    return new MPEG4Source(
            track->meta, mDataSource, track->timescale, track->sampleTable);
}

到此為止就是講視頻源進行了A\V的分離,其過程是通過Stagefrightplay多媒體框架——>Awesomeplay——>MPEG4Extractor——>MPEG4Source.這幾個過程。

4. 准備好解碼器

在完成APP側的setDataSource後,就進入prepare操作。在MPS側由如下函數來實現:

status_t MediaPlayerService::Client::prepareAsync()
{
    ALOGV("[%d] prepareAsync", mConnId);
    sp p = getPlayer();//stragefrightplay類
    if (p == 0) return UNKNOWN_ERROR;
    status_t ret; 

    ret = p->prepareAsync();
    
#if CALLBACK_ANTAGONIZER
    ALOGD("start Antagonizer");
    if (ret == NO_ERROR) mAntagonizer->start();
#endif
    return ret;
}

getPlayer獲取之前創建的播放器StagefrightPlayer這個對象,繼續執行:

status_t StagefrightPlayer::prepareAsync() {
    return mPlayer->prepareAsync();
}

mPlayer即為Awesomeplayer,實際的實現如下:

status_t AwesomePlayer::prepareAsync_l() {
    if (mFlags & PREPARING) {
        return UNKNOWN_ERROR;  // async prepare already pending
    }

    if (!mQueueStarted) {
        mQueue.start();
        mQueueStarted = true;
    }

    modifyFlags(PREPARING, SET);
    mAsyncPrepareEvent = new AwesomeEvent(
            this, &AwesomePlayer::onPrepareAsyncEvent);//onPrepareAsyncEvent回調函數,事件處理

    mQueue.postEvent(mAsyncPrepareEvent);//傳入類對象AwesomeEvent,mAsyncPrepareEvent

    return OK;
}

在這裡將回答在(一)中提到的事件注冊與處理的這個過程。

4.1 AwesomePlayer中的Event處理機制。

a. 首先這裡需要來看mQueue,他是TimedEventQueue類的對象,稱為時間事件隊列。首次調用是,需要進行start。

void TimedEventQueue::start() {
    if (mRunning) {
        return;
    }

    mStopped = false;

    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

    pthread_create(&mThread, &attr, ThreadWrapper, this);//創建一個進程

    pthread_attr_destroy(&attr);

    mRunning = true;
}

顯而易見的是,這個e事件處理機制脫離了Awesomeplayer,獨立創建了一個線程ThradWrapper,其內部調用thredEntry來處理

void TimedEventQueue::threadEntry() {
    prctl(PR_SET_NAME, (unsigned long)"TimedEventQueue", 0, 0, 0);

    for (;;) {
        int64_t now_us = 0;
        sp event;
            while (mQueue.empty()) {
                mQueueNotEmptyCondition.wait(mLock);
            }
.......
}

該函數就是在不斷等待著有事件需要處理,類似於一個Queue裡面非空則一直阻塞,等待signal喚醒。

b.我們來看AwesomeEvent類繼承了TimedEventQueue的Event內部類。其構造函數表明,將一個函數指針作為一個參數維護在mMethod。可以猜測到這個函數將會作為事件發生時的處理函數。那麼這個過程如何觸發呢?

struct AwesomeEvent : public TimedEventQueue::Event {
    AwesomeEvent(
            AwesomePlayer *player,
            void (AwesomePlayer::*method)())
        : mPlayer(player),
          mMethod(method) {
    }

protected:
    virtual ~AwesomeEvent() {}

    virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
        (mPlayer->*mMethod)();//調用最終的注冊的處理函數
    }

private:
    AwesomePlayer *mPlayer;
    void (AwesomePlayer::*mMethod)();

    AwesomeEvent(const AwesomeEvent &);
    AwesomeEvent &operator=(const AwesomeEvent &);
};

c. mQueue.postEvent(mAsyncPrepareEvent);//傳入類對象AwesomeEvent,mAsyncPrepareEvent來實現事件的喚醒與處理

TimedEventQueue::event_id TimedEventQueue::postTimedEvent(
        const sp &event, int64_t realtime_us) {
    Mutex::Autolock autoLock(mLock);

    event->setEventID(mNextEventID++);

    List::iterator it = mQueue.begin();
    while (it != mQueue.end() && realtime_us >= (*it).realtime_us) {
        ++it;
    }

    QueueItem item;
    item.event = event;
    item.realtime_us = realtime_us;

    if (it == mQueue.begin()) {
        mQueueHeadChangedCondition.signal();
    }

    mQueue.insert(it, item);//在mQueue中插入event

    mQueueNotEmptyCondition.signal();//發出信號觸發事件

    return event->eventID();
}

將當前的Event對象插入打Awesomeplayer的mQueue隊列中,然後發出signal,喚醒threadEntry線程,讓線程去處理當前的事件。

5.真正進入解碼器創建的世界

void AwesomePlayer::onPrepareAsyncEvent() { //基於隊列和事件機制調用
    Mutex::Autolock autoLock(mLock);

    if (mFlags & PREPARE_CANCELLED) {
        ALOGI("prepare was cancelled before doing anything");
        abortPrepare(UNKNOWN_ERROR);
        return;
    }

    if (mUri.size() > 0) {
        status_t err = finishSetDataSource_l();

        if (err != OK) {
            abortPrepare(err);
            return;
        }
    }

    if (mVideoTrack != NULL && mVideoSource == NULL) {
        status_t err = initVideoDecoder();//初始化視頻解碼器

        if (err != OK) {
            abortPrepare(err);
            return;
        }
    }

    if (mAudioTrack != NULL && mAudioSource == NULL) {
        status_t err = initAudioDecoder();//初始化飲品解碼器

        if (err != OK) {
            abortPrepare(err);
            return;
        }
    }

    modifyFlags(PREPARING_CONNECTED, SET);

    if (isStreamingHTTP()) {
        postBufferingEvent_l();
    } else {
        finishAsyncPrepare_l();//完成異步的prepare
    }
}

對於音視頻解碼器的創建和調用這裡不在做分析,考慮到他是一塊獨特的模塊,將在另一文中進行分析,自己也有很多內容需要進一步消化。












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