Android流媒体的实现

时间:2021-07-20 05:49:47
在mps的setDataSource中,根据url判断应该生成那种dataSource,然后获取默认的player,即nuplayer。进入NuPlayerDriver的setDataSource,
再进入void NuPlayer::setDataSourceAsync(const sp<IStreamSource> &source) {
#ifdef MTK_AOSP_ENHANCEMENT
    mIsStreamSource = true;
#endif
    sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());

    sp<AMessage> notify = new AMessage(kWhatSourceNotify, id());

    msg->setObject("source", new StreamingSource(notify, source));
    msg->post();
}

到了NuPlayer异步消息的世界,进入setDataSourceAsync
首先判断是什么流媒体
        if (!strncasecmp(url, "http://", 7)
                || !strncasecmp(url, "https://", 8)) {
            mDataSourceType = SOURCE_Http;
            ALOGI("Is http Streaming");
生成一个sp<GenericSource> genericSource =
                new GenericSource(notify, mUIDValid, mUID);
然后触发了kWhatSetDataSource
进入NuPlayerDriver的
void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) {
    Mutex::Autolock autoLock(mLock);

    CHECK_EQ(mState, STATE_SET_DATASOURCE_PENDING);

    mAsyncResult = err;
    mState = (err == OK) ? STATE_UNPREPARED : STATE_IDLE;
#ifdef MTK_AOSP_ENHANCEMENT
    ALOGD("after notifySetDataSourceCompleted mState=%d", mState);
#endif
    mCondition.broadcast();
}
注意,mCondition是在MPS调用NuPlayerDriver的API的时候就已经wait了的
status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {
    ALOGV("setDataSource(%p) file(%d)", this, fd);
    Mutex::Autolock autoLock(mLock);

    if (mState != STATE_IDLE) {
        return INVALID_OPERATION;
    }

    mState = STATE_SET_DATASOURCE_PENDING;

    mPlayer->setDataSourceAsync(fd, offset, length);

    while (mState == STATE_SET_DATASOURCE_PENDING) {
        mCondition.wait(mLock);
    }

    return mAsyncResult;
}
好,下面该prepare了

status_t NuPlayerDriver::prepare_l() {
    switch (mState) {
        case STATE_UNPREPARED:
            mState = STATE_PREPARING;

            // Make sure we're not posting any notifications, success or
            // failure information is only communicated through our result
            // code.
            mIsAsyncPrepare = false;
            mPlayer->prepareAsync();
            while (mState == STATE_PREPARING) {
                mCondition.wait(mLock);
            }
            return (mState == STATE_PREPARED) ? OK : UNKNOWN_ERROR;
        case STATE_STOPPED:
            // this is really just paused. handle as seek to start
            mAtEOS = false;
            mState = STATE_STOPPED_AND_PREPARING;
            mIsAsyncPrepare = false;
            mPlayer->seekToAsync(0, true /* needNotify */);
            while (mState == STATE_STOPPED_AND_PREPARING) {
                mCondition.wait(mLock);
            }
            return (mState == STATE_STOPPED_AND_PREPARED) ? OK : UNKNOWN_ERROR;
        default:
            return INVALID_OPERATION;
    };
}

进到NuPlayer的消息处理机制
        case kWhatPrepare:
        {
#ifdef MTK_AOSP_ENHANCEMENT
           ATRACE_ASYNC_BEGIN("Prepare",mPlayerCnt);

            ALOGD("kWhatPrepare, source type = %d", (int)mDataSourceType);
            if (mPrepare == PREPARING)
                break;
            mPrepare = PREPARING;
            if (mSource == NULL) {
                ALOGW("prepare error: source is not ready");
                finishPrepare(UNKNOWN_ERROR);
                break;
            }
#endif
            mSource->prepareAsync();
            break;
        }
这个mSource是GenericSource
进入了onPrepareAsync
此时去create 一个dataSource 
mDataSource = DataSource::CreateFromURI(
                   mHTTPService, uri, &mUriHeaders, &mContentType,
                   static_cast<HTTPBase *>(mHttpSource.get()));

此时就涉及到两个Source,一个是从网络获取数据的Http,一个是cache在本地的流媒体数据

Http的Source被这样创建
mHttpSource = DataSource::CreateMediaHTTP(mHTTPService);
mHttpSource继承于DataSource

第一个Source是聚合在NuCachedSource2中的一个Source

cache的Source被这样创建,本质上也是一个DataSource,因为NuCachedSource2是dataSource的子类

            source = new NuCachedSource2(
                    httpSource,
                    cacheConfig.isEmpty() ? NULL : cacheConfig.string(),
                    disconnectAtHighwatermark);

然后进入initFromDataSource,也就是去选择extractor的类型,这个需要一些数据来判断是哪种流媒体格式

        extractor = MediaExtractor::Create(mDataSource,
                mSniffedMIME.empty() ? NULL: mSniffedMIME.c_str());

那些初始下载的数据是什么时候下的呢?在创建NuCachedSource2的时候就开始了
这是另外一个线程在做的事情
    ssize_t n = mSource->readAt(
            mCacheOffset + mCache->totalSize(), page->mData, kPageSize);
一次性获取kPageSize大小的数据
此时是靠IMediaHTTPConnection来通过匿名共享内存和binder获取的

Bn的部分在MediaHTTPConnection.java (base\media\java\android\media) 

而它使用的又是 HttpURLConnection,是现在libcore下,属于基础架构


回到Extractor,它会调用ssize_t NuCachedSource2::readAt(off64_t offset, void *data, size_t size) {
发kWhatRead调用onRead,然后调用readInternal,从mCache里面copy数据
mCache->copy(delta, data, avail);

有多少拷多少,data是输出参数,给Extractor

注意,判断是什么类型是需要一点数据,这点数据往往就是tmd的tts数据了

bool DataSource::sniff(
        String8 *mimeType, float *confidence, sp<AMessage> *meta) {

策略是所有Sniff,挨个闻一遍,哪个confidence高用谁的,Sniff是Extractor的一个副产品