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/heif_hw_decoder.h"
17 #include "hardware/imagecodec/image_codec_list.h"
18 #include "hardware/imagecodec/image_codec_log.h"
19 #include "hardware/imagecodec/type_converter.h"
20 #include "media_errors.h" // foundation/multimedia/image_framework/interfaces/innerkits/include/
21 #include "syspara/parameters.h" // base/startup/init/interfaces/innerkits/include/
22 #include <fstream>
23 #include <algorithm>
24 #include <climits>
25
26 namespace OHOS::ImagePlugin {
27 using namespace std;
28 using namespace HdiCodecNamespace;
29
30 // LCOV_EXCL_START
IsValid() const31 bool GridInfo::IsValid() const
32 {
33 IF_TRUE_RETURN_VAL_WITH_MSG((displayWidth == 0 || displayHeight == 0), false,
34 "invalid displaySize: [%{public}ux%{public}u]", displayWidth, displayHeight);
35 IF_TRUE_RETURN_VAL(!enableGrid, true);
36 IF_TRUE_RETURN_VAL_WITH_MSG((cols == 0 || rows == 0), false,
37 "invalid gridSize: [%{public}ux%{public}u]", cols, rows);
38 IF_TRUE_RETURN_VAL_WITH_MSG((tileWidth == 0 || tileHeight == 0), false,
39 "invalid tileSize: [%{public}ux%{public}u]", tileWidth, tileHeight);
40 uint32_t expCols = static_cast<uint32_t>(ceil(static_cast<float>(displayWidth) / static_cast<float>(tileWidth)));
41 IF_TRUE_RETURN_VAL_WITH_MSG(expCols != cols, false,
42 "invalid cols, expect %{public}u, get %{public}u", expCols, cols);
43 uint32_t expRows = static_cast<uint32_t>(ceil(static_cast<float>(displayHeight) / static_cast<float>(tileHeight)));
44 IF_TRUE_RETURN_VAL_WITH_MSG(expRows != rows, false,
45 "invalid rows, expect %{public}u, get %{public}u", expRows, rows);
46 return true;
47 }
48 // LCOV_EXCL_STOP
49
HeifDecoderCallback(HeifHardwareDecoder * heifDecoder)50 HeifHardwareDecoder::HeifDecoderCallback::HeifDecoderCallback(HeifHardwareDecoder* heifDecoder)
51 : heifDecoder_(heifDecoder)
52 {}
53
OnError(ImageCodecError err)54 void HeifHardwareDecoder::HeifDecoderCallback::OnError(ImageCodecError err)
55 {
56 heifDecoder_->SignalError();
57 }
58
OnOutputFormatChanged(const Format & format)59 void HeifHardwareDecoder::HeifDecoderCallback::OnOutputFormatChanged(const Format &format)
60 {}
61
OnInputBufferAvailable(uint32_t index,std::shared_ptr<ImageCodecBuffer> buffer)62 void HeifHardwareDecoder::HeifDecoderCallback::OnInputBufferAvailable(uint32_t index,
63 std::shared_ptr<ImageCodecBuffer> buffer)
64 {
65 lock_guard<mutex> lk(heifDecoder_->inputMtx_);
66 heifDecoder_->inputList_.emplace_back(index, buffer);
67 heifDecoder_->inputCond_.notify_all();
68 }
69
OnOutputBufferAvailable(uint32_t index,std::shared_ptr<ImageCodecBuffer> buffer)70 void HeifHardwareDecoder::HeifDecoderCallback::OnOutputBufferAvailable(uint32_t index,
71 std::shared_ptr<ImageCodecBuffer> buffer)
72 {
73 lock_guard<mutex> lk(heifDecoder_->outputMtx_);
74 heifDecoder_->outputList_.emplace_back(index, buffer);
75 heifDecoder_->outputCond_.notify_all();
76 }
77
HeifHardwareDecoder()78 HeifHardwareDecoder::HeifHardwareDecoder() : uvOffsetForOutput_(0)
79 {
80 heifDecoderImpl_ = ImageCodec::Create();
81 }
82
~HeifHardwareDecoder()83 HeifHardwareDecoder::~HeifHardwareDecoder()
84 {
85 if (releaseThread_.joinable()) {
86 releaseThread_.join();
87 }
88 Reset();
89 }
90
91 // LCOV_EXCL_START
AllocateOutputBuffer(uint32_t width,uint32_t height,int32_t pixelFmt)92 sptr<SurfaceBuffer> HeifHardwareDecoder::AllocateOutputBuffer(uint32_t width, uint32_t height, int32_t pixelFmt)
93 {
94 HeifPerfTracker tracker(__FUNCTION__);
95 LOGI("BufferInfo: width=%{public}u, height=%{public}u, pixelFmt=%{public}d", width, height, pixelFmt);
96 IF_TRUE_RETURN_VAL_WITH_MSG(heifDecoderImpl_ == nullptr, nullptr, "failed to create heif decoder");
97 IF_TRUE_RETURN_VAL_WITH_MSG((width == 0 || height == 0), nullptr,
98 "invalid size=[%{public}ux%{public}u]", width, height);
99 optional<PixelFmt> fmt = TypeConverter::GraphicFmtToFmt(static_cast<GraphicPixelFormat>(pixelFmt));
100 IF_TRUE_RETURN_VAL(!fmt.has_value(), nullptr);
101
102 uint64_t usage;
103 int32_t err = heifDecoderImpl_->GetOutputBufferUsage(usage);
104 IF_TRUE_RETURN_VAL_WITH_MSG(err != IC_ERR_OK, nullptr, "failed to get output buffer usage, err=%{public}d", err);
105 sptr<SurfaceBuffer> output = SurfaceBuffer::Create();
106 IF_TRUE_RETURN_VAL_WITH_MSG(output == nullptr, nullptr, "failed to create output");
107 BufferRequestConfig config = {
108 .width = width,
109 .height = height,
110 .strideAlignment = STRIDE_ALIGNMENT,
111 .format = pixelFmt,
112 .usage = usage,
113 .timeout = 0
114 };
115 GSError ret = output->Alloc(config);
116 IF_TRUE_RETURN_VAL_WITH_MSG(ret != GSERROR_OK, nullptr, "failed to alloc output, ret=%{public}d", ret);
117 return output;
118 }
119 // LCOV_EXCL_STOP
120
IsValueInRange(uint32_t value,HdiCodecNamespace::RangeValue range)121 static bool IsValueInRange(uint32_t value, HdiCodecNamespace::RangeValue range)
122 {
123 return (value >= static_cast<uint32_t>(range.min)) && (value <= static_cast<uint32_t>(range.max));
124 }
125
126 // LCOV_EXCL_START
IsHardwareDecodeSupported(const GridInfo & gridInfo)127 bool HeifHardwareDecoder::IsHardwareDecodeSupported(const GridInfo& gridInfo)
128 {
129 string decoderName = heifDecoderImpl_->GetComponentName();
130 vector<CodecCompCapability> capList = GetCapList();
131 auto it = find_if(capList.begin(), capList.end(), [decoderName](const CodecCompCapability& cap) {
132 return (cap.compName == decoderName);
133 });
134 if (it == capList.end()) {
135 LOGE("can not find heif hw decoder");
136 return false;
137 }
138 uint32_t widthToCheck = gridInfo.enableGrid ? gridInfo.tileWidth : gridInfo.displayWidth;
139 uint32_t heightToCheck = gridInfo.enableGrid ? gridInfo.tileHeight : gridInfo.displayHeight;
140 HdiCodecNamespace::RangeValue widthRange = {
141 .min = it->port.video.minSize.width,
142 .max = it->port.video.maxSize.width
143 };
144 HdiCodecNamespace::RangeValue heightRange = {
145 .min = it->port.video.minSize.height,
146 .max = it->port.video.maxSize.height
147 };
148 bool isValidSize = false;
149 if (it->canSwapWidthHeight) {
150 LOGD("decoder support swap width and height");
151 isValidSize = (IsValueInRange(widthToCheck, widthRange) && IsValueInRange(heightToCheck, heightRange)) ||
152 (IsValueInRange(heightToCheck, widthRange) && IsValueInRange(widthToCheck, heightRange));
153 } else {
154 isValidSize = IsValueInRange(widthToCheck, widthRange) && IsValueInRange(heightToCheck, heightRange);
155 }
156 if (!isValidSize) {
157 LOGE("unsupported size: [%{public}ux%{public}u]", widthToCheck, heightToCheck);
158 return false;
159 }
160 return true;
161 }
162
SetCallbackForDecoder()163 bool HeifHardwareDecoder::SetCallbackForDecoder()
164 {
165 HeifPerfTracker tracker(__FUNCTION__);
166 shared_ptr<HeifDecoderCallback> cb = make_shared<HeifDecoderCallback>(this);
167 int32_t ret = heifDecoderImpl_->SetCallback(cb);
168 if (ret != IC_ERR_OK) {
169 LOGE("failed to set callback for decoder, err=%{public}d", ret);
170 return false;
171 }
172 return true;
173 }
174
ConfigureDecoder(const GridInfo & gridInfo,sptr<SurfaceBuffer> & output)175 bool HeifHardwareDecoder::ConfigureDecoder(const GridInfo& gridInfo, sptr<SurfaceBuffer>& output)
176 {
177 HeifPerfTracker tracker(__FUNCTION__);
178 Format format;
179 if (gridInfo.enableGrid) {
180 format.SetValue(ImageCodecDescriptionKey::WIDTH, gridInfo.tileWidth);
181 format.SetValue(ImageCodecDescriptionKey::HEIGHT, gridInfo.tileHeight);
182 } else {
183 format.SetValue(ImageCodecDescriptionKey::WIDTH, gridInfo.displayWidth);
184 format.SetValue(ImageCodecDescriptionKey::HEIGHT, gridInfo.displayHeight);
185 }
186 static constexpr double OUTPUT_FRAME_RATE = 120.0;
187 format.SetValue(ImageCodecDescriptionKey::FRAME_RATE, OUTPUT_FRAME_RATE);
188 format.SetValue(ImageCodecDescriptionKey::VIDEO_FRAME_RATE_ADAPTIVE_MODE, true);
189 format.SetValue(ImageCodecDescriptionKey::PIXEL_FORMAT, output->GetFormat());
190 format.SetValue(ImageCodecDescriptionKey::ENABLE_HEIF_GRID, gridInfo.enableGrid);
191 if (!gridInfo.enableGrid || packedInputFlag_) {
192 static constexpr uint32_t INPUT_BUFFER_CNT = 3;
193 format.SetValue(ImageCodecDescriptionKey::INPUT_BUFFER_COUNT, INPUT_BUFFER_CNT);
194 }
195 if (!gridInfo.enableGrid || !packedInputFlag_) {
196 static constexpr uint32_t OUTPUT_BUFFER_CNT = 1;
197 format.SetValue(ImageCodecDescriptionKey::OUTPUT_BUFFER_COUNT, OUTPUT_BUFFER_CNT);
198 }
199 if (packedInputFlag_) {
200 static constexpr char HEIF_HW_DECODER_NAME[] = "heif_hw_decoder";
201 format.SetValue(ImageCodecDescriptionKey::PROCESS_NAME, string(HEIF_HW_DECODER_NAME));
202 }
203 int32_t ret = heifDecoderImpl_->Configure(format);
204 if (ret != IC_ERR_OK) {
205 LOGE("failed to configure decoder, err=%{public}d", ret);
206 return false;
207 }
208 return true;
209 }
210
SetOutputBuffer(const GridInfo & gridInfo,sptr<SurfaceBuffer> output)211 bool HeifHardwareDecoder::SetOutputBuffer(const GridInfo& gridInfo, sptr<SurfaceBuffer> output)
212 {
213 HeifPerfTracker tracker(__FUNCTION__);
214 if (gridInfo.enableGrid) {
215 return true;
216 }
217 int32_t ret = heifDecoderImpl_->SetOutputBuffer(output);
218 if (ret != IC_ERR_OK) {
219 LOGE("failed to set output buffer, err=%{public}d", ret);
220 return false;
221 }
222 return true;
223 }
224
GetUvPlaneOffsetFromSurfaceBuffer(sptr<SurfaceBuffer> & surfaceBuffer,uint64_t & offset)225 bool HeifHardwareDecoder::GetUvPlaneOffsetFromSurfaceBuffer(sptr<SurfaceBuffer>& surfaceBuffer, uint64_t& offset)
226 {
227 IF_TRUE_RETURN_VAL_WITH_MSG(surfaceBuffer == nullptr, false, "invalid surface buffer");
228 OH_NativeBuffer_Planes* outputPlanes = nullptr;
229 GSError ret = surfaceBuffer->GetPlanesInfo(reinterpret_cast<void**>(&outputPlanes));
230 IF_TRUE_RETURN_VAL_WITH_MSG((ret != GSERROR_OK || outputPlanes == nullptr), false,
231 "GetPlanesInfo failed, GSError=%{public}d", ret);
232 IF_TRUE_RETURN_VAL_WITH_MSG(outputPlanes->planeCount < PLANE_BUTT, false,
233 "invalid yuv buffer, %{public}u", outputPlanes->planeCount);
234 int32_t pixelFmt = surfaceBuffer->GetFormat();
235 if (pixelFmt == GRAPHIC_PIXEL_FMT_YCBCR_420_SP || pixelFmt == GRAPHIC_PIXEL_FMT_YCBCR_P010) {
236 offset = outputPlanes->planes[PLANE_U].offset;
237 } else {
238 offset = outputPlanes->planes[PLANE_V].offset;
239 }
240 return true;
241 }
242 // LCOV_EXCL_STOP
243
DumpSingleInput(const std::string & type,const GridInfo & gridInfo,const std::vector<std::vector<uint8_t>> & inputs)244 void HeifHardwareDecoder::DumpSingleInput(const std::string& type, const GridInfo& gridInfo,
245 const std::vector<std::vector<uint8_t>>& inputs)
246 {
247 char inFilePath[MAX_PATH_LEN] = {0};
248 int ret = -1;
249 if (gridInfo.enableGrid) {
250 ret = sprintf_s(inFilePath, sizeof(inFilePath), "%s/in_%s_%ux%u_grid_%ux%u_%ux%u.bin",
251 DUMP_PATH, type.c_str(), gridInfo.displayWidth, gridInfo.displayHeight,
252 gridInfo.tileWidth, gridInfo.tileHeight, gridInfo.cols, gridInfo.rows);
253 } else {
254 ret = sprintf_s(inFilePath, sizeof(inFilePath), "%s/in_%s_%ux%u_nogrid.bin",
255 DUMP_PATH, type.c_str(), gridInfo.displayWidth, gridInfo.displayHeight);
256 }
257 if (ret == -1) {
258 LOGE("failed to dump input %{public}s", type.c_str());
259 return;
260 }
261 char realpathRes[PATH_MAX] = {0};
262 realpath(inFilePath, realpathRes);
263 if (realpathRes == nullptr || !verify_file(realpathRes)) {
264 LOGE("%{public}s is invalid", realpathRes);
265 return;
266 }
267 std::ofstream dumpInFile;
268 dumpInFile.open(std::string(realpathRes), std::ios_base::binary | std::ios_base::trunc);
269 if (!dumpInFile.is_open()) {
270 LOGE("failed to open %{public}s", realpathRes);
271 return;
272 }
273 for (size_t i = 0; i < inputs.size(); ++i) {
274 if ((i == 0) && (type == "data")) {
275 continue;
276 }
277 const vector<uint8_t>& one = inputs[i];
278 dumpInFile.write(reinterpret_cast<char*>(const_cast<uint8_t*>(one.data())), one.size());
279 if ((i == 0) && (type == "xps")) {
280 break;
281 }
282 }
283 dumpInFile.close();
284 }
285
DumpInput(const GridInfo & gridInfo,const std::vector<std::vector<uint8_t>> & inputs)286 void HeifHardwareDecoder::DumpInput(const GridInfo& gridInfo, const std::vector<std::vector<uint8_t>>& inputs)
287 {
288 DumpSingleInput("all", gridInfo, inputs);
289 DumpSingleInput("xps", gridInfo, inputs);
290 DumpSingleInput("data", gridInfo, inputs);
291 }
292
GetPackedInputFlag()293 void HeifHardwareDecoder::GetPackedInputFlag()
294 {
295 packedInputFlag_ = false;
296 if (heifDecoderImpl_ != nullptr) {
297 (void)heifDecoderImpl_->GetPackedInputFlag(packedInputFlag_);
298 }
299 }
300
IsPackedInputSupported()301 bool HeifHardwareDecoder::IsPackedInputSupported()
302 {
303 GetPackedInputFlag();
304 return packedInputFlag_;
305 }
306
DoDecode(const GridInfo & gridInfo,std::vector<std::vector<uint8_t>> & inputs,sptr<SurfaceBuffer> & output)307 uint32_t HeifHardwareDecoder::DoDecode(const GridInfo& gridInfo, std::vector<std::vector<uint8_t>>& inputs,
308 sptr<SurfaceBuffer>& output)
309 {
310 LOGI("GridInfo: displayWidth=%{public}u, displayHeight=%{public}u, enableGrid=%{public}d, " \
311 "cols=%{public}u, rows=%{public}u, tileWidth=%{public}u, tileHeight=%{public}u",
312 gridInfo.displayWidth, gridInfo.displayHeight, gridInfo.enableGrid, gridInfo.cols, gridInfo.rows,
313 gridInfo.tileWidth, gridInfo.tileHeight);
314 HeifPerfTracker tracker(__FUNCTION__);
315 IF_TRUE_RETURN_VAL(!gridInfo.IsValid(), Media::ERR_IMAGE_INVALID_PARAMETER);
316 IF_TRUE_RETURN_VAL_WITH_MSG(output == nullptr, Media::ERR_IMAGE_INVALID_PARAMETER, "null output");
317 IF_TRUE_RETURN_VAL_WITH_MSG(inputs.size() < MIN_SIZE_OF_INPUT, Media::ERR_IMAGE_INVALID_PARAMETER,
318 "input size < %{public}zu", MIN_SIZE_OF_INPUT);
319 IF_TRUE_RETURN_VAL_WITH_MSG(heifDecoderImpl_ == nullptr, Media::ERR_IMAGE_DECODE_FAILED,
320 "failed to create heif decoder");
321 if (OHOS::system::GetBoolParameter("image.codec.dump", false)) {
322 DumpInput(gridInfo, inputs);
323 }
324 IF_TRUE_RETURN_VAL(!IsHardwareDecodeSupported(gridInfo), Media::ERR_IMAGE_HW_DECODE_UNSUPPORT);
325 IF_TRUE_RETURN_VAL(!SetCallbackForDecoder(), Media::ERR_IMAGE_DECODE_FAILED);
326 GetPackedInputFlag();
327 IF_TRUE_RETURN_VAL(!ConfigureDecoder(gridInfo, output), Media::ERR_IMAGE_DECODE_FAILED);
328 IF_TRUE_RETURN_VAL(!SetOutputBuffer(gridInfo, output), Media::ERR_IMAGE_DECODE_FAILED);
329 Reset();
330 output_ = output;
331 IF_TRUE_RETURN_VAL(!GetUvPlaneOffsetFromSurfaceBuffer(output_, uvOffsetForOutput_),
332 Media::ERR_IMAGE_DECODE_FAILED);
333 gridInfo_ = gridInfo;
334 thread inputThread(&HeifHardwareDecoder::SendInputBufferLoop, this, inputs);
335 thread outputThread(&HeifHardwareDecoder::ReceiveOutputBufferLoop, this);
336 int32_t ret = heifDecoderImpl_->Start();
337 if (ret != IC_ERR_OK) {
338 LOGE("failed to start decoder, err=%{public}d", ret);
339 SignalError();
340 }
341 if (inputThread.joinable()) {
342 inputThread.join();
343 }
344 if (outputThread.joinable()) {
345 outputThread.join();
346 }
347 releaseThread_ = thread([this] {
348 this->ReleaseDecoder();
349 });
350 IF_TRUE_RETURN_VAL_WITH_MSG(hasErr_, Media::ERR_IMAGE_DECODE_FAILED, "err occured during decode");
351 FlushOutput();
352 if (OHOS::system::GetBoolParameter("image.codec.dump", false)) {
353 DumpOutput();
354 }
355 return Media::SUCCESS;
356 }
357
358 // LCOV_EXCL_START
ReleaseDecoder()359 void HeifHardwareDecoder::ReleaseDecoder()
360 {
361 HeifPerfTracker tracker(__FUNCTION__);
362 int32_t ret = heifDecoderImpl_->Release();
363 if (ret != IC_ERR_OK) {
364 LOGE("failed to release decoder, err=%{public}d", ret);
365 }
366 }
367 // LCOV_EXCL_STOP
368
Reset()369 void HeifHardwareDecoder::Reset()
370 {
371 hasErr_ = false;
372 output_ = nullptr;
373 inputList_.clear();
374 outputList_.clear();
375 }
376
377 // LCOV_EXCL_START
FlushOutput()378 void HeifHardwareDecoder::FlushOutput()
379 {
380 if (output_->GetUsage() & BUFFER_USAGE_MEM_MMZ_CACHE) {
381 GSError err = output_->Map();
382 if (err != GSERROR_OK) {
383 LOGW("Map failed, GSError=%{public}d", err);
384 return;
385 }
386 err = output_->FlushCache();
387 if (err != GSERROR_OK) {
388 LOGW("FlushCache failed, GSError=%{public}d", err);
389 }
390 }
391 }
392 // LCOV_EXCL_STOP
393
GetOutputPixelFmtDesc()394 string HeifHardwareDecoder::GetOutputPixelFmtDesc()
395 {
396 optional<PixelFmt> fmt = TypeConverter::GraphicFmtToFmt(static_cast<GraphicPixelFormat>(output_->GetFormat()));
397 if (fmt.has_value()) {
398 return fmt->strFmt;
399 }
400 return "unknown";
401 }
402
403 // LCOV_EXCL_START
DumpOutput()404 void HeifHardwareDecoder::DumpOutput()
405 {
406 string pixelFmtDesc = GetOutputPixelFmtDesc();
407 char outputFilePath[MAX_PATH_LEN] = {0};
408 int ret = 0;
409 if (gridInfo_.enableGrid) {
410 ret = sprintf_s(outputFilePath, sizeof(outputFilePath), "%s/out_%s_%ux%u_grid_%ux%u_%ux%u.bin",
411 DUMP_PATH, pixelFmtDesc.c_str(), gridInfo_.displayWidth, gridInfo_.displayHeight,
412 gridInfo_.tileWidth, gridInfo_.tileHeight, gridInfo_.cols, gridInfo_.rows);
413 } else {
414 ret = sprintf_s(outputFilePath, sizeof(outputFilePath), "%s/out_%s_%ux%u_nogrid.bin",
415 DUMP_PATH, pixelFmtDesc.c_str(), gridInfo_.displayWidth, gridInfo_.displayHeight);
416 }
417 if (ret == -1) {
418 LOGE("failed to create dump file");
419 return;
420 }
421 LOGI("dump result to: %{public}s", outputFilePath);
422
423 std::ofstream dumpOutFile;
424 dumpOutFile.open(std::string(outputFilePath), std::ios_base::binary | std::ios_base::trunc);
425 if (!dumpOutFile.is_open()) {
426 LOGE("failed to dump decode result");
427 return;
428 }
429
430 GSError err = output_->InvalidateCache();
431 if (err != GSERROR_OK) {
432 LOGW("InvalidateCache failed, GSError=%{public}d", err);
433 }
434 dumpOutFile.write(reinterpret_cast<char*>(output_->GetVirAddr()), output_->GetSize());
435 dumpOutFile.close();
436 }
437 // LCOV_EXCL_STOP
438
GetTimestampInUs()439 int64_t HeifHardwareDecoder::GetTimestampInUs()
440 {
441 auto now = chrono::steady_clock::now();
442 return chrono::duration_cast<chrono::microseconds>(now.time_since_epoch()).count();
443 }
444
PrepareInputCodecBuffer(const vector<vector<uint8_t>> & inputs,size_t inputIndex,shared_ptr<ImageCodecBuffer> & buffer)445 int32_t HeifHardwareDecoder::PrepareInputCodecBuffer(const vector<vector<uint8_t>>& inputs, size_t inputIndex,
446 shared_ptr<ImageCodecBuffer>& buffer)
447 {
448 HeifPerfTracker tracker(__FUNCTION__);
449 int64_t pts = GetTimestampInUs();
450 if (inputIndex >= inputs.size()) {
451 buffer->SetBufferCirculateInfo(pts, OMX_BUFFERFLAG_EOS, 0, 0);
452 return 0;
453 }
454 const vector<uint8_t>& one = inputs[inputIndex];
455 if (one.empty()) {
456 LOGW("inputs[%{public}zu] is empty", inputIndex);
457 return -1;
458 }
459 errno_t ret = memcpy_s(buffer->GetAddr(), static_cast<size_t>(buffer->GetCapacity()), one.data(), one.size());
460 if (ret != EOK) {
461 LOGE("failed to get input");
462 return -1;
463 }
464 uint32_t flag = inputIndex == 0 ? OMX_BUFFERFLAG_CODECCONFIG : 0;
465 int32_t size = static_cast<int32_t>(one.size());
466 buffer->SetBufferCirculateInfo(pts, flag, static_cast<uint32_t>(size), 0);
467 return size;
468 }
469
470 // LCOV_EXCL_START
WaitForOmxToReturnInputBuffer(uint32_t & bufferId,shared_ptr<ImageCodecBuffer> & buffer)471 bool HeifHardwareDecoder::WaitForOmxToReturnInputBuffer(uint32_t& bufferId, shared_ptr<ImageCodecBuffer>& buffer)
472 {
473 unique_lock<mutex> lk(inputMtx_);
474 bool ret = inputCond_.wait_for(lk, chrono::milliseconds(BUFFER_CIRCULATE_TIMEOUT_IN_MS), [this] {
475 return !inputList_.empty();
476 });
477 if (!ret) {
478 return false;
479 }
480 std::tie(bufferId, buffer) = inputList_.front();
481 inputList_.pop_front();
482 return true;
483 }
484
SendInputBufferLoop(const vector<vector<uint8_t>> & inputs)485 void HeifHardwareDecoder::SendInputBufferLoop(const vector<vector<uint8_t>>& inputs)
486 {
487 LOGD("in");
488 size_t inputIndex = 0;
489 bool eos = false;
490 uint32_t inputTimeoutCnt = 0;
491 while (!eos && !HasError()) {
492 uint32_t bufferId;
493 shared_ptr<ImageCodecBuffer> buffer;
494 if (!WaitForOmxToReturnInputBuffer(bufferId, buffer)) {
495 LOGE("input time out");
496 ++inputTimeoutCnt;
497 if (inputTimeoutCnt >= MAX_TIMEOUT_CNT) {
498 SignalError();
499 }
500 continue;
501 }
502 if (buffer == nullptr) {
503 LOGE("got null input buffer");
504 break;
505 }
506 inputTimeoutCnt = 0;
507 int32_t size = PrepareInputCodecBuffer(inputs, inputIndex, buffer);
508 if (size >= 0) {
509 int32_t ret = heifDecoderImpl_->QueueInputBuffer(bufferId);
510 if (ret != IC_ERR_OK) {
511 LOGE("failed to queue input buffer");
512 }
513 }
514 ++inputIndex;
515 eos = (size == 0);
516 }
517 LOGD("out");
518 }
519
WaitForOmxToReturnOutputBuffer(uint32_t & bufferId,shared_ptr<ImageCodecBuffer> & buffer)520 bool HeifHardwareDecoder::WaitForOmxToReturnOutputBuffer(uint32_t& bufferId, shared_ptr<ImageCodecBuffer>& buffer)
521 {
522 unique_lock<mutex> lk(outputMtx_);
523 bool ret = outputCond_.wait_for(lk, chrono::milliseconds(BUFFER_CIRCULATE_TIMEOUT_IN_MS), [this] {
524 return !outputList_.empty();
525 });
526 if (!ret) {
527 return false;
528 }
529 std::tie(bufferId, buffer) = outputList_.front();
530 outputList_.pop_front();
531 return true;
532 }
533 // LCOV_EXCL_STOP
534
CopyRawYuvData(const RawYuvCopyInfo & src,const RawYuvCopyInfo & dst,uint32_t dirtyWidth,uint32_t dirtyHeight)535 bool HeifHardwareDecoder::CopyRawYuvData(const RawYuvCopyInfo& src, const RawYuvCopyInfo& dst,
536 uint32_t dirtyWidth, uint32_t dirtyHeight)
537 {
538 IF_TRUE_RETURN_VAL_WITH_MSG((dst.yStart == nullptr || dst.uvStart == nullptr),
539 false, "can not get addr from dst buffer");
540 IF_TRUE_RETURN_VAL_WITH_MSG((src.yStart == nullptr || src.uvStart == nullptr),
541 false, "can not get addr from src buffer");
542 errno_t ret = EOK;
543 // copy Y plane
544 for (uint32_t row = 0; (row < dirtyHeight) && (ret == EOK); ++row) {
545 ret = memcpy_s(dst.yStart + dst.yOffset + row * dst.stride, static_cast<size_t>(dirtyWidth),
546 src.yStart + src.yOffset + row * src.stride, static_cast<size_t>(dirtyWidth));
547 }
548 // copy UV plane
549 uint32_t dirtyHeightForUvPlane = (dirtyHeight + SAMPLE_RATIO_FOR_YUV420_SP - 1) / SAMPLE_RATIO_FOR_YUV420_SP;
550 for (uint32_t row = 0; (row < dirtyHeightForUvPlane) && (ret == EOK); ++row) {
551 ret = memcpy_s(dst.uvStart + dst.uvOffset + row * dst.stride, static_cast<size_t>(dirtyWidth),
552 src.uvStart + src.uvOffset + row * src.stride, static_cast<size_t>(dirtyWidth));
553 }
554 if (ret != EOK) {
555 LOGE("failed to copy grid data, err=%{public}d", ret);
556 return false;
557 }
558 return true;
559 }
560
561 // LCOV_EXCL_START
CalculateDirtyLen(uint32_t displayLen,uint32_t gridLen,uint32_t totalGrid,uint32_t curGrid)562 uint32_t HeifHardwareDecoder::CalculateDirtyLen(uint32_t displayLen, uint32_t gridLen,
563 uint32_t totalGrid, uint32_t curGrid)
564 {
565 uint32_t dirtyLen = 0;
566 if (gridLen >= displayLen) {
567 dirtyLen = displayLen;
568 } else {
569 dirtyLen = gridLen;
570 if (curGrid + 1 == totalGrid) {
571 dirtyLen = displayLen - curGrid * gridLen;
572 }
573 }
574 return dirtyLen;
575 }
576
AssembleOutput(uint32_t outputIndex,shared_ptr<ImageCodecBuffer> & buffer)577 void HeifHardwareDecoder::AssembleOutput(uint32_t outputIndex, shared_ptr<ImageCodecBuffer>& buffer)
578 {
579 HeifPerfTracker tracker(__FUNCTION__);
580
581 uint64_t srcUvOffset = 0;
582 sptr<SurfaceBuffer> srcSurfaceBuffer = buffer->GetSurfaceBuffer();
583 if (!GetUvPlaneOffsetFromSurfaceBuffer(srcSurfaceBuffer, srcUvOffset)) {
584 SignalError();
585 return;
586 }
587
588 RawYuvCopyInfo dst;
589 dst.yStart = static_cast<uint8_t*>(output_->GetVirAddr());
590 dst.stride = static_cast<uint32_t>(output_->GetStride());
591 dst.uvStart = dst.yStart + uvOffsetForOutput_;
592 dst.yStride = static_cast<uint32_t>(uvOffsetForOutput_ / static_cast<uint64_t>(dst.stride));
593 RawYuvCopyInfo src;
594 src.yStart = buffer->GetAddr();
595 src.stride = static_cast<uint32_t>(buffer->GetStride());
596 src.uvStart = src.yStart + srcUvOffset;
597 src.yStride = static_cast<uint32_t>(srcUvOffset / static_cast<uint64_t>(src.stride));
598 src.yOffset = 0;
599 src.uvOffset = 0;
600
601 uint32_t decodedRows = outputIndex / gridInfo_.cols;
602 uint32_t decodedCols = outputIndex % gridInfo_.cols;
603 uint32_t dirtyWidth = CalculateDirtyLen(dst.stride, src.stride, gridInfo_.cols, decodedCols);
604 uint32_t dirtyHeight = CalculateDirtyLen(dst.yStride, src.yStride, gridInfo_.rows, decodedRows);
605 dst.yOffset = decodedRows * dst.stride * gridInfo_.tileHeight + decodedCols * src.stride;
606 dst.uvOffset = decodedRows * dst.stride * gridInfo_.tileHeight / SAMPLE_RATIO_FOR_YUV420_SP +
607 decodedCols * src.stride;
608
609 if (!CopyRawYuvData(src, dst, dirtyWidth, dirtyHeight)) {
610 LOGE("failed to assemble output(grid=%{public}d))", outputIndex);
611 SignalError();
612 }
613 }
614
ReceiveOutputBufferLoop()615 void HeifHardwareDecoder::ReceiveOutputBufferLoop()
616 {
617 LOGD("in");
618 uint32_t outputIndex = 0;
619 uint32_t outputTimeoutCnt = 0;
620 while (!HasError() && (outputTimeoutCnt < MAX_TIMEOUT_CNT)) {
621 uint32_t bufferId;
622 shared_ptr<ImageCodecBuffer> buffer;
623 if (!WaitForOmxToReturnOutputBuffer(bufferId, buffer)) {
624 LOGE("output time out");
625 ++outputTimeoutCnt;
626 continue;
627 }
628 if (buffer == nullptr) {
629 LOGE("null output buffer");
630 break;
631 }
632 outputTimeoutCnt = 0;
633 uint32_t flag = buffer->GetBufferFlag();
634 if (flag & OMX_BUFFERFLAG_EOS) {
635 LOGD("output eos, quit loop");
636 break;
637 }
638 if (gridInfo_.enableGrid) {
639 AssembleOutput(outputIndex, buffer);
640 }
641 ++outputIndex;
642 int32_t ret = heifDecoderImpl_->ReleaseOutputBuffer(bufferId);
643 if (ret != IC_ERR_OK) {
644 LOGE("failed to release output buffer");
645 }
646 }
647 LOGD("out");
648 }
649 // LCOV_EXCL_STOP
650
SignalError()651 void HeifHardwareDecoder::SignalError()
652 {
653 std::lock_guard<std::mutex> lk(errMtx_);
654 hasErr_ = true;
655 }
656
HasError()657 bool HeifHardwareDecoder::HasError()
658 {
659 std::lock_guard<std::mutex> lk(errMtx_);
660 return hasErr_;
661 }
662 } // namespace OHOS::ImagePlugin