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 applicable 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 "hardware/imagecodec/image_codec.h"
17 #include "hardware/imagecodec/image_decoder.h"
18 #include "hardware/imagecodec/image_codec_list.h"
19 #include "hardware/imagecodec/image_codec_log.h"
20 #include "syspara/parameters.h" // base/startup/init/interfaces/innerkits/include/
21 #include "qos.h"
22
23 namespace OHOS::ImagePlugin {
24 using namespace std;
25 using namespace HdiCodecNamespace;
26
IsSecureMode(const string & name)27 static bool IsSecureMode(const string &name)
28 {
29 string prefix = ".secure";
30 if (name.length() <= prefix.length()) {
31 return false;
32 }
33 return (name.rfind(prefix) == (name.length() - prefix.length()));
34 }
35
Create()36 shared_ptr<ImageCodec> ImageCodec::Create()
37 {
38 vector<CodecCompCapability> capList = GetCapList();
39 shared_ptr<ImageCodec> codec;
40 string name;
41 for (const auto& cap : capList) {
42 if (cap.role == MEDIA_ROLETYPE_VIDEO_HEVC && cap.type == VIDEO_DECODER && !IsSecureMode(cap.compName)) {
43 name = cap.compName;
44 codec = make_shared<ImageDecoder>();
45 break;
46 }
47 }
48 if ((codec != nullptr) && (codec->InitWithName(name) == IC_ERR_OK)) {
49 return codec;
50 }
51 return nullptr;
52 }
53
SetCallback(const shared_ptr<ImageCodecCallback> & callback)54 int32_t ImageCodec::SetCallback(const shared_ptr<ImageCodecCallback> &callback)
55 {
56 HLOGI(">>");
57 function<void(ParamSP)> proc = [&](ParamSP msg) {
58 msg->SetValue("callback", callback);
59 };
60 return DoSyncCall(MsgWhat::SET_CALLBACK, proc);
61 }
62
Configure(const Format & format)63 int32_t ImageCodec::Configure(const Format &format)
64 {
65 function<void(ParamSP)> proc = [&](ParamSP msg) {
66 msg->SetValue("format", format);
67 };
68 return DoSyncCall(MsgWhat::CONFIGURE, proc);
69 }
70
QueueInputBuffer(uint32_t index)71 int32_t ImageCodec::QueueInputBuffer(uint32_t index)
72 {
73 function<void(ParamSP)> proc = [&](ParamSP msg) {
74 msg->SetValue(BUFFER_ID, index);
75 };
76 return DoSyncCall(MsgWhat::QUEUE_INPUT_BUFFER, proc);
77 }
78
ReleaseOutputBuffer(uint32_t index)79 int32_t ImageCodec::ReleaseOutputBuffer(uint32_t index)
80 {
81 function<void(ParamSP)> proc = [&](ParamSP msg) {
82 msg->SetValue(BUFFER_ID, index);
83 };
84 return DoSyncCall(MsgWhat::RELEASE_OUTPUT_BUFFER, proc);
85 }
86
GetInputFormat(Format & format)87 int32_t ImageCodec::GetInputFormat(Format& format)
88 {
89 HLOGI(">>");
90 ParamSP reply;
91 int32_t ret = DoSyncCallAndGetReply(MsgWhat::GET_INPUT_FORMAT, nullptr, reply);
92 if (ret != IC_ERR_OK) {
93 HLOGE("failed to get input format");
94 return ret;
95 }
96 IF_TRUE_RETURN_VAL_WITH_MSG(!reply->GetValue("format", format),
97 IC_ERR_UNKNOWN, "input format not replied");
98 return IC_ERR_OK;
99 }
100
GetOutputFormat(Format & format)101 int32_t ImageCodec::GetOutputFormat(Format& format)
102 {
103 ParamSP reply;
104 int32_t ret = DoSyncCallAndGetReply(MsgWhat::GET_OUTPUT_FORMAT, nullptr, reply);
105 if (ret != IC_ERR_OK) {
106 HLOGE("failed to get output format");
107 return ret;
108 }
109 IF_TRUE_RETURN_VAL_WITH_MSG(!reply->GetValue("format", format),
110 IC_ERR_UNKNOWN, "output format not replied");
111 return IC_ERR_OK;
112 }
113
Start()114 int32_t ImageCodec::Start()
115 {
116 HLOGI(">>");
117 return DoSyncCall(MsgWhat::START, nullptr);
118 }
119
Release()120 int32_t ImageCodec::Release()
121 {
122 HLOGI(">>");
123 return DoSyncCall(MsgWhat::RELEASE, nullptr);
124 }
125
GetOutputBufferUsage(uint64_t & usage)126 int32_t ImageCodec::GetOutputBufferUsage(uint64_t& usage)
127 {
128 ParamSP reply;
129 int32_t ret = DoSyncCallAndGetReply(MsgWhat::GET_OUTPUT_BUFFER_USAGE, nullptr, reply);
130 if (ret != IC_ERR_OK) {
131 HLOGE("failed to get output buffer usage");
132 return ret;
133 }
134 IF_TRUE_RETURN_VAL_WITH_MSG(!reply->GetValue("usage", usage),
135 IC_ERR_UNKNOWN, "output buffer usage not replied");
136 return IC_ERR_OK;
137 }
138
SetOutputBuffer(sptr<SurfaceBuffer> output)139 int32_t ImageCodec::SetOutputBuffer(sptr<SurfaceBuffer> output)
140 {
141 HLOGI(">>");
142 std::function<void(ParamSP)> proc = [&](ParamSP msg) {
143 msg->SetValue("output", output);
144 };
145 return DoSyncCall(MsgWhat::SET_OUTPUT_BUFFER, proc);
146 }
147
SetPackedInputFlag(bool flag)148 int32_t ImageCodec::SetPackedInputFlag(bool flag)
149 {
150 HLOGI(">>");
151 std::function<void(ParamSP)> proc = [&](ParamSP msg) {
152 msg->SetValue("packedInputFlag", flag);
153 };
154 return DoSyncCall(MsgWhat::SET_PACKED_INPUT_FLAG, proc);
155 }
156
GetPackedInputCapability(bool & flag)157 int32_t ImageCodec::GetPackedInputCapability(bool& flag)
158 {
159 ParamSP reply;
160 int32_t ret = DoSyncCallAndGetReply(MsgWhat::GET_PACKED_CAPABILITY, nullptr, reply);
161 if (ret != IC_ERR_OK) {
162 HLOGE("failed to get packed input flag");
163 return ret;
164 }
165 IF_TRUE_RETURN_VAL_WITH_MSG(!reply->GetValue("isPackedInputSupported", flag),
166 IC_ERR_UNKNOWN, "packed input flag not replied");
167 return IC_ERR_OK;
168 }
169 /**************************** public functions end ****************************/
170
ImageCodec(OMX_VIDEO_CODINGTYPE codingType,bool isEncoder)171 ImageCodec::ImageCodec(OMX_VIDEO_CODINGTYPE codingType, bool isEncoder)
172 : isEncoder_(isEncoder), codingType_(codingType)
173 {
174 debugMode_ = OHOS::system::GetBoolParameter("image.codec.debug", false);
175 dumpMode_ = OHOS::system::GetBoolParameter("image.codec.dump", false);
176 LOGD(">> debug mode = %{public}d, dump mode = %{public}d", debugMode_, dumpMode_);
177
178 uninitializedState_ = make_shared<UninitializedState>(this);
179 initializedState_ = make_shared<InitializedState>(this);
180 startingState_ = make_shared<StartingState>(this);
181 runningState_ = make_shared<RunningState>(this);
182 outputPortChangedState_ = make_shared<OutputPortChangedState>(this);
183 stoppingState_ = make_shared<StoppingState>(this);
184 StateMachine::ChangeStateTo(uninitializedState_);
185 }
186
~ImageCodec()187 ImageCodec::~ImageCodec()
188 {
189 HLOGI(">>");
190 MsgHandleLoop::Stop();
191 ReleaseComponent();
192 }
193
InitWithName(const string & name)194 int32_t ImageCodec::InitWithName(const string &name)
195 {
196 function<void(ParamSP)> proc = [&](ParamSP msg) {
197 msg->SetValue("name", name);
198 };
199 return DoSyncCall(MsgWhat::INIT, proc);
200 }
201
ToString(BufferOwner owner)202 const char* ImageCodec::ToString(BufferOwner owner)
203 {
204 switch (owner) {
205 case BufferOwner::OWNED_BY_US:
206 return "us";
207 case BufferOwner::OWNED_BY_USER:
208 return "user";
209 case BufferOwner::OWNED_BY_OMX:
210 return "omx";
211 default:
212 return "";
213 }
214 }
215
ToString(MsgWhat what)216 const char* ImageCodec::ToString(MsgWhat what)
217 {
218 static const map<MsgWhat, const char*> m = {
219 { INIT, "INIT" },
220 { SET_CALLBACK, "SET_CALLBACK" },
221 { CONFIGURE, "CONFIGURE" },
222 { START, "START" },
223 { GET_INPUT_FORMAT, "GET_INPUT_FORMAT" },
224 { GET_OUTPUT_FORMAT, "GET_OUTPUT_FORMAT" },
225 { QUEUE_INPUT_BUFFER, "QUEUE_INPUT_BUFFER" },
226 { RELEASE_OUTPUT_BUFFER, "RELEASE_OUTPUT_BUFFER" },
227 { RELEASE, "RELEASE" },
228 { GET_OUTPUT_BUFFER_USAGE, "GET_OUTPUT_BUFFER_USAGE" },
229 { SET_OUTPUT_BUFFER, "SET_OUTPUT_BUFFER" },
230 { GET_PACKED_CAPABILITY, "GET_PACKED_CAPABILITY" },
231 { SET_PACKED_INPUT_FLAG, "SET_PACKED_INPUT_FLAG" },
232 { CODEC_EVENT, "CODEC_EVENT" },
233 { OMX_EMPTY_BUFFER_DONE, "OMX_EMPTY_BUFFER_DONE" },
234 { OMX_FILL_BUFFER_DONE, "OMX_FILL_BUFFER_DONE" },
235 { CHECK_IF_STUCK, "CHECK_IF_STUCK" },
236 { FORCE_SHUTDOWN, "FORCE_SHUTDOWN" },
237 };
238 auto it = m.find(what);
239 if (it != m.end()) {
240 return it->second;
241 }
242 return "UNKNOWN";
243 }
244
ReplyErrorCode(MsgId id,int32_t err)245 void ImageCodec::ReplyErrorCode(MsgId id, int32_t err)
246 {
247 if (id == ASYNC_MSG_ID) {
248 return;
249 }
250 ParamSP reply = make_shared<ParamBundle>();
251 reply->SetValue("err", err);
252 PostReply(id, reply);
253 }
254
GetPixelFmtFromUser(const Format & format)255 bool ImageCodec::GetPixelFmtFromUser(const Format &format)
256 {
257 is10Bit_ = false;
258 optional<PixelFmt> fmt;
259 int32_t graphicFmt;
260 if (format.GetValue(ImageCodecDescriptionKey::PIXEL_FORMAT, graphicFmt)) {
261 if (!isPackedInputSupported_) {
262 if (graphicFmt == GRAPHIC_PIXEL_FMT_YCBCR_P010) {
263 is10Bit_ = true;
264 graphicFmt = GRAPHIC_PIXEL_FMT_YCBCR_420_SP;
265 } else if (graphicFmt == GRAPHIC_PIXEL_FMT_YCRCB_P010) {
266 is10Bit_ = true;
267 graphicFmt = GRAPHIC_PIXEL_FMT_YCRCB_420_SP;
268 }
269 }
270 fmt = TypeConverter::GraphicFmtToFmt(static_cast<GraphicPixelFormat>(graphicFmt));
271 } else {
272 HLOGE("pixel format unspecified");
273 return false;
274 }
275 if (!fmt.has_value()) {
276 HLOGE("pixel format unsupported");
277 return false;
278 }
279 configuredFmt_ = fmt.value();
280 HLOGI("configured pixel format is %{public}s", configuredFmt_.strFmt.c_str());
281 return true;
282 }
283
GetFrameRateFromUser(const Format & format)284 optional<double> ImageCodec::GetFrameRateFromUser(const Format &format)
285 {
286 double frameRate;
287 if (format.GetValue(ImageCodecDescriptionKey::FRAME_RATE, frameRate) && frameRate > 0) {
288 LOGD("user set frame rate %{public}.2f", frameRate);
289 return frameRate;
290 }
291 return nullopt;
292 }
293
SetFrameRateAdaptiveMode(const Format & format)294 int32_t ImageCodec::SetFrameRateAdaptiveMode(const Format &format)
295 {
296 if (!format.ContainKey(ImageCodecDescriptionKey::VIDEO_FRAME_RATE_ADAPTIVE_MODE)) {
297 return IC_ERR_UNKNOWN;
298 }
299
300 WorkingFrequencyParam param {};
301 InitOMXParamExt(param);
302 if (!GetParameter(OMX_IndexParamWorkingFrequency, param)) {
303 HLOGW("get working freq param failed");
304 return IC_ERR_UNKNOWN;
305 }
306 HLOGI("level cnt is %{public}d, set level to %{public}d", param.level, param.level - 1);
307 param.level = param.level - 1;
308
309 if (!SetParameter(OMX_IndexParamWorkingFrequency, param)) {
310 HLOGW("set working freq param failed");
311 return IC_ERR_UNKNOWN;
312 }
313 return IC_ERR_OK;
314 }
315
SetProcessName(const Format & format)316 int32_t ImageCodec::SetProcessName(const Format &format)
317 {
318 string processName;
319 if (!format.GetValue(ImageCodecDescriptionKey::PROCESS_NAME, processName)) {
320 return IC_ERR_UNKNOWN;
321 }
322 HLOGI("processName name is %{public}s", processName.c_str());
323
324 ProcessNameParam param {};
325 InitOMXParamExt(param);
326 if (strcpy_s(param.processName, sizeof(param.processName), processName.c_str()) != EOK) {
327 HLOGW("strcpy failed");
328 return IC_ERR_UNKNOWN;
329 }
330 if (!SetParameter(OMX_IndexParamProcessName, param)) {
331 HLOGW("set process name failed");
332 return IC_ERR_UNKNOWN;
333 }
334 return IC_ERR_OK;
335 }
336
SetVideoPortInfo(OMX_DIRTYPE portIndex,const PortInfo & info)337 int32_t ImageCodec::SetVideoPortInfo(OMX_DIRTYPE portIndex, const PortInfo& info)
338 {
339 if (info.pixelFmt.has_value()) {
340 CodecVideoPortFormatParam param;
341 InitOMXParamExt(param);
342 param.portIndex = portIndex;
343 param.codecCompressFormat = info.codingType;
344 param.codecColorFormat = info.pixelFmt->graphicFmt;
345 param.framerate = info.frameRate * FRAME_RATE_COEFFICIENT;
346 if (!SetParameter(OMX_IndexCodecVideoPortFormat, param)) {
347 HLOGE("set port format failed");
348 return IC_ERR_UNKNOWN;
349 }
350 }
351 {
352 OMX_PARAM_PORTDEFINITIONTYPE def;
353 InitOMXParam(def);
354 def.nPortIndex = portIndex;
355 if (!GetParameter(OMX_IndexParamPortDefinition, def)) {
356 HLOGE("get port definition failed");
357 return IC_ERR_UNKNOWN;
358 }
359 def.format.video.nFrameWidth = info.width;
360 def.format.video.nFrameHeight = info.height;
361 def.format.video.eCompressionFormat = info.codingType;
362 // we dont set eColorFormat here because it has been set by CodecVideoPortFormatParam
363 def.format.video.xFramerate = info.frameRate * FRAME_RATE_COEFFICIENT;
364 if (portIndex == OMX_DirInput && info.inputBufSize.has_value()) {
365 def.nBufferSize = info.inputBufSize.value();
366 }
367 if (info.bufferCnt.has_value()) {
368 def.nBufferCountActual = info.bufferCnt.value();
369 }
370 if (!SetParameter(OMX_IndexParamPortDefinition, def)) {
371 HLOGE("set port definition failed");
372 return IC_ERR_UNKNOWN;
373 }
374 if (portIndex == OMX_DirOutput) {
375 if (outputFormat_ == nullptr) {
376 outputFormat_ = make_shared<Format>();
377 }
378 outputFormat_->SetValue(ImageCodecDescriptionKey::FRAME_RATE, info.frameRate);
379 }
380 }
381
382 return (portIndex == OMX_DirInput) ? UpdateInPortFormat() : UpdateOutPortFormat();
383 }
384
PrintPortDefinition(const OMX_PARAM_PORTDEFINITIONTYPE & def)385 void ImageCodec::PrintPortDefinition(const OMX_PARAM_PORTDEFINITIONTYPE& def)
386 {
387 const OMX_VIDEO_PORTDEFINITIONTYPE& video = def.format.video;
388 HLOGD("----- %{public}s port definition -----", (def.nPortIndex == OMX_DirInput) ? "INPUT" : "OUTPUT");
389 HLOGD("bEnabled %{public}d, bPopulated %{public}d", def.bEnabled, def.bPopulated);
390 HLOGD("nBufferCountActual %{public}u, nBufferSize %{public}u", def.nBufferCountActual, def.nBufferSize);
391 HLOGD("nFrameWidth x nFrameHeight (%{public}u x %{public}u), framerate %{public}u(%{public}.2f)",
392 video.nFrameWidth, video.nFrameHeight, video.xFramerate, video.xFramerate / FRAME_RATE_COEFFICIENT);
393 HLOGD(" nStride x nSliceHeight (%{public}u x %{public}u)", video.nStride, video.nSliceHeight);
394 HLOGD("eCompressionFormat %{public}d(%{public}#x), eColorFormat %{public}d(%{public}#x)",
395 video.eCompressionFormat, video.eCompressionFormat, video.eColorFormat, video.eColorFormat);
396 HLOGD("----------------------------------");
397 }
398
GetPortDefinition(OMX_DIRTYPE portIndex,OMX_PARAM_PORTDEFINITIONTYPE & def)399 int32_t ImageCodec::GetPortDefinition(OMX_DIRTYPE portIndex, OMX_PARAM_PORTDEFINITIONTYPE& def)
400 {
401 InitOMXParam(def);
402 def.nPortIndex = portIndex;
403 if (!GetParameter(OMX_IndexParamPortDefinition, def)) {
404 HLOGE("get %{public}s port definition failed", (portIndex == OMX_DirInput ? "input" : "output"));
405 return IC_ERR_INVALID_VAL;
406 }
407 if (def.nBufferSize == 0 || def.nBufferSize > MAX_IMAGE_CODEC_BUFFER_SIZE) {
408 HLOGE("invalid nBufferSize %{public}u", def.nBufferSize);
409 return IC_ERR_INVALID_VAL;
410 }
411 PrintPortDefinition(def);
412 return IC_ERR_OK;
413 }
414
AllocateHardwareBuffers(OMX_DIRTYPE portIndex)415 int32_t ImageCodec::AllocateHardwareBuffers(OMX_DIRTYPE portIndex)
416 {
417 HeifPerfTracker tracker(__FUNCTION__);
418 OMX_PARAM_PORTDEFINITIONTYPE def;
419 int32_t ret = GetPortDefinition(portIndex, def);
420 IF_TRUE_RETURN_VAL(ret != IC_ERR_OK, ret);
421
422 vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
423 pool.clear();
424 for (uint32_t i = 0; i < def.nBufferCountActual; ++i) {
425 shared_ptr<OmxCodecBuffer> omxBuffer = make_shared<OmxCodecBuffer>();
426 omxBuffer->size = sizeof(OmxCodecBuffer);
427 omxBuffer->version.version.majorVersion = 1;
428 omxBuffer->bufferType = CODEC_BUFFER_TYPE_DMA_MEM_FD;
429 omxBuffer->fd = -1;
430 omxBuffer->allocLen = def.nBufferSize;
431 omxBuffer->fenceFd = -1;
432 shared_ptr<OmxCodecBuffer> outBuffer = make_shared<OmxCodecBuffer>();
433 ret = compNode_->AllocateBuffer(portIndex, *omxBuffer, *outBuffer);
434 if (ret != HDF_SUCCESS) {
435 HLOGE("Failed to AllocateBuffer on %{public}s port", (portIndex == OMX_DirInput ? "input" : "output"));
436 return IC_ERR_INVALID_VAL;
437 }
438 shared_ptr<ImageCodecBuffer> imgCodecBuffer = ImageCodecBuffer::CreateDmaBuffer(outBuffer->fd,
439 static_cast<int32_t>(def.nBufferSize), static_cast<int32_t>(def.format.video.nStride));
440 if (imgCodecBuffer == nullptr || imgCodecBuffer->GetCapacity() != static_cast<int32_t>(def.nBufferSize)) {
441 HLOGE("AllocateHardwareBuffers failed");
442 return IC_ERR_NO_MEMORY;
443 }
444 BufferInfo bufInfo;
445 bufInfo.isInput = (portIndex == OMX_DirInput) ? true : false;
446 bufInfo.owner = BufferOwner::OWNED_BY_US;
447 bufInfo.surfaceBuffer = nullptr;
448 bufInfo.imgCodecBuffer = imgCodecBuffer;
449 bufInfo.omxBuffer = outBuffer;
450 bufInfo.bufferId = outBuffer->bufferId;
451 bufInfo.CleanUpUnusedInfo();
452 pool.push_back(bufInfo);
453 }
454 return IC_ERR_OK;
455 }
456
AllocateSurfaceBuffers(OMX_DIRTYPE portIndex,bool isOutputPortSettingChanged,sptr<SurfaceBuffer> output)457 int32_t ImageCodec::AllocateSurfaceBuffers(OMX_DIRTYPE portIndex, bool isOutputPortSettingChanged,
458 sptr<SurfaceBuffer> output)
459 {
460 HeifPerfTracker tracker(__FUNCTION__);
461 OMX_PARAM_PORTDEFINITIONTYPE def;
462 int32_t ret = GetPortDefinition(portIndex, def);
463 if (ret != IC_ERR_OK) {
464 return ret;
465 }
466 vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
467 pool.clear();
468 bool canReuseOutputBuffer = false;
469 if (isPackedInputSupported_) {
470 canReuseOutputBuffer = (output != nullptr);
471 } else {
472 canReuseOutputBuffer = (output != nullptr) && (!is10Bit_ || isOutputPortSettingChanged);
473 }
474 for (uint32_t i = 0; i < def.nBufferCountActual; ++i) {
475 shared_ptr<ImageCodecBuffer> imgCodecBuffer = canReuseOutputBuffer ?
476 ImageCodecBuffer::CreateSurfaceBuffer(output) : ImageCodecBuffer::CreateSurfaceBuffer(requestCfg_);
477 if (imgCodecBuffer == nullptr) {
478 HLOGE("AllocateSurfaceBuffers failed");
479 return IC_ERR_NO_MEMORY;
480 }
481 sptr<SurfaceBuffer> surfaceBuffer = imgCodecBuffer->GetSurfaceBuffer();
482 IF_TRUE_RETURN_VAL_WITH_MSG(surfaceBuffer == nullptr, IC_ERR_INVALID_VAL, "failed to get surfacebuffer");
483 shared_ptr<OmxCodecBuffer> omxBuffer = isEncoder_ ?
484 DynamicSurfaceBufferToOmxBuffer() : SurfaceBufferToOmxBuffer(surfaceBuffer);
485 IF_TRUE_RETURN_VAL(omxBuffer == nullptr, IC_ERR_INVALID_VAL);
486 shared_ptr<OmxCodecBuffer> outBuffer = make_shared<OmxCodecBuffer>();
487 int32_t hdiRet = compNode_->UseBuffer(portIndex, *omxBuffer, *outBuffer);
488 if (hdiRet != HDF_SUCCESS) {
489 HLOGE("Failed to UseBuffer on %{public}s port", (portIndex == OMX_DirInput ? "input" : "output"));
490 return IC_ERR_INVALID_VAL;
491 }
492 BufferInfo bufInfo;
493 bufInfo.isInput = (portIndex == OMX_DirInput) ? true : false;
494 bufInfo.owner = BufferOwner::OWNED_BY_US;
495 bufInfo.surfaceBuffer = surfaceBuffer;
496 bufInfo.imgCodecBuffer = imgCodecBuffer;
497 bufInfo.omxBuffer = outBuffer;
498 bufInfo.bufferId = outBuffer->bufferId;
499 pool.push_back(bufInfo);
500 }
501
502 return IC_ERR_OK;
503 }
504
SurfaceBufferToOmxBuffer(const sptr<SurfaceBuffer> & surfaceBuffer)505 shared_ptr<OmxCodecBuffer> ImageCodec::SurfaceBufferToOmxBuffer(const sptr<SurfaceBuffer>& surfaceBuffer)
506 {
507 BufferHandle* bufferHandle = surfaceBuffer->GetBufferHandle();
508 IF_TRUE_RETURN_VAL_WITH_MSG(bufferHandle == nullptr, nullptr, "surfacebuffer has null bufferhandle");
509 auto omxBuffer = make_shared<OmxCodecBuffer>();
510 omxBuffer->size = sizeof(OmxCodecBuffer);
511 omxBuffer->version.version.majorVersion = 1;
512 omxBuffer->bufferType = CODEC_BUFFER_TYPE_HANDLE;
513 omxBuffer->bufferhandle = new NativeBuffer(bufferHandle);
514 omxBuffer->fd = -1;
515 omxBuffer->allocLen = surfaceBuffer->GetSize();
516 omxBuffer->fenceFd = -1;
517 return omxBuffer;
518 }
519
DynamicSurfaceBufferToOmxBuffer()520 shared_ptr<OmxCodecBuffer> ImageCodec::DynamicSurfaceBufferToOmxBuffer()
521 {
522 auto omxBuffer = make_shared<OmxCodecBuffer>();
523 omxBuffer->size = sizeof(OmxCodecBuffer);
524 omxBuffer->version.version.majorVersion = 1;
525 omxBuffer->bufferType = CODEC_BUFFER_TYPE_DYNAMIC_HANDLE;
526 omxBuffer->fd = -1;
527 omxBuffer->allocLen = 0;
528 omxBuffer->fenceFd = -1;
529 return omxBuffer;
530 }
531
FindBufferInfoByID(OMX_DIRTYPE portIndex,uint32_t bufferId)532 ImageCodec::BufferInfo* ImageCodec::FindBufferInfoByID(OMX_DIRTYPE portIndex, uint32_t bufferId)
533 {
534 vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
535 for (BufferInfo &info : pool) {
536 if (info.bufferId == bufferId) {
537 return &info;
538 }
539 }
540 HLOGE("unknown buffer id %{public}u", bufferId);
541 return nullptr;
542 }
543
FindBufferIndexByID(OMX_DIRTYPE portIndex,uint32_t bufferId)544 optional<size_t> ImageCodec::FindBufferIndexByID(OMX_DIRTYPE portIndex, uint32_t bufferId)
545 {
546 const vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
547 for (size_t i = 0; i < pool.size(); i++) {
548 if (pool[i].bufferId == bufferId) {
549 return i;
550 }
551 }
552 HLOGE("unknown buffer id %{public}u", bufferId);
553 return nullopt;
554 }
555
NotifyUserToFillThisInBuffer(BufferInfo & info)556 void ImageCodec::NotifyUserToFillThisInBuffer(BufferInfo &info)
557 {
558 callback_->OnInputBufferAvailable(info.bufferId, info.imgCodecBuffer);
559 ChangeOwner(info, BufferOwner::OWNED_BY_USER);
560 }
561
OnQueueInputBuffer(const MsgInfo & msg,BufferOperationMode mode)562 void ImageCodec::OnQueueInputBuffer(const MsgInfo &msg, BufferOperationMode mode)
563 {
564 uint32_t bufferId;
565 (void)msg.param->GetValue(BUFFER_ID, bufferId);
566 BufferInfo* bufferInfo = FindBufferInfoByID(OMX_DirInput, bufferId);
567 if (bufferInfo == nullptr) {
568 ReplyErrorCode(msg.id, IC_ERR_INVALID_VAL);
569 return;
570 }
571 if (bufferInfo->owner != BufferOwner::OWNED_BY_USER) {
572 HLOGE("wrong ownership: buffer id=%{public}d, owner=%{public}s", bufferId, ToString(bufferInfo->owner));
573 ReplyErrorCode(msg.id, IC_ERR_INVALID_VAL);
574 return;
575 }
576 bufferInfo->imgCodecBuffer->GetBufferCirculateInfo(bufferInfo->omxBuffer->pts,
577 bufferInfo->omxBuffer->flag,
578 bufferInfo->omxBuffer->filledLen,
579 bufferInfo->omxBuffer->offset);
580 ChangeOwner(*bufferInfo, BufferOwner::OWNED_BY_US);
581 ReplyErrorCode(msg.id, IC_ERR_OK);
582 OnQueueInputBuffer(mode, bufferInfo);
583 }
584
OnQueueInputBuffer(BufferOperationMode mode,BufferInfo * info)585 void ImageCodec::OnQueueInputBuffer(BufferOperationMode mode, BufferInfo* info)
586 {
587 switch (mode) {
588 case KEEP_BUFFER: {
589 return;
590 }
591 case RESUBMIT_BUFFER: {
592 if (inputPortEos_) {
593 HLOGI("input already eos, keep this buffer");
594 return;
595 }
596 bool eos = (info->omxBuffer->flag & OMX_BUFFERFLAG_EOS);
597 if (!eos && info->omxBuffer->filledLen == 0) {
598 HLOGI("this is not a eos buffer but not filled, ask user to re-fill it");
599 NotifyUserToFillThisInBuffer(*info);
600 return;
601 }
602 if (eos) {
603 inputPortEos_ = true;
604 }
605 int32_t ret = NotifyOmxToEmptyThisInBuffer(*info);
606 if (ret != IC_ERR_OK) {
607 SignalError(IC_ERR_UNKNOWN);
608 }
609 return;
610 }
611 default: {
612 HLOGE("SHOULD NEVER BE HERE");
613 return;
614 }
615 }
616 }
617
NotifyOmxToEmptyThisInBuffer(BufferInfo & info)618 int32_t ImageCodec::NotifyOmxToEmptyThisInBuffer(BufferInfo& info)
619 {
620 info.Dump(compUniqueStr_, dumpMode_);
621 info.EndCpuAccess();
622 int32_t ret = compNode_->EmptyThisBuffer(*(info.omxBuffer));
623 if (ret != HDF_SUCCESS) {
624 HLOGE("EmptyThisBuffer failed");
625 return IC_ERR_UNKNOWN;
626 }
627 ChangeOwner(info, BufferOwner::OWNED_BY_OMX);
628 return IC_ERR_OK;
629 }
630
NotifyOmxToFillThisOutBuffer(BufferInfo & info)631 int32_t ImageCodec::NotifyOmxToFillThisOutBuffer(BufferInfo& info)
632 {
633 info.omxBuffer->flag = 0;
634 int32_t ret = compNode_->FillThisBuffer(*(info.omxBuffer));
635 if (ret != HDF_SUCCESS) {
636 HLOGE("outBufId = %{public}u failed", info.bufferId);
637 return IC_ERR_UNKNOWN;
638 }
639 ChangeOwner(info, BufferOwner::OWNED_BY_OMX);
640 return IC_ERR_OK;
641 }
642
OnOMXFillBufferDone(const OmxCodecBuffer & omxBuffer,BufferOperationMode mode)643 void ImageCodec::OnOMXFillBufferDone(const OmxCodecBuffer& omxBuffer, BufferOperationMode mode)
644 {
645 optional<size_t> idx = FindBufferIndexByID(OMX_DirOutput, omxBuffer.bufferId);
646 if (!idx.has_value()) {
647 return;
648 }
649 BufferInfo& info = outputBufferPool_[idx.value()];
650 if (info.owner != BufferOwner::OWNED_BY_OMX) {
651 HLOGE("wrong ownership: buffer id=%{public}d, owner=%{public}s", info.bufferId, ToString(info.owner));
652 return;
653 }
654 info.omxBuffer->offset = omxBuffer.offset;
655 info.omxBuffer->filledLen = omxBuffer.filledLen;
656 info.omxBuffer->pts = omxBuffer.pts;
657 info.omxBuffer->flag = omxBuffer.flag;
658 ChangeOwner(info, BufferOwner::OWNED_BY_US);
659 OnOMXFillBufferDone(mode, info, idx.value());
660 }
661
OnOMXFillBufferDone(BufferOperationMode mode,BufferInfo & info,size_t bufferIdx)662 void ImageCodec::OnOMXFillBufferDone(BufferOperationMode mode, BufferInfo& info, size_t bufferIdx)
663 {
664 switch (mode) {
665 case KEEP_BUFFER:
666 return;
667 case RESUBMIT_BUFFER: {
668 if (outputPortEos_) {
669 HLOGI("output eos, keep this buffer");
670 return;
671 }
672 bool eos = (info.omxBuffer->flag & OMX_BUFFERFLAG_EOS);
673 if (!eos && info.omxBuffer->filledLen == 0) {
674 HLOGD("it's not a eos buffer but not filled, ask omx to re-fill it");
675 NotifyOmxToFillThisOutBuffer(info);
676 return;
677 }
678 NotifyUserOutBufferAvaliable(info);
679 if (eos) {
680 outputPortEos_ = true;
681 }
682 return;
683 }
684 case FREE_BUFFER:
685 EraseBufferFromPool(OMX_DirOutput, bufferIdx);
686 return;
687 default:
688 HLOGE("SHOULD NEVER BE HERE");
689 return;
690 }
691 }
692
NotifyUserOutBufferAvaliable(BufferInfo & info)693 void ImageCodec::NotifyUserOutBufferAvaliable(BufferInfo &info)
694 {
695 info.BeginCpuAccess();
696 info.Dump(compUniqueStr_, dumpMode_);
697 shared_ptr<OmxCodecBuffer> omxBuffer = info.omxBuffer;
698 info.imgCodecBuffer->SetBufferCirculateInfo(omxBuffer->pts, omxBuffer->flag,
699 omxBuffer->filledLen, omxBuffer->offset);
700 callback_->OnOutputBufferAvailable(info.bufferId, info.imgCodecBuffer);
701 ChangeOwner(info, BufferOwner::OWNED_BY_USER);
702 }
703
OnReleaseOutputBuffer(const MsgInfo & msg,BufferOperationMode mode)704 void ImageCodec::OnReleaseOutputBuffer(const MsgInfo &msg, BufferOperationMode mode)
705 {
706 uint32_t bufferId;
707 (void)msg.param->GetValue(BUFFER_ID, bufferId);
708 optional<size_t> idx = FindBufferIndexByID(OMX_DirOutput, bufferId);
709 if (!idx.has_value()) {
710 ReplyErrorCode(msg.id, IC_ERR_INVALID_VAL);
711 return;
712 }
713 BufferInfo& info = outputBufferPool_[idx.value()];
714 if (info.owner != BufferOwner::OWNED_BY_USER) {
715 HLOGE("wrong ownership: buffer id=%{public}d, owner=%{public}s", bufferId, ToString(info.owner));
716 ReplyErrorCode(msg.id, IC_ERR_INVALID_VAL);
717 return;
718 }
719 HLOGD("outBufId = %{public}u", bufferId);
720 ChangeOwner(info, BufferOwner::OWNED_BY_US);
721 ReplyErrorCode(msg.id, IC_ERR_OK);
722
723 switch (mode) {
724 case KEEP_BUFFER: {
725 return;
726 }
727 case RESUBMIT_BUFFER: {
728 if (outputPortEos_) {
729 HLOGI("output eos, keep this buffer");
730 return;
731 }
732 int32_t ret = NotifyOmxToFillThisOutBuffer(info);
733 if (ret != IC_ERR_OK) {
734 SignalError(IC_ERR_UNKNOWN);
735 }
736 return;
737 }
738 case FREE_BUFFER: {
739 EraseBufferFromPool(OMX_DirOutput, idx.value());
740 return;
741 }
742 default: {
743 HLOGE("SHOULD NEVER BE HERE");
744 return;
745 }
746 }
747 }
748
ReclaimBuffer(OMX_DIRTYPE portIndex,BufferOwner owner,bool erase)749 void ImageCodec::ReclaimBuffer(OMX_DIRTYPE portIndex, BufferOwner owner, bool erase)
750 {
751 vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
752 for (size_t i = pool.size(); i > 0;) {
753 i--;
754 BufferInfo& info = pool[i];
755 if (info.owner == owner) {
756 ChangeOwner(info, BufferOwner::OWNED_BY_US);
757 if (erase) {
758 EraseBufferFromPool(portIndex, i);
759 }
760 }
761 }
762 }
763
IsAllBufferOwnedByUs(OMX_DIRTYPE portIndex)764 bool ImageCodec::IsAllBufferOwnedByUs(OMX_DIRTYPE portIndex)
765 {
766 const vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
767 for (const BufferInfo& info : pool) {
768 if (info.owner != BufferOwner::OWNED_BY_US) {
769 return false;
770 }
771 }
772 return true;
773 }
774
IsAllBufferOwnedByUs()775 bool ImageCodec::IsAllBufferOwnedByUs()
776 {
777 return IsAllBufferOwnedByUs(OMX_DirInput) && IsAllBufferOwnedByUs(OMX_DirOutput);
778 }
779
EraseOutBuffersOwnedByUs()780 void ImageCodec::EraseOutBuffersOwnedByUs()
781 {
782 // traverse index in reverse order because we need to erase index from vector
783 for (size_t i = outputBufferPool_.size(); i > 0;) {
784 i--;
785 const BufferInfo& info = outputBufferPool_[i];
786 if (info.owner == BufferOwner::OWNED_BY_US) {
787 EraseBufferFromPool(OMX_DirOutput, i);
788 }
789 }
790 }
791
ClearBufferPool(OMX_DIRTYPE portIndex)792 void ImageCodec::ClearBufferPool(OMX_DIRTYPE portIndex)
793 {
794 const vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
795 for (size_t i = pool.size(); i > 0;) {
796 i--;
797 EraseBufferFromPool(portIndex, i);
798 }
799 }
800
FreeOmxBuffer(OMX_DIRTYPE portIndex,const BufferInfo & info)801 void ImageCodec::FreeOmxBuffer(OMX_DIRTYPE portIndex, const BufferInfo& info)
802 {
803 if (compNode_ && info.omxBuffer) {
804 int32_t omxRet = compNode_->FreeBuffer(portIndex, *(info.omxBuffer));
805 if (omxRet != HDF_SUCCESS) {
806 HLOGW("notify omx to free buffer failed");
807 }
808 }
809 }
810
DoSyncCall(MsgWhat msgType,function<void (ParamSP)> oper)811 int32_t ImageCodec::DoSyncCall(MsgWhat msgType, function<void(ParamSP)> oper)
812 {
813 ParamSP reply;
814 return DoSyncCallAndGetReply(msgType, oper, reply);
815 }
816
DoSyncCallAndGetReply(MsgWhat msgType,function<void (ParamSP)> oper,ParamSP & reply)817 int32_t ImageCodec::DoSyncCallAndGetReply(MsgWhat msgType, function<void(ParamSP)> oper, ParamSP &reply)
818 {
819 ParamSP msg = make_shared<ParamBundle>();
820 IF_TRUE_RETURN_VAL_WITH_MSG(msg == nullptr, IC_ERR_NO_MEMORY, "out of memory");
821 if (oper) {
822 oper(msg);
823 }
824 bool ret = MsgHandleLoop::SendSyncMsg(msgType, msg, reply);
825 IF_TRUE_RETURN_VAL_WITH_MSG(!ret, IC_ERR_UNKNOWN, "wait msg %{public}d time out", msgType);
826 int32_t err;
827 IF_TRUE_RETURN_VAL_WITH_MSG(reply == nullptr || !reply->GetValue("err", err),
828 IC_ERR_UNKNOWN, "error code of msg %{public}d not replied", msgType);
829 return err;
830 }
831
ChangeOmxToTargetState(CodecStateType & state,CodecStateType targetState)832 void ImageCodec::ChangeOmxToTargetState(CodecStateType &state, CodecStateType targetState)
833 {
834 int32_t ret = compNode_->SendCommand(CODEC_COMMAND_STATE_SET, targetState, {});
835 if (ret != HDF_SUCCESS) {
836 HLOGE("failed to change omx state, ret=%{public}d", ret);
837 return;
838 }
839
840 int tryCnt = 0;
841 do {
842 if (tryCnt++ > 10) { // try up to 10 times
843 HLOGE("failed to change to state(%{public}d), abort", targetState);
844 state = CODEC_STATE_INVALID;
845 break;
846 }
847 this_thread::sleep_for(10ms); // wait 10ms
848 ret = compNode_->GetState(state);
849 if (ret != HDF_SUCCESS) {
850 HLOGE("failed to get omx state, ret=%{public}d", ret);
851 }
852 } while (ret == HDF_SUCCESS && state != targetState && state != CODEC_STATE_INVALID);
853 }
854
RollOmxBackToLoaded()855 bool ImageCodec::RollOmxBackToLoaded()
856 {
857 CodecStateType state;
858 int32_t ret = compNode_->GetState(state);
859 if (ret != HDF_SUCCESS) {
860 HLOGE("failed to get omx node status(ret=%{public}d), can not perform state rollback", ret);
861 return false;
862 }
863 HLOGI("current omx state (%{public}d)", state);
864 switch (state) {
865 case CODEC_STATE_EXECUTING: {
866 ChangeOmxToTargetState(state, CODEC_STATE_IDLE);
867 [[fallthrough]];
868 }
869 case CODEC_STATE_IDLE: {
870 ChangeOmxToTargetState(state, CODEC_STATE_LOADED);
871 [[fallthrough]];
872 }
873 case CODEC_STATE_LOADED:
874 case CODEC_STATE_INVALID: {
875 return true;
876 }
877 default: {
878 HLOGE("invalid omx state: %{public}d", state);
879 return false;
880 }
881 }
882 }
883
CleanUpOmxNode()884 void ImageCodec::CleanUpOmxNode()
885 {
886 if (compNode_ == nullptr) {
887 return;
888 }
889
890 if (RollOmxBackToLoaded()) {
891 for (const BufferInfo& info : inputBufferPool_) {
892 FreeOmxBuffer(OMX_DirInput, info);
893 }
894 for (const BufferInfo& info : outputBufferPool_) {
895 FreeOmxBuffer(OMX_DirOutput, info);
896 }
897 }
898 }
899
ReleaseComponent()900 void ImageCodec::ReleaseComponent()
901 {
902 CleanUpOmxNode();
903 if (compMgr_ != nullptr) {
904 compMgr_->DestroyComponent(componentId_);
905 }
906 compNode_ = nullptr;
907 compCb_ = nullptr;
908 compMgr_ = nullptr;
909 componentId_ = 0;
910 componentName_.clear();
911 }
912
ForceShutdown(int32_t generation)913 int32_t ImageCodec::ForceShutdown(int32_t generation)
914 {
915 if (generation != stateGeneration_) {
916 HLOGE("ignoring stale force shutdown message: #%{public}d (now #%{public}d)",
917 generation, stateGeneration_);
918 return IC_ERR_OK;
919 }
920 HLOGI("force to shutdown");
921 isShutDownFromRunning_ = true;
922 notifyCallerAfterShutdownComplete_ = false;
923 auto err = compNode_->SendCommand(CODEC_COMMAND_STATE_SET, CODEC_STATE_IDLE, {});
924 if (err == HDF_SUCCESS) {
925 ChangeStateTo(stoppingState_);
926 }
927 return IC_ERR_OK;
928 }
929
SignalError(ImageCodecError err)930 void ImageCodec::SignalError(ImageCodecError err)
931 {
932 HLOGE("fatal error happened: errCode=%{public}d", err);
933 hasFatalError_ = true;
934 callback_->OnError(err);
935 }
936
DeferMessage(const MsgInfo & info)937 void ImageCodec::DeferMessage(const MsgInfo &info)
938 {
939 deferredQueue_.push_back(info);
940 }
941
ProcessDeferredMessages()942 void ImageCodec::ProcessDeferredMessages()
943 {
944 for (const MsgInfo &info : deferredQueue_) {
945 StateMachine::OnMsgReceived(info);
946 }
947 deferredQueue_.clear();
948 }
949
ReplyToSyncMsgLater(const MsgInfo & msg)950 void ImageCodec::ReplyToSyncMsgLater(const MsgInfo& msg)
951 {
952 syncMsgToReply_[msg.type].push(make_pair(msg.id, msg.param));
953 }
954
GetFirstSyncMsgToReply(MsgInfo & msg)955 bool ImageCodec::GetFirstSyncMsgToReply(MsgInfo& msg)
956 {
957 auto iter = syncMsgToReply_.find(msg.type);
958 if (iter == syncMsgToReply_.end()) {
959 return false;
960 }
961 tie(msg.id, msg.param) = iter->second.front();
962 iter->second.pop();
963 return true;
964 }
965
966 /**************************** HdiCallback functions begin ****************************/
EventHandler(CodecEventType event,const EventInfo & info)967 int32_t ImageCodec::HdiCallback::EventHandler(CodecEventType event, const EventInfo &info)
968 {
969 LOGD("event = %{public}d, data1 = %{public}u, data2 = %{public}u", event, info.data1, info.data2);
970 ParamSP msg = make_shared<ParamBundle>();
971 msg->SetValue("event", event);
972 msg->SetValue("data1", info.data1);
973 msg->SetValue("data2", info.data2);
974 codec_->SendAsyncMsg(MsgWhat::CODEC_EVENT, msg);
975 return HDF_SUCCESS;
976 }
977
EmptyBufferDone(int64_t appData,const OmxCodecBuffer & buffer)978 int32_t ImageCodec::HdiCallback::EmptyBufferDone(int64_t appData, const OmxCodecBuffer& buffer)
979 {
980 ParamSP msg = make_shared<ParamBundle>();
981 msg->SetValue(BUFFER_ID, buffer.bufferId);
982 codec_->SendAsyncMsg(MsgWhat::OMX_EMPTY_BUFFER_DONE, msg);
983 return HDF_SUCCESS;
984 }
985
FillBufferDone(int64_t appData,const OmxCodecBuffer & buffer)986 int32_t ImageCodec::HdiCallback::FillBufferDone(int64_t appData, const OmxCodecBuffer& buffer)
987 {
988 ParamSP msg = make_shared<ParamBundle>();
989 msg->SetValue("omxBuffer", buffer);
990 codec_->SendAsyncMsg(MsgWhat::OMX_FILL_BUFFER_DONE, msg);
991 return HDF_SUCCESS;
992 }
993 /**************************** HdiCallback functions begin ****************************/
994
995 /**************************** BufferInfo functions begin ****************************/
CleanUpUnusedInfo()996 void ImageCodec::BufferInfo::CleanUpUnusedInfo()
997 {
998 if (omxBuffer == nullptr || omxBuffer->fd < 0) {
999 return;
1000 }
1001 if (omxBuffer->fd == 0) {
1002 LOGW("fd of omxbuffer should never be 0");
1003 }
1004 close(omxBuffer->fd);
1005 omxBuffer->fd = -1;
1006 }
1007
BeginCpuAccess()1008 void ImageCodec::BufferInfo::BeginCpuAccess()
1009 {
1010 if (surfaceBuffer && (surfaceBuffer->GetUsage() & BUFFER_USAGE_MEM_MMZ_CACHE)) {
1011 GSError err = surfaceBuffer->InvalidateCache();
1012 if (err != GSERROR_OK) {
1013 LOGW("InvalidateCache failed, GSError=%{public}d", err);
1014 }
1015 }
1016 }
1017
EndCpuAccess()1018 void ImageCodec::BufferInfo::EndCpuAccess()
1019 {
1020 if (surfaceBuffer && (surfaceBuffer->GetUsage() & BUFFER_USAGE_MEM_MMZ_CACHE)) {
1021 GSError err = surfaceBuffer->Map();
1022 if (err != GSERROR_OK) {
1023 LOGW("Map failed, GSError=%{public}d", err);
1024 return;
1025 }
1026 err = surfaceBuffer->FlushCache();
1027 if (err != GSERROR_OK) {
1028 LOGW("FlushCache failed, GSError=%{public}d", err);
1029 }
1030 }
1031 }
1032 /**************************** BufferInfo functions end ****************************/
1033 } // namespace OHOS::ImagePlugin