• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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