1/*
2 * Copyright (c) 2023 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, outParam;
82    ObjectToVector(param, inParam);
83    auto ret = component->GetParameter(OMX_IndexParamPortDefinition, inParam, outParam);
84    if (ret != HDF_SUCCESS) {
85        HDF_LOGE("GetParameter OMX_IndexParamPortDefinition error");
86        return false;
87    }
88
89    VectorToObject(outParam, param);
90    param.format.video.nFrameWidth = WIDTH;
91    param.format.video.nFrameHeight = HEIGHT;
92    param.format.video.nStride = AlignUp(WIDTH);
93    param.format.video.nSliceHeight = HEIGHT;
94    param.format.video.eColorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
95    std::vector<int8_t> enc;
96    ObjectToVector(param, enc);
97    ret = component->SetParameter(OMX_IndexParamPortDefinition, enc);
98    if (ret != HDF_SUCCESS) {
99        HDF_LOGE("SetParameter OMX_IndexParamPortDefinition error");
100        return false;
101    }
102
103    std::vector<int8_t> data;
104    UseBufferType type;
105    type.size = sizeof(UseBufferType);
106    type.version.s.nVersionMajor = 1;
107    type.portIndex = port;
108    type.bufferType = bufferType;
109    ObjectToVector(type, data);
110    ret = component->SetParameter(OMX_IndexParamUseBufferType, data);
111    if (ret != HDF_SUCCESS) {
112        HDF_LOGE("SetParameter OMX_IndexParamUseBufferType error");
113        return false;
114    }
115    return true;
116}
117
118bool FunctionUtil::FillCodecBufferWithBufferHandle(shared_ptr<OmxCodecBuffer> omxBuffer)
119{
120    AllocInfo alloc = {.width = WIDTH,
121                       .height = HEIGHT,
122                       .usage = HBM_USE_CPU_READ | HBM_USE_CPU_WRITE | HBM_USE_MEM_DMA,
123                       .format = PIXEL_FMT_YCBCR_420_SP};
124
125    BufferHandle *bufferHandle = nullptr;
126    if (buffer_ == nullptr) {
127        HDF_LOGE("buffer_ is nullptr");
128        return false;
129    }
130    auto ret = buffer_->AllocMem(alloc, bufferHandle);
131    if (ret != HDF_SUCCESS) {
132        HDF_LOGE("AllocMem error");
133        return false;
134    }
135    omxBuffer->bufferhandle = new NativeBuffer(bufferHandle);
136    return true;
137}
138
139bool FunctionUtil::UseDynaBuffer(sptr<ICodecComponent> component, enum PortIndex port, int bufferCount,
140    int bufferSize)
141{
142    if (bufferCount <= 0 || bufferSize <= 0) {
143        HDF_LOGE("bufferCount <= 0 or bufferSize <= 0");
144        return false;
145    }
146
147    for (int i = 0; i < bufferCount; i++) {
148        auto omxBuffer = std::make_shared<OmxCodecBuffer>();
149        InitOmxCodecBuffer(*omxBuffer.get(), CODEC_BUFFER_TYPE_DYNAMIC_HANDLE);
150        FillCodecBufferWithBufferHandle(omxBuffer);
151        omxBuffer->allocLen = WIDTH * HEIGHT * NUMERATOR / DENOMINATOR;
152
153        OmxCodecBuffer outBuffer;
154        auto ret = component->UseBuffer(static_cast<uint32_t>(port), *omxBuffer.get(), outBuffer);
155        if (ret != HDF_SUCCESS) {
156            HDF_LOGE("UseBuffer error");
157            return false;
158        }
159
160        omxBuffer->bufferId = outBuffer.bufferId;
161        auto bufferInfo = std::make_shared<BufferInfo>();
162        bufferInfo->omxBuffer = omxBuffer;
163        inputBuffers_.emplace(std::make_pair(omxBuffer->bufferId, bufferInfo));
164    }
165    return true;
166}
167
168bool FunctionUtil::UseHandleBuffer(sptr<ICodecComponent> component, enum PortIndex port,
169    int bufferCount, int bufferSize)
170{
171    if (bufferCount <= 0 || bufferSize <= 0) {
172        HDF_LOGE("bufferCount <= 0 or bufferSize <= 0");
173        return false;
174    }
175
176    for (int i = 0; i < bufferCount; i++) {
177        auto omxBuffer = std::make_shared<OmxCodecBuffer>();
178        InitOmxCodecBuffer(*omxBuffer.get(), CODEC_BUFFER_TYPE_HANDLE);
179        FillCodecBufferWithBufferHandle(omxBuffer);
180        omxBuffer->allocLen = WIDTH * HEIGHT * NUMERATOR / DENOMINATOR;
181
182        OmxCodecBuffer outBuffer;
183        int32_t ret = component->UseBuffer(static_cast<uint32_t>(port), *omxBuffer.get(), outBuffer);
184        if (ret != HDF_SUCCESS) {
185            HDF_LOGE("UseBuffer error");
186            return false;
187        }
188
189        omxBuffer->bufferId = outBuffer.bufferId;
190        auto bufferInfo = std::make_shared<BufferInfo>();
191        bufferInfo->omxBuffer = omxBuffer;
192        outputBuffers_.emplace(std::make_pair(omxBuffer->bufferId, bufferInfo));
193    }
194    return true;
195}
196
197bool FunctionUtil::UseBufferOnPort(sptr<ICodecComponent> component, enum PortIndex port,
198    int32_t bufferCount, int32_t bufferSize)
199{
200    for (int i = 0; i < bufferCount; i++) {
201        std::shared_ptr<OmxCodecBuffer> omxBuffer = std::make_shared<OmxCodecBuffer>();
202        int fd = OHOS::AshmemCreate(0, bufferSize);
203        shared_ptr<OHOS::Ashmem> sharedMem = make_shared<OHOS::Ashmem>(fd, bufferSize);
204        InitCodecBufferWithAshMem(port, bufferSize, omxBuffer, sharedMem);
205        OmxCodecBuffer outBuffer;
206        int32_t err = component->UseBuffer(static_cast<uint32_t>(port), *omxBuffer.get(), outBuffer);
207        if (err != HDF_SUCCESS) {
208            HDF_LOGE("UseBuffer error");
209            sharedMem->UnmapAshmem();
210            sharedMem->CloseAshmem();
211            return false;
212        }
213
214        omxBuffer->bufferId = outBuffer.bufferId;
215        omxBuffer->fd = FD_DEFAULT;
216        std::shared_ptr<BufferInfo> bufferInfo = std::make_shared<BufferInfo>();
217        bufferInfo->omxBuffer = omxBuffer;
218        bufferInfo->sharedMem = sharedMem;
219        if (port == PortIndex::INDEX_INPUT) {
220            inputBuffers_.emplace(std::make_pair(omxBuffer->bufferId, bufferInfo));
221        } else {
222            outputBuffers_.emplace(std::make_pair(omxBuffer->bufferId, bufferInfo));
223        }
224    }
225    return true;
226}
227
228bool FunctionUtil::AllocateBufferOnPort(sptr<ICodecComponent> component, enum PortIndex port,
229    int32_t bufferCount, int32_t bufferSize)
230{
231    for (int i = 0; i < bufferCount; i++) {
232        std::shared_ptr<OmxCodecBuffer> omxBuffer = std::make_shared<OmxCodecBuffer>();
233        InitOmxCodecBuffer(*omxBuffer.get(), CODEC_BUFFER_TYPE_AVSHARE_MEM_FD);
234        omxBuffer->allocLen = bufferSize;
235        if (port == PortIndex::INDEX_INPUT) {
236            omxBuffer->type = READ_ONLY_TYPE;
237        } else {
238            omxBuffer->type = READ_WRITE_TYPE;
239        }
240
241        OmxCodecBuffer outBuffer;
242        auto err = component->AllocateBuffer(static_cast<uint32_t>(port), *omxBuffer.get(), outBuffer);
243        if (err != HDF_SUCCESS) {
244            HDF_LOGE("AllocateBuffer error");
245            return false;
246        }
247        omxBuffer->type = outBuffer.type;
248        omxBuffer->bufferId = outBuffer.bufferId;
249
250        int fd = outBuffer.fd;
251        shared_ptr<OHOS::Ashmem> sharedMem = make_shared<OHOS::Ashmem>(fd, bufferSize);
252
253        std::shared_ptr<BufferInfo> bufferInfo = std::make_shared<BufferInfo>();
254        bufferInfo->omxBuffer = omxBuffer;
255        bufferInfo->sharedMem = sharedMem;
256        if (port == PortIndex::INDEX_INPUT) {
257            sharedMem->MapReadAndWriteAshmem();
258            inputBuffers_.emplace(std::make_pair(omxBuffer->bufferId, bufferInfo));
259        } else {
260            sharedMem->MapReadOnlyAshmem();
261            outputBuffers_.emplace(std::make_pair(omxBuffer->bufferId, bufferInfo));
262        }
263    }
264    return true;
265}
266
267bool FunctionUtil::FreeBufferOnPort(sptr<ICodecComponent> component, enum PortIndex port)
268{
269    int32_t ret;
270    std::map<int32_t, std::shared_ptr<BufferInfo>> &buffer = inputBuffers_;
271    if (port == PortIndex::INDEX_OUTPUT) {
272        buffer = outputBuffers_;
273    }
274    for (auto [bufferId, bufferInfo] : buffer) {
275        ret = component->FreeBuffer(static_cast<uint32_t>(port), *bufferInfo->omxBuffer.get());
276        if (ret != HDF_SUCCESS) {
277            HDF_LOGE("FreeBuffer error");
278            return false;
279        }
280    }
281    buffer.clear();
282    return true;
283}
284
285int32_t FunctionUtil::GetPortParameter(sptr<ICodecComponent> component, PortIndex index,
286    OMX_PARAM_PORTDEFINITIONTYPE &param)
287{
288    InitParam(param);
289    param.nPortIndex = static_cast<OMX_U32>(index);
290    std::vector<int8_t> inParam;
291    ObjectToVector(param, inParam);
292
293    std::vector<int8_t> outParam;
294    auto ret = component->GetParameter(OMX_IndexParamPortDefinition, inParam, outParam);
295    VectorToObject(outParam, param);
296    return ret;
297}
298
299bool FunctionUtil::PushAlongParam(OmxCodecBuffer &omxBuffer)
300{
301    const std::string processName = "cast_engine_service";
302    ProcessNameParam nameParam;
303    this->InitExtParam(nameParam);
304    int32_t ret = strcpy_s(nameParam.processName, sizeof(nameParam.processName), processName.c_str());
305    if (ret != EOK) {
306        return false;
307    }
308
309    uint32_t size = sizeof(nameParam);
310    uint8_t *ptr = reinterpret_cast<uint8_t*>(&nameParam);
311    for (uint32_t i = 0; i < size; i++) {
312        omxBuffer.alongParam.push_back(*(ptr + i));
313    }
314
315    return true;
316}
317
318bool FunctionUtil::FillAndEmptyAllBuffer(sptr<ICodecComponent> component, CodecBufferType type)
319{
320    int32_t ret;
321    auto iter = outputBuffers_.begin();
322    for (; iter != outputBuffers_.end(); iter++) {
323        auto bufferInfo = iter->second;
324        if (type != bufferInfo->omxBuffer->bufferType) {
325            continue;
326        }
327        ret = component->FillThisBuffer(*bufferInfo->omxBuffer.get());
328        if (ret != HDF_SUCCESS) {
329            HDF_LOGE("FillThisBuffer error");
330            return false;
331        }
332    }
333    iter = inputBuffers_.begin();
334    for (; iter != inputBuffers_.end(); iter++) {
335        auto bufferInfo = iter->second;
336        if (type != bufferInfo->omxBuffer->bufferType) {
337            continue;
338        }
339        if (type == CODEC_BUFFER_TYPE_DYNAMIC_HANDLE && (!PushAlongParam(*bufferInfo->omxBuffer.get()))) {
340            HDF_LOGE("PushAlongParam error");
341            return false;
342        }
343        ret = component->EmptyThisBuffer(*bufferInfo->omxBuffer.get());
344        if (ret != HDF_SUCCESS) {
345            HDF_LOGE("EmptyThisBuffer error");
346            return false;
347        }
348    }
349    return true;
350}
351
352bool FunctionUtil::WaitState(sptr<ICodecComponent> component, CodecStateType objState)
353{
354    CodecStateType state = CODEC_STATE_INVALID;
355    uint32_t count = 0;
356    do {
357        usleep(WAIT_TIME);
358        auto ret = component->GetState(state);
359        if (ret != HDF_SUCCESS) {
360            HDF_LOGE("EmptyThisBuffer error");
361            return false;
362        }
363        count++;
364    } while (state != objState && count <= MAX_WAIT);
365    return true;
366}
367
368