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

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

【此章节小节编号就接着上一章节排列】
dequeueBufferFromNativeWindow()实现分析:

// [frameworks/av/media/libstagefright/ACodec.cpp]
ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() {
    ANativeWindowBuffer *buf;
    CHECK(mNativeWindow.get() != NULL);

    if (mTunneled) {
        ALOGW("dequeueBufferFromNativeWindow() should not be called in tunnel"
              " video playback mode mode!");
        return NULL;
    }

    if (mFatalError) {
        ALOGW("not dequeuing from native window due to fatal error");
        return NULL;
    }

    int fenceFd = -1;
    // 注意:此处处理为双循环处理Surface缓冲区和ACodec自身输出缓冲区,并进行匹配关联
    do {
    	// 从Surface缓冲区中获取出队列Buffer和fenceFd描述符
        status_t err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf, &fenceFd);
        if (err != 0) {
            ALOGE("dequeueBuffer failed: %s(%d).", asString(err), err);
            return NULL;
        }

        // 是否为陈旧或损坏buffer
        bool stale = false;
        // 注意该循环处理是从输出队列末尾开始递交Buffer
        // 备注:在Surface存在时,此处递交的输出缓冲区分配是在【allocateOutputMetadataBuffers()】该流程中分配的。
        for (size_t i = mBuffers[kPortIndexOutput].size(); i > 0;) {
            i--;
        	// 当前输出缓冲区末尾Buffer
            BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i);

            // mGraphicBuffer值默认为null
            if (info->mGraphicBuffer != NULL &&
                    info->mGraphicBuffer->handle == buf->handle) {
                // mGraphicBuffer不为空且它们的数据访问句柄指针相同时
                
                // Since consumers can attach buffers to BufferQueues, it is possible
                // that a known yet stale buffer can return from a surface that we
                // once used.  We can simply ignore this as we have already dequeued
                // this buffer properly.  NOTE: this does not eliminate all cases,
                // e.g. it is possible that we have queued the valid buffer to the
                // NW, and a stale copy of the same buffer gets dequeued - which will
                // be treated as the valid buffer by ACodec.
                if (info->mStatus != BufferInfo::OWNED_BY_NATIVE_WINDOW) {
                	// Buffer使用权状态非NW类型时
                    ALOGI("dequeued stale buffer %p. discarding", buf);
                    // 丢弃出列的陈旧Buffer,标记为损坏,并跳出内循环,继续获取下一个Surface的Buffer
                    stale = true;
                    break;
                }
                // 获取到新的Surface Buffer
                
                ALOGV("dequeued buffer #%u with age %u, graphicBuffer %p",
                        (unsigned)(info - &mBuffers[kPortIndexOutput][0]),
                        mDequeueCounter - info->mDequeuedAt,
                        info->mGraphicBuffer->handle);

                // 标记为US
                info->mStatus = BufferInfo::OWNED_BY_US;
                // 该方法只是debug,不分析
                info->setWriteFence(fenceFd, "dequeueBufferFromNativeWindow");
                // 更新出队列缓冲区Buffer的渲染信息
                // 见下面分析
                updateRenderInfoForDequeuedBuffer(buf, fenceFd, info);
                // 直接返回匹配到的输出buffer
                return info;
            }
        }

        // 非元数据Buffer模式时,也有可能接收到一个以前未注册的Buffer,
        // 若是这种buffer则将它视为陈旧数据。同样可能在元数据模式时,
        // 在这种情况下会被视为常规Buffer,但它是不可取的。
        // It is also possible to receive a previously unregistered buffer
        // in non-meta mode. These should be treated as stale buffers. The
        // same is possible in meta mode, in which case, it will be treated
        // as a normal buffer, which is not desirable.
        // TODO: fix this.
        // 因此此处修复这种情况
        if (!stale && !storingMetadataInDecodedBuffers()) {
        	// 非陈旧buffer并且非元数据Buffer模式时
        	// 出队列未识别(未接受或陈旧)的Buffer,丢弃它
            ALOGI("dequeued unrecognized (stale) buffer %p. discarding", buf);
            stale = true;
        }
        if (stale) {
        	// 此处丢弃处理,只是重置为null
            // TODO: detach stale buffer, but there is no API yet to do it.
            buf = NULL;
        }
        // buf 获取失败时将循环获取
    } while (buf == NULL);
	
	// 获取最老旧的未出队列缓冲区Buffer
    // get oldest undequeued buffer
    BufferInfo *oldest = NULL;
    // 从输出端口队列末尾开始处理
    for (size_t i = mBuffers[kPortIndexOutput].size(); i > 0;) {
        i--;
        BufferInfo *info =
            &mBuffers[kPortIndexOutput].editItemAt(i);
        if (info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW &&
            (oldest == NULL ||
             // 避免潜在的问题,出队列计数值反向循环 
             // avoid potential issues from counter rolling over
             mDequeueCounter - info->mDequeuedAt >
                    mDequeueCounter - oldest->mDequeuedAt)) {
            // 找到一个老旧的未出队列缓冲区Buffer     
            oldest = info;
        }
    }

    // it is impossible dequeue a buffer when there are no buffers with ANW
    CHECK(oldest != NULL);
    // it is impossible to dequeue an unknown buffer in non-meta mode, as the
    // while loop above does not complete
    CHECK(storingMetadataInDecodedBuffers());

    // 丢弃LRU信息中的Buffer并替换为新Buffer
    // discard buffer in LRU info and replace with new buffer
    // from方法此前讲述过,就是指针强制转换
    oldest->mGraphicBuffer = GraphicBuffer::from(buf);
    // 此处标记为true,即为新GraphicBuffer
    oldest->mNewGraphicBuffer = true;
    // 标记使用权为US
    oldest->mStatus = BufferInfo::OWNED_BY_US;
    oldest->setWriteFence(fenceFd, "dequeueBufferFromNativeWindow for oldest");
    // 断开追踪该帧buffer,其实际就是从mRenderTracker的mRenderQueue渲染队列中移除能够匹配上的buffer。不展开分析
    mRenderTracker.untrackFrame(oldest->mRenderInfo);
    // 重置该buffer的已渲染帧信息为null
    oldest->mRenderInfo = NULL;

    // buffer替换
    ALOGV("replaced oldest buffer #%u with age %u, graphicBuffer %p",
            (unsigned)(oldest - &mBuffers[kPortIndexOutput][0]),
            mDequeueCounter - oldest->mDequeuedAt,
            oldest->mGraphicBuffer->handle);

    // 更新出队列缓冲区Buffer的渲染信息
    // 见已有分析 
    updateRenderInfoForDequeuedBuffer(buf, fenceFd, oldest);
    return oldest;
}

updateRenderInfoForDequeuedBuffer(buf, fenceFd, info)实现分析:
更新出队列缓冲区Buffer的渲染信息

// [frameworks/av/media/libstagefright/ACodec.cpp]
void ACodec::updateRenderInfoForDequeuedBuffer(
        ANativeWindowBuffer *buf, int fenceFd, BufferInfo *info) {
	
	// 前面有提到过,mRenderInfo为记录的已渲染帧信息,
	// mRenderTracker为管理mRenderInfo的帧渲染追踪器,用于跟踪它的状态等。
	// 调用updateInfoForDequeuedBuffer方法来更新出队列缓冲区Buffer的帧渲染信息。
	// 注意最后一个参数的计算结果为:获取到当前BufferInfo对象在队列中的索引
	// 见下面分析
    info->mRenderInfo =
        mRenderTracker.updateInfoForDequeuedBuffer(
                buf, fenceFd, info - &mBuffers[kPortIndexOutput][0]);

    // 检查所有fence信息,看有没有Fence已发出(同步点)信号
    // check for any fences already signaled
    // 已渲染帧队列的通知
    // 见下面分析
    notifyOfRenderedFrames(false /* dropIncomplete */, info->mRenderInfo);
}

mRenderTracker.updateInfoForDequeuedBuffer()实现分析:

// [frameworks/av/media/libstagefright/FrameRenderTracker.cpp]
FrameRenderTracker::Info *FrameRenderTracker::updateInfoForDequeuedBuffer(
        ANativeWindowBuffer *buf, int fenceFd, int index) {
    // 索引必须有效    
    if (index < 0) {
        return NULL;
    }

    // 判断是否为已渲染帧Buffer
    // see if this is a buffer that was to be rendered
    // mRenderQueue为渲染帧Buffer队列
    // renderInfo为mRenderQueue队列末尾数据下一个无效结尾buffer即通常为null
    std::list<Info>::iterator renderInfo = mRenderQueue.end();
    for (std::list<Info>::iterator it = mRenderQueue.begin();
            it != mRenderQueue.end(); ++it) {
        // 循环判断是否该数据访问句柄指针已存在    
        if (it->mGraphicBuffer->handle == buf->handle) {
        	// 存在即此为已渲染帧Buffer,缓存它
            renderInfo = it;
            break;
        }
    }
    if (renderInfo == mRenderQueue.end()) {
    	// 未匹配上已渲染帧时,可能是在Fence文件描述符发出信号后取消的,返回NULL
        // could have been canceled after fence has signaled
        return NULL;
    }

    if (renderInfo->mIndex >= 0) {
    	// 索引有效时,表示该Buffer在此前已出队列了,因此do nothing
        // buffer has been dequeued before, so there is nothing to do
        return NULL;
    }

    // 判断当前渲染帧是否被丢弃了,即fenceFd描述符小于0时表示被丢弃了
    // (我们也可以推断出小于0时fenceFd是无效或者是已入队列fence的副本,然后,我们也没有办法弄清楚)
    // was this frame dropped (we could also infer this if the fence is invalid or a dup of
    // the queued fence; however, there is no way to figure that out.)
    if (fenceFd < 0) {
    	// 当前buffer帧信息为新的或者已被丢弃了,因此从渲染帧队列中移除它
        // frame is new or was dropped
        mRenderQueue.erase(renderInfo);
        return NULL;
    }

    // 缓存出队列的fence和Buffer索引
    // store dequeue fence and buffer index
    renderInfo->mFence = new Fence(::dup(fenceFd));
    renderInfo->mIndex = index;
    // 返回该已渲染帧信息对象内存地址即指针
    return &*renderInfo;
}

notifyOfRenderedFrames(false /* dropIncomplete */, info->mRenderInfo)实现分析:
检查所有fence信息,看有没有Fence已发出(同步点)信号,已渲染帧队列的通知
dropIncomplete表示是否非完整丢弃,mRenderInfo可能为null

// [frameworks/av/media/libstagefright/ACodec.cpp]
void ACodec::notifyOfRenderedFrames(bool dropIncomplete, FrameRenderTracker::Info *until) {
	// 检查Fence信息并获取译渲染帧列表
	// 见下面分析
    std::list<FrameRenderTracker::Info> done =
        mRenderTracker.checkFencesAndGetRenderedFrames(until, dropIncomplete);

    // unlink untracked frames
    // 断开链接未追踪的帧
    // 也就是说上面放回的done列表帧数据为未追踪帧列表,其实也就是已渲染帧队列,不需要再追踪了
    for (std::list<FrameRenderTracker::Info>::const_iterator it = done.cbegin();
            it != done.cend(); ++it) {
        // 获取其缓存的在输出端口队列中的索引    
        ssize_t index = it->getIndex();
        if (index >= 0 && (size_t)index < mBuffers[kPortIndexOutput].size()) {
        	// 索引有效时,重置该索引对应输出端口Buffer缓存的已渲染帧信息
        	// 备注:也就是说每个输出Buffer可对应缓存一个已渲染帧信息
            mBuffers[kPortIndexOutput].editItemAt(index).mRenderInfo = NULL;
        } else if (index >= 0) {
        	// 这种情况不应该发生,也就是索引大于输出端口队列最大值
            // THIS SHOULD NEVER HAPPEN
            ALOGE("invalid index %zd in %zu", index, mBuffers[kPortIndexOutput].size());
        }
    }

    // 回调MediaCodec的监听回调类该方法
    // 通知已渲染帧的输出帧队列
    // 备注:该流程不再分析了,只需要指定NuPlayerDecoder实际没有接收该消息的。
    // 它最终会回调MediaCodec.setOnFrameRenderedNotification()该方法设置的AMessage监听回调通知消息类。 TODO
    mCallback->onOutputFramesRendered(done);
}

mRenderTracker.checkFencesAndGetRenderedFrames(until, dropIncomplete)实行分析:

// [frameworks/av/media/libstagefright/FrameRenderTracker.cpp]
std::list<FrameRenderTracker::Info> FrameRenderTracker::checkFencesAndGetRenderedFrames(
        const FrameRenderTracker::Info *until, bool dropIncomplete) {
    std::list<Info> done;

    // complete any frames queued prior to this and drop any incomplete ones if requested
    // 完成在此之前的任何帧,并在dropIncomplete为true请求时删除任何未完成的帧
    for (std::list<Info>::iterator it = mRenderQueue.begin();
            it != mRenderQueue.end(); ) {
        // 是否drop丢弃帧    
        bool drop = false; // whether to drop each frame
        if (it->mIndex < 0) {
        	// 由前面可知该字段表示,该Buffer在出队列时在输出端口队列中的索引
        	// 小于0则表示该帧数据还未出队列(或者已选择在一个“隧道”Surface上)
            // frame not yet dequeued (or already rendered on a tunneled surface)
            drop = dropIncomplete;
        } else if (it->mFence != NULL) {
        	// 检查该Buffer的fence同步点是否已发出(同步)信号
            // check if fence signaled
            nsecs_t signalTime = it->mFence->getSignalTime();
            if (signalTime < 0) { // invalid fence
            	// (同步点)信号时间小于0则表示无效同步点时间,因此必须drop丢弃该帧
                drop = true;
            } else if (signalTime == INT64_MAX) { // unsignaled fence
            	// 该同步信号时间有效但还未发出同步信号,即该帧Buffer目前状态为不完整或未完成的帧
                drop = dropIncomplete;
            } else { // signaled
            	// fence已发出同步信号时间点(同步点)
                // save render time
                // 清除Fence同步信息
                // 注意重要概念:此处的处理逻辑我们可以基本执行Fence信息的作用
                // 即当当前buffer的fence已发出同步信号时间点(同步点)【即渲染时间点】时,也就不再需要该fence信息了
                it->mFence.clear();
                // 缓存渲染时间戳
                it->mRenderTimeNs = signalTime;
            }
        }
        // Buffer指针比较,判断是否找到了该Buffer
        bool foundFrame = (Info *)&*it == until;

		// 注:返回在队列开始的有信号fence的帧,因为它们是按提交顺序的,
		// 我们不需要等待任何中间帧。也返回任何被丢弃的帧。
        // Return frames with signaled fences at the start of the queue, as they are
        // in submit order, and we don't have to wait for any in-between frames.
        // Also return any dropped frames.
        if (drop || (it->mFence == NULL && it == mRenderQueue.begin())) {
        	// 丢弃帧或渲染队列开始有信号帧【(同步点时间戳)即有渲染时间戳帧】时
        	// 仍然设置(未渲染的)被丢弃帧Buffer的渲染时间戳mRenderTimeNs为-1
            // (unrendered) dropped frames have their mRenderTimeNs still set to -1
        	// 备注:此处splice处理,将会在done列表buffer末尾添加当前Buffer(it),它的实现原理为
        	// 将元素从一个列表转移到另一个列表,没有复制或移动元素,只重新指向列表节点的内部指针。
            done.splice(done.end(), mRenderQueue, it++);
        } else {
            ++it;
        }
        if (foundFrame) {
        	// 匹配上该buffer帧时退出
            break;
        }
    }

    return done;
}

1.1.2.2、mCodec->signalSubmitOutputMetadataBufferIfEOS_workaround()实现分析:
若输入EOS时通知提交输入元数据缓冲区
英文注意已经说明了该流程处理的作用:一些编解码器可能在处理输入缓冲区之前返回它们。如果我们已经在输入端口上发出了EOS信号,这将导致Codec停止工作。现在,如果输入端口上有EOS,但输出端口上还没有,则继续提交一个输出缓冲区要求编解码器继续输出剩余的已编码或已解码数据,直到输出端也EOS才停止工作。

// [frameworks/av/media/libstagefright/ACodec.cpp]

// *** NOTE: THE FOLLOWING WORKAROUND WILL BE REMOVED ***
// Some codecs may return input buffers before having them processed.
// This causes a halt if we already signaled an EOS on the input
// port.  For now keep submitting an output buffer if there was an
// EOS on the input port, but not yet on the output port.
void ACodec::signalSubmitOutputMetadataBufferIfEOS_workaround() {
    if (mPortEOS[kPortIndexInput] && !mPortEOS[kPortIndexOutput] &&
            mMetadataBuffersToSubmit > 0) {
        // 输入端口EOS,输出端口未EOS,并且mMetadataBuffersToSubmit该值大于0时
        // 发送[kWhatSubmitOutputMetadataBufferIfEOS]事件消息处理
        (new AMessage(kWhatSubmitOutputMetadataBufferIfEOS, this))->post();
    }
}

ACodec接收[kWhatSubmitOutputMetadataBufferIfEOS]事件消息处理,最终会将该消息事件递交给当前ExecutingState实现者状态:
该处理其实可以不用关注,因为它将被移除。

// [frameworks/av/media/libstagefright/ACodec.cpp]
bool ACodec::ExecutingState::onMessageReceived(const sp<AMessage> &msg) {
    bool handled = false;

    switch (msg->what()) {
        // *** NOTE: THE FOLLOWING WORKAROUND WILL BE REMOVED ***
        case kWhatSubmitOutputMetadataBufferIfEOS:
        {
            if (mCodec->mPortEOS[kPortIndexInput] &&
                    !mCodec->mPortEOS[kPortIndexOutput]) {
                // 输入端口EOS,输出端口未EOS时,将会再次执行1.1.2.1小节的处理流程   
                status_t err = mCodec->submitOutputMetadataBuffer();
                if (err == OK) {
                	// 可以看到会重复执行该流程,直到输出端口也EOS或mMetadataBuffersToSubmit小于等于0时
                    mCodec->signalSubmitOutputMetadataBufferIfEOS_workaround();
                }
            }
            return true;
        }
    }
}

1.2、postFillThisBuffer(info)实现分析:
要求客户端去填充(待解码或待编码的原始音频或视频)数据

// [frameworks/av/media/libstagefright/ACodec.cpp]
void ACodec::BaseState::postFillThisBuffer(BufferInfo *info) {
	// 输入端口已EOS则不再处理后续输入buffer了
    if (mCodec->mPortEOS[kPortIndexInput]) {
        return;
    }

    // 检查该输入buffer当前使用权状态必须在我们自己这里
    CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US);

    // 设置输入格式消息对象给该输入端口Buffer的MediaCodecBuffer字段去缓存
    info->mData->setFormat(mCodec->mInputFormat);
    // 由前面分析可知,此处执行ACodec Buffer通道对象填充该输入Buffer数据请求处理,
    // mBufferChannel该对象变量创建在早前【MediaCodec::init】初始化过程介绍过的。
    // 见下面分析
    mCodec->mBufferChannel->fillThisBuffer(info->mBufferID);
    // 清除当前递交的Buffer,也就是说,当前Buffer递交给MediaCodec后,
    // ACodec中必须清除实际Buffer对象指针并会减少它的指针计数。
    info->mData.clear();
    // 标记该Buffer目前使用权为输入流端即数据源端
    info->mStatus = BufferInfo::OWNED_BY_UPSTREAM;
}

mCodec->mBufferChannel->fillThisBuffer(info->mBufferID)实现分析:
由前面分析可知,此处执行ACodec Buffer通道对象填充该输入Buffer数据请求处理,mBufferChannel该对象变量创建在早前【MediaCodec::init】初始化过程介绍过的。
它就是管理MediaCodec和ACodec之间输入输出Buffer数据传递的通道,该通道注册了【kWhatInputBufferFilled】和【kWhatOutputBufferDrained】消息来完成输入输出请求消息事件发送给ACodec来接收传递给MediaCodec,在该通道中MediaCodec通过设置BufferCallback监听类,该监听类注册AMessage监听事件消息【kWhatCodecNotify】来接收来自ACodec的数据输入输出请求。
这就是MediaCodec和ACodec数据传递工作原理。
参数为输入端口Buffer Id

// [frameworks/av/media/libstagefright/ACodecBufferChannel.cpp]
void ACodecBufferChannel::fillThisBuffer(IOMX::buffer_id bufferId) {
    ALOGV("fillThisBuffer #%d", bufferId);
    // 原子性操作输入端口队列mInputBuffers
    // 备注:需要记得该Buffer通道的输入输出端口队列在此前流程中分配的
    std::shared_ptr<const std::vector<const BufferInfo>> array(
            std::atomic_load(&mInputBuffers));
	// 查询Buffer id返回ID相等时的item位置访问迭代器
	// 见下面分析
    BufferInfoIterator it = findBufferId(array, bufferId);

    if (it == array->end()) {
    	// 未匹配到目标item
        ALOGE("fillThisBuffer: unrecognized buffer #%d", bufferId);
        return;
    }
    // 备注:通常都为相同Buffer的,不相同的情况是,在setInputBufferArray输入端口队列初始化时
    // 并结合BufferInfo构造函数分析可知,若输入为加密Buffer时才不相同,否则都相同。
    if (it->mClientBuffer != it->mCodecBuffer) {
    	// Client客户端Buffer和编解码Buffer非同一个Buffer时
    	// 设置mCodecBuffer的输入格式给客户端Buffer信息去缓存
        it->mClientBuffer->setFormat(it->mCodecBuffer->format());
    }

    // 由上面分析可知,该回调监听是在MediaCodec中
    // 见下面分析
    // 备注:std::distance(array->begin(), it)实现其实际就是获取到当前item对象[it]在队列array中的索引
    mCallback->onInputBufferAvailable(
            std::distance(array->begin(), it),
            it->mClientBuffer);
}

findBufferId(array, bufferId)实现分析:
返回值定义为:也就是返回list列表的符合匹配条件的Item访问Iterator迭代器,下面实现很简单,就是使用C++的std::find_if方法来查询列表数据中符合匹配条件【bufferId == info.mBufferId】的item返回访问它的迭代器对象。

// [frameworks/av/media/libstagefright/ACodecBufferChannel.cpp]
using BufferInfoIterator = std::vector<const BufferInfo>::const_iterator;

static BufferInfoIterator findBufferId(
        const std::shared_ptr<const std::vector<const BufferInfo>> &array,
        IOMX::buffer_id bufferId) {
    return std::find_if(
            array->begin(), array->end(),
            [bufferId](const BufferInfo &info) { return bufferId == info.mBufferId; });
}

mCallback->onInputBufferAvailable(std::distance(array->begin(), it), it->mClientBuffer)实现分析:
填充输入Buffer
参数为输入buffer及其在输入端口队列中索引

// [frameworks/av/media/libstagefright/MediaCodec.cpp]
void BufferCallback::onInputBufferAvailable(
        size_t index, const sp<MediaCodecBuffer> &buffer) {
    // mNotify为【kWhatCodecNotify】事件通知消息,MediaCodec接收处理
    sp<AMessage> notify(mNotify->dup());
    // 设置子事件类型【kWhatFillThisBuffer】(填充输入Buffer)
    notify->setInt32("what", kWhatFillThisBuffer);
    // 缓存输入Buffer及其索引
    notify->setSize("index", index);
    notify->setObject("buffer", buffer);
    notify->post();
}

MediaCodec接收处理【kWhatCodecNotify】事件通知消息子事件类型【kWhatFillThisBuffer】:

// [frameworks/av/media/libstagefright/MediaCodec.cpp]
void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
    switch (msg->what()) {
        case kWhatCodecNotify:
        {
            int32_t what;
            CHECK(msg->findInt32("what", &what));

            switch (what) {
            	// 填充输入Buffer事件
                case kWhatFillThisBuffer:
                {
                    // 更新填充输入Buffer
                    // 见1.2.1小节分析
                    /* size_t index = */updateBuffers(kPortIndexInput, msg);

                    // 检查状态
                    if (mState == FLUSHING
                            || mState == STOPPING
                            || mState == RELEASING) {
                        // 状态不对时
                        // 返回当前输入Buffer给Codec
                        // 见1.2.2小节分析
                        returnBuffersToCodecOnPort(kPortIndexInput);
                        break;
                    }

                    if (!mCSD.empty()) {
                    	// CSD列表数据不为空时,关于CSD数据见前面已有分析
                    	// 从输入端口队列中(获取)出队列Buffer索引
                    	// 见1.2.3小节分析
                        ssize_t index = dequeuePortBuffer(kPortIndexInput);
                        // 理论上应该为0即输入端口队列第一个buffer
                        CHECK_GE(index, 0);

                        // 注释:如果在调用confiure中指定了特定的编解码器数据作为格式的一部分,
                        // 并且如果还有更多csd剩余,我们在这里提交它,客户端只有在这些数据耗尽后才能访问输入缓冲区。
                        // If codec specific data had been specified as
                        // part of the format in the call to configure and
                        // if there's more csd left, we submit it here
                        // clients only get access to input buffers once
                        // this data has been exhausted.

                        // (入队列)递交CSD信息到输入端口Buffer
                    	// 见1.2.4小节分析
                        status_t err = queueCSDInputBuffer(index);

                        if (err != OK) {
                        	// 失败时
                            ALOGE("queueCSDInputBuffer failed w/ error %d",
                                  err);

                            // 设置mFlag添加标志位【kFlagStickyError】并记录mStickyError为err
                            setStickyError(err);
                            // 有必要的话发送活动通知事件消息给NuPlayerDecoder
                            // 暂不分析该流程,因为该流程一般情况下不使用的,主要是接收当前输入输出端口队列的大小参数。
                            postActivityNotificationIfPossible();

                            // 取消(将要)执行还未执行的(输入输出队列)出队列操作流程,见早前已有分析
                            cancelPendingDequeueOperations();
                        }
                        break;
                    }

                    if (mFlags & kFlagIsAsync) {
                    	// 根据前面MediaCodec配置过程可知NuPlayerDecoder配置时设置了mCallback回调监听,
                    	// 将会执行异步编解码流程,因此走此处
                    	// 并且mHaveInputSurface该值为false
                    	// 备注:mHaveInputSurface标志位作用:是否有输入Surface,注意它并不是表示MediaPlayer设置了Surface,
                    	// 而是MediaCodec接收来自持久性输入Surface时,该标识为true。
                        if (!mHaveInputSurface) {
                            if (mState == FLUSHED) {
                            	// 若当前状态为FLUSHED已清除状态时,设置该标志位为true
                                mHavePendingInputBuffers = true;
                            } else {
                            	// 否则正在工作状态时
                            	// 请求填充输入Buffer
                            	// 见1.2.5小节分析
                                onInputBufferAvailable();
                            }
                        }
                    } else if (mFlags & kFlagDequeueInputPending) {
                    	// 该流程暂不关注 TODO
                        CHECK(handleDequeueInputBuffer(mDequeueInputReplyID));

                        ++mDequeueInputTimeoutGeneration;
                        mFlags &= ~kFlagDequeueInputPending;
                        mDequeueInputReplyID = 0;
                    } else {
                    	// 该流程不关注
                        postActivityNotificationIfPossible();
                    }
                    break;
                }
                
            }
        }
    }
}

1.2.1、/* size_t index = */updateBuffers(kPortIndexInput, msg)实现分析:
更新填充(输入或输出)Buffer
由于本章节接下来内容篇幅过长,因此必须放入另一章节分析,请查看:
【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 7】【03】

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值