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 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- icodec_component_manager.h 58 59 | 接口名称 | 功能描述 | 60 | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------| 61 | int32_t CreateComponent(sptr<ICodecComponent>& component, uint32_t& componentId,<br />const std::string& compName, int64_t appData, const sptr<ICodecCallback>& callbacks) | 创建Codec组件实例 | 62 | int32_t DestoryComponent(uint32_t componentId) | 销毁Codec组件实例 | 63 64- icodec_component.h 65 66 | 接口名称 | 功能描述 | 67 | ------------------------------------------------------------ | ---------------------- | 68 | int32_t SendCommand(CodecCommandType cmd, uint32_t param, const std::vector<int8_t>& cmdData) | 发送命令给组件 | 69 | int32_t GetParameter(uint32_t index, const std::vector<int8_t>& inParamStruct, std::vector<int8_t>& outParamStruct) | 获取组件参数设置 | 70 | int32_t SetParameter(uint32_t index, const std::vector<int8_t>& paramStruct) | 设置组件需要的参数 | 71 | int32_t GetState(CodecStateType& state) | 获取组件的状态 | 72 | int32_t UseBuffer(uint32_t portIndex, const OmxCodecBuffer& inBuffer, OmxCodecBuffer& outBuffer) | 指定组件端口的buffer | 73 | int32_t FreeBuffer(uint32_t portIndex, const OmxCodecBuffer& buffer) | 释放buffer | 74 | int32_t EmptyThisBuffer(const OmxCodecBuffer& buffer) | 编解码输入待处理buffer | 75 | int32_t FillThisBuffer(const OmxCodecBuffer& buffer) | 编解码输出填充buffer | 76 77- icodec_callback.h 78 79 | 接口名称 | 功能描述 | 80 | ------------------------------------------------------------ | ---------------------------------- | 81 | int32_t EventHandler(CodecEventType event, const EventInfo& info) | 事件上报 | 82 | int32_t EmptyBufferDone(int64_t appData, const OmxCodecBuffer& buffer) | 上报输入buffer编码或者解码处理完毕 | 83 | int32_t FillBufferDone(int64_t appData, const 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_codeccomponentmanagerDriverEntry结构体,实现Bind、Init、Release函数指针。 92 93```c 94static struct HdfDriverEntry g_codeccomponentmanagerDriverEntry = { 95 .moduleVersion = 1, 96 .moduleName = "codec_component_manager_service", 97 .Bind = HdfCodecComponentManagerDriverBind, 98 .Init = HdfCodecComponentManagerDriverInit, 99 .Release = HdfCodecComponentManagerDriverRelease, 100}; // 将Codec HDI的HdfDriverEntry结构体注册到HDF上 101``` 102 103- HdfCodecComponentManagerDriverBind:将HDF中device绑定到HdfCodecComponentManagerHost,将codec service注册到HDF框架。 104 105 ```c 106 static int HdfCodecComponentManagerDriverBind(struct HdfDeviceObject *deviceObject) 107 { 108 CODEC_LOGI("HdfCodecComponentManagerDriverBind enter"); 109 110 auto *hdfCodecComponentManagerHost = new (std::nothrow) HdfCodecComponentManagerHost; 111 if (hdfCodecComponentManagerHost == nullptr) { 112 CODEC_LOGE("failed to create create HdfCodecComponentManagerHost object"); 113 return HDF_FAILURE; 114 } 115 116 hdfCodecComponentManagerHost->ioService.Dispatch = CodecComponentManagerDriverDispatch; 117 hdfCodecComponentManagerHost->ioService.Open = NULL; 118 hdfCodecComponentManagerHost->ioService.Release = NULL; 119 120 auto serviceImpl = ICodecComponentManager::Get(true); 121 if (serviceImpl == nullptr) { 122 CODEC_LOGE("failed to get of implement service"); 123 delete hdfCodecComponentManagerHost; 124 return HDF_FAILURE; 125 } 126 127 hdfCodecComponentManagerHost->stub = 128 OHOS::HDI::ObjectCollector::GetInstance().GetOrNewObject(serviceImpl, ICodecComponentManager::GetDescriptor()); 129 if (hdfCodecComponentManagerHost->stub == nullptr) { 130 CODEC_LOGE("failed to get stub object"); 131 delete hdfCodecComponentManagerHost; 132 return HDF_FAILURE; 133 } 134 135 deviceObject->service = &hdfCodecComponentManagerHost->ioService; 136 return HDF_SUCCESS; 137 } 138 ``` 139 140- HdfCodecComponentManagerDriverInit:加载HCS(HDF Configuration Source)中的属性配置。 141 142 ```c 143 static int HdfCodecComponentManagerDriverInit(struct HdfDeviceObject *deviceObject) 144 { 145 CODEC_LOGI("HdfCodecComponentManagerDriverInit enter"); 146 if (DevHostRegisterDumpHost(CodecDfxService::DevCodecHostDump) != HDF_SUCCESS) { 147 CODEC_LOGE("DevHostRegisterDumpHost error!"); 148 } 149 return HDF_SUCCESS; 150 } 151 ``` 152 153- HdfCodecComponentTypeDriverRelease:释放驱动实例。 154 155 ```c 156 static void HdfCodecComponentManagerDriverRelease(struct HdfDeviceObject *deviceObject) 157 { 158 CODEC_LOGI("HdfCodecComponentManagerDriverRelease enter"); 159 if (deviceObject->service == nullptr) { 160 CODEC_LOGE("HdfCodecComponentManagerDriverRelease not initted"); 161 return; 162 } 163 164 auto *hdfCodecComponentManagerHost = 165 CONTAINER_OF(deviceObject->service, struct HdfCodecComponentManagerHost, ioService); 166 delete hdfCodecComponentManagerHost; 167 } 168 ``` 169 170#### Driver的HCS配置 171HCS配置包括两部分: 172 173- device相关配置。 174- 支持的组件相关配置。 175 176HCS配置内容包括:驱动节点、加载顺序、服务名称等。HCS语法可参考[配置管理](driver-hdf-manage.md)。 177 178以RK3568开发板为例,标准系统配置文件路径(其它系统暂不涉及): 179vendor/hihope/rk3568/hdf_config/uhdf/ 180 1811. device相关配置 182 183 在device_info.hcs的codec_host中增加codec_component_manager_service配置,具体配置如下: 184 ```c 185 codec :: host { 186 hostName = "codec_host"; 187 priority = 50; 188 gid = ["codec_host", "uhdf_driver", "vendor_mpp_driver"]; 189 codec_omx_idl_device :: device { 190 device0 :: deviceNode { 191 policy = 2; // 自动加载,非延迟加载 192 priority = 100; // 优先级 193 moduleName = "libcodec_driver.z.so"; // 驱动的动态库 194 serviceName = "codec_component_manager_service"; // 配置驱动的服务名 195 deviceMatchAttr = "media_codec_capabilities"; // 属性配置 196 } 197 } 198 } 199 ``` 200 2012. 支持的组件相关配置 202 203 在media_codec\media_codec_capabilities.hcs中增加组件配置,具体配置如下: 204 ```c 205 /* node name explanation -- HDF_video_hw_enc_avc_rk: 206 ** 207 ** HDF____________video__________________hw____________________enc____________avc_______rk 208 ** | | | | | | 209 ** HDF or OMX video or audio hardware or software encoder or decoder mime vendor 210 */ 211 HDF_video_hw_enc_avc_rk { 212 role = 1; // AvCodecRole配置 213 type = 1; // CodecType配置 214 name = "OMX.rk.video_encoder.avc"; // 组件名配置 215 supportProfiles = [1, 32768, 2, 32768, 8, 32768]; // 支持的profile配置 216 maxInst = 4; // 最大实例数量配置 217 isSoftwareCodec = false; // 硬件/软件 218 processModeMask = []; // CodecProcessMode配置 219 capsMask = [0x01]; // CodecCapsMask配置 220 minBitRate = 1; // 最小比特率 221 maxBitRate = 40000000; // 最大比特率 222 minWidth = 176; // 视频最小宽 223 minHeight = 144; // 视频最小高 224 maxWidth = 1920; // 视频最大宽 225 maxHeight = 1088; // 视频最大高 226 widthAlignment = 16; // 水平对齐 227 heightAlignment = 8; // 垂直对齐 228 minBlockCount = 0xFFFFFFFF; 229 maxBlockCount = 0xFFFFFFFF; 230 minBlocksPerSecond = 0xFFFFFFFF; 231 maxBlocksPerSecond = 0xFFFFFFFF; 232 blockSizeWidth = 0xFFFFFFFF; 233 blockSizeHeight = 0xFFFFFFFF; 234 supportPixelFmts = [28, 24, 20, 12]; // 支持的颜色列表,Display支持的颜色列表 235 measuredFrameRate = [320, 240, 165, 165, 720, 480, 149, 149, 1280, 720, 73, 73, 1920, 1080, 18, 18]; 236 bitRateMode = [1, 2]; // 比特率模式,BitRateMode 237 minFrameRate = 0; // 帧率配置 238 maxFrameRate = 0; 239 } 240 ``` 241 242### 使用实例 243在按照开发步骤进行相关操作后,Codec模块完成了基本的驱动适配,用户可使用Codec模块提供的HDI接口进行下一步的开发。Codec HDI核心功能如下: 244 2451. 提供Codec HDI接口供北向视频服务调用,实现视频服务的基本编解码。 2462. 作为标准南向接口,保证南向OEM产商实现HDI-adapter的规范性,保证生态良性演进。 247 248用户开发步骤如下所示: 249 2501. 初始化,包括接口实例、回调的初始化和对应的组件的初始化; 2512. 设置编解码参数和配置信息,如视频宽、高和码率等; 2523. 输入输出Buffer申请; 2534. 编解码Buffer流转,使组件进入CODEC_STATE_EXECUTING状态,并处理相应的回调; 2545. 接口去初始化,销毁buffer,关闭组件并释放所有的接口对象; 255 256#### 初始化 257初始化过程包括接口的初始化,回调的初始化以及组件的创建。 258```cpp 259// 初始化Codec HDI ComponentManager实例 260omxMgr_ = ICodecComponentManager::Get(false); 261if ((omxMgr_ == nullptr)) { 262 HDF_LOGE("%{public}s omxMgr_ is null", __func__); 263 return false; 264} 265// 初始化回调 266callback_ = new CodecHdiCallback(shared_from_this()); 267if ((callback_ == nullptr)) { 268 HDF_LOGE("%{public}s callback_ is null", __func__); 269 return false; 270} 271// 新建组件实例 272err = omxMgr_->CreateComponent(client_, componentId_, compName, reinterpret_cast<int64_t>(this), callback_); 273if (err != HDF_SUCCESS) { 274 HDF_LOGE("%{public}s failed to CreateComponent", __func__); 275 return false; 276} 277``` 278 279#### 设置编解码参数和配置信息 280Codec HDI编解码参数配置,包括输入输出数据的宽和高,输入数据格式和输出数据格式。 281```cpp 282// 设置输入端口图片的宽高 283OMX_PARAM_PORTDEFINITIONTYPE param; 284if (util_->InitParam(param) != HDF_SUCCESS) { 285 return HDF_FAILURE; 286} 287param.nPortIndex = static_cast<uint32_t>(PortIndex::PORT_INDEX_INPUT); 288 289std::vector<int8_t> inVec, outVec; 290util_->ObjectToVector(param, inVec); 291auto err = client_->GetParameter(OMX_IndexParamPortDefinition, inVec, outVec); 292if (err != HDF_SUCCESS) { 293 HDF_LOGE("%{public}s failed PortIndex::PORT_INDEX_INPUT, index is OMX_IndexParamPortDefinition", __func__); 294 return err; 295} 296util_->VectorToObject(outVec, param); 297 298HDF_LOGI("PortIndex::PORT_INDEX_INPUT: eCompressionFormat = %{public}d, eColorFormat = %{public}d ", 299 param.format.video.eCompressionFormat, param.format.video.eColorFormat); 300util_->setParmValue(param, width_, height_, stride_); 301util_->ObjectToVector(param, inVec); 302err = client_->SetParameter(OMX_IndexParamPortDefinition, inVec); 303if (err != HDF_SUCCESS) { 304 HDF_LOGE("%{public}s failed with PortIndex::PORT_INDEX_INPUT, index is OMX_IndexParamPortDefinition", __func__); 305 return err; 306} 307// 输出宽、高和格式设置 308if (util_->InitParam(param) != HDF_SUCCESS) { 309 return HDF_FAILURE; 310} 311param.nPortIndex = static_cast<uint32_t>(PortIndex::PORT_INDEX_OUTPUT); 312util_->ObjectToVector(param, inVec); 313err = client_->GetParameter(OMX_IndexParamPortDefinition, inVec, outVec); 314if (err != HDF_SUCCESS) { 315 HDF_LOGE("%{public}s failed with PortIndex::PORT_INDEX_OUTPUT, index is OMX_IndexParamPortDefinition", 316 __func__); 317 return err; 318} 319util_->VectorToObject(outVec, param); 320 321HDF_LOGI("PortIndex::PORT_INDEX_OUTPUT eCompressionFormat = %{public}d, eColorFormat=%{public}d", 322 param.format.video.eCompressionFormat, param.format.video.eColorFormat); 323util_->setParmValue(param, width_, height_, stride_); 324param.format.video.eColorFormat = AV_COLOR_FORMAT; // 输出数据格式设置为YUV420SP 325err = client_->SetParameter(OMX_IndexParamPortDefinition, inVec); 326if (err != HDF_SUCCESS) { 327 HDF_LOGE("%{public}s failed with PortIndex::PORT_INDEX_OUTPUT, index is OMX_IndexParamPortDefinition", 328 __func__); 329 return err; 330} 331// 设置输入数据为H264/H265格式数据 332OMX_VIDEO_PARAM_PORTFORMATTYPE param; 333if (util_->InitParam(param) != HDF_SUCCESS) { 334 return false; 335} 336param.nPortIndex = (uint32_t)PortIndex::PORT_INDEX_INPUT; 337std::vector<int8_t> inVec, outVec; 338util_->ObjectToVector(param, inVec); 339auto err = client_->GetParameter(OMX_IndexParamVideoPortFormat, inVec, outVec); 340if (err != HDF_SUCCESS) { 341 HDF_LOGE("%{public}s failed with PortIndex::PORT_INDEX_INPUT", __func__); 342 return false; 343} 344util_->VectorToObject(outVec, param); 345 346HDF_LOGI("set Format PortIndex::PORT_INDEX_INPUT eCompressionFormat = %{public}d, eColorFormat=%{public}d", 347 param.eCompressionFormat, param.eColorFormat); 348param.xFramerate = FRAME // 30帧 349if (codecMime_ == codecMime::AVC) { 350 param.eCompressionFormat = OMX_VIDEO_CodingAVC; // H264 351} else { 352 param.eCompressionFormat = (OMX_VIDEO_CODINGTYPE)CODEC_OMX_VIDEO_CodingHEVC; // H265 353} 354 355util_->ObjectToVector(param, inVec); 356err = client_->SetParameter(OMX_IndexParamVideoPortFormat, inVec); 357if (err != HDF_SUCCESS) { 358 HDF_LOGE("%{public}s failed with PortIndex::PORT_INDEX_INPUT", __func__); 359 return false; 360} 361``` 362 363#### 申请输入输出Buffer 364此处讲解输入输出buffer的申请的整个过程,我们需要按照下面的步骤依次执行: 365 3661. 用户通过UseBuffer申请输入输出Buffer,并保存bufferId,后续buffer轮转可以直接通过bufferId来操作。 3672. 用户需要判断对应的端口是否是使能状态,如果不是,需要先将对应的端口设置为使能状态。 3683. 用户通过SendCommand将组件的状态为修改为CODEC_STATE_IDLE,需要等待其结果通知。 369```cpp 370// 输入端口buffer申请 371auto err = UseBufferOnPort(PortIndex::PORT_INDEX_INPUT); 372if (err != HDF_SUCCESS) { 373 HDF_LOGE("%{public}s UseBufferOnPort PortIndex::PORT_INDEX_INPUT error", __func__); 374 return false; 375} 376// 输出端口buffer申请 377err = UseBufferOnPort(PortIndex::PORT_INDEX_OUTPUT); 378if (err != HDF_SUCCESS) { 379 HDF_LOGE("%{public}s UseBufferOnPort PortIndex::PORT_INDEX_OUTPUT error", __func__); 380 return false; 381} 382// 发送命令使组件进入OMX_StateIdle状态 383std::vector<int8_t> cmdData; 384auto err = client_->SendCommand(CODEC_COMMAND_STATE_SET, CODEC_STATE_IDLE, cmdData); 385if (err != HDF_SUCCESS) { 386 HDF_LOGE("%{public}s failed to SendCommand with CODEC_COMMAND_STATE_SET:CODEC_STATE_IDLE", __func__); 387 return false; 388} 389``` 390 391UseBufferOnPort实现如下: 392 393```cpp 394int32_t CodecHdiDecode::UseBufferOnPort(PortIndex portIndex) 395{ 396 HDF_LOGI("%{public}s enter, portIndex = %{public}d", __func__, portIndex); 397 int bufferSize = 0; 398 int bufferCount = 0; 399 bool PortEnable = false; 400 // 获取端口buffer参数 401 OMX_PARAM_PORTDEFINITIONTYPE param; 402 if (util_->InitParam(param) != HDF_SUCCESS) { 403 return HDF_FAILURE; 404 } 405 param.nPortIndex = static_cast<OMX_U32>(portIndex); 406 407 std::vector<int8_t> inVec, outVec; 408 util_->ObjectToVector(param, inVec); 409 auto err = client_->GetParameter(OMX_IndexParamPortDefinition, inVec, outVec); 410 if (err != HDF_SUCCESS) { 411 HDF_LOGE("%{public}s failed to GetParameter with OMX_IndexParamPortDefinition : portIndex[%{public}d]", 412 __func__, portIndex); 413 return err; 414 } 415 util_->VectorToObject(outVec, param); 416 417 bufferSize = param.nBufferSize; 418 bufferCount = param.nBufferCountActual; 419 portEnable = param.bEnabled; 420 HDF_LOGI("buffer index [%{public}d], buffer size [%{public}d], " 421 "buffer count [%{public}d], portEnable[%{public}d], ret [%{public}d]", 422 portIndex, bufferSize, bufferCount, portEnable, err); 423 // 设置端口buffer 424 if (useBufferHandle_ && portIndex == PortIndex::PORT_INDEX_OUTPUT) { 425 err = UseBufferHandle(bufferCount, bufferSize); 426 } else { 427 err = UseBufferOnPort(portIndex, bufferCount, bufferSize); 428 } 429 // 检查端口是否可用状态 430 if (!portEnable) { 431 err = client_->SendCommand(CODEC_COMMAND_PORT_ENABLE, static_cast<uint32_t>(portIndex), {}); 432 if (err != HDF_SUCCESS) { 433 HDF_LOGE("%{public}s SendCommand OMX_CommandPortEnable::PortIndex::PORT_INDEX_INPUT error", __func__); 434 return err; 435 } 436 } 437 return HDF_SUCCESS; 438} 439 440int32_t CodecHdiDecode::UseBufferOnPort(PortIndex portIndex, int bufferCount, int bufferSize) 441{ 442 if (bufferCount <= 0 || bufferSize <= 0) { 443 HDF_LOGE("UseBufferOnPort bufferCount <= 0 or bufferSize <= 0"); 444 return HDF_ERR_INVALID_PARAM; 445 } 446 for (int i = 0; i < bufferCount; i++) { 447 std::shared_ptr<OmxCodecBuffer> omxBuffer = std::make_shared<OmxCodecBuffer>(); 448 omxBuffer->size = sizeof(OmxCodecBuffer); 449 omxBuffer->version.s.nVersionMajor = 1; 450 omxBuffer->bufferType = CODEC_BUFFER_TYPE_AVSHARE_MEM_FD; 451 int fd = AshmemCreate(0, bufferSize); 452 shared_ptr<Ashmem> sharedMem = make_shared<Ashmem>(fd, bufferSize); 453 omxBuffer->fd = fd; 454 omxBuffer->bufferhandle = nullptr; 455 omxBuffer->allocLen = bufferSize; 456 omxBuffer->fenceFd = -1; 457 omxBuffer->pts = 0; 458 omxBuffer->flag = 0; 459 460 if (portIndex == PortIndex::PORT_INDEX_INPUT) { 461 omxBuffer->type = READ_ONLY_TYPE; // ReadOnly 462 sharedMem->MapReadAndWriteAshmem(); 463 } else { 464 omxBuffer->type = READ_WRITE_TYPE; 465 sharedMem->MapReadOnlyAshmem(); 466 } 467 OmxCodecBuffer outBuffer; 468 auto err = client_->UseBuffer((uint32_t)portIndex, *omxBuffer.get(), outBuffer); 469 if (err != HDF_SUCCESS) { 470 HDF_LOGE("%{public}s failed to UseBuffer with portIndex[%{public}d]", __func__, portIndex); 471 sharedMem->UnmapAshmem(); 472 sharedMem->CloseAshmem(); 473 sharedMem = nullptr; 474 return err; 475 } 476 omxBuffer->bufferId = outBuffer.bufferId; 477 omxBuffer->fd = -1; 478 HDF_LOGI("UseBuffer returned bufferID [%{public}d]", omxBuffer->bufferId); 479 480 std::shared_ptr<BufferInfo> bufferInfo = std::make_shared<BufferInfo>(); 481 bufferInfo->omxBuffer = omxBuffer; 482 bufferInfo->avSharedPtr = sharedMem; 483 bufferInfo->portIndex = portIndex; 484 omxBuffers_.emplace(std::make_pair(omxBuffer->bufferId, bufferInfo)); 485 if (portIndex == PortIndex::PORT_INDEX_INPUT) { 486 unUsedInBuffers_.push_back(omxBuffer->bufferId); 487 } else { 488 unUsedOutBuffers_.push_back(omxBuffer->bufferId); 489 } 490 } 491 492 return HDF_SUCCESS; 493} 494``` 495 496#### 编解码Buffer流转 497用户需要先将组件设置为CODEC_STATE_EXECUTING状态,然后填充输入buffer,读取输出buffer,进行buffer的轮转。 498 499```cpp 500// 设置组件进入OMX_StateExecuting状态并开始buffer的轮转 501HDF_LOGI("...command to CODEC_STATE_EXECUTING...."); 502auto err = client_->SendCommand(CODEC_COMMAND_STATE_SET, CODEC_STATE_EXECUTING, {}); 503if (err != HDF_SUCCESS) { 504 HDF_LOGE("%{public}s failed to SendCommand with CODEC_COMMAND_STATE_SET:CODEC_STATE_IDLE", __func__); 505 return; 506} 507// 设置输出buffer填充 508if (!FillAllTheBuffer()) { 509 HDF_LOGE("%{public}s FillAllTheBuffer error", __func__); 510 return; 511} 512// 填充输入buffer 513auto t1 = std::chrono::system_clock::now(); 514bool eosFlag = false; 515while (!eosFlag) { 516 if (this->exit_) { 517 break; 518 } 519 int bufferID = GetFreeBufferId(); 520 if (bufferID < 0) { 521 usleep(10000); // 10000 for wait 10ms 522 continue; 523 } 524 auto iter = omxBuffers_.find(bufferID); 525 if (iter == omxBuffers_.end()) { 526 continue; 527 } 528 auto bufferInfo = iter->second; 529 void *sharedAddr = const_cast<void *>(bufferInfo->avSharedPtr->ReadFromAshmem(0, 0)); 530 eosFlag = this->ReadOnePacket(fpIn_, static_cast<char *>(sharedAddr), bufferInfo->omxBuffer->filledLen); 531 bufferInfo->omxBuffer->offset = 0; 532 if (eosFlag) { 533 bufferInfo->omxBuffer->flag = OMX_BUFFERFLAG_EOS; 534 } 535 err = client_->EmptyThisBuffer(*bufferInfo->omxBuffer.get()); 536 if (err != HDF_SUCCESS) { 537 HDF_LOGE("%{public}s EmptyThisBuffer error", __func__); 538 return; 539 } 540} 541// wait 542while (!this->exit_) { 543 usleep(10000); // 10000 for wait 10ms 544} 545// 解码完成后使组件进入OMX_StateIdle状态 546auto t2 = std::chrono::system_clock::now(); 547std::chrono::duration<double> diff = t2 - t1; 548HDF_LOGI("cost %{public}f, count=%{public}d", diff.count(), count_); 549(void)client_->SendCommand(CODEC_COMMAND_STATE_SET, CODEC_STATE_IDLE, {}); 550return; 551} 552``` 553 554当在rk开发板上进行解码时,由于其OMX的实现不支持数据的分帧,所以需要手动分帧,目前简单实现按照起始码0x000001或0x00000001分帧发送到服务端处理。分帧代码如下: 555 556```cpp 557// 文件分帧读取实现 558bool CodecHdiDecode::ReadOnePacket(FILE *fp, char *buf, uint32_t &filledCount) 559{ 560 // 读取起始码 561 size_t t = fread(buf, 1, START_CODE_SIZE_FRAME, fp); 562 if (t < START_CODE_SIZE_FRAME) { 563 return true; 564 } 565 char *temp = buf; 566 temp += START_CODE_SIZE_FRAME; 567 bool ret = true; 568 while (!feof(fp)) { 569 (void)fread(temp, 1, 1, fp); 570 if (*temp != START_CODE) { 571 temp++; 572 continue; 573 } 574 // 检查起始码 575 if ((temp[START_CODE_OFFSET_ONE] == 0) && (temp[START_CODE_OFFSET_SEC] == 0) && 576 (temp[START_CODE_OFFSET_THIRD] == 0)) { 577 fseek(fp, -START_CODE_SIZE_FRAME, SEEK_CUR); 578 temp -= (START_CODE_SIZE_FRAME - 1); 579 ret = false; 580 break; 581 } 582 if ((temp[START_CODE_OFFSET_ONE] == 0) && (temp[START_CODE_OFFSET_SEC] == 0)) { 583 fseek(fp, -START_CODE_SIZE_SLICE, SEEK_CUR); 584 temp -= (START_CODE_SIZE_SLICE - 1); 585 ret = false; 586 break; 587 } 588 temp++; 589 } 590 filledCount = (temp - buf); 591 return ret; 592} 593``` 594 595Codec HDI提供3个回调函数:EventHandler,EmptyBufferDone和FillBufferDone。 596 597- EventHandler:主要命令完成后的通知,例如:CODEC_STATE_IDLE转为CODEC_STATE_EXECUTING的命令执行成功通知等。 598- EmptyBufferDone:输入数据消费完毕,客户端需要重新填入待编解码数据,再次调用EmptyThisBuffer。 599- FillBufferDone:输出数据填充完毕,客户端需要读取已编码/解码数据,再次调用FillThisBuffer。 600 601```cpp 602// EmptyBufferDone回调处理示例 603int32_t CodecHdiDecode::OnEmptyBufferDone(const struct OmxCodecBuffer &buffer) 604{ 605 HDF_LOGI("OnEmptyBufferDone, bufferId [%{public}d]", buffer.bufferId); 606 unique_lock<mutex> ulk(lockInputBuffers_); 607 unUsedInBuffers_.push_back(buffer.bufferId); 608 return HDF_SUCCESS; 609} 610// FillBufferDone回调处理示例 611int32_t CodecHdiDecode::OnFillBufferDone(const struct OmxCodecBuffer &buffer) 612{ 613 HDF_LOGI("OnFillBufferDone, bufferId [%{public}d]", buffer.bufferId); 614 if (exit_) { 615 return HDF_SUCCESS; 616 } 617 618 auto iter = omxBuffers_.find(buffer.bufferId); 619 if ((iter == omxBuffers_.end()) || (iter->second == nullptr)) { 620 return HDF_SUCCESS; 621 } 622 count_++; 623 // read buffer 624 auto bufferInfo = iter->second; 625 if (bufferInfo->avSharedPtr != nullptr) { 626 const void *addr = bufferInfo->avSharedPtr->ReadFromAshmem(buffer.filledLen, buffer.offset); 627 (void)fwrite(addr, 1, buffer.filledLen, fpOut_); 628 } else if (bufferInfo->bufferHandle != nullptr && gralloc_ != nullptr) { 629 gralloc_->Mmap(*bufferInfo->bufferHandle); 630 (void)fwrite(bufferInfo->bufferHandle->virAddr, 1, buffer.filledLen, fpOut_); 631 gralloc_->Unmap(*bufferInfo->bufferHandle); 632 } 633 634 (void)fflush(fpOut_); 635 if (buffer.flag == OMX_BUFFERFLAG_EOS) { 636 // end 637 exit_ = true; 638 HDF_LOGI("OnFillBufferDone the END coming"); 639 return HDF_SUCCESS; 640 } 641 // call fillthisbuffer again 642 auto err = client_->FillThisBuffer(*bufferInfo->omxBuffer.get()); 643 if (err != HDF_SUCCESS) { 644 HDF_LOGE("%{public}s FillThisBuffer error", __func__); 645 return HDF_SUCCESS; 646 } 647 return HDF_SUCCESS; 648} 649// EventHandler示例 650int32_t CodecHdiDecode::EventHandler(CodecEventType event, const EventInfo &info) 651{ 652 switch (event) { 653 case CODEC_EVENT_CMD_COMPLETE: { 654 CodecCommandType cmd = (CodecCommandType)info.data1; 655 if (CODEC_COMMAND_STATE_SET == cmd) { 656 HDF_LOGI("CODEC_COMMAND_STATE_SET reached, status is %{public}d", info.data2); 657 this->OnStatusChanged(); 658 } 659 break; 660 } 661 case OMX_EventPortSettingsChanged: { 662 HDF_LOGI("OMX_EventPortSeetingsChanged reached"); 663 this->HandleEventPortSettingsChanged(info.data1, info.data2); 664 } 665 666 default: 667 break; 668 } 669 670 return HDF_SUCCESS; 671} 672``` 673 674#### 接口去初始化 675组件关闭前,需要将组件状态修改为CODEC_STATE_IDLE,然后开始释放输入输出Buffer,再将组件状态修改为CODEC_STATE_LOADED,最后再调用DestoryComponent去关闭组件。 676 677##### Buffer释放示例 678 679```cpp 680// 发送命令使组件进入OMX_StateLoaded状态 681client_->SendCommand(CODEC_COMMAND_STATE_SET, CODEC_STATE_LOADED, {}); 682 683// 释放所有申请的buffer 684auto iter = omxBuffers_.begin(); 685while (iter != omxBuffers_.end()) { 686 auto bufferInfo = iter->second; 687 iter = omxBuffers_.erase(iter); 688 (void)client_->FreeBuffer((uint32_t)bufferInfo->portIndex, *bufferInfo->omxBuffer.get()); 689 bufferInfo = nullptr; 690} 691 692unUsedInBuffers_.clear(); 693unUsedOutBuffers_.clear(); 694 695// buffer释放后组件即进入OMX_StateLoaded状态 696CodecStateType status = CODEC_STATE_INVALID; 697int32_t err = HDF_SUCCESS; 698int32_t tryCount = 3; 699do { 700 err = client_->GetState(status); 701 if (err != HDF_SUCCESS) { 702 HDF_LOGE("%s GetState error [%{public}x]", __func__, err); 703 break; 704 } 705 if (status != CODEC_STATE_LOADED) { 706 HDF_LOGI("Wait for OMX_StateLoaded status"); 707 this->WaitForStatusChanged(); 708 } 709 tryCount--; 710} while ((status != CODEC_STATE_LOADED) && (tryCount > 0)); 711``` 712 713##### 组件实例释放示例 714 715```cpp 716// 组件实例释放 717void CodecHdiDecode::Release() 718{ 719 omxMgr_->DestoryComponent(componentId_); 720 client_ = nullptr; 721 callback_ = nullptr; 722 omxMgr_ = nullptr; 723} 724``` 725 726# 常见问题 727 728## 解码过程中部分绿屏 729 730**现象描述** 731 732解码过程中,开始能正常解码,后续绿屏比较多。 733 734**可能原因** 735 736OpenMax不支持分帧。 737 738**解决办法** 739 740上层在调用EmptyThisBuffer时,需要按照每次一帧的方式传入。 741 742## 解码过程中全是绿屏 743 744**现象描述** 745 746解码过程中,解码失败全部播放不了。 747 748**可能原因** 749 750OpenMax对AVCC格式的数据处理,第一帧一定要是extra_data,可能没有正常输入extra_data导致AVCC格式解码失败。 751 752**解决办法** 753 754将sps和pps按照extrea_data格式写入buffer,并设置好buffer的flag为OMX_BUFFERFLAG_EXTRADATA。 755 756## 编码输出播放不了 757 758**现象描述** 759 760编码输出视频不正确,将生成的视频流(如H264流)写入文件后,通过ffplay工具播放不了。 761 762**可能原因** 763 7641. 输出端口的xFramerate参数未正常设置。 7652. 如果设置了参数OMX_VIDEO_PARAM_AVCTYPE,请检查此参数是否正确。 766 767**解决办法** 768 769请看编码时codec_host的日志,搜索“encode params init settings”,确认是否出现异常的参数。如果是framerate为0,则是原因1,需要将正常的framerate左移16位;如果是其它参数异常,可能是原因2,需要检查其相应的参数。 770 771 772# 参考 773 774如果您想了解更多关于Codec特性的源码及使用信息,请参考[Codec驱动代码仓](https://gitee.com/openharmony/drivers_peripheral/tree/master/codec)。