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