1# Codec 2 3## 概述 4### 功能简介 5 6OpenHarmony Codec HDI(Hardware Device Interface)驱动框架基于OpenMax实现了视频硬件编解码驱动,提供Codec基础能力接口给上层媒体服务调用,包括获取组件编解码能力、创建组件、参数设置、数据的轮转和控制、以及销毁组件等功能,实现对视频数据的编解码处理(可以将YUV/RGB等格式的视频数据编码成H264/H265等编码格式,也可以将H264/H265等裸流数据解码成YUV/RGB等格式数据)。本文主要介绍基于HDF(Hardware Driver Foundation)驱动框架开发的Codec编解码功能。 7 8Codec HDI驱动框架基于HDF驱动框架实现。Codec HDI驱动架构组成: 9 10**图 1** Codec HDI驱动框架 11 12![image](figures/Codec框架图.png "Codec HDI驱动框架图") 13 14- Codec HDI Callback Remote Service:匿名Callback服务,通过该服务,可以处理回调。 15- Codec HDI Interface:提供了基于OpenMax的标准接口,上层可通过这些接口来实现硬件的编解码。 16- Codec HDI Adapter:HDI 实现层,实现了HDI Interface接口,并与OpenMax IL 对接。 17- OpenMax IL Interface:OpenMax IL接口,Codec HDI驱动直接对接OpenMax IL层。 18- Vendor Impl:厂商适配层,各大厂商适配的OpenMax 实现层。 19- Codec Hardware:硬件解码设备。 20 21### 基本概念 22在进行开发前,开发者应了解一下基本概念: 23 24- 采样率 25 26 采样率就是每秒从连续信号中提取并组成离散信号的采样个数,用赫兹(Hz)来表示。 27 28- OpenMax IL 29 30 OpenMax IL定义了硬件或软件编解码的标准,使得应用程序和媒体框架能够以统一的方式与多媒体编解码器和支持的组件进行交互。 31 32- 帧率 33 34 帧率就是每秒内传输的图片的帧数,也可以理解为图形处理器每秒能够刷新几次。帧率越大,画面越流畅;帧率越小,画面越有跳动感。 35 36- 码率 37 38 视频的码率是指在单位时间内传输的视频数据数量,一般用kbps作为单位。码率越高,视频就越清晰,反之则画面粗糙而且多马赛克。 39 40- 组件 41 42 组件就是指的OpenMax IL 组件,是对视频流中模块的抽象,本文中的组件指的是编解码组件,专门处理视频的编解码。 43 44### 约束与限制 45 46Codec HDI只针对标准系统,其它系统暂不支持。 47 48接口约束和限制参考[OpenMax IL标准](https://www.khronos.org/api/openmax/il)。 49 50## 开发指导 51 52### 场景介绍 53Codec模块主要完成对视频数据的硬件编解码,将H264等裸流数据转化成图形支持的YUV或者RGB数据,也支持将图形的YUV或RGB数据编码成H264等数据格式。 54 55### 接口说明 56 57- codec_component_manager.h 58 59 | 接口名称 | 功能描述 | 60 | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------| 61 | int32_t (*CreateComponent)(struct CodecComponentType **component, uint32_t *componentId, char *compName, int64_t appData, struct CodecCallbackType *callbacks) | 创建Codec组件实例 | 62 | int32_t (*DestroyComponent)(uint32_t componentId) | 销毁Codec组件实例 | 63 64- codec_component _if.h 65 66 | 接口名称 | 功能描述 | 67 | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | 68 | int32_t (*SendCommand)(struct CodecComponentType *self, enum OMX_COMMANDTYPE cmd, uint32_t param, int8_t *cmdData, uint32_t cmdDataLen) | 发送命令给组件 | 69 | int32_t (*GetParameter)(struct CodecComponentType *self, uint32_t paramIndex, int8_t *paramStruct, uint32_t paramStructLen) | 获取组件参数设置 | 70 | int32_t (*SetParameter)(struct CodecComponentType *self, uint32_t index, int8_t *paramStruct, uint32_t paramStructLen) | 设置组件需要的参数 | 71 | int32_t (*GetState)(struct CodecComponentType *self, enum OMX_STATETYPE *state) | 获取组件的状态 | 72 | int32_t (*UseBuffer)(struct CodecComponentType *self, uint32_t portIndex, struct OmxCodecBuffer *buffer) | 指定组件端口的buffer | 73 | int32_t (*FreeBuffer)(struct CodecComponentType *self, uint32_t portIndex, const struct OmxCodecBuffer *buffer) | 释放buffer | 74 | int32_t (*EmptyThisBuffer)(struct CodecComponentType *self, const struct OmxCodecBuffer *buffer) | 编解码输入待处理buffer | 75 | int32_t (*FillThisBuffer)(struct CodecComponentType *self, const struct OmxCodecBuffer *buffer) | 编解码输出填充buffer | 76 77- codec_callback_if.h 78 79 | 接口名称 | 功能描述 | 80 | ---------------------------------------------------------------------------------------------------------------- |----------------------------------- | 81 | int32_t (*EventHandler)(struct CodecCallbackType *self, enum OMX_EVENTTYPE event, struct EventInfo *info) | 事件上报 | 82 | int32_t (*EmptyBufferDone)(struct CodecCallbackType *self, int64_t appData, const struct OmxCodecBuffer *buffer) | 上报输入buffer编码或者解码处理完毕 | 83 | int32_t (*FillBufferDone)(struct CodecCallbackType *self, int64_t appData, const struct OmxCodecBuffer *buffer) | 上报输出buffer填充完毕 | 84 85更多接口请参考[Codec驱动仓](https://gitee.com/openharmony/drivers_peripheral/tree/master/codec)。 86 87### 开发步骤 88Codec HDI驱动的开发过程主要包含以下步骤: 89 90#### Driver的注册及初始化 91定义Codec HDI的HdfDriverEntry结构体,该结构体中定义了Driver初始化的方法,填充g_codecComponentDriverEntry结构体,实现Bind、Init、Release函数指针。 92 93```c 94struct HdfDriverEntry g_codecComponentDriverEntry = { 95 .moduleVersion = 1, 96 .moduleName = "codec_hdi_omx_server", 97 .Bind = HdfCodecComponentTypeDriverBind, 98 .Init = HdfCodecComponentTypeDriverInit, 99 .Release = HdfCodecComponentTypeDriverRelease, 100}; 101HDF_INIT(g_codecComponentDriverEntry); // 将Codec HDI的HdfDriverEntry结构体注册到HDF上 102``` 103 104- HdfCodecComponentTypeDriverBind:将HDF中device绑定到CodecComponentTypeHost,将codec service注册到HDF框架。 105 106 ```c 107 int32_t HdfCodecComponentTypeDriverBind(struct HdfDeviceObject *deviceObject) 108 { 109 HDF_LOGI("HdfCodecComponentTypeDriverBind enter."); 110 struct HdfCodecComponentTypeHost *omxcomponenttypeHost = 111 (struct HdfCodecComponentTypeHost *)OsalMemAlloc(sizeof(struct HdfCodecComponentTypeHost)); 112 if (omxcomponenttypeHost == NULL) { 113 HDF_LOGE("HdfCodecComponentTypeDriverBind OsalMemAlloc HdfCodecComponentTypeHost failed!"); 114 return HDF_FAILURE; 115 } 116 int ret = HdfDeviceObjectSetInterfaceDesc(deviceObject, COMPONENT_MANAGER_SERVICE_DESC); 117 if (ret != HDF_SUCCESS) { 118 HDF_LOGE("Failed to set interface desc"); 119 return ret; 120 } 121 122 omxcomponenttypeHost->ioservice.Dispatch = CodecComponentTypeDriverDispatch; 123 omxcomponenttypeHost->ioservice.Open = NULL; 124 omxcomponenttypeHost->ioservice.Release = NULL; 125 omxcomponenttypeHost->service = CodecComponentManagerSerivceGet(); 126 if (omxcomponenttypeHost->service == NULL) { 127 OsalMemFree(omxcomponenttypeHost); 128 return HDF_FAILURE; 129 } 130 131 deviceObject->service = &omxcomponenttypeHost->ioservice; 132 return HDF_SUCCESS; 133 } 134 ``` 135 136- HdfCodecComponentTypeDriverInit:加载HCS(HDF Configuration Source)中的属性配置。 137 138 ```c 139 int32_t HdfCodecComponentTypeDriverInit(struct HdfDeviceObject *deviceObject) 140 { 141 HDF_LOGI("HdfCodecComponentTypeDriverInit enter."); 142 if (deviceObject == NULL) { 143 return HDF_FAILURE; 144 } 145 InitDataNode(deviceObject->property); 146 if (LoadCapabilityData() != HDF_SUCCESS) { 147 ClearCapabilityData(); 148 } 149 return HDF_SUCCESS; 150 } 151 ``` 152 153- HdfCodecComponentTypeDriverRelease:释放驱动实例。 154 155 ```c 156 void HdfCodecComponentTypeDriverRelease(struct HdfDeviceObject *deviceObject) 157 { 158 HDF_LOGI("HdfCodecComponentTypeDriverRelease enter."); 159 struct HdfCodecComponentTypeHost *omxcomponenttypeHost = 160 CONTAINER_OF(deviceObject->service, struct HdfCodecComponentTypeHost, ioservice); 161 OmxComponentManagerSeriveRelease(omxcomponenttypeHost->service); 162 OsalMemFree(omxcomponenttypeHost); 163 ClearCapabilityData(); 164 } 165 ``` 166 167#### Driver的HCS配置 168HCS配置包括两部分: 169 170- device相关配置。 171- 支持的组件相关配置。 172 173HCS配置内容包括:驱动节点、加载顺序、服务名称等。HCS语法可参考[配置管理](driver-hdf-manage.md)。 174 175以RK3568开发板为例,标准系统配置文件路径(其它系统暂不涉及): 176vendor/hihope/rk3568/hdf_config/uhdf/ 177 1781. device相关配置 179 180 在device_info.hcs的codec_host中增加codec_omx_service配置,具体配置如下: 181 ```c 182 codec :: host { 183 hostName = "codec_host"; 184 priority = 50; 185 gid = ["codec_host", "uhdf_driver", "vendor_mpp_driver"]; 186 codec_omx_device :: device { 187 device0 :: deviceNode { 188 policy = 2; // 自动加载,非延迟加载 189 priority = 100; // 优先级 190 moduleName = "libcodec_hdi_omx_server.z.so"; // 驱动的动态库 191 serviceName = "codec_hdi_omx_service"; // 配置驱动的服务名 192 deviceMatchAttr = "codec_component_capabilities"; // 属性配置 193 } 194 } 195 } 196 ``` 197 1982. 支持的组件相关配置 199 200 在media_codec\codec_component_capabilities.hcs中增加组件配置,具体配置如下: 201 ```c 202 /* node name explanation -- HDF_video_hw_enc_avc_rk: 203 ** 204 ** HDF____________video__________________hw____________________enc____________avc_______rk 205 ** | | | | | | 206 ** HDF or OMX video or audio hardware or software encoder or decoder mime vendor 207 */ 208 HDF_video_hw_enc_avc_rk { 209 role = 1; // AvCodecRole配置 210 type = 1; // CodecType配置 211 name = "OMX.rk.video_encoder.avc"; // 组件名配置 212 supportProfiles = [1, 32768, 2, 32768, 8, 32768]; // 支持的profile配置 213 maxInst = 4; // 最大实例数量配置 214 isSoftwareCodec = false; // 硬件/软件 215 processModeMask = []; // CodecProcessMode配置 216 capsMask = [0x01]; // CodecCapsMask配置 217 minBitRate = 1; // 最小比特率 218 maxBitRate = 40000000; // 最大比特率 219 minWidth = 176; // 视频最小宽 220 minHeight = 144; // 视频最小高 221 maxWidth = 1920; // 视频最大宽 222 maxHeight = 1088; // 视频最大高 223 widthAlignment = 16; // 水平对齐 224 heightAlignment = 8; // 垂直对齐 225 minBlockCount = 0xFFFFFFFF; 226 maxBlockCount = 0xFFFFFFFF; 227 minBlocksPerSecond = 0xFFFFFFFF; 228 maxBlocksPerSecond = 0xFFFFFFFF; 229 blockSizeWidth = 0xFFFFFFFF; 230 blockSizeHeight = 0xFFFFFFFF; 231 supportPixelFmts = [28, 24, 30, 22, 7, 3, 14, 13, 20, 26, 27, 12]; // 支持的颜色列表,Display支持的颜色列表 232 measuredFrameRate = [320, 240, 165, 165, 720, 480, 149, 149, 1280, 720, 73, 73, 1920, 1080, 18, 18]; 233 bitRateMode = [1, 2]; // 比特率模式,BitRateMode 234 minFrameRate = 0; // 帧率配置 235 maxFrameRate = 0; 236 } 237 ``` 238 239### 使用实例 240在按照开发步骤进行相关操作后,Codec模块完成了基本的驱动适配,用户可使用Codec模块提供的HDI接口进行下一步的开发。Codec HDI核心功能如下: 241 2421. 提供Codec HDI接口供北向视频服务调用,实现视频服务的基本编解码。 2432. 作为标准南向接口,保证南向OEM产商实现HDI-adapter的规范性,保证生态良性演进。 244 245用户开发步骤如下所示: 246 2471. 初始化,包括接口实例、回调的初始化和对应的组件的初始化; 2482. 设置编解码参数和配置信息,如视频宽、高和码率等; 2493. 输入输出Buffer申请; 2504. 编解码Buffer流转,使组件进入OMX_Executing状态,并处理相应的回调; 2515. 接口去初始化,销毁buffer,关闭组件并释放所有的接口对象; 252 253#### 初始化 254初始化过程包括接口的初始化,回调的初始化以及组件的创建。 255```cpp 256// 初始化Codec HDI ComponentManager实例 257omxMgr_ = GetCodecComponentManager(); 258 259// 初始化回调 260callback_ = CodecCallbackTypeStubGetInstance(); 261if (!omxMgr_ || !callback_) { 262 FUNC_EXIT_ERR(); 263 return false; 264} 265// 设置回调函数指针 266callback_->EventHandler = &OMXCore::OnEvent; 267callback_->EmptyBufferDone = &OMXCore::OnEmptyBufferDone; 268callback_->FillBufferDone = &OMXCore::OnFillBufferDone; 269 270// 新建组件实例 271uint32_t err = HDF_SUCCESS; 272if (codec == codecMime::AVC) { 273 err = omxMgr_->CreateComponent(&client_, &componentId_, const_cast<char *>(DECODER_AVC), (int64_t)this, 274 callback_); 275} else { 276 err = omxMgr_->CreateComponent(&client_, &componentId_, const_cast<char *>(DECODER_HEVC), (int64_t)this, 277 callback_); 278} 279``` 280 281#### 设置编解码参数和配置信息 282Codec HDI编解码参数配置,包括输入输出数据的宽和高,输入数据格式和输出数据格式。 283```cpp 284// 设置输入端口图片的宽高 285OMX_PARAM_PORTDEFINITIONTYPE param; 286InitParam(param); 287param.nPortIndex = (uint32_t)PortIndex::PORT_INDEX_INPUT; 288auto err = client_->GetParameter(client_, OMX_IndexParamPortDefinition, (int8_t *)¶m, sizeof(param)); 289if (err != HDF_SUCCESS) { 290 HDF_LOGE("%{public}s failed PortIndex::PORT_INDEX_INPUT, index is OMX_IndexParamPortDefinition", __func__); 291 return false; 292} 293HDF_LOGI("PortIndex::PORT_INDEX_INPUT: eCompressionFormat = %{public}d, eColorFormat = %{public}d ", 294 param.format.video.eCompressionFormat, param.format.video.eColorFormat); 295param.format.video.nFrameWidth = width_; 296param.format.video.nFrameHeight = height_; 297param.format.video.nStride = width_; 298param.format.video.nSliceHeight = height_; 299err = client_->SetParameter(client_, OMX_IndexParamPortDefinition, (int8_t *)¶m, sizeof(param)); 300if (err != HDF_SUCCESS) { 301 HDF_LOGE("%{public}s failed with PortIndex::PORT_INDEX_INPUT, index is OMX_IndexParamPortDefinition", __func__); 302 return false; 303} 304// 输出宽、高和格式设置 305InitParam(param); 306param.nPortIndex = (uint32_t)PortIndex::PORT_INDEX_OUTPUT; 307err = client_->GetParameter(client_, OMX_IndexParamPortDefinition, (int8_t *)¶m, sizeof(param)); 308if (err != HDF_SUCCESS) { 309 HDF_LOGE("%{public}s failed with PortIndex::PORT_INDEX_OUTPUT, index is OMX_IndexParamPortDefinition", __func__); 310 return false; 311} 312HDF_LOGI("PortIndex::PORT_INDEX_OUTPUT eCompressionFormat = %{public}d, eColorFormat=%{public}d", 313 param.format.video.eCompressionFormat, param.format.video.eColorFormat); 314param.format.video.nFrameWidth = width_; 315param.format.video.nFrameHeight = height_; 316param.format.video.nStride = width_; 317param.format.video.nSliceHeight = height_; 318param.format.video.eColorFormat = AV_COLOR_FORMAT; // 输出数据格式设置为YUV420SP 319err = client_->SetParameter(client_, OMX_IndexParamPortDefinition, (int8_t *)¶m, sizeof(param)); 320if (err != HDF_SUCCESS) { 321 HDF_LOGE("%{public}s failed with PortIndex::PORT_INDEX_OUTPUT, index is OMX_IndexParamPortDefinition", 322 __func__); 323 return false; 324} 325// 设置输入数据为H264/H265格式数据 326OMX_VIDEO_PARAM_PORTFORMATTYPE param; 327InitParam(param); 328param.nPortIndex = (uint32_t)PortIndex::PORT_INDEX_INPUT; 329auto err = client_->GetParameter(client_, OMX_IndexParamVideoPortFormat, (int8_t *)¶m, sizeof(param)); 330if (err != HDF_SUCCESS) { 331 HDF_LOGE("%{public}s failed with PortIndex::PORT_INDEX_INPUT", __func__); 332 return false; 333} 334HDF_LOGI("set Format PortIndex::PORT_INDEX_INPUT eCompressionFormat = %{public}d, eColorFormat=%{public}d", 335 param.eCompressionFormat, param.eColorFormat); 336param.xFramerate = FRAME; // 30帧 337if (codecMime_ == codecMime::AVC) { 338 param.eCompressionFormat = OMX_VIDEO_CodingAVC; // H264 339} else { 340 param.eCompressionFormat = (OMX_VIDEO_CODINGTYPE)CODEC_OMX_VIDEO_CodingHEVC; // H265 341} 342 343err = client_->SetParameter(client_, OMX_IndexParamVideoPortFormat, (int8_t *)¶m, sizeof(param)); 344if (err != HDF_SUCCESS) { 345 HDF_LOGE("%{public}s failed with PortIndex::PORT_INDEX_INPUT", __func__); 346 return false; 347} 348``` 349 350#### 申请输入输出Buffer 351此处讲解输入输出buffer的申请的整个过程,我们需要按照下面的步骤依次执行: 352 3531. 用户通过UseBuffer申请输入输出Buffer,并保存bufferId,后续buffer轮转可以直接通过bufferId来操作。 3542. 用户需要判断对应的端口是否是使能状态,如果不是,需要先将对应的端口设置为使能状态。 3553. 用户通过SendCommand将组件的状态为修改为OMX_StateIdle,需要等待其结果通知。 356```cpp 357// 输入端口buffer申请 358auto ret = UseBufferOnPort(PortIndex::PORT_INDEX_INPUT); 359if (!ret) { 360 HDF_LOGE("%{public}s UseBufferOnPort PortIndex::PORT_INDEX_INPUT error", __func__); 361 return false; 362} 363// 输出端口buffer申请 364ret = UseBufferOnPort(PortIndex::PORT_INDEX_OUTPUT); 365if (!ret) { 366 HDF_LOGE("%{public}s UseBufferOnPort PortIndex::PORT_INDEX_OUTPUT error", __func__); 367 return false; 368} 369// 发送命令使组件进入OMX_StateIdle状态 370auto err = client_->SendCommand(client_, OMX_CommandStateSet, OMX_StateIdle, NULL, 0); 371if (err != HDF_SUCCESS) { 372 HDF_LOGE("%{public}s failed to SendCommand with OMX_CommandStateSet:OMX_StateIdle", __func__); 373 return false; 374} 375HDF_LOGI("Wait for OMX_StateIdle status"); 376this->WaitForStatusChanged(); 377``` 378 379UseBufferOnPort实现如下: 380 381```cpp 382bool CodecHdiDecode::UseBufferOnPort(enum PortIndex portIndex) 383{ 384 HDF_LOGI("%{public}s enter, portIndex = %{public}d", __func__, portIndex); 385 int bufferSize = 0; 386 int bufferCount = 0; 387 bool bPortEnable = false; 388 // 获取端口buffer参数 389 OMX_PARAM_PORTDEFINITIONTYPE param; 390 InitParam(param); 391 param.nPortIndex = (OMX_U32)portIndex; 392 auto err = client_->GetParameter(client_, OMX_IndexParamPortDefinition, (int8_t *)¶m, sizeof(param)); 393 if (err != HDF_SUCCESS) { 394 HDF_LOGE("%{public}s failed to GetParameter with OMX_IndexParamPortDefinition : portIndex[%{public}d]", 395 __func__, portIndex); 396 return false; 397 } 398 bufferSize = param.nBufferSize; 399 bufferCount = param.nBufferCountActual; 400 bPortEnable = param.bEnabled; 401 HDF_LOGI("buffer index [%{public}d], buffer size [%{public}d], " 402 "buffer count [%{public}d], portEnable[%{public}d], err [%{public}d]", 403 portIndex, bufferSize, bufferCount, bPortEnable, err); 404 { 405 OMX_PARAM_BUFFERSUPPLIERTYPE param; 406 InitParam(param); 407 param.nPortIndex = (uint32_t)portIndex; 408 auto err = client_->GetParameter(client_, OMX_IndexParamCompBufferSupplier, (int8_t *)¶m, sizeof(param)); 409 HDF_LOGI("param.eBufferSupplier[%{public}d] isSupply [%{public}d], err [%{public}d]", param.eBufferSupplier, 410 this->isSupply_, err); 411 } 412 // 设置端口buffer 413 UseBufferOnPort(portIndex, bufferCount, bufferSize); 414 // 检查端口是否可用状态 415 if (!bPortEnable) { 416 auto err = client_->SendCommand(client_, OMX_CommandPortEnable, (uint32_t)portIndex, NULL, 0); 417 if (err != HDF_SUCCESS) { 418 HDF_LOGE("%{public}s SendCommand OMX_CommandPortEnable::PortIndex::PORT_INDEX_INPUT error", __func__); 419 return false; 420 } 421 } 422 return true; 423} 424 425bool CodecHdiDecode::UseBufferOnPort(enum PortIndex portIndex, int bufferCount, int bufferSize) 426{ 427 for (int i = 0; i < bufferCount; i++) { 428 OmxCodecBuffer *omxBuffer = new OmxCodecBuffer(); 429 memset_s(omxBuffer, sizeof(OmxCodecBuffer), 0, sizeof(OmxCodecBuffer)); 430 omxBuffer->size = sizeof(OmxCodecBuffer); 431 omxBuffer->version.s.nVersionMajor = 1; 432 omxBuffer->bufferType = BUFFER_TYPE_AVSHARE_MEM_FD; 433 int fd = AshmemCreate(0, bufferSize); 434 shared_ptr<Ashmem> sharedMem = make_shared<Ashmem>(fd, bufferSize); 435 omxBuffer->bufferLen = FD_SIZE; 436 omxBuffer->buffer = (uint8_t *)(unsigned long)fd; 437 omxBuffer->allocLen = bufferSize; 438 omxBuffer->fenceFd = -1; 439 440 if (portIndex == PortIndex::PORT_INDEX_INPUT) { 441 omxBuffer->type = READ_ONLY_TYPE; // ReadOnly 442 sharedMem->MapReadAndWriteAshmem(); 443 } else { 444 omxBuffer->type = READ_WRITE_TYPE; 445 sharedMem->MapReadOnlyAshmem(); 446 } 447 auto err = client_->UseBuffer(client_, (uint32_t)portIndex, omxBuffer); 448 if (err != HDF_SUCCESS) { 449 HDF_LOGE("%{public}s failed to UseBuffer with portIndex[%{public}d]", __func__, portIndex); 450 sharedMem->UnmapAshmem(); 451 sharedMem->CloseAshmem(); 452 sharedMem = nullptr; 453 return false; 454 } 455 omxBuffer->bufferLen = 0; 456 HDF_LOGI("UseBuffer returned bufferID [%{public}d]", omxBuffer->bufferId); 457 458 BufferInfo *bufferInfo = new BufferInfo; 459 bufferInfo->omxBuffer = omxBuffer; 460 bufferInfo->avSharedPtr = sharedMem; 461 bufferInfo->portIndex = portIndex; 462 omxBuffers_.insert(std::make_pair<int, BufferInfo *>(omxBuffer->bufferId, std::move(bufferInfo))); 463 if (portIndex == PortIndex::PORT_INDEX_INPUT) { 464 unUsedInBuffers_.push_back(omxBuffer->bufferId); 465 } else { 466 unUsedOutBuffers_.push_back(omxBuffer->bufferId); 467 } 468 int fdret = (int)omxBuffer->buffer; 469 HDF_LOGI("{bufferID = %{public}d, srcfd = %{public}d, retfd = %{public}d}", omxBuffer->bufferId, fd, fdret); 470 } 471 return true; 472} 473``` 474 475#### 编解码Buffer流转 476用户需要先将组件设置为OMX_StateExecuting状态,然后填充输入buffer,读取输出buffer,进行buffer的轮转。 477 478```cpp 479// 设置组件进入OMX_StateExecuting状态并开始buffer的轮转 480HDF_LOGI("...command to OMX_StateExecuting...."); 481auto err = client_->SendCommand(client_, OMX_CommandStateSet, OMX_StateExecuting, NULL, 0); 482if (err != HDF_SUCCESS) { 483 HDF_LOGE("%{public}s failed to SendCommand with OMX_CommandStateSet:OMX_StateIdle", __func__); 484 return; 485} 486// 设置输出buffer填充 487for (auto bufferId : unUsedOutBuffers_) { 488 HDF_LOGI("fill bufferid [%{public}d]", bufferId); 489 auto iter = omxBuffers_.find(bufferId); 490 if (iter != omxBuffers_.end()) { 491 BufferInfo *bufferInfo = iter->second; 492 auto err = client_->FillThisBuffer(client_, bufferInfo->pOmxBuffer); 493 if (err != HDF_SUCCESS) { 494 HDF_LOGE("FillThisBuffer error"); 495 FUNC_EXIT_ERR(); 496 return; 497 } 498 } 499} 500// 填充输入buffer 501bool bEndOfFile = false; 502while (!bEndOfFile) { 503 int bufferID = GetFreeBufferId(); 504 if (this->exit_) { 505 break; 506 } 507 if (bufferID < 0) { 508 usleep(10000); 509 continue; 510 } 511 auto iter = omxBuffers_.find(bufferID); 512 if (iter == omxBuffers_.end()) { 513 continue; 514 } 515 BufferInfo *bufferInfo = iter->second; 516 void *sharedAddr = (void *)bufferInfo->avSharedPtr->ReadFromAshmem(0, 0); 517 bool bEOS = (size_t)this->ReadOnePacket(fpIn_, (char *)sharedAddr, bufferInfo->omxBuffer->filledLen); 518 HDF_LOGI("read data size is %{public}d", bufferInfo->omxBuffer->filledLen); 519 bufferInfo->omxBuffer->offset = 0; 520 if (bEOS) { 521 bufferInfo->omxBuffer->flag = OMX_BUFFERFLAG_EOS; 522 bEndOfFile = true; 523 } 524 auto err = client_->EmptyThisBuffer(client_, bufferInfo->omxBuffer); 525 if (err != HDF_SUCCESS) { 526 HDF_LOGE("%{public}s EmptyThisBuffer error", __func__); 527 return; 528 } 529} 530// wait 531while (!this->exit_) { 532 usleep(10000); 533 continue; 534} 535// 解码完成后使组件进入OMX_StateIdle状态 536client_->SendCommand(client_, OMX_CommandStateSet, OMX_StateIdle, NULL, 0); 537``` 538 539当在rk开发板上进行解码时,由于其OMX的实现不支持数据的分帧,所以需要手动分帧,目前简单实现按照起始码0x000001或0x00000001分帧发送到服务端处理。分帧代码如下: 540 541```cpp 542// 文件分帧读取实现 543bool OMXCore::ReadOnePacket(FILE* fp, char* buf, uint32_t& nFilled) 544{ 545 // 先读取4个字节 546 size_t t = fread(buf, 1, 4, fp); 547 if (t < 4) { 548 // 文件读取结束 549 return true; 550 } 551 size_t filled = 0; 552 filled = 4; 553 554 bool bRet = true; 555 while (!feof(fp)) { 556 fread(buf + filled, 1, 1, fp); 557 if (buf[filled] == 1) { 558 // 检查起始码 559 if ((buf[filled - 1] == 0) && 560 (buf[filled - 2] == 0) && 561 (buf[filled - 3] == 0)) { 562 fseek(fp, -4, SEEK_CUR); 563 filled -= 3; 564 bRet = false; 565 break; 566 } else if ((buf[filled - 1] == 0) && 567 (buf[filled - 2] == 0)) { 568 fseek(fp, -3, SEEK_CUR); 569 filled -= 2; 570 bRet = false; 571 break; 572 } 573 } 574 filled++; 575 } 576 nFilled = filled; 577 return bRet; 578} 579``` 580 581Codec HDI提供3个回调函数:EventHandler,EmptyBufferDone和FillBufferDone。 582 583- EventHandler:主要命令完成后的通知,例如:OMX_StateIdle转为OMX_StateExecuting的命令执行成功通知等。 584- EmptyBufferDone:输入数据消费完毕,客户端需要重新填入待编解码数据,再次调用EmptyThisBuffer。 585- FillBufferDone:输出数据填充完毕,客户端需要读取已编码/解码数据,再次调用FillThisBuffer。 586 587```cpp 588// EmptyBufferDone回调处理示例 589int32_t OMXCore::OnEmptyBufferDone(struct CodecCallbackType *self, int8_t *pAppData, uint32_t pAppDataLen, 590 const struct OmxCodecBuffer *pBuffer) 591{ 592 HDF_LOGI("onEmptyBufferDone: pBuffer.bufferID [%{public}d]", pBuffer->bufferId); 593 g_core->OnEmptyBufferDone(pBuffer); 594 return HDF_SUCCESS; 595} 596int32_t OMXCore::OnEmptyBufferDone(const struct OmxCodecBuffer *pBuffer) 597{ 598 unique_lock<mutex> ulk(mLockInputBuffers_); 599 unUsedInBuffers_.push_back(pBuffer->bufferId); 600 return HDF_SUCCESS; 601} 602// FillBufferDone回调处理示例 603int32_t OMXCore::OnFillBufferDone(struct CodecCallbackType *self, int8_t *pAppData, uint32_t pAppDataLen, 604 struct OmxCodecBuffer *pBuffer) 605{ 606 HDF_LOGI("onFillBufferDone: pBuffer.bufferID [%{public}d]", pBuffer->bufferId); 607 g_core->OnFillBufferDone(pBuffer); 608 return HDF_SUCCESS; 609} 610int32_t OMXCore::onFillBufferDone(struct OmxCodecBuffer* pBuffer) 611{ 612 // 根据bufferID找到buffer 613 if (bExit_) { 614 return HDF_SUCCESS; 615 } 616 617 auto iter = omxBuffers_.find(pBuffer->bufferId); 618 if (iter == omxBuffers_.end() || !iter->second) { 619 return HDF_SUCCESS; 620 } 621 // 取出输出的数据 622 BufferInfo *pBufferInfo = iter->second; 623 const void *addr = pBufferInfo->avSharedPtr->ReadFromAshmem(pBuffer->filledLen, pBuffer->offset); 624 // 解码数据保存到文件 625 fwrite(addr, 1, pBuffer->filledLen, fpOut_.get()); 626 fflush(fpOut_.get()); 627 // 重置buffer数据 628 pBuffer->offset = 0; 629 pBuffer->filledLen = 0; 630 if (pBuffer->flag == OMX_BUFFERFLAG_EOS) { 631 // 结束 632 bExit_ = true; 633 HDF_LOGI("OnFillBufferDone the END coming"); 634 return HDF_SUCCESS; 635 } 636 // 再次调用FillThisBuffer 637 auto err = client_->FillThisBuffer(client_, pBufferInfo->pOmxBuffer); 638 if (err != HDF_SUCCESS) { 639 HDF_LOGE("FillThisBuffer error"); 640 return HDF_SUCCESS; 641 } 642 return HDF_SUCCESS; 643} 644 645// EventHandler示例 646int32_t CodecHdiDecode::OnEvent(struct CodecCallbackType *self, enum OMX_EVENTTYPE event, struct EventInfo *info) 647{ 648 HDF_LOGI("onEvent: appData[0x%{public}p], eEvent [%{public}d], " 649 "nData1[%{public}d]", info->appData, event, info->data1); 650 switch (event) { 651 case OMX_EventCmdComplete: { 652 OMX_COMMANDTYPE cmd = (OMX_COMMANDTYPE)info->data1; 653 if (OMX_CommandStateSet == cmd) { 654 HDF_LOGI("OMX_CommandStateSet reached, status is %{public}d", info->data2); 655 g_core->onStatusChanged(); 656 } 657 break; 658 } 659 default: 660 break; 661 } 662 return HDF_SUCCESS; 663} 664``` 665 666#### 接口去初始化 667组件关闭前,需要将组件状态修改为OMX_StateIdle,然后开始释放输入输出Buffer,再将组件状态修改为OMX_StateLoaded,最后再调用DestoryComponent去关闭组件。 668 669##### Buffer释放示例 670 671```cpp 672// 发送命令使组件进入OMX_StateLoaded状态 673client_->SendCommand(client_, OMX_CommandStateSet, OMX_StateLoaded, nullptr, 0); 674 675// 释放所有申请的buffer 676auto iter = omxBuffers_.begin(); 677while (iter != omxBuffers_.end()) { 678 BufferInfo *bufferInfo = iter->second; 679 client_->FreeBuffer(client_, (uint32_t)bufferInfo->portIndex, bufferInfo->omxBuffer); 680 delete bufferInfo; 681 iter++; 682} 683omxBuffers_.clear(); 684unUsedInBuffers_.clear(); 685unUsedOutBuffers_.clear(); 686 687enum OMX_STATETYPE status; 688client_->GetState(client_, &status); 689// buffer释放后组件即进入OMX_StateLoaded状态 690if (status != OMX_StateLoaded) { 691 HDF_LOGI("Wait for OMX_StateLoaded status"); 692 this->WaitForStatusChanged(); 693} else { 694 HDF_LOGI(" status is %{public}d", status); 695} 696``` 697 698##### 组件实例释放示例 699 700```cpp 701// 组件实例释放 702void OMXCore::Release() { 703 omxMgr_->DestoryComponent(client_); 704 client_ = nullptr; 705 CodecComponentManagerRelease(); 706} 707``` 708 709# 常见问题 710 711## 解码过程中部分绿屏 712 713**现象描述** 714 715解码过程中,开始能正常解码,后续绿屏比较多。 716 717**可能原因** 718 719OpenMax不支持分帧。 720 721**解决办法** 722 723上层在调用EmptyThisBuffer时,需要按照每次一帧的方式传入。 724 725## 解码过程中全是绿屏 726 727**现象描述** 728 729解码过程中,解码失败全部播放不了。 730 731**可能原因** 732 733OpenMax对AVCC格式的数据处理,第一帧一定要是extra_data,可能没有正常输入extra_data导致AVCC格式解码失败。 734 735**解决办法** 736 737将sps和pps按照extrea_data格式写入buffer,并设置好buffer的flag为OMX_BUFFERFLAG_EXTRADATA。 738 739## 编码输出播放不了 740 741**现象描述** 742 743编码输出视频不正确,将生成的视频流(如H264流)写入文件后,通过ffplay工具播放不了。 744 745**可能原因** 746 7471. 输出端口的xFramerate参数未正常设置。 7482. 如果设置了参数OMX_VIDEO_PARAM_AVCTYPE,请检查此参数是否正确。 749 750 751**解决办法** 752 753请看编码时codec_host的日志,搜索“encode params init settings”,确认是否出现异常的参数。如果是framerate为0,则是原因1,需要将正常的framerate左移16位;如果是其它参数异常,可能是原因2,需要检查其相应的参数。 754 755 756# 参考 757 758如果您想了解更多关于Codec特性的源码及使用信息,请参考[Codec驱动代码仓](https://gitee.com/openharmony/drivers_peripheral/tree/master/codec)。