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

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

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

【此章节小节编号将重新排序】
onInputBufferFetched(msg)实现分析:
输入Buffer已获取完成处理流程,参数为获取到的携带输入Buffer的消息对象

// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp]
bool NuPlayer::Decoder::onInputBufferFetched(const sp<AMessage> &msg) {
   
    if (mCodec == NULL) {
   
    	// 未初始化错误码
        ALOGE("[%s] onInputBufferFetched without a valid codec", mComponentName.c_str());
        handleError(NO_INIT);
        return false;
    }

    size_t bufferIx;
    // 已获取到的输入Buffer索引
    CHECK(msg->findSize("buffer-ix", &bufferIx));
    // 该索引必须小于当前输入缓冲区个数
    CHECK_LT(bufferIx, mInputBuffers.size());
    // 获取该索引在输入端口队列中对应的Buffer
    sp<MediaCodecBuffer> codecBuffer = mInputBuffers[bufferIx];

    sp<ABuffer> buffer;
    // 提取已获取到的实际数据Buffer对象指针,若获取成功则表示有Buffer
    bool hasBuffer = msg->findBuffer("buffer", &buffer);
    // 判断是否需要拷贝实际数据Buffer,默认为true
    bool needsCopy = true;

    if (buffer == NULL /* includes !hasBuffer */) {
   
    	// 没有实际数据buffer时
    	// 流错误码,默认为EOS
        int32_t streamErr = ERROR_END_OF_STREAM;
        // 获取错误码
        CHECK(msg->findInt32("err", &streamErr) || !hasBuffer);

        // 错误码必须不能为OK
        CHECK(streamErr != OK);

        // attempt to queue EOS
        // 尝试入队列该Buffer到输入端口队列缓冲区,
        // 也就是递交当前已填充输入Buffer给Codec的输入端口队列
        // 见第1小节分析
        // 备注:这里递交的输入Buffer是无效数据,下面流程中才会递交有效已填充输入Buffer
        status_t err = mCodec->queueInputBuffer(
                bufferIx,
                0,
                0,
                0,
                MediaCodec::BUFFER_FLAG_EOS);
        if (err == OK) {
   
        	// 成功之后就将当前buffer index对应的该列表中item设置为false,
        	// 即标志当前index的输入Buffer未出队列状态了,也就是说该buffer已填充完成时修改该状态
            mInputBufferIsDequeued.editItemAt(bufferIx) = false;
        } else if (streamErr == ERROR_END_OF_STREAM) {
   
        	// 流错误码为EOS时,转换为递交Buffer返回错误码
            streamErr = err;
            // err will not be ERROR_END_OF_STREAM
        }

        if (streamErr != ERROR_END_OF_STREAM) {
   
        	// 非EOS时
            ALOGE("Stream error for [%s] (err=%d), EOS %s queued",
                    mComponentName.c_str(),
                    streamErr,
                    err == OK ? "successfully" : "unsuccessfully");
            // 通知NuPlayer【kWhatError】事件消息错误
            // 见早前章节中已有分析        
            handleError(streamErr);
        }
    } else {
   
    	// 有输入Buffer数据时
        sp<AMessage> extra;
        // 获取额外数据消息
        if (buffer->meta()->findMessage("extra", &extra) && extra != NULL) {
   
        	// 获取成功时
            int64_t resumeAtMediaTimeUs;
            // 获取本次恢复播放媒体时间点
            if (extra->findInt64(
                        "resume-at-mediaTimeUs", &resumeAtMediaTimeUs)) {
   
                // 获取该恢复播放媒体时间点时间戳成功时     
                ALOGV("[%s] suppressing rendering until %lld us",
                        mComponentName.c_str(), (long long)resumeAtMediaTimeUs);
                // 全局记录该值
                // 备注:此处处理也非常重要,跳过渲染直到帧数据到达此恢复播放媒体时间点时才会递交Buffer给解码进行解码播放
               	// 重要处理的是在上层APP指定seek模式为seek到指定媒体时间点(可能为非关键帧)时才能播放的情况处理。
                mSkipRenderingUntilMediaTimeUs = resumeAtMediaTimeUs;
            }
        }

        sp<ABuffer> hdr10PlusInfo;
        if (buffer->meta()->findBuffer("hdr10-plus-info", &hdr10PlusInfo) &&
                hdr10PlusInfo != NULL) {
   
           // 获取HDR增强信息成功时
           sp<AMessage> hdr10PlusMsg = new AMessage;
           hdr10PlusMsg->setBuffer("hdr10-plus-info", hdr10PlusInfo);
           // 重新设置该配置信息参数给底层组件
           // 见此前已有流程分析
           mCodec->setParameters(hdr10PlusMsg);
        }

        int64_t timeUs = 0;
        uint32_t flags = 0;
        // 获取该帧Buffer显示时间戳PTS
        CHECK(buffer->meta()->findInt64("timeUs", &timeUs));

        int32_t eos, csd;
        // we do not expect SYNCFRAME for decoder
        if (buffer->meta()->findInt32("eos", &eos) && eos) {
   
        	// eos状态Buffer时,添加Buffer EOS flag
            flags |= MediaCodec::BUFFER_FLAG_EOS;
        } else if (buffer->meta()->findInt32("csd", &csd) && csd) {
   
        	// 若为CSD负责数据Buffer时,标记当前Buffer flag为BUFFER_FLAG_CODECCONFIG
            flags |= MediaCodec::BUFFER_FLAG_CODECCONFIG;
        }

        // Modular DRM
        MediaBufferBase *mediaBuf = NULL;
        NuPlayerDrm::CryptoInfo *cryptInfo = NULL;

		// 从上面needsCopy标识默认为true可知,
		// ACodec递交Buffer时都默认需要拷贝负载数据给Codec支持的Buffer
        // copy into codec buffer
        if (needsCopy) {
   
            // 实际负载数据不能大于codecBuffer的容量值
            if (buffer->size() > codecBuffer->capacity()) {
   
            	// 否则通知Buffer太小错误码
                handleError(ERROR_BUFFER_TOO_SMALL);
                // 然后将当前Buffer index放入到已出队列输入Buffer中
                // 备注:也就是说让Decoder重新去填充输入Buffer
                mDequeuedInputBuffers.push_back(bufferIx);
                return false;
            }

            if (buffer->data() != NULL) {
   
            	// 实际负载数据Buffer不为空时
            	// 设置负载数据有效范围
                codecBuffer->setRange(0, buffer->size());
                // 拷贝数据
                memcpy(codecBuffer->data(), buffer->data(), buffer->size());
            } else {
    // No buffer->data()
            	// buffer负载数据为空时
            	// 此处处理的是DRM加密数据源情况,不关注它
            	
                //Modular DRM
                sp<RefBase> holder;
                if (buffer->meta()->findObject("mediaBufferHolder", &holder)) {
   
                    mediaBuf = (holder != nullptr) ?
                        static_cast<MediaBufferHolder*>(holder.get())->mediaBuffer() : nullptr;
                }
                if (mediaBuf != NULL) {
   
                    if (mediaBuf->size() > codecBuffer->capacity()) {
   
                        handleError(ERROR_BUFFER_TOO_SMALL);
                        mDequeuedInputBuffers.push_back(bufferIx);
                        return false;
                    }

                    codecBuffer->setRange(0,
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值