1/*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <cstdio>
17#include <unistd.h>
18#include "codec_function_utils.h"
19#include <gtest/gtest.h>
20#include <securec.h>
21#include <servmgr_hdi.h>
22
23#define HDF_LOG_TAG codec_hdi_test
24
25using namespace std;
26using namespace OHOS::HDI::Codec::V3_0;
27using namespace OHOS::HDI::Display::Buffer::V1_0;
28using namespace OHOS::HDI::Display::Composer::V1_0;
29IDisplayBuffer *FunctionUtil::buffer_ = nullptr;
30
31FunctionUtil::FunctionUtil(CodecVersionType version)
32{
33    buffer_ = IDisplayBuffer::Get();
34    version_ = version;
35}
36
37FunctionUtil::~FunctionUtil()
38{
39    buffer_ = nullptr;
40}
41
42uint32_t FunctionUtil::AlignUp(uint32_t width)
43{
44    return (((width) + ALIGNMENT - 1) & (~(ALIGNMENT - 1)));
45}
46
47void FunctionUtil::InitOmxCodecBuffer(OmxCodecBuffer &buffer, CodecBufferType type)
48{
49    buffer.bufferType = type;
50    buffer.fenceFd = ERROE_FENCEFD;
51    buffer.version = version_;
52    buffer.allocLen = BUFFER_SIZE;
53    buffer.fd = FD_DEFAULT;
54    buffer.bufferhandle = nullptr;
55    buffer.pts = 0;
56    buffer.flag = 0;
57    buffer.size = sizeof(OmxCodecBuffer);
58    buffer.type = READ_ONLY_TYPE;
59}
60
61void FunctionUtil::InitCodecBufferWithAshMem(enum PortIndex port, int bufferSize, shared_ptr<OmxCodecBuffer> omxBuffer,
62    shared_ptr<OHOS::Ashmem> sharedMem)
63{
64    InitOmxCodecBuffer(*omxBuffer.get(), CODEC_BUFFER_TYPE_AVSHARE_MEM_FD);
65    omxBuffer->fd = sharedMem->GetAshmemFd();
66    omxBuffer->allocLen = bufferSize;
67    if (port == PortIndex::INDEX_INPUT) {
68        omxBuffer->type = READ_ONLY_TYPE;
69        sharedMem->MapReadAndWriteAshmem();
70    } else {
71        omxBuffer->type = READ_WRITE_TYPE;
72        sharedMem->MapReadOnlyAshmem();
73    }
74}
75
76bool FunctionUtil::InitBufferHandleParameter(sptr<ICodecComponent> component, OMX_PARAM_PORTDEFINITIONTYPE &param,
77    uint32_t port, CodecBufferType bufferType)
78{
79    InitParam(param);
80    param.nPortIndex = port;
81    std::vector<int8_t> inParam;
82    std::vector<int8_t> outParam;
83    ObjectToVector(param, inParam);
84    auto ret = component->GetParameter(OMX_IndexParamPortDefinition, inParam, outParam);
85    if (ret != HDF_SUCCESS) {
86        HDF_LOGE("GetParameter OMX_IndexParamPortDefinition error");
87        return false;
88    }
89
90    VectorToObject(outParam, param);
91    param.format.video.nFrameWidth = WIDTH;
92    param.format.video.nFrameHeight = HEIGHT;
93    param.format.video.nStride = AlignUp(WIDTH);
94    param.format.video.nSliceHeight = HEIGHT;
95    param.format.video.eColorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
96    std::vector<int8_t> enc;
97    ObjectToVector(param, enc);
98    ret = component->SetParameter(OMX_IndexParamPortDefinition, enc);
99    if (ret != HDF_SUCCESS) {
100        HDF_LOGE("SetParameter OMX_IndexParamPortDefinition error");
101        return false;
102    }
103
104    std::vector<int8_t> data;
105    UseBufferType type;
106    type.size = sizeof(UseBufferType);
107    type.version.s.nVersionMajor = 1;
108    type.portIndex = port;
109    type.bufferType = bufferType;
110    ObjectToVector(type, data);
111    ret = component->SetParameter(OMX_IndexParamUseBufferType, data);
112    if (ret != HDF_SUCCESS) {
113        HDF_LOGE("SetParameter OMX_IndexParamUseBufferType error");
114        return false;
115    }
116    return true;
117}
118
119bool FunctionUtil::FillCodecBufferWithBufferHandle(shared_ptr<OmxCodecBuffer> omxBuffer)
120{
121    AllocInfo alloc = {.width = WIDTH,
122                       .height = HEIGHT,
123                       .usage = HBM_USE_CPU_READ | HBM_USE_CPU_WRITE | HBM_USE_MEM_DMA,
124                       .format = PIXEL_FMT_YCBCR_420_SP};
125
126    BufferHandle *bufferHandle = nullptr;
127    if (buffer_ == nullptr) {
128        HDF_LOGE("buffer_ is nullptr");
129        return false;
130    }
131    auto ret = buffer_->AllocMem(alloc, bufferHandle);
132    if (ret != HDF_SUCCESS) {
133        HDF_LOGE("AllocMem error");
134        return false;
135    }
136    omxBuffer->bufferhandle = new NativeBuffer(bufferHandle);
137    return true;
138}
139
140bool FunctionUtil::UseDynaBuffer(sptr<ICodecComponent> component, enum PortIndex port, int bufferCount,
141    int bufferSize)
142{
143    if (bufferCount <= 0 || bufferSize <= 0) {
144        HDF_LOGE("bufferCount <= 0 or bufferSize <= 0");
145        return false;
146    }
147
148    for (int i = 0; i < bufferCount; i++) {
149        auto omxBuffer = std::make_shared<OmxCodecBuffer>();
150        InitOmxCodecBuffer(*omxBuffer.get(), CODEC_BUFFER_TYPE_DYNAMIC_HANDLE);
151        FillCodecBufferWithBufferHandle(omxBuffer);
152        omxBuffer->allocLen = WIDTH * HEIGHT * NUMERATOR / DENOMINATOR;
153
154        OmxCodecBuffer outBuffer;
155        auto ret = component->UseBuffer(static_cast<uint32_t>(port), *omxBuffer.get(), outBuffer);
156        if (ret != HDF_SUCCESS) {
157            HDF_LOGE("UseBuffer error");
158            return false;
159        }
160
161        omxBuffer->bufferId = outBuffer.bufferId;
162        auto bufferInfo = std::make_shared<BufferInfo>();
163        bufferInfo->omxBuffer = omxBuffer;
164        inputBuffers_.emplace(std::make_pair(omxBuffer->bufferId, bufferInfo));
165    }
166    return true;
167}
168
169bool FunctionUtil::UseHandleBuffer(sptr<ICodecComponent> component, enum PortIndex port,
170    int bufferCount, int bufferSize)
171{
172    if (bufferCount <= 0 || bufferSize <= 0) {
173        HDF_LOGE("bufferCount <= 0 or bufferSize <= 0");
174        return false;
175    }
176
177    for (int i = 0; i < bufferCount; i++) {
178        auto omxBuffer = std::make_shared<OmxCodecBuffer>();
179        InitOmxCodecBuffer(*omxBuffer.get(), CODEC_BUFFER_TYPE_HANDLE);
180        FillCodecBufferWithBufferHandle(omxBuffer);
181        omxBuffer->allocLen = WIDTH * HEIGHT * NUMERATOR / DENOMINATOR;
182
183        OmxCodecBuffer outBuffer;
184        int32_t ret = component->UseBuffer(static_cast<uint32_t>(port), *omxBuffer.get(), outBuffer);
185        if (ret != HDF_SUCCESS) {
186            HDF_LOGE("UseBuffer error");
187            return false;
188        }
189
190        omxBuffer->bufferId = outBuffer.bufferId;
191        auto bufferInfo = std::make_shared<BufferInfo>();
192        bufferInfo->omxBuffer = omxBuffer;
193        outputBuffers_.emplace(std::make_pair(omxBuffer->bufferId, bufferInfo));
194    }
195    return true;
196}
197
198bool FunctionUtil::UseBufferOnPort(sptr<ICodecComponent> component, enum PortIndex port,
199    int32_t bufferCount, int32_t bufferSize)
200{
201    for (int i = 0; i < bufferCount; i++) {
202        std::shared_ptr<OmxCodecBuffer> omxBuffer = std::make_shared<OmxCodecBuffer>();
203        int fd = OHOS::AshmemCreate(0, bufferSize);
204        shared_ptr<OHOS::Ashmem> sharedMem = make_shared<OHOS::Ashmem>(fd, bufferSize);
205        InitCodecBufferWithAshMem(port, bufferSize, omxBuffer, sharedMem);
206        OmxCodecBuffer outBuffer;
207        int32_t err = component->UseBuffer(static_cast<uint32_t>(port), *omxBuffer.get(), outBuffer);
208        if (err != HDF_SUCCESS) {
209            HDF_LOGE("UseBuffer error");
210            sharedMem->UnmapAshmem();
211            sharedMem->CloseAshmem();
212            return false;
213        }
214
215        omxBuffer->bufferId = outBuffer.bufferId;
216        omxBuffer->fd = FD_DEFAULT;
217        std::shared_ptr<BufferInfo> bufferInfo = std::make_shared<BufferInfo>();
218        bufferInfo->omxBuffer = omxBuffer;
219        bufferInfo->sharedMem = sharedMem;
220        if (port == PortIndex::INDEX_INPUT) {
221            inputBuffers_.emplace(std::make_pair(omxBuffer->bufferId, bufferInfo));
222        } else {
223            outputBuffers_.emplace(std::make_pair(omxBuffer->bufferId, bufferInfo));
224        }
225    }
226    return true;
227}
228
229bool FunctionUtil::AllocateBufferOnPort(sptr<ICodecComponent> component, enum PortIndex port,
230    int32_t bufferCount, int32_t bufferSize)
231{
232    for (int i = 0; i < bufferCount; i++) {
233        std::shared_ptr<OmxCodecBuffer> omxBuffer = std::make_shared<OmxCodecBuffer>();
234        InitOmxCodecBuffer(*omxBuffer.get(), CODEC_BUFFER_TYPE_AVSHARE_MEM_FD);
235        omxBuffer->allocLen = bufferSize;
236        if (port == PortIndex::INDEX_INPUT) {
237            omxBuffer->type = READ_ONLY_TYPE;
238        } else {
239            omxBuffer->type = READ_WRITE_TYPE;
240        }
241
242        OmxCodecBuffer outBuffer;
243        auto err = component->AllocateBuffer(static_cast<uint32_t>(port), *omxBuffer.get(), outBuffer);
244        if (err != HDF_SUCCESS) {
245            HDF_LOGE("AllocateBuffer error");
246            return false;
247        }
248        omxBuffer->type = outBuffer.type;
249        omxBuffer->bufferId = outBuffer.bufferId;
250
251        int fd = outBuffer.fd;
252        shared_ptr<OHOS::Ashmem> sharedMem = make_shared<OHOS::Ashmem>(fd, bufferSize);
253
254        std::shared_ptr<BufferInfo> bufferInfo = std::make_shared<BufferInfo>();
255        bufferInfo->omxBuffer = omxBuffer;
256        bufferInfo->sharedMem = sharedMem;
257        if (port == PortIndex::INDEX_INPUT) {
258            sharedMem->MapReadAndWriteAshmem();
259            inputBuffers_.emplace(std::make_pair(omxBuffer->bufferId, bufferInfo));
260        } else {
261            sharedMem->MapReadOnlyAshmem();
262            outputBuffers_.emplace(std::make_pair(omxBuffer->bufferId, bufferInfo));
263        }
264    }
265    return true;
266}
267
268bool FunctionUtil::FreeBufferOnPort(sptr<ICodecComponent> component, enum PortIndex port)
269{
270    int32_t ret;
271    std::map<int32_t, std::shared_ptr<BufferInfo>> &buffer = inputBuffers_;
272    if (port == PortIndex::INDEX_OUTPUT) {
273        buffer = outputBuffers_;
274    }
275    for (auto [bufferId, bufferInfo] : buffer) {
276        ret = component->FreeBuffer(static_cast<uint32_t>(port), *bufferInfo->omxBuffer.get());
277        if (ret != HDF_SUCCESS) {
278            HDF_LOGE("FreeBuffer error");
279            return false;
280        }
281    }
282    buffer.clear();
283    return true;
284}
285
286int32_t FunctionUtil::GetPortParameter(sptr<ICodecComponent> component, PortIndex index,
287    OMX_PARAM_PORTDEFINITIONTYPE &param)
288{
289    InitParam(param);
290    param.nPortIndex = static_cast<OMX_U32>(index);
291    std::vector<int8_t> inParam;
292    ObjectToVector(param, inParam);
293
294    std::vector<int8_t> outParam;
295    auto ret = component->GetParameter(OMX_IndexParamPortDefinition, inParam, outParam);
296    VectorToObject(outParam, param);
297    return ret;
298}
299
300bool FunctionUtil::PushAlongParam(OmxCodecBuffer &omxBuffer)
301{
302    const std::string processName = "cast_engine_service";
303    ProcessNameParam nameParam;
304    this->InitExtParam(nameParam);
305    int32_t ret = strcpy_s(nameParam.processName, sizeof(nameParam.processName), processName.c_str());
306    if (ret != EOK) {
307        return false;
308    }
309
310    uint32_t size = sizeof(nameParam);
311    uint8_t *ptr = reinterpret_cast<uint8_t*>(&nameParam);
312    for (uint32_t i = 0; i < size; i++) {
313        omxBuffer.alongParam.push_back(*(ptr + i));
314    }
315
316    return true;
317}
318
319bool FunctionUtil::FillAndEmptyAllBuffer(sptr<ICodecComponent> component, CodecBufferType type)
320{
321    int32_t ret;
322    auto iter = outputBuffers_.begin();
323    for (; iter != outputBuffers_.end(); iter++) {
324        auto bufferInfo = iter->second;
325        if (type != bufferInfo->omxBuffer->bufferType) {
326            continue;
327        }
328        ret = component->FillThisBuffer(*bufferInfo->omxBuffer.get());
329        if (ret != HDF_SUCCESS) {
330            HDF_LOGE("FillThisBuffer error");
331            return false;
332        }
333    }
334    iter = inputBuffers_.begin();
335    for (; iter != inputBuffers_.end(); iter++) {
336        auto bufferInfo = iter->second;
337        if (type != bufferInfo->omxBuffer->bufferType) {
338            continue;
339        }
340        if (type == CODEC_BUFFER_TYPE_DYNAMIC_HANDLE && (!PushAlongParam(*bufferInfo->omxBuffer.get()))) {
341            HDF_LOGE("PushAlongParam error");
342            return false;
343        }
344        ret = component->EmptyThisBuffer(*bufferInfo->omxBuffer.get());
345        if (ret != HDF_SUCCESS) {
346            HDF_LOGE("EmptyThisBuffer error");
347            return false;
348        }
349    }
350    return true;
351}
352
353bool FunctionUtil::WaitState(sptr<ICodecComponent> component, CodecStateType objState)
354{
355    CodecStateType state = CODEC_STATE_INVALID;
356    uint32_t count = 0;
357    do {
358        usleep(WAIT_TIME);
359        auto ret = component->GetState(state);
360        if (ret != HDF_SUCCESS) {
361            HDF_LOGE("EmptyThisBuffer error");
362            return false;
363        }
364        count++;
365    } while (state != objState && count <= MAX_WAIT);
366    return true;
367}
368
369