【五】Android MediaPlayer整体架构源码分析 -【prepareAsync/prepare数据准备处理流程】【Part 1】

承接上一章节分析:【四】Android MediaPlayer整体架构源码分析 -【设置视频显示surface】
完整系列章节分析:【一】Android MediaPlayer整体架构源码分析 -【初始化和创建】
本系列文章分析的安卓源码版本:【Android 10.0 版本】

prepareAsync/prepare方法实现流程分析
MediaPlayer提供了两个prepare处理流程:同步和异步执行。
异步执行prepareAsync示例:

// 推荐都使用异步进行,然后在【mPreparedListener】异步监听回调中,
// 调用start()方法【mMediaPlayer.start();】即可开始播放
mMediaPlayer.prepareAsync();

两个prepare方法实现:
均在MediaPlayer.java类中

    /**
     * Prepares the player for playback, synchronously.
     *
     * After setting the datasource and the display surface, you need to either
     * call prepare() or prepareAsync(). For files, it is OK to call prepare(),
     * which blocks until MediaPlayer is ready for playback.
     *
     * @throws IllegalStateException if it is called in an invalid state
     */
    public void prepare() throws IOException, IllegalStateException {
    	// 调用native层实现
        _prepare();
        // 扫描内部字幕流track处理
        scanInternalSubtitleTracks();

        // DrmInfo, if any, has been resolved by now.
        synchronized (mDrmLock) {
            mDrmInfoResolved = true;
        }
    }

    private native void _prepare() throws IOException, IllegalStateException;

    /**
     * Prepares the player for playback, asynchronously.
     *
     * After setting the datasource and the display surface, you need to either
     * call prepare() or prepareAsync(). For streams, you should call prepareAsync(),
     * which returns immediately, rather than blocking until enough data has been
     * buffered.
     *
     * @throws IllegalStateException if it is called in an invalid state
     */
    public native void prepareAsync() throws IllegalStateException;

native层对应实现:【省略其他】

// [frameworks/base/media/jni/android_media_MediaPlayer.cpp]
static const JNINativeMethod gMethods[] = {
    {"_prepare",            "()V",                              (void *)android_media_MediaPlayer_prepare},
    {"prepareAsync",        "()V",                              (void *)android_media_MediaPlayer_prepareAsync}
}

此章节将只分析prepareAsync异步实现,同步实现prepare流程其实类似,就是会阻塞调用端线程直到有足够缓冲数据进行播放才完成。
android_media_MediaPlayer_prepareAsync() 实现分析:

// [frameworks/base/media/jni/android_media_MediaPlayer.cpp]
static void
android_media_MediaPlayer_prepareAsync(JNIEnv *env, jobject thiz)
{
    // 获取native层此前创建缓存的MediaPlayer对象
    // 备注:该实现不再分析,请参考前面已分析章节
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
    if (mp == NULL ) {
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        return;
    }

    // 此处将处理设置Surface在播放器初始化之前而失败的问题,即再次尝试设置它,
    // 而根据我们前面第四章节分析可知,若此前已成功设置了其实不会再次设置的,已做判断处理。
    // 该处理流程请查看第四章节内容分析
    // Handle the case where the display surface was set before the mp was
    // initialized. We try again to make it stick.
    sp<IGraphicBufferProducer> st = getVideoSurfaceTexture(env, thiz);
    mp->setVideoSurfaceTexture(st);

	// 调用native层MediaPlayer的prepareAsync()方法
    process_media_player_call( env, thiz, mp->prepareAsync(), "java/io/IOException", "Prepare Async failed." );
}

native层MediaPlayer的prepareAsync()方法实现分析:

// [frameworks/av/media/libmedia/mediaplayer.cpp]
status_t MediaPlayer::prepareAsync()
{
    ALOGV("prepareAsync");
    // 加锁执行
    Mutex::Autolock _l(mLock);
    return prepareAsync_l();
}

prepareAsync_l()实现分析:

// [frameworks/av/media/libmedia/mediaplayer.cpp]
// must call with lock held
status_t MediaPlayer::prepareAsync_l()
{
	// 由前面章节分析可知,mPlayer是MediaPlayerService::Client在客户端的Bp端代理对象,
	// 调用Client代理对象中方法即BpMediaPlayer代理类的对应方法实现。
	// 因此后面的分析不在分析Binder机制的处理流程,直接分析其Bn实现端即BnMediaPlayer实现类即MediaPlayerService::Client对应实现。
    if ( (mPlayer != 0) && ( mCurrentState & (MEDIA_PLAYER_INITIALIZED | MEDIA_PLAYER_STOPPED) ) ) {
    	// mPlayer不为空并且当前状态是已初始化状态或已停止状态,则进入
    	
    	// 判断音频属性参数是否java层有设置,一般情况下是设置了,
    	// 通过java层调用【mMediaPlayer.setAudioAttributes(mAudioAttributes);】
        if (mAudioAttributesParcel != NULL) {
        	// 见第1小节分析
            mPlayer->setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, *mAudioAttributesParcel);
        } else {
        	// 未设置时,只设置音频流类型信息,默认为音乐类型【AUDIO_STREAM_MUSIC】
        	// 见第2小节分析
            mPlayer->setAudioStreamType(mStreamType);
        }
        // 标记当前状态为正在prepare流程状态
        mCurrentState = MEDIA_PLAYER_PREPARING;
        // 见第3小节分析
        return mPlayer->prepareAsync();
    }
	
	// 否则失败
    ALOGE("prepareAsync called in state %d, mPlayer(%p)", mCurrentState, mPlayer.get());
    return INVALID_OPERATION;
}

关于native层Binder机制实现原理请查看:
Android C++底层Binder通信机制原理分析总结【通俗易懂】
1、mPlayer->setParameter() 对应的Bn实现端实现分析:
直接分析其Bn实现端即BnMediaPlayer实现类即MediaPlayerService::Client对应实现。

// [frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp]
status_t MediaPlayerService::Client::setParameter(int key, const Parcel &request) {
    ALOGV("[%d] setParameter(%d)", mConnId, key);
    switch (key) {
    case KEY_PARAMETER_AUDIO_ATTRIBUTES:
    {
        Mutex::Autolock l(mLock);
        // 见下面的分析
        return setAudioAttributes_l(request);
    }
    default:
    	// 默认处理,调用NuPlayerDriver的setParameter方法
    	// 见下面的分析
        sp<MediaPlayerBase> p = getPlayer();
        if (p == 0) { return UNKNOWN_ERROR; }
        return p->setParameter(key, request);
    }
}

Client::setAudioAttributes_l() 实现分析:

// [frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp]
status_t MediaPlayerService::Client::setAudioAttributes_l(const Parcel &parcel)
{
	// 先释放可能存在的旧音频属性参数
    if (mAudioAttributes != NULL) { free(mAudioAttributes); }
    mAudioAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
    if (mAudioAttributes == NULL) {
        return NO_MEMORY;
    }
    // 然后读取新属性值到新分配内存对象中,该实现比较简单,暂不展开分析
    unmarshallAudioAttributes(parcel, mAudioAttributes);

    ALOGV("setAudioAttributes_l() usage=%d content=%d flags=0x%x tags=%s",
            mAudioAttributes->usage, mAudioAttributes->content_type, mAudioAttributes->flags,
            mAudioAttributes->tags);

    // 然后设置给AudioSink对象,该对象是在前面setDataSource章节分析中创建的。
    if (mAudioOutput != 0) {
    	// 见下面的分析
        mAudioOutput->setAudioAttributes(mAudioAttributes);
    }
    return NO_ERROR;
}

mAudioOutput->setAudioAttributes(mAudioAttributes)实现分析:

// [frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp]
void MediaPlayerService::AudioOutput::setAudioAttributes(const audio_attributes_t * attributes) {
    Mutex::Autolock lock(mLock);
    if (attributes == NULL) {
    	// 若传入参数为空,则清空当前缓存旧对象数据
        free(mAttributes);
        mAttributes = NULL;
    } else {
    	// 不为空时
    	
        if (mAttributes == NULL) {
        	// 为空,创建新对象
            mAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
        }
        // 缓存到新对象中【若此前存在,则会覆盖旧对象中的所有值】
        memcpy(mAttributes, attributes, sizeof(audio_attributes_t));
        // 获取该对象中音频流类型【主要处理就是查找音频策略服务的配置信息,暂不展开分析】
        mStreamType = AudioSystem::attributesToStreamType(*attributes);
    }
}

NuPlayerDriver的setParameter() 实现分析:
其实际为空实现,返回无效错误码

// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp]
status_t NuPlayerDriver::setParameter(
        int /* key */, const Parcel & /* request */) {
    return INVALID_OPERATION;
}

2、mPlayer->setAudioStreamType(mStreamType)实现分析:
直接分析其Bn实现端即BnMediaPlayer实现类即MediaPlayerService::Client对应实现。

// [frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp]
status_t MediaPlayerService::Client::setAudioStreamType(audio_stream_type_t type)
{
    ALOGV("[%d] setAudioStreamType(%d)", mConnId, type);
    // TODO: for hardware output, call player instead
    Mutex::Autolock l(mLock);
    if (mAudioOutput != 0) mAudioOutput->setAudioStreamType(type);
    return NO_ERROR;
}

mAudioOutput->setAudioStreamType(type)实现分析:

// [frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp]
void MediaPlayerService::AudioOutput::setAudioStreamType(audio_stream_type_t streamType)
{
    Mutex::Autolock lock(mLock);
    // 只有当mAttributes为空时才缓存该值,避免覆盖mAttributes中设置的该值
    // do not allow direct stream type modification if attributes have been set
    if (mAttributes == NULL) {
        mStreamType = streamType;
    }
}

3、mPlayer->prepareAsync()实现分析:
直接分析其Bn实现端即BnMediaPlayer实现类即MediaPlayerService::Client对应实现。

// [frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp]
status_t MediaPlayerService::Client::prepareAsync()
{
    ALOGV("[%d] prepareAsync", mConnId);
    // 根据前面章节分析,p为NuPlayerDriver对象
    sp<MediaPlayerBase> p = getPlayer();
    if (p == 0) return UNKNOWN_ERROR;
    // 见下面的分析
    status_t ret = p->prepareAsync();

// 此处不分析,默认未定义该宏定义
#if CALLBACK_ANTAGONIZER
    ALOGD("start Antagonizer");
    if (ret == NO_ERROR) mAntagonizer->start();
#endif
    return ret;
}

NuPlayerDriver的prepareAsync()实现分析:

// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp]
status_t NuPlayerDriver::prepareAsync() {
    ALOGV("prepareAsync(%p)", this);
    Mutex::Autolock autoLock(mLock);
	
	// 根据前面章节内容分析,可知若setDataSource完成后,状态将会扭转为 STATE_UNPREPARED 
	// 而若执行了stop流程,则会扭转为 STATE_STOPPED 状态

	// prepare流程只在这两个状态下进行处理,其他状态均失败返回无效错误码
    switch (mState) {
        case STATE_UNPREPARED:
        	// 此前setDataSource已完成状态(STATE_UNPREPARED)时
    		// 扭转状态为 正在prepare流程中 状态
            mState = STATE_PREPARING;
            // 并标记当前是异步prepare操作
            mIsAsyncPrepare = true;
            // 调用NuPlayer该方法
            // 见下面的分析
            mPlayer->prepareAsync();
            return OK;
        case STATE_STOPPED:
        	// 已停止播放状态时
        	// 此处理为seek到start流程,因此该seek流程见后续的seek相关章节
            // this is really just paused. handle as seek to start
            // 标识播放未停止
            mAtEOS = false;
            // 状态扭转为 正在停止播放状态下的prepare流程中
            mState = STATE_STOPPED_AND_PREPARING;
            // 并标记当前是异步prepare操作
            mIsAsyncPrepare = true;
            // 执行seek操作,seek位置为0即从媒体开头播放,并传入默认seek模式,也要求发出seek完成通知
            mPlayer->seekToAsync(0, MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC /* mode */,
                    true /* needNotify */);
            return OK;
        default:
            return INVALID_OPERATION;
    };
}

TODO 后续的seek相关章节分析完成后将会附上链接

mPlayer->prepareAsync() 实现分析:
有此前章节分析可知,直接发送【kWhatPrepare】事件消息给NuPlayer自身接收处理

// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
void NuPlayer::prepareAsync() {
    ALOGV("prepareAsync");

    (new AMessage(kWhatPrepare, this))->post();
}

NuPlayer接收【kWhatPrepare】事件消息处理:
由前面章节分析可知,其直接调用了GenericSource对象的对应方法

// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
    switch (msg->what()) {
        case kWhatPrepare:
        {
            ALOGV("onMessageReceived kWhatPrepare");

            mSource->prepareAsync();
            break;
        }
    }
}

GenericSource的prepareAsync() 实现分析:

// [frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp]
void NuPlayer::GenericSource::prepareAsync() {
    Mutex::Autolock _l(mLock);
    ALOGV("prepareAsync: (looper: %d)", (mLooper != NULL));

	// 第一次调用prepare方法时,将会创建GenericSource数据源处理流程的ALooper消息循环线程
	// [请参考下面ALooper实现机制章节分析链接]
    if (mLooper == NULL) {
        mLooper = new ALooper;
        mLooper->setName("generic");
        mLooper->start();

        mLooper->registerHandler(this);
    }

    // 发送【kWhatPrepareAsync】事件消息给GenericSource自身来接收处理
    // 见下面的分析
    sp<AMessage> msg = new AMessage(kWhatPrepareAsync, this);
    msg->post();
}

关于ALooper消息循环线程实现机制请查看另外章节分析:
Android native层媒体通信架构AHandler/ALooper机制实现源码分析

GenericSourc接收【kWhatPrepareAsync】事件消息处理:

// [frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp]
void NuPlayer::GenericSource::onMessageReceived(const sp<AMessage> &msg) {
    Mutex::Autolock _l(mLock);
    switch (msg->what()) {
      case kWhatPrepareAsync:
      {
          onPrepareAsync();
          break;
      }
	}
}

onPrepareAsync()实现分析:

// [frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp]
void NuPlayer::GenericSource::onPrepareAsync() {
    mDisconnectLock.lock();
    ALOGV("onPrepareAsync: mDataSource: %d", (mDataSource != NULL));

    // 若延迟的数据源对象信息 mDataSource 为空则需要创建
    // 该对象提供读取数据源文件数据功能,并非解复用功能
    // delayed data source creation
    if (mDataSource == NULL) {
        // set to false first, if the extractor
        // comes back as secure, set it to true then.
        // 是否是加密文件格式,先设置为false
        mIsSecure = false;

        // mUri【AString类型】字符串值不为空时,也就是URL网络请求方式的数据源,
        // 目前暂不分析网络数据源,先分析本地数据源处理流程
        if (!mUri.empty()) {
        	// 网络数据源【后续有时间再分析吧】
        	
            const char* uri = mUri.c_str();
            String8 contentType;

            if (!strncasecmp("http://", uri, 7) || !strncasecmp("https://", uri, 8)) {
                sp<DataSource> httpSource;
                mDisconnectLock.unlock();
                httpSource = DataSourceFactory::CreateMediaHTTP(mHTTPService);
                if (httpSource == NULL) {
                    ALOGE("Failed to create http source!");
                    notifyPreparedAndCleanup(UNKNOWN_ERROR);
                    return;
                }
                mDisconnectLock.lock();

                if (!mDisconnected) {
                    mHttpSource = httpSource;
                }
            }

            mLock.unlock();
            mDisconnectLock.unlock();
            // This might take long time if connection has some issue.
            sp<DataSource> dataSource = DataSourceFactory::CreateFromURI(
                   mHTTPService, uri, &mUriHeaders, &contentType,
                   static_cast<HTTPBase *>(mHttpSource.get()));
            mDisconnectLock.lock();
            mLock.lock();
            if (!mDisconnected) {
                mDataSource = dataSource;
            }
        } else {
            // 本地数据源处理流程时
            
            // 默认配置的该系统属性值"media.stagefright.extractremote"为true,
            // 可以通过修改该系统属性值来改变读取数据源的实现方案。
            // requiresDrm()该方法返回值表示文件数据源是否有数字版权管理,通常返回false。
            if (property_get_bool("media.stagefright.extractremote", true) &&
                    !FileSource::requiresDrm(mFd, mOffset, mLength, nullptr /* mime */)) {
                // 因此本地文件播放时通常进入此处处理流程:该流程是使用远程解复用模块来解复用媒体数据,
                // 即可以通过Binder机制来通信连接自定义实现的解复用模块。如高通私有库解复用模块mm-parser
                
                // 根据Binder机制实现原理分析可知,此处获取的是名称为"media.extractor"
                // 即媒体解复用服务的Bp端代理对象,用于和该服务模块进行Binder通信。
                // 通过响应服务注册时的服务名称【"media.extractor"】获取注册过的服务
                sp<IBinder> binder =
                        defaultServiceManager()->getService(String16("media.extractor"));
                if (binder != nullptr) {
                    ALOGD("FileSource remote");
                    // 使用interface_cast转换成对应服务取得Bp代理对象,
        			// 即获取到了BpMediaExtractorService实现,此处转换成它的父类类型赋值给mediaExService对象
                    sp<IMediaExtractorService> mediaExService(
                            interface_cast<IMediaExtractorService>(binder));
                    // Binder机制跨进程调用该方法获取IDataSource对象
                    // 备注:由Binder实现机制可知,该返回对象肯定是Bp代理端BpBinder类型对象即BpDataSource,
                    // 其是在[frameworks/av/media/libmedia/IDataSource.cpp]定义实现的。
                    // 见3.1小节分析
                    sp<IDataSource> source =
                            mediaExService->makeIDataSource(mFd, mOffset, mLength);
                    ALOGV("IDataSource(FileSource): %p %d %lld %lld",
                            source.get(), mFd, (long long)mOffset, (long long)mLength);
                    if (source.get() != nullptr) {
                    	// source对象获取成功时,再次封装成客户端需要的代理类对象DataSource类型
                    	// 见3.2小节分析
                        mDataSource = CreateDataSourceFromIDataSource(source);
                        if (mDataSource != nullptr) {
                        	// 创建远程数据源读取对象DataSource成功后,就不用GenericSource本地再缓存本地文件描述符值了,
                        	// 直接关闭它并置为-1。原因是已经在远程进程中打开了该文件并实现了数据源远程操作对象。
                            // Close the local file descriptor as it is not needed anymore.
                            close(mFd);
                            mFd = -1;
                        }
                        // 备注:这里阐述一下上面这么多代理类实现到底干了啥事,其实两边都是需要DataSource类型的数据源操作对象,
                        // 但是该类型数据对象并没有实现Binder机制,因此两边都需要通过中间的IDataSource即Binder对象来代理控制对方。
                    } else {
                        ALOGW("extractor service cannot make data source");
                    }
                } else {
                    ALOGW("extractor service not running");
                }
            }
            if (mDataSource == nullptr) {
                ALOGD("FileSource local");
                // 若此时还为空,则创建本地(即本线程)解复用模块来处理,不需要跨进程处理
                // FileSource见上面已有的分析
                mDataSource = new FileSource(mFd, mOffset, mLength);
            }
            // TODO: close should always be done on mFd, see the lines following
            // CreateDataSourceFromIDataSource above,
            // and the FileSource constructor should dup the mFd argument as needed.
            // 创建好数据源操作对象后将该值置为无效,原因是FileSource中已经将该文件描述符复制了一份。
            mFd = -1;
        }

        if (mDataSource == NULL) {
        	// 若还是为空,则prepare流程执行失败,解除锁
            ALOGE("Failed to create data source!");
            mDisconnectLock.unlock();
            // 向上层应用通知该错误
            // 见3.3小节分析
            notifyPreparedAndCleanup(UNKNOWN_ERROR);
            return;
        }
    }

    // 检查数据源获取数据方式,即是否需要缓存数据源,
    // 然后将其强制转换为 NuCachedSource2 缓存功能数据对象【其实就是流媒体数据源时会设置的flag】,
    // 本地文件时用不到,因此暂不展开分析
    if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
        mCachedSource = static_cast<NuCachedSource2 *>(mDataSource.get());
    }

    // 释放锁
    mDisconnectLock.unlock();

    // 判断是否是流媒体数据源,true的话则需要缓冲足够多的数据之后才能上报prepare完成状态给上层APP
    // For cached streaming cases, we need to wait for enough
    // buffering before reporting prepared.
    mIsStreaming = (mCachedSource != NULL);

    // 使用DataSource数据源数据创建和初始化媒体提取器,即初始化具体的媒体解复用模块
    // 见3.4小节分析
    // init extractor from data source
    status_t err = initFromDataSource();

    if (err != OK) {
        ALOGE("Failed to init from data source!");
        // 向上层应用通知该错误
        // 见3.3小节分析
        notifyPreparedAndCleanup(err);
        return;
    }

    if (mVideoTrack.mSource != NULL) {
    	// video track不为空时
     	// 获取video track信息的元数据
     	// 见3.5小节分析
        sp<MetaData> meta = getFormatMeta_l(false /* audio */);
        sp<AMessage> msg = new AMessage;
        // 根据名字可知,就是转换媒体元数据对象为AMessage消息对象。
        // 此方法暂不分析,因为其实现就是将音视频track信息的几乎所有元数据读取出来,
        // 并根据不同的编码格式读取对应的一些关键元数据字段值,然后保存在AMessage中。
        // 感兴趣的可以自己看下。
        err = convertMetaDataToMessage(meta, &msg);
        if(err != OK) {
        	// 向上层应用通知该错误
        	// 见3.3小节分析
            notifyPreparedAndCleanup(err);
            return;
        }
        // 通知视频大小变化事件,并参入上面携带所有元数据信息的消息对象
        // 见3.6小节分析
        // 注意:该方法也是在NuPlayer.cpp中实现的。
        notifyVideoSizeChanged(msg);
    }

    // 通知视频相关标志位变化事件【即数据源解复用模块是否可以暂停和seek操作】
    // 传入的参数是一个由bit标志位通过或运算符计算得到的int值。
    // 见3.7小节分析
    // 注意:该方法也是在NuPlayer.cpp中实现的。
    notifyFlagsChanged(
            // FLAG_SECURE will be known if/when prepareDrm is called by the app
            // FLAG_PROTECTED will be known if/when prepareDrm is called by the app
            FLAG_CAN_PAUSE |
            FLAG_CAN_SEEK_BACKWARD |
            FLAG_CAN_SEEK_FORWARD |
            FLAG_CAN_SEEK);

	// 结束(完成)异步prepare处理流程
    // 见3.8小节分析
    finishPrepareAsync();

    ALOGV("onPrepareAsync: Done");
}

3.1、mediaExService->makeIDataSource(mFd, mOffset, mLength)实现分析:
由上面分析可知,Binder机制跨进程调用该方法获取IDataSource对象,因此此处不再讲述Binder机制处理过程(请查看另外章节),直接进入Bn实现端BnMediaExtractorService子类MediaExtractorService实现者该方法分析,如下
【关于android底层解复用模块插件模块化实现原理请查看:Android底层音视频播放媒体提取器【MediaExtractor】的解复用模块demuxers模块化加载和注册流程实现源码分析

// [frameworks/av/services/mediaextractor/MediaExtractorService.cpp]
sp<IDataSource> MediaExtractorService::makeIDataSource(int fd, int64_t offset, int64_t length)
{
	// 通过数据源对象创建工厂创建读取数据源对象DataSource
    // 见3.1.1小节分析
    sp<DataSource> source = DataSourceFactory::CreateFromFd(fd, offset, length);
    // 通过封装DataSource对象创建IDataSource
    // 见3.1.2小节分析
    return CreateIDataSourceFromDataSource(source);
}

3.1.1、DataSourceFactory::CreateFromFd(fd, offset, length)实现分析:
通过数据源对象创建工厂创建读取数据源对象DataSource。比如还提供了网络数据源读取数据对象实现

// [frameworks/av/media/libstagefright/DataSourceFactory.cpp]
sp<DataSource> DataSourceFactory::CreateFromFd(int fd, int64_t offset, int64_t length) {
	// 创建文件数据源读取对象,并执行initCheck()
	// 见下面的分析
    sp<FileSource> source = new FileSource(fd, offset, length);
    return source->initCheck() != OK ? nullptr : source;
}

FileSource类声明:【省略其他代码】

// [frameworks/av/media/libstagefright/include/media/stagefright/FileSource.h]
class FileSource : public ClearFileSource {}

// [frameworks/av/media/libstagefright/include/media/stagefright/ClearFileSource.h]
class ClearFileSource : public DataSource {}

// [frameworks/av/media/libstagefright/include/media/stagefright/DataSource.h]
class DataSource : public DataSourceBase, public virtual RefBase {}

// [frameworks/av/media/libstagefright/include/media/stagefright/DataSourceBase.h]
class DataSourceBase {}

FileSource类构造函数:

// [frameworks/av/media/libstagefright/FileSource.cpp]
FileSource::FileSource(int fd, int64_t offset, int64_t length)
    : ClearFileSource(fd, offset, length),
      mDecryptHandle(NULL),
      mDrmManagerClient(NULL),
      mDrmBufOffset(0),
      mDrmBufSize(0),
      mDrmBuf(NULL) {
}

父类ClearFileSource构造函数:

// [frameworks/av/media/libstagefright/ClearFileSource.cpp]
ClearFileSource::ClearFileSource(int fd, int64_t offset, int64_t length)
    : mFd(fd),
      mOffset(offset),
      mLength(length),
      mName("<null>") {
      // nameForFd(fd) 获取文件描述符对应文件全路径名称实现,见下面的分析
    ALOGV("fd=%d (%s), offset=%lld, length=%lld",
            fd, nameForFd(fd).c_str(), (long long) offset, (long long) length);

    // 检查数据有效性并修正
    if (mOffset < 0) {
        mOffset = 0;
    }
    if (mLength < 0) {
        mLength = 0;
    }
    if (mLength > INT64_MAX - mOffset) {
        mLength = INT64_MAX - mOffset;
    }
    // 文件状态信息结构对象
    struct stat s;
    if (fstat(fd, &s) == 0) {
    	// 读取该信息成功时,再次检查数据有效性并修正
        if (mOffset > s.st_size) {
        	// 偏移量即开始读取位置大于文件长度,则直接修正为文件末尾,并设置待读取长度为0
            mOffset = s.st_size;
            mLength = 0;
        }
        // 若偏移量加待读取量大于文件长度,则修正待读取数据长度为剩余可读取数据长度
        if (mOffset + mLength > s.st_size) {
            mLength = s.st_size - mOffset;
        }
    }
    // 被修正后将会打印log
    if (mOffset != offset || mLength != length) {
        ALOGW("offset/length adjusted from %lld/%lld to %lld/%lld",
                (long long) offset, (long long) length,
                (long long) mOffset, (long long) mLength);
    }

    // 记录格式化后的数据源相关信息名称
    mName = String8::format(
            "FileSource(fd(%s), %lld, %lld)",
            nameForFd(fd).c_str(),
            (long long) mOffset,
            (long long) mLength);

}

nameForFd(fd)实现分析:
该文件描述符对应文件全路径名称。
fd:内核(kernel)利用文件描述符(file descriptor)来访问文件。文件描述符是非负整数。打开现存文件或新建文件时,内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。
文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。
因此在系统中其实可以看到打开的文件描述符路径【/proc/[进程号]/fd/[描述符int值]】,举例:/proc/2335/fd/34223

// [frameworks/av/media/libstagefright/Utils.cpp]
AString nameForFd(int fd) {
    const size_t SIZE = 256;
    char buffer[SIZE];
    AString result;
    // getpid() 获取当前进程ID
    // 将格式化后的字符串写入buffer中
    snprintf(buffer, SIZE, "/proc/%d/fd/%d", getpid(), fd);
    // 文件状态信息结构对象
    struct stat s;
    if (lstat(buffer, &s) == 0) {
    	// 打开该文件描述符路径并获取指向的文件状态信息成功时
        if ((s.st_mode & S_IFMT) == S_IFLNK) {
        	// 读取文件描述符指向的文件链接成功
        	// 读取指向的文件全路径,默认不要超过256个字符长度
            char linkto[256];
            int len = readlink(buffer, linkto, sizeof(linkto));
            if(len > 0) {
                if(len > 255) {
                	// 若超出长度,则将只显示251个字符,后面接三个点即省略号
                    linkto[252] = '.';
                    linkto[253] = '.';
                    linkto[254] = '.';
                    // 赋值为0,即表示linkTo字符数组代表的是字符串值
                    linkto[255] = 0;
                } else {
                    linkto[len] = 0;
                }
                // 添加结果给字符串对象
                result.append(linkto);
            }
        } else {
        	// 读取文件描述符指向的文件链接失败
            result.append("unexpected type for ");
            result.append(buffer);
        }
    } else {
    	// 打开文件描述符的文件失败
        result.append("couldn't open ");
        result.append(buffer);
    }
    return result;

FileSource的initCheck()方法实现:
文件描述符不小于0即返回成功。

// [frameworks/av/media/libstagefright/ClearFileSource.cpp]
status_t ClearFileSource::initCheck() const {
    return mFd >= 0 ? OK : NO_INIT;
}

3.1.2、CreateIDataSourceFromDataSource(source)实现分析:
通过封装DataSource对象创建IDataSource

// [frameworks/av/media/libstagefright/InterfaceUtils.cpp]
sp<IDataSource> CreateIDataSourceFromDataSource(const sp<DataSource> &source) {
    if (source == nullptr) {
        return nullptr;
    }
    // 封装转换成IDataSource类型对象
    return RemoteDataSource::wrap(source);
}

RemoteDataSource::wrap(source)实现分析:
其实从此处可以看出,就是需要封装成可通过Binder机制跨进程来调用的操作类即IDataSource子类RemoteDataSource,其实现了Binder接口,成为了Bn实现端BnDataSource的子类,因此再返回给其他进程调用端跨进程使用。
RemoteDataSource类声明及其部分实现:

// [frameworks/av/media/libmedia/include/media/IDataSource.h]
// A binder interface for implementing a stagefright DataSource remotely.
class IDataSource : public IInterface {}

// [frameworks/av/media/libmedia/include/media/IDataSource.h]
class BnDataSource : public BnInterface<IDataSource> {}

// [frameworks/av/media/libstagefright/include/media/stagefright/RemoteDataSource.h]
// Originally in MediaExtractor.cpp
class RemoteDataSource : public BnDataSource {
public:
    static sp<IDataSource> wrap(const sp<DataSource> &source) {
        if (source.get() == nullptr) {
            return nullptr;
        }
        // 目前源码中看到该方法其实是个空实现,始终返回null
        if (source->getIDataSource().get() != nullptr) {
            return source->getIDataSource();
        }
        // 创建一个封装代理远程数据源操作类
        return new RemoteDataSource(source);
    }

private:
    enum {
        kBufferSize = 64 * 1024,
    };

    sp<IMemory> mMemory;
    sp<DataSource> mSource;
    String8 mName;
    Mutex mCloseLock;

    // 显式构造函数
    explicit RemoteDataSource(const sp<DataSource> &source) {
    	// 缓存
        mSource = source;
        // 内存分配,默认分配64KB大小的数据存储内存
        // 备注:MemoryDealer是一个内存分配管理类,其内部可以自动完成分配和释放内存的动作
        // 后续有时间再分析其源码实现
        sp<MemoryDealer> memoryDealer = new MemoryDealer(kBufferSize, "RemoteDataSource");
        // 返回分配的内存操作对象,若为空则表示内存分配失败
        mMemory = memoryDealer->allocate(kBufferSize);
        if (mMemory.get() == nullptr) {
            ALOGE("Failed to allocate memory!");
        }
        // 格式化的名称
        mName = String8::format("RemoteDataSource(%s)", mSource->toString().string());
    }
}

3.2、CreateDataSourceFromIDataSource(source)实现分析:
将IDataSource(BpBinder)类型数据对象封装成客户端需要的代理类对象DataSource类型。
由于篇幅长度过长,因此放入下一章节分析,请查看:
【五】Android MediaPlayer整体架构源码分析 -【prepareAsync/prepare数据准备处理流程】【Part 2】

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值