• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
180device_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 *)&param, 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 *)&param, 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 *)&param, 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 *)&param, 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 *)&param, 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 *)&param, 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 *)&param, 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 *)&param, 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)