承接上一章节分析:【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 4】【04】
本系列文章分析的安卓源码版本:【Android 10.0 版本】
【此章节小节编号就接着上一章节排列】
8.3.2.2、BufferInfo结构定义:
缓冲区buffer信息结构定义,该结构非常重要,它将是编解码器工作时传递数据的载体。
ACodec内部定义
// [frameworks/av/media/libstagefright/include/media/stagefright/ACodec.h]
struct BufferInfo {
// 当前buffer对象的状态拥有者
enum Status {
// ACodec自身
OWNED_BY_US,
// 底层组件(编解码器)端
OWNED_BY_COMPONENT,
// 推流端
OWNED_BY_UPSTREAM,
// 拉流端
OWNED_BY_DOWNSTREAM,
// Surface端
OWNED_BY_NATIVE_WINDOW,
UNRECOGNIZED, // not a tracked buffer
};
// 一个快速安全判断当前buffer状态的内联方法
static inline Status getSafeStatus(BufferInfo *info) {
return info == NULL ? UNRECOGNIZED : info->mStatus;
}
// buffer id,非常重要的标志了缓冲区中每一个buffer的唯一性
// typedef uint32_t buffer_id;
IOMX::buffer_id mBufferID;
// buffer状态
Status mStatus;
// buffer出队列计数值,它是个随着buffer产生不断递增的计数值
unsigned mDequeuedAt;
// 编解码器buffer数据对象
// 备注:其实际只是个代理ABuffer类的代理类实现,
// 关于ABuffer类的源码实现分析请见早前相关章节分析,因此不再分析。
// 注意:此处的英文注释对我们也有理解上的帮助,
// 也就是该buffer是客户端(ACodec)管理使用的buffer数据类型,
// 如果没有使用数据转换器(此前分析过它),那么该Buffer将会是编解码器的buffer数据类型,
// 否则该buffer将会被分开(另外)分配内存并指向一个IMemory数据类型的引用。
sp<MediaCodecBuffer> mData; // the client's buffer; if not using data conversion, this is
// the codec buffer; otherwise, it is allocated separately
// 这就是需要数据转换器时的指针,指向一个IMemory数据类型的引用
sp<RefBase> mMemRef; // and a reference to the IMemory, so it does not go away
// 需要数据转换器时,此对象将会编解码器的buffer,也就是和上面的mData分开分配内存
sp<MediaCodecBuffer> mCodecData; // the codec's buffer
// 需要数据转换器时,指向IMemory数据类型的引用
sp<RefBase> mCodecRef; // and a reference to the IMemory
// 总结下上面这四个参数的关系:
// 也就是一个Data buffer对象必须对应一个指向IMemory数据类型对象的引用,
// 然后就是若需要数据转换器时上面四个参数将都会使用,否则直接使用前两个参数对象即可(不需要分开分配内存)
// 图形Buffer信息对象
// 其简单声明见下面
sp<GraphicBuffer> mGraphicBuffer;
// 是否为新图形数据buffer
bool mNewGraphicBuffer;
// 当前buffer的Fence文件描述符
// 注意:该值其实非常重要,Fence是系统管理某些Buffer分配和释放(即buffer读写同步机制)的同步机制,
// 由同步点和同步时间线来管理同步
// 后续分析到看详细分析
int mFenceFd;
// 帧渲染跟踪器的记录已渲染帧数据对象
// 其声明定义见下面
FrameRenderTracker::Info *mRenderInfo;
// 根据注释可知,这下面的五个声明只用于debug,因此不分析
// The following field and 4 methods are used for debugging only
// 标记是否为读状态的Fence
bool mIsReadFence;
// Store |fenceFd| and set read/write flag. Log error, if there is already a fence stored.
void setReadFence(int fenceFd, const char *dbg);
void setWriteFence(int fenceFd, const char *dbg);
// Log error, if the current fence is not a read/write fence.
void checkReadFence(const char *dbg);
void checkWriteFence(const char *dbg);
};
GraphicBuffer类声明:
图形Buffer信息类声明
类声明:省略其它代码
// [frameworks/native/libs/ui/include/ui/GraphicBuffer.h]
class GraphicBuffer
: public ANativeObjectBase<ANativeWindowBuffer, GraphicBuffer, RefBase>,
public Flattenable<GraphicBuffer> {}
// [frameworks/native/libs/ui/include/ui/ANativeObjectBase.h]
// 可以看到该父类仅仅只是为了实现一个代理接口,最终还是继承了【NATIVE_TYPE】和【REF】类
/*
* This helper class turns a ANativeXXX object type into a C++
* reference-counted object; with proper type conversions.
*/
template <typename NATIVE_TYPE, typename TYPE, typename REF,
typename NATIVE_BASE = android_native_base_t>
class ANativeObjectBase : public NATIVE_TYPE, public REF {}
// [system/core/libutils/include/utils/Flattenable.h]
// 实现可扁平化数据,处理数据字节的序列化
/*
* The Flattenable protocol allows an object to serialize itself out
* to a byte-buffer and an array of file descriptors.
* Flattenable objects must implement this protocol.
*/
template <typename T>
class Flattenable {
public:
// size in bytes of the flattened object
inline size_t getFlattenedSize() const;
// number of file descriptors to flatten
inline size_t getFdCount() const;
// flattens the object into buffer.
// size should be at least of getFlattenedSize()
// file descriptors are written in the fds[] array but ownership is
// not transfered (ie: they must be dupped by the caller of
// flatten() if needed).
inline status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
// unflattens the object from buffer.
// size should be equal to the value of getFlattenedSize() when the
// object was flattened.
// unflattened file descriptors are found in the fds[] array and
// don't need to be dupped(). ie: the caller of unflatten doesn't
// keep ownership. If a fd is not retained by unflatten() it must be
// explicitly closed.
inline status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
};
FrameRenderTracker::Info类声明:
帧渲染跟踪器的记录已渲染帧数据对象。
根据英文注释基本可以理解该信息类的作用:
追踪渲染器一帧数据信息,主要追踪帧数据的三个状态
// Tracks the render information about a frame. Frames go through several states while
// the render information is tracked:
//
// 1. queued frame: mMediaTime and mGraphicBuffer are set for the frame. mFence is the
// queue fence (read fence). mIndex is negative, and mRenderTimeNs is invalid.
// Key characteristics: mFence is not NULL and mIndex is negative.
//
// 2. dequeued frame: mFence is updated with the dequeue fence (write fence). mIndex is set.
// Key characteristics: mFence is not NULL and mIndex is non-negative. mRenderTime is still
// invalid.
//
// 3. rendered frame or frame: mFence is cleared, mRenderTimeNs is set.
// Key characteristics: mFence is NULL.
//
struct RenderedFrameInfo {
// set by client during onFrameQueued or onFrameRendered
int64_t getMediaTimeUs() const { return mMediaTimeUs; }
// -1 if frame is not yet rendered
nsecs_t getRenderTimeNs() const { return mRenderTimeNs; }
// set by client during updateRenderInfoForDequeuedBuffer; -1 otherwise
ssize_t getIndex() const { return mIndex; }
// creates information for a queued frame
RenderedFrameInfo(int64_t mediaTimeUs, const sp<GraphicBuffer> &graphicBuffer,
const sp<Fence> &fence)
: mMediaTimeUs(mediaTimeUs),
mRenderTimeNs(-1),
mIndex(-1),
mGraphicBuffer(graphicBuffer),
mFence(fence) {
}
// creates information for a frame rendered on a tunneled surface
RenderedFrameInfo(int64_t mediaTimeUs, nsecs_t renderTimeNs)
: mMediaTimeUs(mediaTimeUs),
mRenderTimeNs(renderTimeNs),
mIndex(-1),
mGraphicBuffer(NULL),
mFence(NULL) {
}
private:
// 当前帧媒体时间戳
int64_t mMediaTimeUs;
// 当前帧渲染时间戳
nsecs_t mRenderTimeNs;
// 帧索引
ssize_t mIndex; // to be used by client
// 帧图形数据buffer的指针对象
sp<GraphicBuffer> mGraphicBuffer;
// Fence同步机制的指针对象
sp<Fence> mFence;
// 帧渲染跟踪器,它将跟踪记录当前已渲染帧数据
friend struct FrameRenderTracker;
};
8.3.2.3、客户端使用的buffer缓存数据为VideoNativeMetadata数据类型结构声明定义:
视频native元数据结构
// [frameworks/native/headers/media_plugin/media/hardware/HardwareAPI.h]
struct VideoNativeMetadata {
// 元数据buffer类型枚举,此处必须为kMetadataBufferTypeANWBuffer
// 关于它前面章节已有分析过
MetadataBufferType eType; // must be kMetadataBufferTypeANWBuffer
#ifdef OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
OMX_PTR pBuffer;
#else
// 64位平台默认使用该数据Buffer类型
struct ANativeWindowBuffer* pBuffer;
#endif
int nFenceFd; // -1 if unused
};
// 元数据缓冲区布局,用于将native_handle传递给编解码器
// Meta data buffer layout for passing a native_handle to codec
struct VideoNativeHandleMetadata {
MetadataBufferType eType; // must be kMetadataBufferTypeNativeHandleSource
#ifdef OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
OMX_PTR pHandle;
#else
// buffer元数据native访问句柄指针
// 见下面定义
native_handle_t *pHandle;
#endif
};
native_handle_t 声明定义:
buffer元数据native访问句柄指针
// [system/core/libcutils/include/cutils/native_handle.h]
typedef struct native_handle
{
int version; /* sizeof(native_handle_t) */
int numFds; /* number of file-descriptors at &data[0] */
int numInts; /* number of ints at &data[numFds] */
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wzero-length-array"
#endif
int data[0]; /* numFds + numInts ints */
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
} native_handle_t;
8.3.2.4、mOMXNode->useBuffer(kPortIndexOutput, OMXBuffer::sPreset, &info.mBufferID)实现分析:
请求底层组件输出端口使用该Buffer
现来看OMXBuffer::sPreset定义:可以看到OMXBuffer只是用于进行HIDL数据传递转换的一个类型,完成之后将会被转换为CodecBuffer类型
省略部分代码
// [frameworks/av/media/libmedia/include/media/OMXBuffer.h]
// TODO: After complete HIDL transition, this class would be replaced by
// CodecBuffer.
class OMXBuffer {
public:
// 类静态变量,预设的OMX Buffer
// sPreset is used in places where we are referring to a pre-registered
// buffer on a port. It has type kBufferTypePreset and mRangeLength of 0.
static OMXBuffer sPreset;
private:
friend struct OMXNodeInstance;
friend struct C2OMXNode;
// This is needed temporarily for OMX HIDL transition.
friend inline bool (::android::hardware::media::omx::V1_0::implementation::
wrapAs)(::android::hardware::media::omx::V1_0::CodecBuffer* t,
OMXBuffer const& l);
friend inline bool (::android::hardware::media::omx::V1_0::implementation::
convertTo)(OMXBuffer* l,
::android::hardware::media::omx::V1_0::CodecBuffer const& t);
friend inline bool (::android::hardware::media::omx::V1_0::utils::
wrapAs)(::android::hardware::media::omx::V1_0::CodecBuffer* t,
OMXBuffer const& l);
friend inline bool (::android::hardware::media::omx::V1_0::utils::
convertTo)(OMXBuffer* l,
::android::hardware::media::omx::V1_0::CodecBuffer const& t);
enum BufferType {
kBufferTypeInvalid = 0,
kBufferTypePreset,
kBufferTypeSharedMem,
kBufferTypeANWBuffer, // Use only for non-Treble
kBufferTypeNativeHandle,
kBufferTypeHidlMemory // Mapped to CodecBuffer::Type::SHARED_MEM.
};
BufferType mBufferType;
// kBufferTypePreset
// If the port is operating in byte buffer mode, mRangeLength is the valid
// range length. Otherwise the range info should also be ignored.
OMX_U32 mRangeOffset;
OMX_U32 mRangeLength;
// kBufferTypeSharedMem
sp<IMemory> mMem;
// kBufferTypeANWBuffer
sp<GraphicBuffer> mGraphicBuffer;
// kBufferTypeNativeHandle
sp<NativeHandle> mNativeHandle;
// kBufferTypeHidlMemory
hidl_memory mHidlMemory;
}
sPreset变量的初始化:
// [frameworks/av/media/libmedia/OMXBuffer.cpp]
//static
OMXBuffer OMXBuffer::sPreset(static_cast<sp<MediaCodecBuffer> >(NULL));
// 默认执行该构造函数
OMXBuffer::OMXBuffer(const sp<MediaCodecBuffer>& codecBuffer)
: mBufferType(kBufferTypePreset),
mRangeOffset(codecBuffer != NULL ? codecBuffer->offset() : 0),
mRangeLength(codecBuffer != NULL ? codecBuffer->size() : 0) {
}
useBuffer实现分析:
// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp]
status_t OMXNodeInstance::useBuffer(
OMX_U32 portIndex, const OMXBuffer &omxBuffer, IOMX::buffer_id *buffer) {
if (buffer == NULL) {
ALOGE("b/25884056");
return BAD_VALUE;
}
if (portIndex == kPortIndexInputExtradata || portIndex == kPortIndexOutputExtradata) {
// Allow extradata ports
} else if (portIndex >= NELEM(mNumPortBuffers)) {
return BAD_VALUE;
}
Mutex::Autolock autoLock(mLock);
if (mHandle == NULL) {
return DEAD_OBJECT;
}
// 注意根据前面流程分析,可知mSailed参数此时还是true即表示OMX正在启动状态中
if (!mSailed) {
ALOGE("b/35467458");
android_errorWriteLog(0x534e4554, "35467458");
return BAD_VALUE;
}
// 根据buffer类型判断,默认值为kBufferTypePreset
switch (omxBuffer.mBufferType) {
// 其实该预设Buffer类型默认为字节数据存储的内存buffer,通过下面的useBuffer_1中的分析就会知晓区别了
case OMXBuffer::kBufferTypePreset: {
if (mPortMode[portIndex] != IOMX::kPortModeDynamicANWBuffer
&& mPortMode[portIndex] != IOMX::kPortModeDynamicNativeHandle) {
// 若不是这两中buffer端口模式类型,则直接结束
break;
}
// 请求底层组件输出端口使用该Buffer
// 见8.3.2.4.1小节分析
return useBuffer_l(portIndex, NULL, NULL, buffer);
}
// 此处使用的共享内存Buffer类型,也就是不需要新创建单独内存来存储,直接共享使用【omxBuffer.mMem】
case OMXBuffer::kBufferTypeSharedMem: {
if (mPortMode[portIndex] != IOMX::kPortModePresetByteBuffer
&& mPortMode[portIndex] != IOMX::kPortModeDynamicANWBuffer) {
break;
}
return useBuffer_l(portIndex, omxBuffer.mMem, NULL, buffer);
}
// Surface Buffer类型时
case OMXBuffer::kBufferTypeANWBuffer: {
if (mPortMode[portIndex] != IOMX::kPortModePresetANWBuffer
&& mPortMode[portIndex] != IOMX::kPortModeDynamicANWBuffer) {
// 非这两种端口模式时,退出
break;
}
// 使用图形Buffer
// 见8.3.2.4.2小节分析
return useGraphicBuffer_l(portIndex, omxBuffer.mGraphicBuffer, buffer);
}
// HIDL通信的Buffer类型时
case OMXBuffer::kBufferTypeHidlMemory: {
if (portIndex == kPortIndexInputExtradata || portIndex == kPortIndexOutputExtradata) {
// Allow extradata ports
} else if (mPortMode[portIndex] != IOMX::kPortModePresetByteBuffer
&& mPortMode[portIndex] != IOMX::kPortModeDynamicANWBuffer
&& mPortMode[portIndex] != IOMX::kPortModeDynamicNativeHandle) {
break;
}
// 支持上面五种端口Buffer类型
// 映射数据内存,实际上就是内存映射(Memory Map) 技术,不展开分析
sp<IHidlMemory> hidlMemory = mapMemory(omxBuffer.mHidlMemory);
if (hidlMemory == nullptr) {
// 映射内存失败
ALOGE("OMXNodeInstance useBuffer() failed to map memory");
return NO_MEMORY;
}
// 将前面分析
return useBuffer_l(portIndex, NULL, hidlMemory, buffer);
}
default:
return BAD_VALUE;
break;
}
ALOGE("b/77486542 : bufferType = %d vs. portMode = %d",
omxBuffer.mBufferType, mPortMode[portIndex]);
android_errorWriteLog(0x534e4554, "77486542");
return INVALID_OPERATION;
}
8.3.2.4.1、useBuffer_l(portIndex, NULL, NULL, buffer)实现分析:
请求底层组件输出端口使用该Buffer
// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp]
status_t OMXNodeInstance::useBuffer_l(
OMX_U32 portIndex, const sp<IMemory> ¶ms,
const sp<IHidlMemory> &hParams, IOMX::buffer_id *buffer) {
// Buffer元数据结构对象指针,其实就是此前大致已阐述过的实现,
// 它实际控制管理IMemory、IHidlMemory、GraphicBuffer这三种buffer数据类型的实现。
// 不再详细分析,后续涉及到时会简单提示其实现原理
BufferMeta *buffer_meta;
// OMX Buffer头信息类型对象指针,该数据由底层组件创建返回
OMX_BUFFERHEADERTYPE *header;
OMX_ERRORTYPE err = OMX_ErrorNone;
// 判断是否为媒体元数据(也就是音视频输入输出端口buffer数据)
bool isMetadata;
if (portIndex == kPortIndexInputExtradata || portIndex == kPortIndexOutputExtradata) {
isMetadata = false;
} else {
// mMetadataType记录的就是每个buffer端口媒体元数据类型
// 该值的赋值是在此前的【OMXNodeInstance::setPortMode】流程中分析到的
isMetadata = mMetadataType[portIndex] != kMetadataBufferTypeInvalid;
}
if (portIndex == kPortIndexInputExtradata || portIndex == kPortIndexOutputExtradata) {
// Allow extradata ports
} else if (!isMetadata && mGraphicBufferEnabled[portIndex]) {
ALOGE("b/62948670");
android_errorWriteLog(0x534e4554, "62948670");
return INVALID_OPERATION;
}
// 参数大小,参数指针
size_t paramsSize;
void* paramsPointer;
// 此处可以看出IMemory、IHidlMemory 这两种buffer数据类型不能同时启用
if (params != NULL && hParams != NULL) {
return BAD_VALUE;
}
// 获取其对应的参数大小和参数指针(即指向数据起始位置地址)
if (params != NULL) {
paramsPointer = params->pointer();
paramsSize = params->size();
} else if (hParams != NULL) {
paramsPointer = hParams->getPointer();
paramsSize = hParams->getSize();
} else {
// 都为null时
paramsPointer = nullptr;
}
// 对应buffer数据类型的分配大小
OMX_U32 allottedSize;
if (isMetadata) {
// 输入输出buffer端口时
// 就是计算每种buffer数据类型的结构占用内存大小
if (mMetadataType[portIndex] == kMetadataBufferTypeGrallocSource) {
allottedSize = sizeof(VideoGrallocMetadata);
} else if (mMetadataType[portIndex] == kMetadataBufferTypeANWBuffer) {
allottedSize = sizeof(VideoNativeMetadata);
} else if (mMetadataType[portIndex] == kMetadataBufferTypeNativeHandleSource) {
allottedSize = sizeof(VideoNativeHandleMetadata);
} else {
return BAD_VALUE;
}
} else {
// NULL params is allowed only in metadata mode.
if (paramsPointer == nullptr) {
ALOGE("b/25884056");
return BAD_VALUE;
}
// 参数大小
allottedSize = paramsSize;
}
// 是否为输出图形媒体元数据
bool isOutputGraphicMetadata;
if (portIndex == kPortIndexInputExtradata || portIndex == kPortIndexOutputExtradata) {
isOutputGraphicMetadata = false;
} else {
// true:输出buffer端口,且端口的媒体元数据buffer类型为这两种的其中一种时。否则为false
isOutputGraphicMetadata = (portIndex == kPortIndexOutput) &&
(mMetadataType[portIndex] == kMetadataBufferTypeGrallocSource ||
mMetadataType[portIndex] == kMetadataBufferTypeANWBuffer);
}
// 输出或输入端口请求分配的buffer位标志【值为0和1】,相当于端口索引
uint32_t requiresAllocateBufferBit =
(portIndex == kPortIndexInput)
? kRequiresAllocateBufferOnInputPorts
: kRequiresAllocateBufferOnOutputPorts;
// 注译:我们使用useBuffer来输出元数据,而不考虑【quirks】特殊配置信息
// we use useBuffer for output metadata regardless of quirks
if (!isOutputGraphicMetadata && (mQuirks & requiresAllocateBufferBit) &&
portIndex != kPortIndexOutputExtradata &&
portIndex != kPortIndexInputExtradata) {
// 注译:元数据缓冲区buffer没有跨进程连接,如果不是元数据则只复制
// metadata buffers are not connected cross process; only copy if not meta.
// 此处创建BufferMeta即Buffer媒体元数据结构对象,内部管理不同参数数据类型
buffer_meta = new BufferMeta(
params, hParams, portIndex, !isMetadata /* copy */, NULL /* data */);
// 请求底层组件分配Buffer,这是个宏定义方法,最终还是会调用底层组件的对应方法【AllocateBuffer】
// 这里就对应起来此前流程分析过的【appPrivate】程序私有数据类型字段,即它就是BufferMeta对象指针,allottedSize是它的数据大小
// 举例SoftAVCDec组件实现结果为:
// 底层基类会创建同样大小的内存buffer,然后更新对应端口索引的buffer信息,
// 并会创建【header】数据类型对象并在其对应字段中缓存底层创建的内存buffer指针、
// allottedSize、buffer_meta私有数据、端口索引等字段数据,并添加到底层组件对应端口缓冲区元数据【BufferInfo】中,
// 最后检查底层状态扭转情况。
err = OMX_AllocateBuffer(
mHandle, &header, portIndex, buffer_meta, allottedSize);
if (err != OMX_ErrorNone) {
CLOG_ERROR(allocateBuffer, err,
SIMPLE_BUFFER(portIndex, (size_t)allottedSize,
paramsPointer));
}
} else {
// 否则,将会OMXNode节点实例中自身创建维护一个OMX数据buffer内存
OMX_U8 *data = NULL;
// metadata buffers are not connected cross process
// use a backup buffer instead of the actual buffer
// 使用一个备份buffer来代替实际buffer
if (isMetadata) {
// 是媒体元数据时,创建这么大字节的数据内存
data = new (std::nothrow) OMX_U8[allottedSize];
if (data == NULL) {
return NO_MEMORY;
}
// 字节内存全设置0
memset(data, 0, allottedSize);
// 创建它,此处可以看出来,这种情况创建的数据内存将设置给BufferMeta来管理
buffer_meta = new BufferMeta(
params, hParams, portIndex, false /* copy */, data);
} else {
// 非媒体元数据时
// 直接使用IMemory、IHidlMemory这两种buffer数据类型中的数据地址指针,
// 也就相当于它们此时共用指针来访问相同内存数据
data = static_cast<OMX_U8 *>(paramsPointer);
// 所以此时创建BufferMeta就不再需要传入data了
// 备注:其实最后一个参数数据指针data,只是个备份数据,看实现未使用到
buffer_meta = new BufferMeta(
params, hParams, portIndex, false /* copy */, NULL);
}
// 宏定义方法,将会执行底层组件该方法,让其使用OMXNode中创建的数据
// 举例SoftAVCDec组件实现结果:
// 其实和上面的allocateBuffer方法基本一样处理,
// 只是有一点不同是不需要底层组件创建【allottedSize】大小的【data】,因为【data】上层已创建好了
err = OMX_UseBuffer(
mHandle, &header, portIndex, buffer_meta,
allottedSize, data);
if (err != OMX_ErrorNone) {
CLOG_ERROR(useBuffer, err, SIMPLE_BUFFER(
portIndex, (size_t)allottedSize, data));
}
}
if (err != OMX_ErrorNone) {
delete buffer_meta;
buffer_meta = NULL;
*buffer = 0;
return StatusFromOMXError(err);
}
// 此处也再次表明:Buffer头数据中私有数据pAppPrivate字段值必须为buffer_meta数据类型,
// 这一步就是底层组件创建header对象时完成的
CHECK_EQ(header->pAppPrivate, buffer_meta);
// 备注:其实从上面的处理流程可知,这个Buffer header头信息结构对象是非常重要的,
// 它缓存并控制管理关联了上层和底层组件的Buffer,我们可以通过它来完成端口buffer数据的关联和转换
// 创建一个Buffer id
// 这一步也是非常重要的,因为编解码器就是通过Buffer id来控制输入输出端口缓冲区buffer来实现的
// 见下面分析
*buffer = makeBufferID(header);
// 添加端口索引和buffer id到指定buffer端口的活动(正在使用的)Buffer队列中
// 见下面分析
addActiveBuffer(portIndex, *buffer);
// 根据此前流程中的分析,可知该参数解码器时通常为空,请见此前分析
// 此时也就是如果设置了输入Surface的话,该Surface将会通过Buffer ID直接来接管输入Buffer
// 暂不展开分析
sp<IOMXBufferSource> bufferSource(getBufferSource());
if (bufferSource != NULL && portIndex == kPortIndexInput) {
bufferSource->onInputBufferAdded(*buffer);
}
CLOG_BUFFER(useBuffer, NEW_BUFFER_FMT(
*buffer, portIndex, "%u(%zu)@%p", allottedSize, paramsSize, paramsPointer));
return OK;
}
makeBufferID(header)实现分析:
创建一个Buffer ID
这一步也是非常重要的,因为编解码器就是通过Buffer id来控制输入输出端口缓冲区buffer来实现的
// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp]
IOMX::buffer_id OMXNodeInstance::makeBufferID(OMX_BUFFERHEADERTYPE *bufferHeader) {
if (bufferHeader == NULL) {
return 0;
}
// ID加锁
Mutex::Autolock autoLock(mBufferIDLock);
IOMX::buffer_id buffer;
do { // handle the very unlikely case of ID overflow
// 处理极不可能的ID溢出情况
// Buffer ID计数器值,默认为0,从0开始计数
if (++mBufferIDCount == 0) {
// 此处if内,若发生32位Int溢出时,则会再次从0开始计数
++mBufferIDCount;
}
// 强转得到ID
buffer = (IOMX::buffer_id)mBufferIDCount;
// 再次循环条件判断产生的buffer id是否有效,若已存在则继续下一个
// 备注:mBufferIDToBufferHeader根据名称就知它相当于是个map功能
} while (mBufferIDToBufferHeader.indexOfKey(buffer) >= 0);
// ID有效则缓存它和当前Buffer header头信息指针的映射关系,
// 备注:也就是说后续编解码开始工作时上层和底层都会通过Buffer ID来获取对应的buffer
mBufferIDToBufferHeader.add(buffer, bufferHeader);
// 这也是一个map功能,不过刚好和上面相反存储而已
mBufferHeaderToBufferID.add(bufferHeader, buffer);
// 返回ID
return buffer;
}
addActiveBuffer(portIndex, *buffer)实现分析:
添加端口索引和buffer id到指定buffer端口的活动(正在使用的)Buffer队列中,其实际上是创建一个ActiveBuffer结构对象来存储端口索引和buffer id映射关系
// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp]
void OMXNodeInstance::addActiveBuffer(OMX_U32 portIndex, IOMX::buffer_id id) {
// 创建活动Buffer结构对象,保存并添加到活动Buffer队列中
ActiveBuffer active;
active.mPortIndex = portIndex;
active.mID = id;
mActiveBuffers.push(active);
if (portIndex == kPortIndexInputExtradata || portIndex == kPortIndexOutputExtradata) {
// Allow extradata ports
} else if (portIndex < NELEM(mNumPortBuffers)) {
// mNumPortBuffers数组的大小为2,此处检查索引必须为输入或输出端口索引时才进入
// 递增对应输入或输出端口的Buffer个数,其实际就是记录当前每个端口的Buffer个数
++mNumPortBuffers[portIndex];
}
}
8.3.2.4.2、useGraphicBuffer_l(portIndex, omxBuffer.mGraphicBuffer, buffer)实现分析:
使用图形Buffer
由于本章节接下来内容篇幅过长,因此必须放入另一章节分析,请查看:
【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 4】【06】