Android 9.0 multimedia框架解析(五)ACodec配置解码器过程

2023-12-31 16:50

本文主要是介绍Android 9.0 multimedia框架解析(五)ACodec配置解码器过程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

概述

先来回顾下上篇文章所讲的MediaPlayerService的start过程。MediaPlayerService的start通过层层调用会调用到NuPlayer::onStart,onStart会先启动GenericSource,然后通过异步的方式调用到NuPlayer::instantiateDecoder。在instantiateDecoder中,会创建NuPlayer::Decoder(这个Decoder跟我标题里面的decoder是不一样的,标题里面的decoder指的是实际的解码器,而这里的NuPlayer::Decoder经过层层调用会调用到实际的解码器),然后会进行初始化和配置。从时序图中可以看出Decoder的配置过程会先去查找对应的解码器,然后为为解码器创建ACodec。上一节我们分析到了创建ACodec这里,没有分析ACodec的配置过程。这里我以自己集成的ffmpeg播放视频为场景,分析ACodec配置解码器的过程。

时序图

在这里插入图片描述

configureCodec

从时序图可以看出配置过程会调用到ACodec::configureCodec,这个configureCodec是配置过程的核心函数。
来看看配置的第一步SetComponentRole。

status_t ACodec::configureCodec(const char *mime, const sp<AMessage> &msg) {if (!msg->findInt32("encoder", &encoder)) {		//从AMessage中取出encoder对应的值,如果不存在就认为是要初始化解码器。encoder = false;}......status_t err = setComponentRole(encoder /* isEncoder */, mime);
}
status_t ACodec::setComponentRole(bool isEncoder, const char *mime) {const char *role = GetComponentRole(isEncoder, mime);//根据mime找到组件的ComponentRole......status_t err = SetComponentRole(mOMXNode, role);//把ComponentRole传递给解码器......
}
status_t SetComponentRole(const sp<IOMXNode> &omxNode, const char *role) {OMX_PARAM_COMPONENTROLETYPE roleParams;InitOMXParams(&roleParams);strncpy((char *)roleParams.cRole,role, OMX_MAX_STRINGNAME_SIZE - 1);roleParams.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';return omxNode->setParameter(OMX_IndexParamStandardComponentRole,&roleParams, sizeof(roleParams));//传给解码器
}

omxNode->setParameter就是调用到OMXNodeInstance::setParameter,然后OMXNodeInstance::setParameter会去调用到真正的解码器。来看看我解码器中对于OMX_IndexParamStandardComponentRole会做什么。

OMX_ERRORTYPE SoftFFmpegVideoDec::internalSetParameter(OMX_INDEXTYPE index, const OMX_PTR params)
{switch (index){case OMX_IndexParamStandardComponentRole:{const OMX_PARAM_COMPONENTROLETYPE *roleParams = (const OMX_PARAM_COMPONENTROLETYPE *)params;if (strncmp((const char *)roleParams->cRole,"video_decoder.ffmpeg",OMX_MAX_STRINGNAME_SIZE - 1)){ALOGE("OMX_ErrorUndefined");return OMX_ErrorUndefined;}return OMX_ErrorNone;}
}

可以看出,我会识别roleParams->cRole是否是等于video_decoder.ffmpeg,如果是则返回OMX_ErrorNone,让配置过程继续。
来看看第二步setPortMode。一般来说一个解码器有两个port,一个是input,一个是output。input用于输入数据,output用于存放解码完的数据。我一般只分析output。

status_t ACodec::configureCodec(const char *mime, const sp<AMessage> &msg) {if (haveNativeWindow) {	//如果使用nativewindow......err = setPortMode(kPortIndexOutput, IOMX::kPortModeDynamicANWBuffer);//设置output的端口模式为DynamicANWBuffer}
}
status_t ACodec::setPortMode(int32_t portIndex, IOMX::PortMode mode) {	status_t err = mOMXNode->setPortMode(portIndex, mode);	//调用OMXNodeInstance::setPortMode......mPortMode[portIndex] = mode;return OK;
}
status_t OMXNodeInstance::setPortMode(OMX_U32 portIndex, IOMX::PortMode mode) {switch (mode) {case IOMX::kPortModeDynamicANWBuffer:{if (portIndex == kPortIndexOutput) {	//由于是配置output,所以会先走这里.....status_t err = enableNativeBuffers_l(portIndex, OMX_TRUE /*graphic*/, OMX_TRUE);}(void)enableNativeBuffers_l(portIndex, OMX_FALSE /*graphic*/, OMX_FALSE);return storeMetaDataInBuffers_l(portIndex, OMX_TRUE, NULL);}
}
status_t OMXNodeInstance::enableNativeBuffers_l(....){OMX_STRING name = const_cast<OMX_STRING>(graphic ? "OMX.google.android.index.enableAndroidNativeBuffers"	//graphic为true: "OMX.google.android.index.allocateNativeHandle");OMX_INDEXTYPE index;OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index);	//获取自定义的的index(或许是为了兼容把)if (err == OMX_ErrorNone) {err = OMX_SetParameter(mHandle, index, &params);	//设置参数}
}

来看一下我的解码器是怎么实现OMX_GetExtensionIndex对应的函数接口的

OMX_ERRORTYPE SoftFFmpegVideoDec::getExtensionIndex( const char *name, OMX_INDEXTYPE *index) {if(strcmp(name, SPRD_INDEX_PARAM_ENABLE_ANB) == 0) {ALOGI("getExtensionIndex:%s",SPRD_INDEX_PARAM_ENABLE_ANB);*index = (OMX_INDEXTYPE) OMX_IndexParamEnableAndroidBuffers;	return OMX_ErrorNone;} else......return OMX_ErrorNotImplemented;
}

可以看出我调用OMX_GetExtensionIndex将获取到index为OMX_IndexParamEnableAndroidBuffers的值。再来看看解码器是怎么实现OMX_SetParameter对应的函数接口的。

OMX_ERRORTYPE SoftFFmpegVideoDec::internalSetParameter( OMX_INDEXTYPE index, const OMX_PTR params){case OMX_IndexParamEnableAndroidBuffers:{EnableAndroidNativeBuffersParams *peanbp = (EnableAndroidNativeBuffersParams *)params;PortInfo *pOutPort = editPortInfo(kOutputPortIndex);if (peanbp->enable == OMX_FALSE) {pOutPort->mDef.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar;} else {ALOGI("internalSetParameter, enable AndroidNativeBuffer but i not suppurt");return OMX_ErrorUndefined;}return OMX_ErrorNone;}
}

可以看出我对于想要使能AndroidNativeBuffer的操作是拒绝的。我这里直接返回OMX_ErrorUndefined。所以我实现的解码器并不支持把端口模式PortMode设置成kPortModePresetANWBuffer。不过没事下面的步骤会去尝试把PortMode设置成kPortModePresetByteBuffer,这个kPortModePresetByteBuffer我的解码器是支持的。下面的分析会分析到这里的。
来看看第三个配置步骤,这个配置步骤是我自定义的,因为需要从解析器传递一些参数给解码器。

status_t ACodec::configureCodec(const char *mime, const sp<AMessage> &msg) {if (video) {if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_FFMPEG_VIDEO)) {OMX_AUDIO_PARAM_FFMPEGMODETYPE FfmpegParams;FfmpegParams.nPortIndex = kPortIndexOutput;FfmpegParams.nDisplayWidth = displayWidth;FfmpegParams.nDisplayHeight = displayHeight;strcpy((char *)FfmpegParams.nName,fileName.c_str());err  = mOMXNode->setParameter(OMX_IndexParamVideoFfmpeg,&FfmpegParams, sizeof(OMX_AUDIO_PARAM_FFMPEGMODETYPE));}
}

来看看第四个配置步骤,这里会去设置kPortModePresetByteBuffer,前面设置ANW buffer的场景由于我解码器没有去实现相关代码所以没成功的。

diff --git a/vendor/sprd/modules/media/omx_core/sprd_omx_core.cpp b/vendor/sprd/modules/media/omx_core/sprd_omx_core.cpp
index 6d36606..7b0ab08 100644
--- a/vendor/sprd/modules/media/omx_core/sprd_omx_core.cpp
+++ b/vendor/sprd/modules/media/omx_core/sprd_omx_core.cpp
@@ -58,6 +58,7 @@ static const struct {const char *mRole;} kComponents[] = {
+    { "OMX.google.ffmpegVideo.decoder","soft_ffmpegVideodec","video_decoder.ffmpeg"},{ "OMX.sprd.h263.decoder", "sprd_mpeg4dec", "video_decoder.h263" },

由于我的解码器的名字是OMX.google开头的,所以会走下面这个分支。

status_t ACodec::configureCodec(const char *mime, const sp<AMessage> &msg) {......if (video) {if (haveNativeWindow && mComponentName.startsWith("OMX.google.")) {usingSwRenderer = true;haveNativeWindow = false;(void)setPortMode(kPortIndexOutput, IOMX::kPortModePresetByteBuffer);}}
}
status_t ACodec::setPortMode(int32_t portIndex, IOMX::PortMode mode) {status_t err = mOMXNode->setPortMode(portIndex, mode);......
}
status_t OMXNodeInstance::setPortMode(OMX_U32 portIndex, IOMX::PortMode mode) {......case IOMX::kPortModePresetByteBuffer:{// Disable secure buffer, native buffer and metadata.(void)enableNativeBuffers_l(portIndex, OMX_TRUE /*graphic*/, OMX_FALSE);	//实现了(void)enableNativeBuffers_l(portIndex, OMX_FALSE /*graphic*/, OMX_FALSE);	//没实现(void)storeMetaDataInBuffers_l(portIndex, OMX_FALSE, NULL);	//没实现return OK;}
}
status_t OMXNodeInstance::enableNativeBuffers_l(OMX_U32 portIndex, OMX_BOOL graphic, OMX_BOOL enable){OMX_STRING name = const_cast<OMX_STRING>(graphic ? "OMX.google.android.index.enableAndroidNativeBuffers": "OMX.google.android.index.allocateNativeHandle");OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index);if (err == OMX_ErrorNone) {//如果解码器的getExtensionIndex有显示这个index,则对解码器进行这个index的参数设置EnableAndroidNativeBuffersParams params;InitOMXParams(&params);params.nPortIndex = portIndex;params.enable = enable;err = OMX_SetParameter(mHandle, index, &params);if (!graphic) {if (err == OMX_ErrorNone) {mSecureBufferType[portIndex] =enable ? kSecureBufferTypeNativeHandle : kSecureBufferTypeOpaque;} else if (mSecureBufferType[portIndex] == kSecureBufferTypeUnknown) {mSecureBufferType[portIndex] = kSecureBufferTypeOpaque;}} else {if (err == OMX_ErrorNone) {mGraphicBufferEnabled[portIndex] = enable;  //这里会被设置成false} else if (enable) {mGraphicBufferEnabled[portIndex] = false;	}}} else {
}

这里有两个enableNativeBuffers_l,第一个对应的OMX.google.android.index.enableAndroidNativeBuffers我解码器有实现,所以会成功;第二个OMX.google.android.index.allocateNativeHandle解码器没实现,所以会失败;第三个storeMetaDataInBuffers_l对应的OMX.google.android.index.storeANWBufferInMetadata我也没有实现,所以会失败。但是这两个失败并不会直接影响解码器的配置,因为他们的返回值都被忽略了。然后mGraphicBufferEnabled[portIndex]会被设置成false,这个变量在配置数据缓冲区的时候会用来作判断。

来看看第五个配置步骤,这个步骤会通过index = OMX_IndexParamVideoPortFormat获取解码器的参数,主要是设置input和ouput的数据格式和颜色空间。对于OMX_IndexParamVideoPortFormat的设置参数的接口,可以只做状态返回。

status_t ACodec::configureCodec(const char *mime, const sp<AMessage> &msg) {......if (video) {if (encoder) {err = setupVideoEncoder(mime, msg, outputFormat, inputFormat);} else {//解码器走这里err = setupVideoDecoder(mime, msg, haveNativeWindow, usingSwRenderer, outputFormat);}}
}
status_t ACodec::setupVideoDecoder(const char *mime, const sp<AMessage> &msg, bool haveNativeWindow,bool usingSwRenderer, sp<AMessage> &outputFormat) {OMX_VIDEO_CODINGTYPE compressionFormat;//从kVideoCodingMapEntry中获取这个mime对应的OMX_VIDEO_CODINGTYPE mVideoCodingType,保存到compressionFormat中//然后compressionFormat会拿来跟后面从解码器获取出来的eCompressionFormat作对比,如果一样则继续走。status_t err = GetVideoCodingTypeFromMime(mime, &compressionFormat);err = setVideoPortFormatType(kPortIndexInput, compressionFormat, OMX_COLOR_FormatUnused);if (msg->findInt32("color-format", &tmp)) {	//我没有定义这个,所以不会走这里......}else{err = setSupportedOutputFormat(!haveNativeWindow /* getLegacyFlexibleFormat */);}......}status_t ACodec::setVideoPortFormatType(OMX_U32 portIndex,OMX_VIDEO_CODINGTYPE compressionFormat,OMX_COLOR_FORMATTYPE colorFormat,bool usingNativeBuffers) {//这个函数在这里被拿来设置kPortIndexInputfor (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) {//我解码器对formatParams->nIndex做了限制,so只会走一次status_t err = mOMXNode->getParameter(OMX_IndexParamVideoPortFormat,&format, sizeof(format));}status_t err = mOMXNode->setParameter(OMX_IndexParamVideoPortFormat, &format, sizeof(format));
}
status_t ACodec::setSupportedOutputFormat(bool getLegacyFlexibleFormat) {OMX_VIDEO_PARAM_PORTFORMATTYPE format, legacyFormat;InitOMXParams(&format);format.nPortIndex = kPortIndexOutput;	//这个函数是用来设置output的,所以这里强制赋值了for (OMX_U32 index = 0; ; ++index) {format.nIndex = index;	//解码器只处理nIndex = 0的情况status_t err = mOMXNode->getParameter(OMX_IndexParamVideoPortFormat, &format, sizeof(format));}return mOMXNode->setParameter(OMX_IndexParamVideoPortFormat, &format, sizeof(format));
}
OMX_ERRORTYPE SoftFFmpegVideoDec::internalGetParameter(....){switch (index){case OMX_IndexParamVideoPortFormat:{OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =(OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;if (formatParams->nPortIndex > 1) {return OMX_ErrorUndefined;}if (formatParams->nIndex != 0) {return OMX_ErrorNoMore;}if (formatParams->nPortIndex == 0) {formatParams->eCompressionFormat = OMX_VIDEO_CodinFFMPEG ;//指定数据的压缩格式formatParams->eColorFormat = OMX_COLOR_FormatUnused;//对于input不需要设置颜色类型,毕竟是需要编码的数据formatParams->xFramerate = 0;} else {CHECK_EQ(formatParams->nPortIndex, 1u);PortInfo *pOutPort = editPortInfo(kOutputPortIndex);formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused;formatParams->eColorFormat = pOutPort->mDef.format.video.eColorFormat;formatParams->xFramerate = 0;}return OMX_ErrorNone;}......}
}
OMX_ERRORTYPE SoftFFmpegVideoDec::internalSetParameter(...){case OMX_IndexParamVideoPortFormat:{//对于SetParameter,我没有作什么操作if (formatParams->nPortIndex > 1){return OMX_ErrorUndefined;}if (formatParams->nIndex != 0){return OMX_ErrorNoMore;}return OMX_ErrorNone;}
}

来看看第六个步骤,这个步骤是通过index=OMX_IndexParamPortDefinition来配置解码器的

status_t ACodec::setupVideoDecoder(const char *mime, const sp<AMessage> &msg, bool haveNativeWindow,bool usingSwRenderer, sp<AMessage> &outputFormat) {OMX_VIDEO_CODINGTYPE compressionFormat;//从kVideoCodingMapEntry中获取这个mime对应的OMX_VIDEO_CODINGTYPE mVideoCodingType,保存到compressionFormat中//然后compressionFormat会拿来跟后面从解码器获取出来的eCompressionFormat作对比,如果一样则继续走。status_t err = GetVideoCodingTypeFromMime(mime, &compressionFormat);err = setVideoPortFormatType(kPortIndexInput, compressionFormat, OMX_COLOR_FormatUnused);if (msg->findInt32("color-format", &tmp)) {	//我没有定义这个,所以不会走这里......}else{err = setSupportedOutputFormat(!haveNativeWindow /* getLegacyFlexibleFormat */);}......err = setVideoFormatOnPort(kPortIndexInput, width, height, compressionFormat, frameRateFloat);err = setVideoFormatOnPort(kPortIndexOutput, width, height, OMX_VIDEO_CodingUnused);if (err != OK) {return err;}//下面这两个选项是可选的,也就是说//getExtensionIndex, name: OMX.google.android.index.describeColorAspects//getExtensionIndex, name: OMX.google.android.index.describeHDRStaticInfo//解析器可以不实现这两个extension indexerr = setColorAspectsForVideoDecoder( width, height, haveNativeWindow | usingSwRenderer, msg, outputFormat);if (err == ERROR_UNSUPPORTED) { // support is optionalerr = OK;}err = setHDRStaticInfoForVideoCodec(kPortIndexOutput, msg, outputFormat);if (err == ERROR_UNSUPPORTED) { // support is optionalerr = OK;}}status_t ACodec::setVideoFormatOnPort(OMX_U32 portIndex,int32_t width, int32_t height, OMX_VIDEO_CODINGTYPE compressionFormat,float frameRate) {status_t err = mOMXNode->getParameter(OMX_IndexParamPortDefinition, &def, sizeof(def));video_def->nFrameWidth = width;		//把从解析器获取的视频宽度传递给解码器video_def->nFrameHeight = height;	//把从解析器获取的视频高度传递给解码器err = mOMXNode->setParameter(OMX_IndexParamPortDefinition, &def, sizeof(def));
}
//由于我的解码器没有实现获取参数的OMX_IndexParamPortDefinition,所以会调用父类的internalGetParameter,
OMX_ERRORTYPE SprdSimpleOMXComponent::internalGetParameter(...){case OMX_IndexParamPortDefinition:{OMX_PARAM_PORTDEFINITIONTYPE *defParams = (OMX_PARAM_PORTDEFINITIONTYPE *)params;const PortInfo *port =&mPorts.itemAt(defParams->nPortIndex);memcpy(defParams, &port->mDef, sizeof(port->mDef));//把对应port的信息拷贝给defParams,传递给调用者return OMX_ErrorNone;}
}
OMX_ERRORTYPE SoftFFmpegVideoDec::internalSetParameter(...){case OMX_IndexParamPortDefinition:{PortInfo *port = editPortInfo(defParams->nPortIndex);if(defParams->nPortIndex == 1){//仅仅设置output//对于 setVideoFormatOnPort的调用,我可以不设置mWidth和mHeight,直接使用从解析器传递过来的宽度和高度//不过这样写也没关系,只不过这样写让setVideoFormatOnPort的OMX_IndexParamPortDefinition操作显得多余而已。嘿嘿嘿port->mDef.format.video.nFrameWidth = mWidth;port->mDef.format.video.nFrameHeight = mHeight; port->mDef.format.video.nStride = mWidth;port->mDef.format.video.nSliceHeight = mHeight;port->mDef.nBufferSize =( mWidth*mHeight*3)/2;}}
}

然后回到configureCodec函数它会调用getPortFormat获取input和ouput的格式

status_t ACodec::configureCodec(const char *mime, const sp<AMessage> &msg){......err = getPortFormat(kPortIndexInput, inputFormat);	if (err == OK) {err = getPortFormat(kPortIndexOutput, outputFormat);if (err == OK) {mInputFormat = inputFormat;	//保存格式信息mOutputFormat = outputFormat;//保存格式信息}}
}
status_t ACodec::getPortFormat(OMX_U32 portIndex, sp<AMessage> &notify) {status_t err = mOMXNode->getParameter(OMX_IndexParamPortDefinition, &def, sizeof(def));switch (def.eDomain) {case OMX_PortDomainVideo:{OMX_VIDEO_PORTDEFINITIONTYPE *videoDef = &def.format.video;switch ((int)videoDef->eCompressionFormat) {case OMX_VIDEO_CodingUnused:{notify->setInt32("stride", videoDef->nStride);notify->setInt32("color-format", videoDef->eColorFormat);notify->setInt32("slice-height", videoDef->nSliceHeight);notify->setInt32("width", videoDef->nFrameWidth);notify->setInt32("height", videoDef->nFrameHeight);}}}
}

getPortFormat得到的output的格式是这样的,这些格式信息会保存在ACodec的mInputFormat和mOutputFormat中。

27531 27744 V ACodec  : [OMX.google.ffmpegVideo.decoder] output format is AMessage(what = 0x00000000) = {
27531 27744 V ACodec  :   int32_t color-range = 2
27531 27744 V ACodec  :   int32_t color-standard = 4
27531 27744 V ACodec  :   int32_t color-transfer = 3
27531 27744 V ACodec  :   int32_t using-sw-renderer = 1
27531 27744 V ACodec  :   string mime = "video/raw"
27531 27744 V ACodec  :   int32_t stride = 624
27531 27744 V ACodec  :   int32_t slice-height = 352
27531 27744 V ACodec  :   int32_t color-format = 19
27531 27744 V ACodec  :   Buffer *image-data = 0xe32950e0
27531 27744 V ACodec  :   Rect crop(0, 0, 623, 351)
27531 27744 V ACodec  :   int32_t android._dataspace = 259
27531 27744 V ACodec  :   int32_t width = 624
27531 27744 V ACodec  :   int32_t height = 352
27531 27744 V ACodec  : }

至此,ACodec::configureCodec已经分析完毕。到了这里你可以会发现没有对input和output的缓冲区进行初始化配置,别急下面就来分析。
既然configureCodec已经执行完毕,现在回到Nuplayer::Decoder 消息处理函数的kWhatConfigure,它的下一步工作就是执行mCodec->start(),从时序图可以看出它会执行到ACodec的LoadedState::onMessageReceived的ACodec::kWhatStart分支。这个分支会通过状态机执行到ACodec::LoadedToIdleState::stateEntered函数

void ACodec::LoadedToIdleState::stateEntered(){if ((err = allocateBuffers()) != OK) {.......mCodec->changeState(mCodec->mLoadedState);}
}
status_t ACodec::LoadedToIdleState::allocateBuffers() {status_t err = mCodec->allocateBuffersOnPort(kPortIndexInput);err = mCodec->allocateBuffersOnPort(kPortIndexOutput);
}
status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) {......OMX_PARAM_PORTDEFINITIONTYPE def;InitOMXParams(&def);def.nPortIndex = portIndex;err = mOMXNode->getParameter(OMX_IndexParamPortDefinition, &def, sizeof(def));if (err == OK) {size_t bufSize = def.nBufferSize;/*这里有段log,通过这个log可以直观地知道现在正在为这4个buffer分配内存[log] ACodec  : [OMX.google.ffmpegVideo.decoder] Allocating 4 buffers of size 329472 (from 329472 using PresetByteBuffer) on output port*/for (OMX_U32 i = 0; i < def.nBufferCountActual && err == OK; ++i) {//执行四次,解码器定义的nBufferCountActual......auto transStatus = mAllocator[portIndex]->allocate(bufSize,[&success, &hidlMemToken](bool s,hidl_memory const& m) {success = s;hidlMemToken = m;});//分配内存hidlMem = mapMemory(hidlMemToken);err = mOMXNode->useBuffer(portIndex, hidlMemToken, &info.mBufferID);}}
}
status_t OMXNodeInstance::useBuffer(OMX_U32 portIndex, const OMXBuffer &omxBuffer, IOMX::buffer_id *buffer) {......case OMXBuffer::kBufferTypeHidlMemory: {sp<IHidlMemory> hidlMemory = mapMemory(omxBuffer.mHidlMemory);return useBuffer_l(portIndex, NULL, hidlMemory, buffer);}
}
status_t OMXNodeInstance::useBuffer_l(OMX_U32 portIndex, const sp<IMemory> &params,const sp<IHidlMemory> &hParams, IOMX::buffer_id *buffer) {......if (hParams != NULL) {paramsPointer = hParams->getPointer();paramsSize = hParams->getSize();                    }data = static_cast<OMX_U8 *>(paramsPointer);//这个没有用到,因为我没有实现OMX.google.android.index.storeANWBufferInMetadatabuffer_meta = new BufferMeta(params, hParams, portIndex, false /* copy */, NULL);err = OMX_UseBuffer(                    //SprdSimpleOMXComponent::internalUseBuffermHandle, &header, portIndex, buffer_meta,allottedSize, data);          //所以解码器的pBuffer是从hidl中拿来的buffer地址 nAllocLen是从hidl中拿来的buffer大小
}
//来看看原厂是怎么实现OMX_USeBuffer的
OMX_ERRORTYPE SprdSimpleOMXComponent::internalUseBuffer(OMX_BUFFERHEADERTYPE **header,OMX_U32 portIndex,OMX_PTR appPrivate,OMX_U32 size,OMX_U8 *ptr,BufferPrivateStruct* bufferPrivate) {*header = new OMX_BUFFERHEADERTYPE;(*header)->nSize = sizeof(OMX_BUFFERHEADERTYPE);(*header)->pBuffer = ptr;	//保存从hidl拿来的buffer地址(*header)->nAllocLen = size;	//保存从hidl拿来的buffer大小(*header)->nFilledLen = 0;......(*header)->nOutputPortIndex = portIndex;(*header)->nInputPortIndex = portIndex;PortInfo *port = &mPorts.editItemAt(portIndex);port->mBuffers.push();BufferInfo *buffer =&port->mBuffers.editItemAt(port->mBuffers.size() - 1);ALOGI("internalUseBuffer, header=0x%p, pBuffer=0x%p, size=%d",*header, ptr, size);buffer->mHeader = *header;buffer->mOwnedByUs = false;if (port->mBuffers.size() == port->mDef.nBufferCountActual) {port->mDef.bPopulated = OMX_TRUE;checkTransitions();}return OMX_ErrorNone;
}
//由于我的解码器没有实现获取参数的OMX_IndexParamPortDefinition,所以会调用父类的internalGetParameter,
OMX_ERRORTYPE SprdSimpleOMXComponent::internalGetParameter(...){case OMX_IndexParamPortDefinition:{OMX_PARAM_PORTDEFINITIONTYPE *defParams = (OMX_PARAM_PORTDEFINITIONTYPE *)params;const PortInfo *port =&mPorts.itemAt(defParams->nPortIndex);memcpy(defParams, &port->mDef, sizeof(port->mDef));//把对应port的信息拷贝给defParams,传递给调用者return OMX_ErrorNone;}
}

可以看出allocateBuffers用户或OMX_IndexParamPortDefinition获取input和output的信息。通过SprdSimpleOMXComponent::internalGetParameter可以知道获取的是对应端口的所有信息,比如buffer大小nBufferSize、buffer个数nBufferCountActual、视频分辨率format.video.nFrameWidth和format.video.nFrameHeight。对于input,获取视频编码格式(压缩格式)eCompressionFormat、对于output获取颜色格式eColorFormat(一般是YUV420p)。然后初始化每一个端口的缓冲区,例如ouput,会分配4(nBufferCountActual)个buffer,把每一个buffer的hidlMemToken传递给omx,进而传递相关地址给解码器,这样解码器就可以通过这个地址获取到解析器解析完之后的数据了。

这篇关于Android 9.0 multimedia框架解析(五)ACodec配置解码器过程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:https://blog.csdn.net/qq_27136111/article/details/100011623
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/556621

相关文章

Android实现打开本地pdf文件的两种方式

《Android实现打开本地pdf文件的两种方式》在现代应用中,PDF格式因其跨平台、稳定性好、展示内容一致等特点,在Android平台上,如何高效地打开本地PDF文件,不仅关系到用户体验,也直接影响... 目录一、项目概述二、相关知识2.1 PDF文件基本概述2.2 android 文件访问与存储权限2.

SpringQuartz定时任务核心组件JobDetail与Trigger配置

《SpringQuartz定时任务核心组件JobDetail与Trigger配置》Spring框架与Quartz调度器的集成提供了强大而灵活的定时任务解决方案,本文主要介绍了SpringQuartz定... 目录引言一、Spring Quartz基础架构1.1 核心组件概述1.2 Spring集成优势二、J

Android Studio 配置国内镜像源的实现步骤

《AndroidStudio配置国内镜像源的实现步骤》本文主要介绍了AndroidStudio配置国内镜像源的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录一、修改 hosts,解决 SDK 下载失败的问题二、修改 gradle 地址,解决 gradle

如何配置Spring Boot中的Jackson序列化

《如何配置SpringBoot中的Jackson序列化》在开发基于SpringBoot的应用程序时,Jackson是默认的JSON序列化和反序列化工具,本文将详细介绍如何在SpringBoot中配置... 目录配置Spring Boot中的Jackson序列化1. 为什么需要自定义Jackson配置?2.

MySQL中FIND_IN_SET函数与INSTR函数用法解析

《MySQL中FIND_IN_SET函数与INSTR函数用法解析》:本文主要介绍MySQL中FIND_IN_SET函数与INSTR函数用法解析,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一... 目录一、功能定义与语法1、FIND_IN_SET函数2、INSTR函数二、本质区别对比三、实际场景案例分

PyInstaller打包selenium-wire过程中常见问题和解决指南

《PyInstaller打包selenium-wire过程中常见问题和解决指南》常用的打包工具PyInstaller能将Python项目打包成单个可执行文件,但也会因为兼容性问题和路径管理而出现各种运... 目录前言1. 背景2. 可能遇到的问题概述3. PyInstaller 打包步骤及参数配置4. 依赖

鸿蒙中Axios数据请求的封装和配置方法

《鸿蒙中Axios数据请求的封装和配置方法》:本文主要介绍鸿蒙中Axios数据请求的封装和配置方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1.配置权限 应用级权限和系统级权限2.配置网络请求的代码3.下载在Entry中 下载AxIOS4.封装Htt

Spring中配置ContextLoaderListener方式

《Spring中配置ContextLoaderListener方式》:本文主要介绍Spring中配置ContextLoaderListener方式,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录Spring中配置ContextLoaderLishttp://www.chinasem.cntene

在Android平台上实现消息推送功能

《在Android平台上实现消息推送功能》随着移动互联网应用的飞速发展,消息推送已成为移动应用中不可或缺的功能,在Android平台上,实现消息推送涉及到服务端的消息发送、客户端的消息接收、通知渠道(... 目录一、项目概述二、相关知识介绍2.1 消息推送的基本原理2.2 Firebase Cloud Me

浅谈配置MMCV环境,解决报错,版本不匹配问题

《浅谈配置MMCV环境,解决报错,版本不匹配问题》:本文主要介绍浅谈配置MMCV环境,解决报错,版本不匹配问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录配置MMCV环境,解决报错,版本不匹配错误示例正确示例总结配置MMCV环境,解决报错,版本不匹配在col