【三】Android MediaPlayer整体架构源码分析 -【设置数据源】【Part 3】

承接上一章节分析:
【三】Android MediaPlayer整体架构源码分析 -【设置数据源】【Part 2】
本系列文章分析的安卓源码版本:【Android 10.0 版本】

关于彻底理解掌握AMessage消息循环机制实现原理,推荐先看下系列内容:
Android native层媒体通信架构AHandler/ALooper机制实现源码分析

分析的代码来源如下:

// [frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp]
status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length)
{
    ALOGV("setDataSource fd=%d, offset=%" PRId64 ", length=%" PRId64 "", fd, offset, length);
    // 省略其他上一章节已分析过的代码

	// 见下面的分析
    // now set data source
    return mStatus = setDataSource_post(p, p->setDataSource(fd, offset, length));
}

setDataSource_post(p, p->setDataSource(fd, offset, length))实现分析:
该调用分两部分执行,先执行【p->setDataSource】后执行【setDataSource_post】。
根据前面章节的分析可知,p对象是NuPlayerDriver对象。
p->setDataSource的实现分析:

// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp]
status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {
    ALOGV("setDataSource(%p) file(%d)", this, fd);
    Mutex::Autolock autoLock(mLock);

    // setDataSource必须【STATE_IDLE】空闲状态才能执行,否则其他状态都无效
    if (mState != STATE_IDLE) {
        return INVALID_OPERATION;
    }

    // 更新播放器状态
    mState = STATE_SET_DATASOURCE_PENDING;

    // 根据前面章节分析,可知mPlayer其实就是NuPlayer对象
    // 见下面的分析
    mPlayer->setDataSourceAsync(fd, offset, length);

    while (mState == STATE_SET_DATASOURCE_PENDING) {
    	// C++实现的条件锁,等待上面【setDataSourceAsync】处理流程完成后才唤醒此处
    	// 注意:每次都会执行此处,因此可以知晓上层APP进行的setDataSource操作其实可能会耗时,
    	// 当然一般情况下会非常快的,因为NuPlayer中的处理其实很简单不耗时的,
    	// 除非NuPlayer自身的ALooper消息循环线程本身已阻塞,否则将非常快返回。
        mCondition.wait(mLock);
    }
    // NuPlayer设置数据源完成时,唤醒继续执行此处代码

    // 此处就是打印当前文件名【其实是文件全路径名,可从系统log中看到】
    AVNuUtils::get()->printFileName(fd);
    // 返回设置数据源处理结果状态
    return mAsyncResult;
}

上一章节1.1小节createPlayer(playerType)中遗留了一个内容未分析,p->setUID(mUid) 实现分析(p为NuPlayerDriver对象):
设置调用者用户id

// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp]
status_t NuPlayerDriver::setUID(uid_t uid) {
	// 调用了NuPlayer的该方法
	// 见下面的分析
    mPlayer->setUID(uid);
    mClientUid = uid;

    // mAnalyticsItem 初始化为空,因此暂不分析
    Mutex::Autolock autoLock(mMetricsLock);
    if (mAnalyticsItem) {
        mAnalyticsItem->setUid(mClientUid);
    }

    return OK;
}

mPlayer->setUID(uid)实现分析:
设置调用者用户id,并将mUIDValid标志为true

// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
void NuPlayer::setUID(uid_t uid) {
    mUIDValid = true;
    mUID = uid;
}

NuPlayer的setDataSourceAsync(fd, offset, length)实现分析:
关于AMessage的实现原理和使用,请见文章开头提到的另一章节内容分析。

// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
void NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) {
	// 创建【kWhatSetDataSource】设置数据源事件消息,NuPlayer自身为AHandler子类接收处理
    sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);

	// 创建【kWhatSourceNotify】数据源通知事件消息,NuPlayer自身为AHandler子类接收处理
    sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);

    // 创建一个通用(统一)数据源处理对象
    // GenericSource类声明和构造函数实现分析,见第1小节分析
    sp<GenericSource> source =
            new GenericSource(notify, mUIDValid, mUID, mMediaClock);

    ALOGV("setDataSourceAsync fd %d/%lld/%lld source: %p",
            fd, (long long)offset, (long long)length, source.get());

    // 设置数据源
    // 见第2小节分析
    status_t err = source->setDataSource(fd, offset, length);

    // 若设置失败,则清除数据源对象
    if (err != OK) {
        ALOGE("Failed to set data source!");
        source = NULL;
    }

    // 设置数据源对象source参数
    msg->setObject("source", source);
    // 立即发送该消息给NuPlayer的onMessageReceived()方法接收处理
    // 该消息事件【kWhatSetDataSource】的处理流程,见第3小节分析
    msg->post();
    // 设置数据源类型为DATA_SOURCE_TYPE_GENERIC_FD 
    mDataSourceType = DATA_SOURCE_TYPE_GENERIC_FD;
}

1、GenericSource类声明和构造函数实现分析
GenericSource类声明:【省略其他代码】

// [frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.h]
struct NuPlayer::GenericSource : public NuPlayer::Source,
                                 public MediaBufferObserver // Modular DRM
{}

// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerSource.h]
struct NuPlayer::Source : public AHandler {}

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

GenericSource构造函数:
初始化默认值,这些参数具体作用将在后续分析流程中使用到的地方进行详细分析。

// [frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp]
NuPlayer::GenericSource::GenericSource(
        const sp<AMessage> &notify,
        bool uidValid,
        uid_t uid,
        const sp<MediaClock> &mediaClock)
      // 父类Source构造方法,见下面的分析
    : Source(notify),
      mAudioTimeUs(0),
      mAudioLastDequeueTimeUs(0),
      mVideoTimeUs(0),
      mVideoLastDequeueTimeUs(0),
      mPrevBufferPercentage(-1),
      mPollBufferingGeneration(0),
      mSentPauseOnBuffering(false),
      mAudioDataGeneration(0),
      mVideoDataGeneration(0),
      mFetchSubtitleDataGeneration(0),
      mFetchTimedTextDataGeneration(0),
      mDurationUs(-1LL),
      mAudioIsVorbis(false),
      mIsSecure(false),
      mIsStreaming(false),
      mUIDValid(uidValid),
      mUID(uid),
      mMediaClock(mediaClock),
      mFd(-1),
      // 码率
      mBitrate(-1LL),
      mPendingReadBufferTypes(0) {
    ALOGV("GenericSource");
    CHECK(mediaClock != NULL);
	
    // 数据缓冲设置信息的初始化标记时间【单位为毫秒ms】,初始化值5秒
    mBufferingSettings.mInitialMarkMs = kInitialMarkMs;
    // 数据缓冲设置信息的底缓冲数据时指定的可恢复播放标记时间【单位为毫秒ms】,
    // 初始化值15秒即默认需要缓冲15秒的数据。
    // 该参数作用:即若是由于底缓冲数据需要缓冲数据时造成的暂停播放,
    // 则会在缓冲数据达到该标记时间值时自动恢复播放。
    mBufferingSettings.mResumePlaybackMarkMs = kResumePlaybackMarkMs;
    // 重置(初始化)数据源信息
    // 见下面的分析
    resetDataSource();
}

Source(notify)实现分析:
其实就是缓存该通知NuPlayer接收【kWhatSourceNotify】数据源通知事件消息,即用于通知NuPlayer

// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerSource.h]
struct NuPlayer::Source : public AHandler {
    // The provides message is used to notify the player about various
    // events.
    explicit Source(const sp<AMessage> &notify)
        : mNotify(notify) {
    }
}

resetDataSource() 实现分析:
重置清除(初始化)数据源缓存信息,主要就是做一些清除数据工作,比较简单此处就不一一展开分析了

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

    mHTTPService.clear();
    {
        Mutex::Autolock _l_d(mDisconnectLock);
        mHttpSource.clear();
        mDisconnected = false;
    }
    mUri.clear();
    mUriHeaders.clear();
    mSources.clear();
    if (mFd >= 0) {
    	// 关闭打开的文件
        close(mFd);
        mFd = -1;
    }
    mOffset = 0;
    mLength = 0;
    mStarted = false;
    mPreparing = false;

    mIsDrmProtected = false;
    mIsDrmReleased = false;
    mIsSecure = false;
    mMimes.clear();
}

2、source->setDataSource(fd, offset, length) 实现分析
向通用数据源处理对象设置数据源

// [frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp]
status_t NuPlayer::GenericSource::setDataSource(
        int fd, int64_t offset, int64_t length) {
    // 加锁处理    
    Mutex::Autolock _l(mLock);
    ALOGV("setDataSource %d/%lld/%lld", fd, (long long)offset, (long long)length);

    // 重置清除(初始化)数据源缓存信息
    resetDataSource();
	
	// dup()复制参数旧参数fd 所指的文件描述符值
	// 返回值:当复制成功时, 则返回最小及尚未使用的文件描述词,
	// 若有错误则返回-1, errno 会存放错误代码。
	// 错误代码:EBADF 参数fd 非有效的文件描述词, 或该文件已关闭。
    mFd = dup(fd);
    // 缓存文件偏移量即文件开始读取位置
    mOffset = offset;
    // 缓存文件总大小
    mLength = length;

    // 返回成功OK状态
    // 注意此处英文注释意思:即当前方法只是记录了打开的文件描述符和文件信息,
    // 但没有执行文件数据源的读取处理,而将该处理移到prepareSync()方法中执行,
    // 原因是避免阻塞方法调用端线程。
    // delay data source creation to prepareAsync() to avoid blocking
    // the calling thread in setDataSource for any significant time.
    return OK;
}

3、【kWhatSetDataSource】设置数据源事件消息接收处理:
由前面的ALooper消息循环实现机制分析可知,NuPlayer是允许在自己新创建的ALooper消息循环线程中进行接收消息处理的。

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

            CHECK(mSource == NULL);

            status_t err = OK;
            sp<RefBase> obj;
            // 获取传入的数据源对象source参数
            CHECK(msg->findObject("source", &obj));
            if (obj != NULL) {
            	// 若不为空,则加锁,强转为子类sp<Source>类型数据来缓存
                Mutex::Autolock autoLock(mSourceLock);
                mSource = static_cast<Source *>(obj.get());
            } else {
            	// 未知错误
                err = UNKNOWN_ERROR;
            }

            CHECK(mDriver != NULL);
            // 尝试提升NuPlayerDriver为强引用指针对象
            sp<NuPlayerDriver> driver = mDriver.promote();
            if (driver != NULL) {
            	// 通知设置数据源完成事件
            	// 见下面的分析
                driver->notifySetDataSourceCompleted(err);
            }
            break;
        }
        
        // ... 省略其他代码
    }
}

driver->notifySetDataSourceCompleted(err) 实现分析:

// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp]
void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) {
	// 加锁
    Mutex::Autolock autoLock(mLock);

    // 检查当前状态必须是该状态
    CHECK_EQ(mState, STATE_SET_DATASOURCE_PENDING);

    // 设置数据源完成状态码【成功或失败】
    mAsyncResult = err;
    // 成功则扭转状态为 STATE_UNPREPARED 即已设置数据源但未prepare状态,
    // 否则设置为空闲状态【STATE_IDLE】
    mState = (err == OK) ? STATE_UNPREPARED : STATE_IDLE;
    // 然后激活等待在该条件变量上所有线程【基本上只会有一个线程等待,因为做了状态判断】
    // 即会激活本文开头NuPlayerDriver.setDataSource()中的等待事件被唤醒继续执行,
    // 也就完成了整个设置数据源处理流程
    mCondition.broadcast();
}

本章节【设置数据源处理流程】系列内容分析结束。
后续 prepare 流程请查看后续章节

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值