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- 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
183device_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)