【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 4】【04】
作者:互联网
承接上一章节分析:【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 4】【03】
本系列文章分析的安卓源码版本:【Android 10.0 版本】
【此章节小节编号就接着上一章节排列】
8.3、mCodec->changeState(mCodec->mLoadedToIdleState)实现分析:
扭转状态机状态为IDLE状态机实现者,根据最开始状态机实现分析可知,将会执行该状态实现的进入方法
备注:该流程和前面的8.2流程将会在8.2流程进入到具体组件后同步执行,因为8.2中命令到达组件后组件内部是异步线程回调通知的。
// [frameworks/av/media/libstagefright/ACodec.cpp]
void ACodec::LoadedToIdleState::stateEntered() {
ALOGV("[%s] Now Loaded->Idle", mCodec->mComponentName.c_str());
status_t err;
// 分配(输入输出)缓冲区buffer
// 见下面分析
if ((err = allocateBuffers()) != OK) {
// 分配失败时
ALOGE("Failed to allocate buffers after transitioning to IDLE state "
"(error 0x%08x)",
err);
// 通知MediaCodec回调监听callback该错误发生通知
mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
// 然后必须将底层状态扭转为上一个组件已加载状态,让底层组件做关于本次失败的清除工作
mCodec->mOMXNode->sendCommand(
OMX_CommandStateSet, OMX_StateLoaded);
// 该方法名非常明确的告诉我们了它的功能:
// 检查你(底层组件)的所有缓冲区buffer是否属于我们(上层ACodec),
// 为true则表示属于上层分配的,此时应该由上层发起【freeBuffersOnPort】释放buffer端口的缓冲区buffer
// 备注:关于它们的实现处理将会在后续有时间分析的reset和flush流程中分析到,目前只提一下实现原理 TODO
if (mCodec->allYourBuffersAreBelongToUs(kPortIndexInput)) {
mCodec->freeBuffersOnPort(kPortIndexInput);
}
if (mCodec->allYourBuffersAreBelongToUs(kPortIndexOutput)) {
mCodec->freeBuffersOnPort(kPortIndexOutput);
}
if (mCodec->allYourBuffersAreBelongToUs(kPortIndexInputExtradata)) {
mCodec->freeBuffersOnPort(kPortIndexInputExtradata);
}
if (mCodec->allYourBuffersAreBelongToUs(kPortIndexOutputExtradata)) {
mCodec->freeBuffersOnPort(kPortIndexOutputExtradata);
}
// 此处提醒一下,LoadedState这个状态时表示底层组件已加载状态,
// 但不是编解码器已工作状态,而这里是在分配缓冲区buffer失败的回退状态处理,也就是返回上一个状态
mCodec->changeState(mCodec->mLoadedState);
}
}
allocateBuffers()实现分析:
分配(所有可能的buffer端口)缓冲区buffer
// [frameworks/av/media/libstagefright/ACodec.cpp]
status_t ACodec::LoadedToIdleState::allocateBuffers() {
// 分配(输入端口)缓冲区buffer
// 见下面的分析
status_t err = mCodec->allocateBuffersOnPort(kPortIndexInput);
if (err != OK) {
return err;
}
// 分配(输出端口)缓冲区buffer
err = mCodec->allocateBuffersOnPort(kPortIndexOutput);
if (err != OK) {
return err;
}
// 根据上面的分析可知,该方法在处理这两个输入输出额外数据端口索引buffer时,实际上为空实现
err = mCodec->allocateBuffersOnPort(kPortIndexInputExtradata);
err = mCodec->allocateBuffersOnPort(kPortIndexOutputExtradata);
if (err != OK) {
err = OK; // Ignore Extradata buffer allocation failure
}
// 执行MediaCodec的回调监听类的该方法,通知ACodec及其底层组件启动完成事件
// 见上面流程之后的8.3.7小节分析
mCodec->mCallback->onStartCompleted();
return OK;
}
mCodec->allocateBuffersOnPort(kPortIndexInput)实现分析:
分配(输入输出端口)缓冲区buffer
// [frameworks/av/media/libstagefright/ACodec.cpp]
status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) {
// 对于这两种额外数据buffer端口索引直接return
if (portIndex == kPortIndexInputExtradata ||
portIndex == kPortIndexOutputExtradata) {
return OK;
}
// 只处理分配这两种输入输出端口buffer
CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
// 需要被分配缓冲区buffer的端口索引对于的这两个buffer缓冲区必须此次前为空
CHECK(mAllocator[portIndex] == NULL);
CHECK(mBuffers[portIndex].isEmpty());
status_t err;
if (mNativeWindow != NULL && portIndex == kPortIndexOutput) {
// Surface不为空并且端口索引为输出索引时
// 判断是否应该将元数据存储在解码的缓冲区中
// 见8.3.1小节分析
if (storingMetadataInDecodedBuffers()) {
// 分配输出元数据缓冲区buffer,将元数据存储在该(解码的)缓冲区中
// 见8.3.2小节分析
err = allocateOutputMetadataBuffers();
} else {
// 否的话
// 从Surface中分配输出缓冲区Buffer
// 见8.3.3小节分析
err = allocateOutputBuffersFromNativeWindow();
}
} else {
// Surface为空或非输出端口时,如输入端口时
// 创建初始化OMX该参数结构对象
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
def.nPortIndex = portIndex;
// 获取底层组件该索引参数对象配置信息
err = mOMXNode->getParameter(
OMX_IndexParamPortDefinition, &def, sizeof(def));
if (err == OK) {
// 获取成功时
// 对应端口索引的端口Buffer模式类型
const IOMX::PortMode &mode = mPortMode[portIndex];
// 获取底层每个buffer大小(单位字节),但下面可能会被重新赋值
size_t bufSize = def.nBufferSize;
// 若是有ANWBuffer则总是分配VideoNativeMetadata元数据Buffer
// Always allocate VideoNativeMetadata if using ANWBuffer.
// OMX might use gralloc source internally, but we don't share
// metadata buffer with OMX, OMX has its own headers.
// 根据不同端口模式,计算元数据Buffer结构占用内存大小
if (mode == IOMX::kPortModeDynamicANWBuffer) {
bufSize = sizeof(VideoNativeMetadata);
} else if (mode == IOMX::kPortModeDynamicNativeHandle) {
bufSize = sizeof(VideoNativeHandleMetadata);
}
// Buffer转换大小
size_t conversionBufferSize = 0;
// 数据转换器此前有分析阐述过,不再分析,理想状态是不需要数据转换器(也就是数据存储单元位bit数相同时)
sp<DataConverter> converter = mConverter[portIndex];
if (converter != NULL) {
// here we assume sane conversions of max 4:1, so result fits in int32
// 这里我们假设最大4:1的正常转换,因此结果适合于int32
// 根据不同端口类型计算Buffer转换需要的大小
// 备注:其实它这个数据转换器实现也比较有意思的,可自行查看
if (portIndex == kPortIndexInput) {
conversionBufferSize = converter->sourceSize(bufSize);
} else {
conversionBufferSize = converter->targetSize(bufSize);
}
}
// 字节对齐为32倍数
size_t alignment = 32; // This is the value currently returned by
// MemoryDealer::getAllocationAlignment().
// TODO: Fix this when Treble has
// MemoryHeap/MemoryDealer.
// 打印将要分配的缓冲区实际buffer个数、每个buffer大小
ALOGV("[%s] Allocating %u buffers of size %zu (from %u using %s) on %s port",
mComponentName.c_str(),
def.nBufferCountActual, bufSize, def.nBufferSize, asString(mode),
portIndex == kPortIndexInput ? "input" : "output");
// 验证buffer大小避免为0或溢出,不能大于编解码器最大允许Buffer大小
// kMaxCodecBufferSize = 8192 * 4096 * 4, // 8K RGBA
// verify buffer sizes to avoid overflow in align()
if (bufSize == 0 || max(bufSize, conversionBufferSize) > kMaxCodecBufferSize) {
ALOGE("b/22885421");
return NO_MEMORY;
}
// don't modify bufSize as OMX may not expect it to increase after negotiation
// 不要修改bufSize,因为OMX可能不会期望它在协商后增加
// 字节对齐为32倍数,得到最终对齐字节数
// align方法实现分析,见8.3.4小节分析
size_t alignedSize = align(bufSize, alignment);
size_t alignedConvSize = align(conversionBufferSize, alignment);
// 插件缓冲区所有Buffer字节总和不能大于最大SIZE_MAX
if (def.nBufferCountActual > SIZE_MAX / (alignedSize + alignedConvSize)) {
ALOGE("b/22885421");
return NO_MEMORY;
}
if (mode != IOMX::kPortModePresetSecureBuffer) {
// 非加密Buffer时
// 获取系统共享内存分配器“ashmem”服务
// 备注:关于它的实现暂不分析,只需要知道是分配共享内存即可。
// 匿名共享内存(ashmem),为进程间提供大块共享内存,同时为内核提供回收和管理这个内存的机制。
mAllocator[portIndex] = TAllocator::getService("ashmem");
if (mAllocator[portIndex] == nullptr) {
ALOGE("hidl allocator on port %d is null",
(int)portIndex);
return NO_MEMORY;
}
// TODO: When Treble has MemoryHeap/MemoryDealer, we should
// specify the heap size to be
// def.nBufferCountActual * (alignedSize + alignedConvSize).
}
// 获取对应端口Buffer格式
const sp<AMessage> &format =
portIndex == kPortIndexInput ? mInputFormat : mOutputFormat;
for (OMX_U32 i = 0; i < def.nBufferCountActual && err == OK; ++i) {
hidl_memory hidlMemToken;
sp<TMemory> hidlMem;
sp<IMemory> mem;
// 创建Buffer信息结构对象
BufferInfo info;
// 标记Buffer所有权
info.mStatus = BufferInfo::OWNED_BY_US;
info.mFenceFd = -1;
info.mRenderInfo = NULL;
info.mGraphicBuffer = NULL;
info.mNewGraphicBuffer = false;
if (mode == IOMX::kPortModePresetSecureBuffer) {
// 加密Buffer时,此流程暂不分析,通常非该类型
void *ptr = NULL;
sp<NativeHandle> native_handle;
err = mOMXNode->allocateSecureBuffer(
portIndex, bufSize, &info.mBufferID,
&ptr, &native_handle);
info.mData = (native_handle == NULL)
? new SecureBuffer(format, ptr, bufSize)
: new SecureBuffer(format, native_handle, bufSize);
info.mCodecData = info.mData;
} else {
// 非加密Buffer时
// 是否分配内存成功
bool success;
// 分配该Buffer端口指定大小bufSize的匿名共享内存块hidl_memory
// 该方法传入的是匿名回调方法,早前相关流程有分析过,该方法是HIDL实现的
auto transStatus = mAllocator[portIndex]->allocate(
bufSize,
[&success, &hidlMemToken](
bool s,
hidl_memory const& m) {
// 回调分配内存结果
success = s;
hidlMemToken = m;
});
if (!transStatus.isOk()) {
ALOGE("hidl's AshmemAllocator failed at the "
"transport: %s",
transStatus.description().c_str());
return NO_MEMORY;
}
if (!success) {
return NO_MEMORY;
}
// (共享)内存映射
hidlMem = mapMemory(hidlMemToken);
if (hidlMem == nullptr) {
return NO_MEMORY;
}
// OMXNode使用该分配的Buffer,见前面分析
err = mOMXNode->useBuffer(
portIndex, hidlMemToken, &info.mBufferID);
if (mode == IOMX::kPortModeDynamicANWBuffer) {
// kPortModeDynamicANWBuffer端口模式时
// 直接强转其OMX分配的数据类型
VideoNativeMetadata* metaData = (VideoNativeMetadata*)(
(void*)hidlMem->getPointer());
metaData->nFenceFd = -1;
}
// 创建共享内存Buffer
// SharedMemoryBuffer是MediaCodecBuffer的子类,
// 其实际它只是将IMemory和TMemory这两种Buffer数据访问指针给了父类,
// 最终还是使用ABuffer类管理内存数据
info.mCodecData = new SharedMemoryBuffer(
format, hidlMem);
// 可以看出此字段缓存该共享内存对象引用
info.mCodecRef = hidlMem;
// if we require conversion, allocate conversion buffer for client use;
// otherwise, reuse codec buffer
// 根据注释和前面对于数据转换器的分析可知:
// 若不需要转换器时则Client端Buffer直接使用Codec端共享内存Buffer,
// 需要则将会重新分配新的匿名共享内存给Client端缓冲区使用
if (mConverter[portIndex] != NULL) {
// 需要数据转换器时
CHECK_GT(conversionBufferSize, (size_t)0);
bool success;
mAllocator[portIndex]->allocate(
conversionBufferSize,
[&success, &hidlMemToken](
bool s,
hidl_memory const& m) {
success = s;
hidlMemToken = m;
});
if (!success) {
return NO_MEMORY;
}
hidlMem = mapMemory(hidlMemToken);
if (hidlMem == nullptr) {
return NO_MEMORY;
}
info.mData = new SharedMemoryBuffer(format, hidlMem);
info.mMemRef = hidlMem;
} else {
// 不需要转换器时
info.mData = info.mCodecData;
info.mMemRef = info.mCodecRef;
}
}
// 添加Buffer到输入端口缓冲区队列中
mBuffers[portIndex].push(info);
}
}
}
if (err != OK) {
return err;
}
// 创建对应Buffer端口【BufferAndId】列表数据,用于缓存Buffer和Buffer ID的映射
// ACodecBufferChannel::BufferAndId就定义了两个字段:sp<MediaCodecBuffer> mBuffer; 和 IOMX::buffer_id mBufferId;
std::vector<ACodecBufferChannel::BufferAndId> array(mBuffers[portIndex].size());
for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) {
// 初始化该列表,使用匿名创建item结构对象
array[i] = {mBuffers[portIndex][i].mData, mBuffers[portIndex][i].mBufferID};
}
// 根据端口类型,执行此前初始化流程中创建的编解码器缓冲区Buffer通道mBufferChannel
// 注意:ACodecBufferChannel该类非常重要,它就是通知MediaCodec填充输入数据或消耗输出数据事件
if (portIndex == kPortIndexInput) {
// 设置输入Buffer列表
// 见8.3.5小节分析
mBufferChannel->setInputBufferArray(array);
} else if (portIndex == kPortIndexOutput) {
// 设置输出Buffer列表
// 见8.3.6小节分析
mBufferChannel->setOutputBufferArray(array);
} else {
TRESPASS();
}
return OK;
}
8.3.1、storingMetadataInDecodedBuffers()实现分析:
Surface不为空并且端口索引为输出索引时,判断是否应该将元数据存储在解码的缓冲区中
// [frameworks/av/media/libstagefright/ACodec.cpp]
inline bool storingMetadataInDecodedBuffers() {
// 该方法为ACodec的内联函数实现
// 通过判断输出buffer端口模式是否为动态AndroidNativeWindow数据类型,并且为解码器时,则为true,否则为false
// 备注:由前面分析举例SoftAVCDec组件时,获取到的该输出端口模式为【IOMX::kPortModePresetByteBuffer】,因此此时返回false。
// 另外硬解码器时该返回值通常为true,也就是说硬解码器使用的缓冲区数据类型支持为【kPortModeDynamicANWBuffer】。
return (mPortMode[kPortIndexOutput] == IOMX::kPortModeDynamicANWBuffer) && !mIsEncoder;
}
8.3.2、allocateOutputMetadataBuffers()实现分析:
分配输出元数据缓冲区buffer,将元数据存储在该(解码的)缓冲区中
// [frameworks/av/media/libstagefright/ACodec.cpp]
status_t ACodec::allocateOutputMetadataBuffers() {
CHECK(storingMetadataInDecodedBuffers());
OMX_U32 bufferCount, bufferSize, minUndequeuedBuffers;
// 从Surface中获取配置的输出缓冲区buffer个数、(每个)buffer大小、最小未出队列缓冲区buffer个数
// 见8.3.2.1小节分析
status_t err = configureOutputBuffersFromNativeWindow(
&bufferCount, &bufferSize, &minUndequeuedBuffers,
mFlags & kFlagIsSecure /* preregister */);
if (err != OK)
return err;
// 全局缓存新的Surface的最小未出队缓冲区个数
mNumUndequeuedBuffers = minUndequeuedBuffers;
ALOGV("[%s] Allocating %u meta buffers on output port",
mComponentName.c_str(), bufferCount);
// 循环创建缓冲区buffer
for (OMX_U32 i = 0; i < bufferCount; i++) {
// buffer信息结构对象,并初始化,该结构非常重要
// 见8.3.2.2小节分析
BufferInfo info;
// 标记当前buffer数据状态为Surface拥有权
info.mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
info.mFenceFd = -1;
info.mRenderInfo = NULL;
info.mGraphicBuffer = NULL;
info.mNewGraphicBuffer = false;
// 出队列buffer计数值
info.mDequeuedAt = mDequeueCounter;
// 创建一个bufferSize大小的Client客户端编解码buffer存储对象,它将内部缓存输出格式信息,并代理ABuffer的功能
info.mData = new MediaCodecBuffer(mOutputFormat, new ABuffer(bufferSize));
// base()方法最终指向ABuffer的该方法,其指向的是携带真实数据对象,将其转换为VideoNativeMetadata,
// 也就是客户端使用的buffer缓存数据为VideoNativeMetadata数据类型即视频native元数据结构。
// 该结构声明定义见8.3.2.3小节分析
// Initialize fence fd to -1 to avoid warning in freeBuffer().
((VideoNativeMetadata *)info.mData->base())->nFenceFd = -1;
// 创建时,将Codecs端的数据直接使用Client端的buffer数据,也就是先不考虑需要数据转换器
info.mCodecData = info.mData;
// 请求底层组件输出端口使用该Buffer,将会创建OMXNodeInstance支持的Buffer数据类型,然后创建新的Buffer ID并关联它。
// 备注:由此可以直到,该Buffer ID将ACodec中的Buffer数据和OMX底层的Buffer数据类型进行了映射绑定,
// 后续可以通过该ID来进行编解码数据传递工作。
// 见8.3.2.4小节分析
err = mOMXNode->useBuffer(kPortIndexOutput, OMXBuffer::sPreset, &info.mBufferID);
// 添加Buffer到输出端口缓冲区队列中
mBuffers[kPortIndexOutput].push(info);
ALOGV("[%s] allocated meta buffer with ID %u",
mComponentName.c_str(), info.mBufferID);
}
// 创建完所有需要的Buffer
// 计算当前需要提交的媒体元数据Buffer的个数
mMetadataBuffersToSubmit = bufferCount - minUndequeuedBuffers;
return err;
}
8.3.2.1、configureOutputBuffersFromNativeWindow(&bufferCount, &bufferSize, &minUndequeuedBuffers,mFlags & kFlagIsSecure /* preregister */)实现分:
从Surface中获取配置的输出缓冲区buffer个数、(每个)buffer大小、最小未出队列缓冲区buffer个数。
preregister参数通常为非安全编解码器时为false
// [frameworks/av/media/libstagefright/ACodec.cpp]
status_t ACodec::configureOutputBuffersFromNativeWindow(
OMX_U32 *bufferCount, OMX_U32 *bufferSize,
OMX_U32 *minUndequeuedBuffers, bool preregister) {
// 创建OMX该参数结构对象并初始化
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
def.nPortIndex = kPortIndexOutput;
// 最终将会获取底层组件该输出端口索引对应的端口配置信息
status_t err = mOMXNode->getParameter(
OMX_IndexParamPortDefinition, &def, sizeof(def));
if (err == OK) {
// 成功时,初始化Surface的窗口大小格式和Bit位使用类型
// 见下面分析
err = setupNativeWindowSizeFormatAndUsage(
mNativeWindow.get(), &mNativeWindowUsageBits,
preregister && !mTunneled /* reconnect */);
}
if (err != OK) {
// 失败
mNativeWindowUsageBits = 0;
return err;
}
// (重置)设置Surface的(图形buffer生产者)缓冲区buffer出队超时时间为-1
static_cast<Surface *>(mNativeWindow.get())->setDequeueTimeout(-1);
// Exits here for tunneled video playback codecs -- i.e. skips native window
// buffer allocation step as this is managed by the tunneled OMX omponent
// itself and explicitly sets def.nBufferCountActual to 0.
// 通常该值为false
if (mTunneled) {
// 隧道模式播放时,跳过native window的缓冲区分配。也就将底层组件配置buffer个数信息和参数返回全为0
ALOGV("Tunneled Playback: skipping native window buffer allocation.");
def.nBufferCountActual = 0;
err = mOMXNode->setParameter(
OMX_IndexParamPortDefinition, &def, sizeof(def));
*minUndequeuedBuffers = 0;
*bufferCount = 0;
*bufferSize = 0;
return err;
}
// 从Surface中查询最小未出队缓冲区个数
*minUndequeuedBuffers = 0;
err = mNativeWindow->query(
mNativeWindow.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
(int *)minUndequeuedBuffers);
if (err != 0) {
ALOGE("NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d)",
strerror(-err), -err);
return err;
}
// 下面的注释也比较重要,此处就不翻译了,自行阅读即可,主要说明一些问题存在
// 使用保守(合适大小)分配,同时尝试减少饥饿(数据不足)情况发生。
// 下面也就是处理实际应该(最少)分配的缓冲区个数计算和处理,计算最少buffer个数来使数据消费端能够正常工作
// FIXME: assume that surface is controlled by app (native window
// returns the number for the case when surface is not controlled by app)
// FIXME2: This means that minUndeqeueudBufs can be 1 larger than reported
// For now, try to allocate 1 more buffer, but don't fail if unsuccessful
// Use conservative allocation while also trying to reduce starvation
//
// 1. allocate at least nBufferCountMin + minUndequeuedBuffers - that is the
// minimum needed for the consumer to be able to work
// 2. try to allocate two (2) additional buffers to reduce starvation from
// the consumer
// plus an extra buffer to account for incorrect minUndequeuedBufs
// 额外buffer个数的 2 + 1 的说明见最后第2点英文说明
for (OMX_U32 extraBuffers = 2 + 1; /* condition inside loop */; extraBuffers--) {
// 计算得到合适大小缓冲区新的buffer个数:底层组件要求的最小个数 + Surface的最小未出队缓冲区个数 + 额外数据buffer个数
OMX_U32 newBufferCount =
def.nBufferCountMin + *minUndequeuedBuffers + extraBuffers;
// 赋值给OMX组件该缓冲区buffer实际个数字段
def.nBufferCountActual = newBufferCount;
// 然后设置该更新参数结构对象给底层具体组件去更新它
// 举例SoftAVCDec组件实现结果:
// 该结构对象其它参数未变化,因此只会更新该实际buffer个数值
err = mOMXNode->setParameter(
OMX_IndexParamPortDefinition, &def, sizeof(def));
if (err == OK) {
// 成功时,将Surface的最小未出队缓冲区个数 加上 额外数据buffer个数
*minUndequeuedBuffers += extraBuffers;
break;
}
// 失败时
ALOGW("[%s] setting nBufferCountActual to %u failed: %d",
mComponentName.c_str(), newBufferCount, err);
/* exit condition */
if (extraBuffers == 0) {
return err;
}
}
// Surface设置新的缓冲区实际buffer个数
err = native_window_set_buffer_count(
mNativeWindow.get(), def.nBufferCountActual);
if (err != 0) {
ALOGE("native_window_set_buffer_count failed: %s (%d)", strerror(-err),
-err);
return err;
}
// 成功时,返回缓冲区需要的实际个数及其每个buffer大小(单位字节)
*bufferCount = def.nBufferCountActual;
*bufferSize = def.nBufferSize;
return err;
}
setupNativeWindowSizeFormatAndUsage(mNativeWindow.get(), &mNativeWindowUsageBits,preregister && !mTunneled /* reconnect */)实现分析:
初始化Surface的窗口大小格式和Bit位使用类型(可以称之为 使用位【数】)
备注:Gralloc:表示(buffer)图形分配的意思
// [frameworks/av/media/libstagefright/ACodec.cpp]
status_t ACodec::setupNativeWindowSizeFormatAndUsage(
ANativeWindow *nativeWindow /* nonnull */, int *finalUsage /* nonnull */,
bool reconnect) {
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
def.nPortIndex = kPortIndexOutput;
// 最终将会获取底层组件该输出端口索引对应的端口配置信息
status_t err = mOMXNode->getParameter(
OMX_IndexParamPortDefinition, &def, sizeof(def));
if (err != OK) {
return err;
}
OMX_INDEXTYPE index;
// 获取支持该扩展属性对应索引
// 举例SoftAVCDec组件实现结果:将不支持该扩展类型buffer
err = mOMXNode->getExtensionIndex(
"OMX.google.android.index.AndroidNativeBufferConsumerUsage",
&index);
if (err != OK) {
// 允许底层组件失败
// allow failure
err = OK;
} else {
// 支持的话,则查询Surface中关于bit位数类型使用情况并获取,然后重新设置给底层组件来适配更新
// 备注:通常软编解码器很少支持的
int usageBits = 0;
if (nativeWindow->query(
nativeWindow,
NATIVE_WINDOW_CONSUMER_USAGE_BITS,
&usageBits) == OK) {
OMX_PARAM_U32TYPE params;
InitOMXParams(¶ms);
params.nPortIndex = kPortIndexOutput;
params.nU32 = (OMX_U32)usageBits;
err = mOMXNode->setParameter(index, ¶ms, sizeof(params));
if (err != OK) {
ALOGE("Fail to set AndroidNativeBufferConsumerUsage: %d", err);
return err;
}
}
}
OMX_U32 usage = 0;
// 从OMX IL接口层组件查询使用标志位
// 见下面分析
err = mOMXNode->getGraphicBufferUsage(kPortIndexOutput, &usage);
if (err != 0) {
// 从OMX IL接口层组件查询使用位标志失败
ALOGW("querying usage flags from OMX IL component failed: %d", err);
// XXX: Currently this error is logged, but not fatal.
usage = 0;
}
int omxUsage = usage;
// 判断端口buffer分配是否使用保护
if (mFlags & kFlagIsGrallocUsageProtected) {
usage |= GRALLOC_USAGE_PROTECTED;
}
// 添加视频buffer分配使用类型:硬件纹理、硬件合成、外部显示
// kVideoGrallocUsage = (GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_EXTERNAL_DISP)
usage |= kVideoGrallocUsage;
*finalUsage = usage;
// 设置最后的Surface裁剪尺寸(左上右下四个int参数)结构对象的内存均为0
memset(&mLastNativeWindowCrop, 0, sizeof(mLastNativeWindowCrop));
// 色彩空间模式存储类型
mLastNativeWindowDataSpace = HAL_DATASPACE_UNKNOWN;
ALOGV("gralloc usage: %#x(OMX) => %#x(ACodec)", omxUsage, usage);
// 设置Surface的窗口大小格式和Bit位使用类型(可以称之为 使用位【数】)
// 见下面分析
return setNativeWindowSizeFormatAndUsage(
nativeWindow,
def.format.video.nFrameWidth,
def.format.video.nFrameHeight,
def.format.video.eColorFormat,
mRotationDegrees,
usage,
reconnect);
}
mOMXNode->getGraphicBufferUsage(kPortIndexOutput, &usage)实现分析:
从OMX IL接口层组件查询输出buffer端口的使用标志位
// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp]
status_t OMXNodeInstance::getGraphicBufferUsage(
OMX_U32 portIndex, OMX_U32* usage) {
Mutex::Autolock autoLock(mLock);
if (mHandle == NULL) {
return DEAD_OBJECT;
}
OMX_INDEXTYPE index;
OMX_STRING name = const_cast<OMX_STRING>(
"OMX.google.android.index.getAndroidNativeBufferUsage");
// 获取底层组件对该参数属性支持的索引
// 举例SoftAVCDec组件实现结果:不支持
OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index);
if (err != OMX_ErrorNone) {
CLOG_ERROR(getExtensionIndex, err, "%s", name);
return StatusFromOMXError(err);
}
// 支持的话(通常硬件编解码器支持)
GetAndroidNativeBufferUsageParams params;
InitOMXParams(¶ms);
params.nPortIndex = portIndex;
// 获取底层关于该端口索引支持的数据配置信息
err = OMX_GetParameter(mHandle, index, ¶ms);
if (err != OMX_ErrorNone) {
CLOG_ERROR(getParameter, err, "%s(%#x): %s:%u", name, index,
portString(portIndex), portIndex);
return StatusFromOMXError(err);
}
// 并返回该配置信息中的使用位bit数
*usage = params.nUsage;
return OK;
}
setNativeWindowSizeFormatAndUsage(nativeWindow, def.format.video.nFrameWidth, def.format.video.nFrameHeight, def.format.video.eColorFormat, mRotationDegrees, usage, reconnect)实现分析:
设置Surface的窗口大小格式和Bit位使用类型(可以称之为 使用位【数】)
// [frameworks/av/media/libstagefright/SurfaceUtils.cpp]
status_t setNativeWindowSizeFormatAndUsage(
ANativeWindow *nativeWindow /* nonnull */,
int width, int height, int format, int rotation, int usage, bool reconnect) {
status_t err = NO_ERROR;
// 由英文注释reconnect才参数的作用:某些情况下需要重连Surface才能出队所有的输出buffer(已编码或已解码数据),通常为false
// In some cases we need to reconnect so that we can dequeue all buffers
if (reconnect) {
// 这两个断开和连接Surface处理见早前章节中已有分析,最终也会调用Surface自身的perform方法执行对应功能
// 备注:关于Surface相关的内容将会在后续Surface系列章节分析
err = nativeWindowDisconnect(nativeWindow, "setNativeWindowSizeFormatAndUsage");
if (err != NO_ERROR) {
ALOGE("nativeWindowDisconnect failed: %s (%d)", strerror(-err), -err);
return err;
}
err = nativeWindowConnect(nativeWindow, "setNativeWindowSizeFormatAndUsage");
if (err != NO_ERROR) {
ALOGE("nativeWindowConnect failed: %s (%d)", strerror(-err), -err);
return err;
}
}
// Surface设置缓冲区buffer大小(尺寸),最终也会调用Surface自身的perform方法执行对应功能
err = native_window_set_buffers_dimensions(nativeWindow, width, height);
if (err != NO_ERROR) {
ALOGE("native_window_set_buffers_dimensions failed: %s (%d)", strerror(-err), -err);
return err;
}
// format 该参数为视频色彩像素编码格式比如YUV420、RGB等
// Surface设置缓冲区buffer格式
// 最终也会调用Surface自身的perform方法执行对应功能
err = native_window_set_buffers_format(nativeWindow, format);
if (err != NO_ERROR) {
ALOGE("native_window_set_buffers_format failed: %s (%d)", strerror(-err), -err);
return err;
}
// 此处判断设置的原始视频旋转角度并转换为HAL支持的转换角度类型枚举
int transform = 0;
if ((rotation % 90) == 0) {
// 取余,判断角度是否为90的倍数,是的话进入
switch ((rotation / 90) & 3) {
// 计算是90的几倍
case 1: transform = HAL_TRANSFORM_ROT_90; break;
case 2: transform = HAL_TRANSFORM_ROT_180; break;
case 3: transform = HAL_TRANSFORM_ROT_270; break;
// 默认0
default: transform = 0; break;
}
}
// Surface设置缓冲区buffer转换角度类型
// 最终也会调用Surface自身的perform方法执行对应功能
err = native_window_set_buffers_transform(nativeWindow, transform);
if (err != NO_ERROR) {
ALOGE("native_window_set_buffers_transform failed: %s (%d)", strerror(-err), -err);
return err;
}
// 查询Surface的数据消费使用位,失败将忽略
int consumerUsage = 0;
err = nativeWindow->query(nativeWindow, NATIVE_WINDOW_CONSUMER_USAGE_BITS, &consumerUsage);
if (err != NO_ERROR) {
ALOGW("failed to get consumer usage bits. ignoring");
err = NO_ERROR;
}
// 注译:确保检查Stagefright或视频解码器是否要求了受保护的缓冲区buffer
// 通常为非保护buffer
// Make sure to check whether either Stagefright or the video decoder
// requested protected buffers.
if (usage & GRALLOC_USAGE_PROTECTED) {
// Check if the ANativeWindow sends images directly to SurfaceFlinger.
// 注译:检查是否Surface直接发送图像给SurfaceFlinger
int queuesToNativeWindow = 0;
err = nativeWindow->query(
nativeWindow, NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &queuesToNativeWindow);
if (err != NO_ERROR) {
ALOGE("error authenticating native window: %s (%d)", strerror(-err), -err);
return err;
}
// 检查ANativeWindow的消费者端是否能够处理受保护的内容
// Check if the consumer end of the ANativeWindow can handle protected content.
int isConsumerProtected = 0;
err = nativeWindow->query(
nativeWindow, NATIVE_WINDOW_CONSUMER_IS_PROTECTED, &isConsumerProtected);
if (err != NO_ERROR) {
ALOGE("error query native window: %s (%d)", strerror(-err), -err);
return err;
}
// 如果两个条件都不满足,则拒绝排队进入Surface,返回无权限
// Deny queuing into native window if neither condition is satisfied.
if (queuesToNativeWindow != 1 && isConsumerProtected != 1) {
ALOGE("native window cannot handle protected buffers: the consumer should either be "
"a hardware composer or support hardware protection");
return PERMISSION_DENIED;
}
}
// buffer分配使用位总和
int finalUsage = usage | consumerUsage;
ALOGV("gralloc usage: %#x(producer) + %#x(consumer) = %#x", usage, consumerUsage, finalUsage);
// Surface设置该使用位总和
// 最终也会调用Surface自身的perform方法执行对应功能
err = native_window_set_usage(nativeWindow, finalUsage);
if (err != NO_ERROR) {
ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err);
return err;
}
// Surface设置窗口缩放模式[NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW]
// 即缓冲区在两个维度(即宽高)上缩放以匹配窗口大小
err = native_window_set_scaling_mode(
nativeWindow, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
if (err != NO_ERROR) {
ALOGE("native_window_set_scaling_mode failed: %s (%d)", strerror(-err), -err);
return err;
}
ALOGD("set up nativeWindow %p for %dx%d, color %#x, rotation %d, usage %#x",
nativeWindow, width, height, format, rotation, finalUsage);
return NO_ERROR;
}
8.3.2.2、BufferInfo结构定义:
缓冲区buffer信息结构定义,该结构非常重要,它将是编解码器工作时传递数据的载体。
由于本章节接下来内容篇幅过长,因此必须放入另一章节分析,请查看:
TODO 【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 4】【05】
标签:return,Part,err,MediaPlayer,buffer,源码,缓冲区,OMX,def 来源: https://blog.csdn.net/u012430727/article/details/116803892