【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 9】

承接上一章节分析:【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 7】【03】
本系列文章分析的安卓源码版本:【Android 10.0 版本】

推荐涉及到的知识点:
Binder机制实现原理:Android C++底层Binder通信机制原理分析总结【通俗易懂】
ALooper机制实现原理:Android native层媒体通信架构AHandler/ALooper机制实现源码分析
Binder异常关闭监听:Android native层DeathRecipient对关联进程(如相关Service服务进程)异常关闭通知事件的监听实现源码分析

【此章节小节编号将重新排序】
doRequestBuffers()实现分析:
返回true时表示本次Buffer数据填充不足,因此必须请求填充更多数据
备注:通过下面的分析可知,该方法会在fetchInputData执行时,若无解析数据Buffer时会进行线程wait,等待解析数据生产者端添加缓冲区队列后进行唤醒继续获取Buffer数据。也就是说对于NuPlayerDecoder中的线程可能会被此流程处理而阻塞。
接下来关于GenericSource模块中的处理流程涉及早前流程已有分析时后续分析只会提醒一下,具体请参见前面prepare流程中已有分析过。

// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp]
/*
 * returns true if we should request more data
 */
bool NuPlayer::Decoder::doRequestBuffers() {
   
	// 前面已分析,true时表示输入格式配置信息改变了
    if (isDiscontinuityPending()) {
   
        return false;
    }
    status_t err = OK;
    // while循环执行已出队列输入Buffer的索引队列
    while (err == OK && !mDequeuedInputBuffers.empty()) {
   
    	// err为OK且待填充输入索引队列不为空时
    	// 获取第一个item即索引
        size_t bufferIx = *mDequeuedInputBuffers.begin();
        // 创建AMessage消息并缓存该输入Buffer索引
        sp<AMessage> msg = new AMessage();
        msg->setSize("buffer-ix", bufferIx);
        // 获取输入数据
        // 见下面分析
        err = fetchInputData(msg);
        if (err != OK && err != ERROR_END_OF_STREAM) {
   
        	// 非OK并且非EOS的其他错误码时,停止该流程处理
            // if EOS, need to queue EOS buffer
            // EOS时还是需要获取EOS数据的。
            break;
        }
        // 获取成功时,把当前输入Buffer索引,从待填充输入索引队列中移除
        mDequeuedInputBuffers.erase(mDequeuedInputBuffers.begin());

        if (!mPendingInputMessages.empty()
                || !onInputBufferFetched(msg)) {
   
            // 若待执行输入消息事件队列不为空,或者onInputBufferFetched()即输入Buffer已获取完成处理流程失败时
            
            // 也就是说这个判断的处理效果将是:若mPendingInputMessages队列为空即本次第一次获取输入Buffer成功时,
            // 或此前该队列已都执行完成时,此时将直接执行onInputBufferFetched()流程处理,
            // 否则将会通过前面流程对mPendingInputMessages队列的循环处理来返回该已获取输入Buffer的消息给到编解码器。
            
            // 将会添加当前需要被执行onInputBufferFetched()方法的待执行输入消息事件到其队列中。
            mPendingInputMessages.push_back(msg);
        }
    }

    // EWOULDBLOCK错误码表示:需要更多数据块
    // mSource为早期分析的GenericSource数据源实现对象,
    // 它的feedMoreTSData()方法实现实际上是空实现,始终返回OK。
    // 因此若err错误码为EWOULDBLOCK时,返回值为true,表示数据源提供当前Buffer数据不足。
    return err == -EWOULDBLOCK
            && mSource->feedMoreTSData() == OK;
}

fetchInputData(msg)实现分析:
获取输入数据

// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp]
status_t NuPlayer::Decoder::fetchInputData(sp<AMessage> &reply) {
   
	// 创建Buffer数据访问单元sp对象
    sp<ABuffer> accessUnit;
    // 是否丢弃访问单元
    bool dropAccessUnit = true;
    // while循环获取
    do {
   
    	// 获取(即出队列)Buffer数据访问单元,简单说就是获取数据源解复用后的音视频数据
    	// 调用GenericSource的该方法
    	// 见第1小节分析
    	// 注意第二个参数为sp对象地址,因此不会为空,用于接收已解析数据Buffer的
        status_t err = mSource->dequeueAccessUnit(mIsAudio, &accessUnit);

        if (err == -EWOULDBLOCK) {
   
        	// 返回数据不足错误码
            return err;
        } else if (err != OK) {
   
        	// 其他错误码时
            if (err == INFO_DISCONTINUITY) {
   
            	// INFO_DISCONTINUITY即Buffer非连续性错误码时
                int32_t type;
                // 获取非连续性类型
                CHECK(accessUnit->meta()->findInt32("discontinuity", &type));

                // 判断音频或视频Track数据源格式是否发生变化
                bool formatChange =
                    (mIsAudio &&
                     (type & ATSParser::DISCONTINUITY_AUDIO_FORMAT))
                    || (!mIsAudio &&
                            (type & ATSParser::DISCONTINUITY_VIDEO_FORMAT));

                // 判断媒体播放时间戳发生变化
                bool timeChange = (type & ATSParser::DISCONTINUITY_TIME) != 0;

                // 该log打印在系统运行时默认会打印
                ALOGI("%s discontinuity (format=%d, time=%d)",
                        mIsAudio ? "audio" : "video", formatChange, timeChange);

                // 表示是否为“无缝”格式改变
                // 备注:“无缝”的意思就是可以继续使用当前解码器并不用flush数据,即当前格式改变不影响此前的数据格式配置
                bool seamlessFormatChange = false;
                // 获取新的即已改变的格式信息,见早前相关已分析流程
                sp<AMessage> newFormat = mSource->getFormat(mIsAudio);
                if (formatChange) {
   
                	// 数据源Track格式变化时
                	// 判断是否支持“无缝”格式改变
    				// 见第2小节分析
                    seamlessFormatChange =
                        supportsSeamlessFormatChange(newFormat);
                    // treat seamless format change separately
                    // 单独处理“无缝”格式更改
                    // 非“无缝”格式更改时就代表数据格式改变了
                    formatChange = !seamlessFormatChange;
                }
                // 检查格式改变
                // 备注:根据早前我们的分析可知,AVNuUtils类的实现其实际是高通私有库实现的。
                // 也就是说上面的判断只是系统内部原生判断处理,
                // 厂商还可以根据该输入Buffer数据来自定义修改该格式改变标识
                AVNuUtils::get()->checkFormatChange(&formatChange, accessUnit);

                // 对于格式或时间数据变化,返回EOS到(入队列)EOS输入队列中,然后等待输出端口EOS
                // For format or time change, return EOS to queue EOS input,
                // then wait for EOS on output.
                if (formatChange /* not seamless */) {
   
                	// 格式改变时
                	// 标记格式改变等待为true
                    mFormatChangePending = true;
                    // 返回EOS错误码
                    err = ERROR_END_OF_STREAM;
                } else if (timeChange) {
   
                	// 时间改变时
                	// 记录新格式数据中的CSD数据(到mCSDsForCurrentFormat列表中)
                	// 该方法见此前已有分析
                    rememberCodecSpecificData(newFormat);
                    // 标记时间改变等待
                    mTimeChangePending = true;
                    // 返回EOS错误码
                    err = ERROR_END_OF_STREAM;
                } else if (seamlessFormatChange) {
   
                	// “无缝”格式改变时【重用已存在解码器并且不用flush】
                	// 记录新格式数据中的CSD数据(到mCSDsForCurrentFormat列表中)
                	// 该方法见此前已有分析
                    // reuse existing decoder and don't flush
                    rememberCodecSpecificData(newFormat);
                    // 继续上面的【mSource->dequeueAccessUnit】执行,重新获取新的输入Buffer,也就是说刚获取的数据丢弃了
                    continue;
                } else {
   
                	// 其他情况时,表示该数据流不受非连续性的影响,返回EWOULDBLOCK即数据不足错误码
                    // This stream is unaffected by the discontinuity
                    return -EWOULDBLOCK;
                }
            }

            // reply should only be returned without a buffer set
            // when there is an error (including EOS)
            // 发生异常错误(包括EOS)时,reply传入参数应答消息设置该错误码返回,不能设置buffer
            CHECK(err != OK);

            reply->setInt32("err", err);
            // 返回EOS
            return ERROR_END_OF_STREAM;
        }
		// 获取输入Buffer成功时
			
        // 判断是否drop丢弃访问单元Buffer
        dropAccessUnit = false;
        if (!mIsAudio && !mIsEncrypted) {
   
        	// 非音频并且非加密数据源时,即非加密视频源时
        	
        	// 下面这段注释实际上就是解释当前if判断里面的处理CASE问题,此处不展开翻译,自行阅读吧。
        	// 主要就是做一些异步并发操作同步判断等额外保护措施。
            // Extra safeguard if higher-level behavior changes. Otherwise, not required now.
            // Preventing the buffer from being processed (and sent to codec) if this is a later
            // round of playback but this time without prepareDrm. Or if there is a race between
            // stop (which is not blocking) and releaseDrm allowing buffers being processed after
            // Crypto has been released (GenericSource currently prevents this race though).
            // Particularly doing this check before IsAVCReferenceFrame call to prevent parsing
            // of encrypted data.
            if (mIsEncryptedObservedEarlier) {
   
            	// 处理不当,理论上此值不应该为true,返回无效错误码
                ALOGE("fetchInputData: mismatched mIsEncrypted/mIsEncryptedObservedEarlier (0/1)");

                return INVALID_OPERATION;
            }

            // 获取当前获取到的输入Buffer的元数据中的视频时域层级
            
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值