承接上一章节分析:【六】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,