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

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

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

【此章节小节编号将重新排序】
已消耗该Buffer完成通知事件消息接收处理:
NuPlayerDecoder接收的【kWhatRenderBuffer】已渲染Buffer完成事件应答消息
备注:此时此处的已渲染Buffer指的是音频Buffer,而当该流程处理视频Buffer时视频帧却还未真正渲染,因此此流程就是处理视频帧渲染流程。
因此看此处处理逻辑时,建议先看完上面音频处理流程和后面【4.2】小节的视频消耗渲染处理流程后,再来统一看此处处理。

// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp]
void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) {
    ALOGV("[%s] onMessage: %s", mComponentName.c_str(), msg->debugString().c_str());

    switch (msg->what()) {
        case kWhatRenderBuffer:
        {
            if (!isStaleReply(msg)) {
            	// 非Buffer队列旧消息应答时
            	// 回调渲染Buffer处理流程
            	// 见下面分析
                onRenderBuffer(msg);
            }
            break;
        }
	}
}

isStaleReply(msg)实现:
判断是否为旧消息应答,true时为旧消息应答
其实现很简单,就是返回当前Buffer输出队列对应的代数值
备注:也就是每一次flush或者configure流程执行,都将更新该代数值,即该代数值标记着当前正在使用的输出队列

// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp]
bool NuPlayer::Decoder::isStaleReply(const sp<AMessage> &msg) {
    int32_t generation;
    CHECK(msg->findInt32("generation", &generation));
    // 代数值不同表示为旧消息应答
    return generation != mBufferGeneration;
}

onRenderBuffer(msg)实现分析:
回调渲染Buffer处理流程

// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp]
void NuPlayer::Decoder::onRenderBuffer(const sp<AMessage> &msg) {
    status_t err;
    int32_t render;
    size_t bufferIx;
    int32_t eos;
    size_t size;
    // 获取当前输出buffer index索引
    CHECK(msg->findSize("buffer-ix", &bufferIx));

    if (!mIsAudio) {
    	// 非音频解码器即为视频解码器时
        int64_t timeUs;
        // 获取输出队列中对应buffer 和 其PTS帧显示时间戳
        sp<MediaCodecBuffer> buffer = mOutputBuffers[bufferIx];
        buffer->meta()->findInt64("timeUs", &timeUs);

		// 字幕流解码器处理,判断显示合适字幕
		// 字幕解码器暂不分析
        if (mCCDecoder != NULL && mCCDecoder->isSelected()) {
            mCCDecoder->display(timeUs);
        }
    }

    if (mCodec == NULL) {
    	// Codec未初始化错误
        err = NO_INIT;
    } else if (msg->findInt32("render", &render) && render) {
    	// 获取该视频帧是否渲染标志位,当为1即true时需要渲染视频帧
        int64_t timestampNs;
        // 获取【该视频帧使用(系统)真实时间戳表示的PTS】,单位纳秒
        CHECK(msg->findInt64("timestampNs", &timestampNs));
        // 请求渲染该帧视频输出Buffer并释放它
        // 见第1小节分析
        err = mCodec->renderOutputBufferAndRelease(bufferIx, timestampNs);
    } else {
    	// 其它情况即音频或视频帧Buffer时
    	// 视频走此处表示该视频帧将不会被渲染
        if (!msg->findInt32("eos", &eos) || !eos ||
                !msg->findSize("size", &size) || size) {
            // 非eos 或 无"eos"字段 或 无"size"字段 或 buffer有负载大小时 进入
            // 被丢弃的视频帧总数目递增【只记录视频丢弃帧数】
    		// 备注:若为音频软解码器那么该值将不会增加,默认为0
            mNumOutputFramesDropped += !mIsAudio;
        }
        // 释放音频或视频帧输出Buffer
        // 见第2小节分析
        err = mCodec->releaseOutputBuffer(bufferIx);
    }
    if (err != OK) {
    	// 执行失败
        ALOGE("failed to release output buffer for [%s] (err=%d)",
                mComponentName.c_str(), err);
        // 通知该失败事件,见此前已有分析
        handleError(err);
    }
    if (msg->findInt32("eos", &eos) && eos
            && isDiscontinuityPending()) {
        // eos状态的buffer,并且当音频或视频输出格式发生改变时,进入
        // 完成处理非连续帧
        // 备注:目前暂不分析该流程 TODO
        // 简单说下,它可能将会flush并且通知NuPlayer【kWhatInputDiscontinuity】该事件去关闭解码器或重新扫描源
        finishHandleDiscontinuity(true /* flushOnTimeChange */);
    }
}

1、mCodec->renderOutputBufferAndRelease(bufferIx, timestampNs)实现分析:
请求渲染该帧视频输出Buffer并释放它

// [frameworks/av/media/libstagefright/MediaCodec.cpp]
status_t MediaCodec::renderOutputBufferAndRelease(size_t index, int64_t timestampNs) {
	// 发送【kWhatReleaseOutputBuffer】释放输出buffer事件消息,并携带三个参数
    sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, this);
    // 输出buffer索引
    msg->setSize("index", index);
    // 要求渲染该帧buffer
    msg->setInt32("render", true);
    // 渲染该输出buffer的系统真实时间戳
    msg->setInt64("timestampNs", timestampNs);

    sp<AMessage> response;
    // 同步发送并等待响应
    return PostAndAwaitResponse(msg, &response);
}

MediaCodec接收【kWhatReleaseOutputBuffer】释放输出buffer事件消息处理:
备注:由后面第2小节分析可知,该流程处理实际上是音视频共同处理流程,只是携带不同参数去区别处理。

// [frameworks/av/media/libstagefright/MediaCodec.cpp]
void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
    switch (msg->what()) {
        case kWhatReleaseOutputBuffer:
        {
            // 应答消息
            sp<AReplyToken> replyID;
            CHECK(msg->senderAwaitsResponse(&replyID));

            if (!isExecuting()) {
            	// MediaClock未执行状态中时,应答无效错误码
                PostReplyWithError(replyID, INVALID_OPERATION);
                break;
            } else if (mFlags & kFlagStickyError) {
            	// 粘性错误时 应答该错误
                PostReplyWithError(replyID, getStickyError());
                break;
            }

            // 回调执行释放输出buffer处理流程
            status_t err = onReleaseOutputBuffer(msg);

            // 应答请求端并返回错误码
            PostReplyWithError(replyID, err);
            break;
        }
	}
}

onReleaseOutputBuffer(msg)实现分析:
回调执行释放(音频或视频)输出buffer处理流程

// [frameworks/av/media/libstagefright/MediaCodec.cpp]
status_t MediaCodec::onReleaseOutputBuffer(const sp<AMessage> &msg) {
    size_t index;
    // 获取输出buffer索引
    CHECK(msg->findSize("index", &index));

    int32_t render;
    // 渲染标志位,未设置时为0即false,不渲染处理
    if (!msg->findInt32("render", &render)) {
        render = 0;
    }

    if (!isExecuting()) {
    	// MediaClock未执行状态中时,返回无效错误码
        return -EINVAL;
    }

    if (index >= mPortBuffers[kPortIndexOutput].size()) {
    	// 索引无效 越界错误码
        return -ERANGE;
    }

    // index对应的输出Buffer
    BufferInfo *info = &mPortBuffers[kPortIndexOutput][index];

    if (info->mData == nullptr || !info->mOwnedByClient) {
    	// 实际负载buffer为空 或 当前输出buffer拥有权不在Client端 时
    	// 返回访问权限错误码
        return -EACCES;
    }

    // 输出端口Buffer锁加锁代码块,同步边界
    // synchronization boundary for getBufferAndFormat
    sp<MediaCodecBuffer> buffer;
    {
        Mutex::Autolock al(mBufferLock);
        // 更改为false,即Client递交该Buffer给MediaCodec访问处理
        info->mOwnedByClient = false;
        // 实际负载(音频或视频)数据
        buffer = info->mData;
        // 清除原来队列中的Buffer携带的实际负载数据对象智能指针,并指针计数减1
        info->mData.clear();
    }

    if (render && buffer->size() != 0) {
    	// 需要渲染(视频帧) 并且 (视频帧)实际负载数据不为0时
        int64_t mediaTimeUs = -1;
        // 获取当前视频帧PTS媒体显示时间
        buffer->meta()->findInt64("timeUs", &mediaTimeUs);

        int64_t renderTimeNs = 0;
        // 获取【该视频帧使用(系统)真实时间戳表示的PTS】,单位纳秒
        if (!msg->findInt64("timestampNs", &renderTimeNs)) {
        	// 无该字段时
        	// 如果客户端没有请求特定的显示时间戳,则使用PTS媒体时间戳
            // use media timestamp if client did not request a specific render timestamp
            ALOGV("using buffer PTS of %lld", (long long)mediaTimeUs);
            renderTimeNs = mediaTimeUs * 1000;
        }

        if (mSoftRenderer != NULL) {
        	// 软渲染器存在时,进行视频帧渲染处理
        	// 由此前章节可知通常软视频解码器时将会创建它
        	// 备注:关于该软渲染器不展开分析,只阐述下它的功能原理作用,
            // 就是在软解码器时进行render渲染请求时,
            // 根据色彩空间类型对YUV等数据的进行重新处理转换为Surface接收的数据,然后显示,
            // 内部主要调用【mNativeWindow->dequeueBuffer】和【mNativeWindow->queueBuffer】来完成的。
            std::list<FrameRenderTracker::Info> doneFrames = mSoftRenderer->render(
                    buffer->data(), buffer->size(), mediaTimeUs, renderTimeNs,
                    mPortBuffers[kPortIndexOutput].size(), buffer->format());

            // 正在运行中时,通知已渲染帧,该通知消息默认未使用的,不关注
            // if we are running, notify rendered frames
            if (!doneFrames.empty() && mState == STARTED && mOnFrameRenderedNotification != NULL) {
                sp<AMessage> notify = mOnFrameRenderedNotification->dup();
                sp<AMessage> data = new AMessage;
                if (CreateFramesRenderedMessage(doneFrames, data)) {
                    notify->setMessage("data", data);
                    notify->post();
                }
            }
        }
        // Buffer通道对象执行渲染输出Buffer处理
        // 见1.1小节分析
        mBufferChannel->renderOutputBuffer(buffer, renderTimeNs);
    } else {
    	// 丢弃不需要渲染的(音频或视频)输出Buffer
    	// 见1.2小节分析
        mBufferChannel->discardBuffer(buffer);
    }

    return OK;
}

1.1、mBufferChannel->renderOutputBuffer(buffer, renderTimeNs)实现分析:
Buffer通道对象执行(已)渲染输出Buffer处理
备注:由上面的软渲染器实现,我们必须需要知道的是,软渲染器实际上已经渲染了该帧数据,也就是说此处1.1小节中的处理也会包括两种流程处理,即若未使用软渲染器渲染过,则会在1.1中进行判断符合条件后渲染视频帧,否则将只会release它。
该条件则为实际负载数据大小是否为0,若已渲染则其数据可读大小应该为0了才对。

在说明一下Surface的渲染流程简单的说即为:
从【mNativeWindow->dequeueBuffer】中获取一个生产者输入图形buffer,将此处视频输出帧数据放进去,然后通过【mNativeWindow->queueBuffer】将其图形buffer放入Surface中进行渲染。

// [frameworks/av/media/libstagefright/ACodecBufferChannel.cpp]
status_t ACodecBufferChannel::renderOutputBuffer(
        const sp<MediaCodecBuffer> &buffer, int64_t timestampNs) {
    // 输出Buffer队列    
    std::shared_ptr<const std::vector<const BufferInfo>> array(
            std::atomic_load(&mOutputBuffers));
    // 获取该buffer的item访问迭代器,见此前已有分析
    BufferInfoIterator it = findClientBuffer(array, buffer);
    if (it == array->end()) {
    	// 未查询到
        return -ENOENT;
    }

    ALOGV("renderOutputBuffer #%d", it->mBufferId);
    // 由前面分析可知,mOutputBufferDrained对应ACodec处理的【kWhatOutputBufferDrained】已渲染输出帧事件消息
    sp<AMessage> msg = mOutputBufferDrained->dup();
    // 设置这些参数
    msg->setObject("buffer", buffer);
    msg->setInt32("buffer-id", it->mBufferId);
    msg->setInt32("render", true);
    msg->setInt64("timestampNs", timestampNs);
    msg->post();
    return OK;
}

ACodec接收处理【kWhatOutputBufferDrained】事件消息:

// [frameworks/av/media/libstagefright/ACodec.cpp]
bool ACodec::BaseState::onMessageReceived(const sp<AMessage> &msg) {
    switch (msg->what()) {
        case kWhatOutputBufferDrained:
        {
            onOutputBufferDrained(msg);
            break;
        }
	}
}

onOutputBufferDrained(msg)实现分析:

// [frameworks/av/media/libstagefright/ACodec.cpp]
void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) {
    IOMX::buffer_id bufferID;
    // 获取输出Buffer id
    CHECK(msg->findInt32("buffer-id", (int32_t*)&bufferID));
    sp<RefBase> obj;
    // 获取实际负载数据Buffer 并强转
    CHECK(msg->findObject("buffer", &obj));
    sp<MediaCodecBuffer> buffer = static_cast<MediaCodecBuffer *>(obj.get());
    // 是否丢弃该帧 标志位
    int32_t discarded = 0;
    msg->findInt32("discarded", &discarded);

    ssize_t index;
    // 根据Buffer ID来查询对应Buffer,见此前已有分析
    BufferInfo *info = mCodec->findBufferByID(kPortIndexOutput, bufferID, &index);
    // 获取该输出Buffer的(拥有权)状态类型,见此前已有分析
    BufferInfo::Status status = BufferInfo::getSafeStatus(info);
    // 该状态表示输出流端拥有权。否则失败处理
    if (status != BufferInfo::OWNED_BY_DOWNSTREAM) {
    	// 拥有权状态错误时
        ALOGE("Wrong ownership in OBD: %s(%d) buffer #%u", _asString(status), status, bufferID);
        // 该方法其实际只是dump打印当前输入端口队列所有Buffer信息
        mCodec->dumpBuffers(kPortIndexOutput);
        // 通知MediaCodec该转移Buffer失败错误码,见此前已有分析
        mCodec->signalError(OMX_ErrorUndefined, FAILED_TRANSACTION);
        return;
    }

    int64_t timeUs = -1;
    // 获取该帧PTS
    buffer->meta()->findInt64("timeUs", &timeUs);
    // 判断是否跳过该帧,该方法在ACodec中是空实现,始终返回false。
    bool skip = mCodec->getDSModeHint(msg, timeUs);

    // 缓存实际负载数据
    info->mData = buffer;
    int32_t render;
    if (!skip && mCodec->mNativeWindow != NULL
            && msg->findInt32("render", &render) && render != 0
            && !discarded && buffer->size() != 0) {
        // 存在Surface 并且 渲染标识位为true 并且 非丢弃标识 并且 有效负载数据大小不为0 时
        // 执行渲染处理流程    
        ATRACE_NAME("render");
        // 客户端希望该Buffer被渲染显示
        // The client wants this buffer to be rendered.

        // 记录剪切尺寸:左上右下
        android_native_rect_t crop;
        if (buffer->format()->findRect("crop", &crop.left, &crop.top, &crop.right, &crop.bottom)) {
        	// 获取剪切尺寸:左上右下成功时
        	// NOTE: Surface使用扩展的右下坐标,因此将其都加1
            // NOTE: native window uses extended right-bottom coordinate
            ++crop.right;
            ++crop.bottom;
            // 比较当前全局缓存的最后一次Surface的图像剪切尺寸的字节内存是否一致
            if (memcmp(&crop, &mCodec->mLastNativeWindowCrop, sizeof(crop)) != 0) {
				// 不一样时,更新它
                mCodec->mLastNativeWindowCrop = crop;
                // 并设置给Surface更新
                status_t err = native_window_set_crop(mCodec->mNativeWindow.get(), &crop);
                ALOGW_IF(err != NO_ERROR, "failed to set crop: %d", err);
            }
        }

        int32_t dataSpace;
        // 色彩空间模式类型
        if (buffer->format()->findInt32("android._dataspace", &dataSpace)
                && dataSpace != mCodec->mLastNativeWindowDataSpace) {
            // 不相同时,设置给Surface更新   
            status_t err = native_window_set_buffers_data_space(
                    mCodec->mNativeWindow.get(), (android_dataspace)dataSpace);
            // 缓存更新
            mCodec->mLastNativeWindowDataSpace = dataSpace;
            ALOGW_IF(err != NO_ERROR, "failed to set dataspace: %d", err);
        }
        if (buffer->format()->contains("hdr-static-info")) {
        	// 包含HDR静态信息字段时
            HDRStaticInfo info;
            // 获取HDR静态信息,见此前已有分析
            if (ColorUtils::getHDRStaticInfoFromFormat(buffer->format(), &info)
                && memcmp(&mCodec->mLastHDRStaticInfo, &info, sizeof(info))) {
                // 获取成功并不一样时,更新它
                // 设置给Surface,见此前已有分析
                setNativeWindowHdrMetadata(mCodec->mNativeWindow.get(), &info);
                mCodec->mLastHDRStaticInfo = info;
            }
        }

        sp<ABuffer> hdr10PlusInfo;
        if (buffer->format()->findBuffer("hdr10-plus-info", &hdr10PlusInfo)
                && hdr10PlusInfo != nullptr && hdr10PlusInfo->size() > 0
                && hdr10PlusInfo != mCodec->mLastHdr10PlusBuffer) {
            // 获取HDR增强版本信息成功、有效且不一样时
            // 设置给Surface,见此前已有分析
            native_window_set_buffers_hdr10_plus_metadata(mCodec->mNativeWindow.get(),
                    hdr10PlusInfo->size(), hdr10PlusInfo->data());
            mCodec->mLastHdr10PlusBuffer = hdr10PlusInfo;
        }
		
		// 保存发送给Surface的Buffer,以便在返回时获得渲染时间
        // save buffers sent to the surface so we can get render time when they return
        int64_t mediaTimeUs = -1;
        // 视频帧PTS
        buffer->meta()->findInt64("timeUs", &mediaTimeUs);
        if (mediaTimeUs >= 0) {
        	// PTS有效时
        	// 缓存记录该帧信息数据到视频帧渲染追踪器对象缓冲队列中,暂不展开,自行分析
            mCodec->mRenderTracker.onFrameQueued(
                    mediaTimeUs, info->mGraphicBuffer, new Fence(::dup(info->mFenceFd)));
        }

        int64_t timestampNs = 0;
        // 获取【该视频帧使用(系统)真实时间戳表示的PTS】,单位纳秒
        if (!msg->findInt64("timestampNs", &timestampNs)) {
        	// 获取失败时
        	// 如果客户端没有请求特定的显示时间戳,则使用PTS媒体时间戳
            // use media timestamp if client did not request a specific render timestamp
            if (buffer->meta()->findInt64("timeUs", &timestampNs)) {
            	// 获取PTS媒体时间戳成功时
                ALOGV("using buffer PTS of %lld", (long long)timestampNs);
                timestampNs *= 1000;
            }
        }

        status_t err;
        // 设置当前Buffer真实待播放时间
        err = native_window_set_buffers_timestamp(mCodec->mNativeWindow.get(), timestampNs);
        ALOGW_IF(err != NO_ERROR, "failed to set buffer timestamp: %d", err);

        // debug 方法不关注
        info->checkReadFence("onOutputBufferDrained before queueBuffer");
        // 将该事件负责数据图形Buffer放回Surface的数据生产者队列中,
        // 也就是说【mGraphicBuffer】该buffer是此前流程输出缓冲区分配时从Surface的该数据生产者队列中获取来的,
        // 因此填充完真正的视频帧实际负载数据后,将其交还给Surface去渲染该帧处理。
        err = mCodec->mNativeWindow->queueBuffer(
                    mCodec->mNativeWindow.get(), info->mGraphicBuffer.get(), info->mFenceFd);
        // 重置为-1            
        info->mFenceFd = -1;
        if (err == OK) {
        	// 成功递交输出Buffer给Surface时
        	// 设置该输出Buffer使用拥有权在Surface模块
            info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
        } else {
        	// 失败时
            ALOGE("queueBuffer failed in onOutputBufferDrained: %d", err);
            // 通知该错误给MediaCodec,并标记其Buffer状态为US
            mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
            info->mStatus = BufferInfo::OWNED_BY_US;
            // keeping read fence as write fence to avoid clobbering
            // false,保持读fence为写fence,以避免冲突
            info->mIsReadFence = false;
        }
    } else {
    	// 其他情况,不需要渲染时,此时可能为音频或视频Buffer
        if (mCodec->mNativeWindow != NULL && (discarded || buffer->size() != 0)) {
        	// Surface存在 并且 【标记为丢弃帧或buffer实际负载大小不为0】 时
        	// 将读fence移到写fence,以避免冲突
            // move read fence into write fence to avoid clobbering
            info->mIsReadFence = false;
            ATRACE_NAME("frame-drop");
        }
        // 标记其Buffer状态为US
        info->mStatus = BufferInfo::OWNED_BY_US;
    }

    // 获取队列端口模式,见此前分析可知,在正常工作状态ExecutingState中,值为【RESUBMIT_BUFFERS】
    PortMode mode = getPortMode(kPortIndexOutput);

    switch (mode) {
        case KEEP_BUFFERS:
        {// 根据注释可知,这种情况不合理,因此做了容错处理
            // XXX fishy, revisit!!! What about the FREE_BUFFERS case below?

            if (info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) {
            	// 若该Buffer已递交给Surface时
            	// 注:不能重新递交刚刚已渲染的Buffer,而是将空闲的Buffer出队列。
                // We cannot resubmit the buffer we just rendered, dequeue
                // the spare instead.

                // 重新从Surface的数据生产者队列中获取新的空闲Buffer,见此前已有分析
                info = mCodec->dequeueBufferFromNativeWindow();
            }
            break;
        }
		
		// 重递交Buffer模式
        case RESUBMIT_BUFFERS:
        {
            if (!mCodec->mPortEOS[kPortIndexOutput]) {
            	// 输出端口队列未EOS时
                if (info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) {
            		// 若该Buffer已递交给Surface时
            		// 注:不能重新递交刚刚已渲染的Buffer,而是将空闲的Buffer出队列。
                    // We cannot resubmit the buffer we just rendered, dequeue
                    // the spare instead.

                    // 重新从Surface的数据生产者队列中获取新的空闲Buffer,见此前已有分析
                    info = mCodec->dequeueBufferFromNativeWindow();
                }

                if (info != NULL) {
                	// info不为空时
                    ALOGV("[%s] calling fillBuffer %u",
                         mCodec->mComponentName.c_str(), info->mBufferID);
                    info->checkWriteFence("onOutputBufferDrained::RESUBMIT_BUFFERS");
                    // 再次递交新的待填充的输出Buffer给底层组件去填充编解码数据,见此前已有分析
                    // 备注:如此输出Buffer队列也完成了一个循环处理。
                    status_t err = mCodec->fillBuffer(info);
                    if (err != OK) {
                    	// 递交失败时,通知MediaCodec该事件
                        mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
                    }
                }
            }
            break;
        }

        case FREE_BUFFERS:
        {
        	// 释放该Buffer
        	// 见下面分析
            status_t err = mCodec->freeBuffer(kPortIndexOutput, index);
            if (err != OK) {
                // 失败时,通知MediaCodec该事件
                mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
            }
            break;
        }

        default:
            ALOGE("Invalid port mode: %d", mode);
            return;
    }
}

mCodec->freeBuffer(kPortIndexOutput, index)实现分析:
释放该Buffer

// [frameworks/av/media/libstagefright/ACodec.cpp]
status_t ACodec::freeBuffer(OMX_U32 portIndex, size_t i) {
	// 获取指定端口队列中指针索引的Buffer
    BufferInfo *info = &mBuffers[portIndex].editItemAt(i);
    status_t err = OK;

    // there should not be any fences in the metadata
    // 元数据中不应该有任何Fence信息
    if (mPortMode[portIndex] == IOMX::kPortModeDynamicANWBuffer && info->mCodecData != NULL
            && info->mCodecData->size() >= sizeof(VideoNativeMetadata)) {
        // 该元数据的fence有效时    
        int fenceFd = ((VideoNativeMetadata *)info->mCodecData->base())->nFenceFd;
        if (fenceFd >= 0) {
            ALOGW("unreleased fence (%d) in %s metadata buffer %zu",
                    fenceFd, portIndex == kPortIndexInput ? "input" : "output", i);
        }
    }

    // 判断当前Buffer使用拥有权状态
    switch (info->mStatus) {
        case BufferInfo::OWNED_BY_US:
        	// ACodec自身持有时
            if (portIndex == kPortIndexOutput && mNativeWindow != NULL) {
            	// 输出端口并且Surface不为空时
            	// 请求取消该Buffer,递还给Surface,见此前章节已有分析
                (void)cancelBufferToNativeWindow(info);
            }
            FALLTHROUGH_INTENDED;

        case BufferInfo::OWNED_BY_NATIVE_WINDOW:
        	// 若为Surface使用权时
        	// 备注:也就是说这时候的输出Buffer其实际已经交还给Surface了,
        	// 因此只需要请求底层组件对应端口队列释放其队列中该ID的buffer即可
        	// 请求底层组件释放在Buffer
        	// 见下面分析
            err = mOMXNode->freeBuffer(portIndex, info->mBufferID);
            break;

        default:
        	// 其它状态则为错误
            ALOGE("trying to free buffer not owned by us or ANW (%d)", info->mStatus);
            err = FAILED_TRANSACTION;
            break;
    }

    if (info->mFenceFd >= 0) {
    	// fence大于0时直接关闭该文件描述符
        ::close(info->mFenceFd);
    }

    if (portIndex == kPortIndexOutput) {
    	// 输出端口时
    	// 视频帧渲染追踪器将取消追踪该buffer帧信息
        mRenderTracker.untrackFrame(info->mRenderInfo, i);
        info->mRenderInfo = NULL;
    }

    // remove buffer even if mOMXNode->freeBuffer fails
    // 从对应端口队列中移除它,即使OMXNode释放buffer失败。
    mBuffers[portIndex].removeAt(i);
    return err;
}

mOMXNode->freeBuffer(portIndex, info->mBufferID)实现分析:
请求底层组件释放在Buffer

// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp]
status_t OMXNodeInstance::freeBuffer(
        OMX_U32 portIndex, IOMX::buffer_id buffer) {
    // 加锁访问    
    Mutex::Autolock autoLock(mLock);
    if (mHandle == NULL) {
        return DEAD_OBJECT;
    }

    CLOG_BUFFER(freeBuffer, "%s:%u %#x", portString(portIndex), portIndex, buffer);

    // 从正在使用buffer队列中移除该buffer
    // 见下面分析
    removeActiveBuffer(portIndex, buffer);

    // 从对应端口队列中,查询该buffer id对应的buffer,见此前亦有分析
    OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer, portIndex);
    if (header == NULL) {
        ALOGE("b/25884056");
        return BAD_VALUE;
    }
    // 私有数据对象强制转换
    BufferMeta *buffer_meta = static_cast<BufferMeta *>(header->pAppPrivate);

	// 注:在调用OMX_FreeBuffer之前,先让客户端Buffer失效。
	// 如果不是,客户端中挂起的事件可能会在释放后访问缓冲区队列。
    // Invalidate buffers in the client side first before calling OMX_FreeBuffer.
    // If not, pending events in the client side might access the buffers after free.
    // 使该buffer id无效
    // 见下面分析
    invalidateBufferID(buffer);

	// 宏定义调用,最终执行了实际组件的FreeBuffer对应方法去释放该header对应的Buffer
	// 暂不展开分析了
    OMX_ERRORTYPE err = OMX_FreeBuffer(mHandle, portIndex, header);
    CLOG_IF_ERROR(freeBuffer, err, "%s:%u %#x", portString(portIndex), portIndex, buffer);
	
	// 然后释放该buffer
    delete buffer_meta;
    buffer_meta = NULL;

    return StatusFromOMXError(err);
}

removeActiveBuffer(portIndex, buffer)实现分析:
从正在使用buffer队列中移除该buffer

// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp]
void OMXNodeInstance::removeActiveBuffer(
        OMX_U32 portIndex, IOMX::buffer_id id) {
    // 循环查询到对应buffer id的该buffer,然后从mActiveBuffers即正在使用buffer队列中移除该buffer
    for (size_t i = 0; i < mActiveBuffers.size(); ++i) {
        if (mActiveBuffers[i].mPortIndex == portIndex
                && mActiveBuffers[i].mID == id) {
            mActiveBuffers.removeItemsAt(i);

            if (portIndex == kPortIndexInputExtradata || portIndex == kPortIndexOutputExtradata) {
                // Allow extradata ports
            } else if (portIndex < NELEM(mNumPortBuffers)) {
            	// 该端口buffer个数减少
                --mNumPortBuffers[portIndex];
            }
            return;
        }
    }

     CLOGW("Attempt to remove an active buffer [%#x] we know nothing about...", id);
}

invalidateBufferID(buffer)实现分析:
使该buffer id无效

// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp]
void OMXNodeInstance::invalidateBufferID(IOMX::buffer_id buffer) {
    if (buffer == 0) {
        return;
    }
    // 加锁访问
    Mutex::Autolock autoLock(mBufferIDLock);
    // 获取该Buffer ID在其队列中的索引
    ssize_t index = mBufferIDToBufferHeader.indexOfKey(buffer);
    if (index < 0) {
    	// 已无效则不处理
        CLOGW("invalidateBufferID: buffer %u not found", buffer);
        return;
    }
    // 从这两个队列中移除该索引对应的item映射数据,也就完成了该任务
    mBufferHeaderToBufferID.removeItem(mBufferIDToBufferHeader.valueAt(index));
    mBufferIDToBufferHeader.removeItemsAt(index);
}

1.2、mBufferChannel->discardBuffer(buffer)实现分析:
丢弃不需要渲染的(音频或视频)输出Buffer
备注:此方法实现分析见在此前【Part 7】【03】部分中已有分析,此处直接定位其输出Buffer的【mOutputBufferDrained】事件消息处理分析。而该事件也和上面1.1小节中最终也是相同的事件消息处理流程,通过携带的数据参数不同来区别,因此见1.1小节中分析。

2、mCodec->releaseOutputBuffer(bufferIx)实现分析:
释放音频或视频帧输出Buffer
可以看到它其实和第1小节中的处理是同一个事件处理的,只是携带不同参数去区别处理。

// [frameworks/av/media/libstagefright/MediaCodec.cpp]
status_t MediaCodec::releaseOutputBuffer(size_t index) {
    sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, this);
    msg->setSize("index", index);

    sp<AMessage> response;
    return PostAndAwaitResponse(msg, &response);
}

本章结束

整个系列文章结束语:
【Android MediaPlayer整体架构源码分析】到此处基本完成了它的整体架构源码实现分析,在此过程中,这是一次完成系统性的工作任务总结和技术积累,也学习到了更多关于系统层Binder新技术HIDL、功能架构、模块架构、C及C++语言特性多种使用方式、媒体框架架构及其工作原理和细节处理,以及还有多各种数据算法处理等非常棒的知识技术、架构设计、设计模式,另外也增强了对于实际音视频数据的理论知识点进行的处理技术,扩展增加了技术能力范围及其程序设计思维和解决问题能力。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值