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