1  /*
2   * Copyright (c) 2023 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/jpeg_hw_decoder.h"
17  
18  #include <vector>
19  #include <algorithm>
20  #include <chrono>
21  #include <securec.h>
22  
23  #include "hardware/jpeg_marker_define.h"
24  #include "hdf_base.h"
25  #include "iremote_object.h"
26  #include "iproxy_broker.h"
27  #include "media_errors.h"
28  #include "src/codec/SkJpegUtility.h"
29  #include "src/codec/SkJpegDecoderMgr.h"
30  #include "src/codec/SkJpegCodec.h"
31  
32  namespace OHOS::ImagePlugin {
33  using namespace OHOS::HDI::Codec::Image::V2_0;
34  using namespace OHOS::HDI::Display::Buffer::V1_0;
35  
36  static std::mutex g_codecMtx;
37  static sptr<ICodecImage> g_codecMgr;
38  
39  class CodecJpegDeathRecipient : public IRemoteObject::DeathRecipient {
40  public:
OnRemoteDied(const wptr<IRemoteObject> & object)41      void OnRemoteDied(const wptr<IRemoteObject> &object) override
42      {
43          JPEG_HW_LOGW("codec_image_service died");
44          std::lock_guard<std::mutex> lk(g_codecMtx);
45          g_codecMgr = nullptr;
46      }
47  };
48  
GetCodecManager()49  static sptr<ICodecImage> GetCodecManager()
50  {
51      std::lock_guard<std::mutex> lk(g_codecMtx);
52      if (g_codecMgr) {
53          return g_codecMgr;
54      }
55      JPEG_HW_LOGI("need to get ICodecImage");
56      g_codecMgr = ICodecImage::Get();
57      if (g_codecMgr == nullptr) {
58          return nullptr;
59      }
60      bool isDeathRecipientAdded = false;
61      const sptr<OHOS::IRemoteObject> &remote = OHOS::HDI::hdi_objcast<ICodecImage>(g_codecMgr);
62      if (remote) {
63          sptr<CodecJpegDeathRecipient> deathCallBack(new CodecJpegDeathRecipient());
64          isDeathRecipientAdded = remote->AddDeathRecipient(deathCallBack);
65      }
66      if (!isDeathRecipientAdded) {
67          JPEG_HW_LOGE("failed to add deathRecipient for ICodecImage!");
68      }
69      return g_codecMgr;
70  }
71  
LifeSpanTimer(std::string desc)72  JpegHardwareDecoder::LifeSpanTimer::LifeSpanTimer(std::string desc) : desc_(desc)
73  {
74      startTimeInUs_ = GetCurrentTimeInUs();
75  }
76  
~LifeSpanTimer()77  JpegHardwareDecoder::LifeSpanTimer::~LifeSpanTimer()
78  {
79      static constexpr float MILLISEC_TO_MICROSEC = 1000.0f;
80      int64_t timeSpanInUs = GetCurrentTimeInUs() - startTimeInUs_;
81      JPEG_HW_LOGD("%{public}s cost: %{public}.2f ms",
82                   desc_.c_str(), static_cast<float>(timeSpanInUs / MILLISEC_TO_MICROSEC));
83  }
84  
GetCurrentTimeInUs()85  int64_t JpegHardwareDecoder::LifeSpanTimer::GetCurrentTimeInUs()
86  {
87      auto now = std::chrono::steady_clock::now();
88      return std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count();
89  }
90  
JpegHardwareDecoder()91  JpegHardwareDecoder::JpegHardwareDecoder()
92  {
93      inputBuffer_.fenceFd = -1;
94      hwDecoder_ = GetCodecManager();
95      bufferMgr_ = GetBufferMgr();
96  }
97  
~JpegHardwareDecoder()98  JpegHardwareDecoder::~JpegHardwareDecoder()
99  {
100      hwDecoder_ = nullptr;
101      bufferMgr_ = nullptr;
102  }
103  
IsHardwareDecodeSupported(const std::string & srcImgFormat,OHOS::Media::Size srcImgSize)104  bool JpegHardwareDecoder::IsHardwareDecodeSupported(const std::string& srcImgFormat, OHOS::Media::Size srcImgSize)
105  {
106      if (hwDecoder_ == nullptr) {
107          JPEG_HW_LOGE("failed to get hardware decoder!");
108          return false;
109      }
110      if (srcImgFormat != JPEG_FORMAT_DESC) {
111          JPEG_HW_LOGE("invalid image format: %{public}s", srcImgFormat.c_str());
112          return false;
113      }
114      std::vector<CodecImageCapability> capList;
115      auto ret = hwDecoder_->GetImageCapability(capList);
116      if (ret != HDF_SUCCESS) {
117          JPEG_HW_LOGE("failed to get image capability, err=%{public}d!", ret);
118          return false;
119      }
120  
121      for (const CodecImageCapability& cap : capList) {
122          JPEG_HW_LOGD("cap info: isSoftwareCodec=%{public}d, role=%{public}d, type=%{public}d, " \
123                       "maxSize=[%{public}u*%{public}u], minSize=[%{public}u*%{public}u]",
124                       cap.isSoftwareCodec, cap.role, cap.type, cap.maxWidth, cap.maxHeight,
125                       cap.minWidth, cap.minHeight);
126          if (!cap.isSoftwareCodec && cap.role == CODEC_IMAGE_JPEG &&
127              cap.type == CODEC_IMAGE_TYPE_DECODER &&
128              srcImgSize.width >= static_cast<int32_t>(cap.minWidth) &&
129              srcImgSize.width <= static_cast<int32_t>(cap.maxWidth) &&
130              srcImgSize.height >= static_cast<int32_t>(cap.minHeight) &&
131              srcImgSize.height <= static_cast<int32_t>(cap.maxHeight)) {
132              JPEG_HW_LOGD("decoder(%{public}s) selected", cap.name.c_str());
133              return true;
134          }
135      }
136      JPEG_HW_LOGE("no available hardware decoder, img=[%{public}ux%{public}u]", srcImgSize.width, srcImgSize.height);
137      return false;
138  }
139  
GetJpegCompressInfo(SkCodec * codec)140  static jpeg_decompress_struct* GetJpegCompressInfo(SkCodec *codec)
141  {
142      if (codec == nullptr) {
143          JPEG_HW_LOGE("invalid input codec!");
144          return nullptr;
145      }
146      SkJpegCodec* jpegCodec = static_cast<SkJpegCodec*>(codec);
147      if (jpegCodec == nullptr) {
148          JPEG_HW_LOGE("invalid input jpeg codec!");
149          return nullptr;
150      }
151      if (jpegCodec->decoderMgr() == nullptr) {
152          JPEG_HW_LOGE("invalid input jpeg codec mgr!");
153          return nullptr;
154      }
155      return jpegCodec->decoderMgr()->dinfo();
156  }
157  
CheckInputColorFmt(SkCodec * codec)158  bool JpegHardwareDecoder::CheckInputColorFmt(SkCodec *codec)
159  {
160      jpeg_decompress_struct* jpegCompressInfo = GetJpegCompressInfo(codec);
161      if (jpegCompressInfo == nullptr) {
162          JPEG_HW_LOGE("failed to get jpeg info");
163          return false;
164      }
165      if (jpegCompressInfo->jpeg_color_space != JCS_YCbCr &&
166          jpegCompressInfo->jpeg_color_space != JCS_GRAYSCALE) {
167          JPEG_HW_LOGI("unsupported in color: %{public}d", jpegCompressInfo->jpeg_color_space);
168          return false;
169      }
170      return true;
171  }
172  
Decode(SkCodec * codec,ImagePlugin::InputDataStream * srcStream,OHOS::Media::Size srcImgSize,uint32_t sampleSize,CodecImageBuffer & outputBuffer)173  uint32_t JpegHardwareDecoder::Decode(SkCodec *codec, ImagePlugin::InputDataStream *srcStream,
174                                       OHOS::Media::Size srcImgSize, uint32_t sampleSize, CodecImageBuffer& outputBuffer)
175  {
176      LifeSpanTimer decodeTimer("jpeg hardware decode");
177      JPEG_HW_LOGD("img=[%{public}ux%{public}u], sampleSize=%{public}u",
178                   srcImgSize.width, srcImgSize.height, sampleSize);
179      if (hwDecoder_ == nullptr || bufferMgr_ == nullptr) {
180          JPEG_HW_LOGE("failed to get hardware decoder or failed to get buffer manager!");
181          return Media::ERR_IMAGE_DECODE_ABNORMAL;
182      }
183      if (!IsHardwareDecodeSupported(JPEG_FORMAT_DESC, srcImgSize) || !CheckInputColorFmt(codec)) {
184          return Media::ERR_IMAGE_DATA_UNSUPPORT;
185      }
186  
187      decodeInfo_.sampleSize = sampleSize;
188      bool ret = InitDecoder();
189      ret = ret && PrepareInputData(codec, srcStream);
190      ret = ret && DoDecode(outputBuffer);
191      RecycleAllocatedResource();
192      if (ret) {
193          JPEG_HW_LOGD("jpeg hardware decode succeed");
194          return Media::SUCCESS;
195      }
196      return Media::ERR_IMAGE_DECODE_FAILED;
197  }
198  
AssembleComponentInfo(jpeg_decompress_struct * jpegCompressInfo)199  bool JpegHardwareDecoder::AssembleComponentInfo(jpeg_decompress_struct* jpegCompressInfo)
200  {
201      static constexpr int ONE_COMPONENT = 1;
202      static constexpr int THREE_COMPONENTS = 3;
203      if (jpegCompressInfo->num_components != ONE_COMPONENT &&
204          jpegCompressInfo->num_components != THREE_COMPONENTS) {
205          JPEG_HW_LOGE("unsupported component number: %{public}d!", jpegCompressInfo->num_components);
206          return false;
207      }
208      decodeInfo_.numComponents = jpegCompressInfo->num_components;
209      for (int i = 0; i < jpegCompressInfo->num_components; ++i) {
210          decodeInfo_.compInfo.emplace_back(CodecJpegCompInfo {
211              .componentId = jpegCompressInfo->comp_info[i].component_id,
212              .componentIndex = jpegCompressInfo->comp_info[i].component_index,
213              .hSampFactor = jpegCompressInfo->comp_info[i].h_samp_factor,
214              .vSampFactor = jpegCompressInfo->comp_info[i].v_samp_factor,
215              .quantTableNo = jpegCompressInfo->comp_info[i].quant_tbl_no,
216              .dcTableNo = jpegCompressInfo->comp_info[i].dc_tbl_no,
217              .acTableNo = jpegCompressInfo->comp_info[i].ac_tbl_no,
218              .infoFlag = true
219          });
220      }
221      return true;
222  }
223  
HuffmanTblTransform(JHUFF_TBL * huffTbl,CodecJpegHuffTable & tbl)224  bool JpegHardwareDecoder::HuffmanTblTransform(JHUFF_TBL* huffTbl, CodecJpegHuffTable& tbl)
225  {
226      if (huffTbl == nullptr) {
227          return false;
228      }
229      // length of bits is defined as 16 in jpeg specification
230      // bits defined in struct JHUFF_TBL has length of 17, bits[0] is unused
231      static constexpr int LIST_BITS_OFFSET = 1;
232      static constexpr int LIST_BITS_LEN = 16;
233      static constexpr int MAX_LIST_HUFFVAL_LEN = 256;
234      int actualHuffValLen = 0;
235      for (int i = LIST_BITS_OFFSET; i <= LIST_BITS_LEN; ++i) {
236          actualHuffValLen += huffTbl->bits[i];
237      }
238      JPEG_HW_LOGD("actualHuffValLen=%{public}d", actualHuffValLen);
239      if (actualHuffValLen > MAX_LIST_HUFFVAL_LEN) {
240          JPEG_HW_LOGE("invalid huffVal len: %{public}d", actualHuffValLen);
241          return false;
242      }
243      tbl.tableFlag = true;
244      tbl.bits = std::vector<uint8_t>(&huffTbl->bits[LIST_BITS_OFFSET],
245                                      &huffTbl->bits[LIST_BITS_OFFSET] + LIST_BITS_LEN);
246      tbl.huffVal = std::vector<uint8_t>(huffTbl->huffval, &huffTbl->huffval[actualHuffValLen]);
247      return true;
248  }
249  
AssembleHuffmanTable(jpeg_decompress_struct * jpegCompressInfo)250  void JpegHardwareDecoder::AssembleHuffmanTable(jpeg_decompress_struct* jpegCompressInfo)
251  {
252      static constexpr int HUFFMAN_TBL_CNT = 4;
253      for (int i = 0; i < HUFFMAN_TBL_CNT; ++i) {
254          CodecJpegHuffTable dcTbl;
255          if (HuffmanTblTransform(jpegCompressInfo->dc_huff_tbl_ptrs[i], dcTbl)) {
256              decodeInfo_.dcHuffTbl.emplace_back(dcTbl);
257          }
258  
259          CodecJpegHuffTable acTbl;
260          if (HuffmanTblTransform(jpegCompressInfo->ac_huff_tbl_ptrs[i], acTbl)) {
261              decodeInfo_.acHuffTbl.emplace_back(acTbl);
262          }
263      }
264  }
265  
AssembleQuantizationTable(jpeg_decompress_struct * jpegCompressInfo)266  void JpegHardwareDecoder::AssembleQuantizationTable(jpeg_decompress_struct* jpegCompressInfo)
267  {
268      for (int i = 0; i < NUM_QUANT_TBLS; ++i) {
269          if (jpegCompressInfo->quant_tbl_ptrs[i]) {
270              uint16_t* quantStart = reinterpret_cast<uint16_t *>(jpegCompressInfo->quant_tbl_ptrs[i]->quantval);
271              decodeInfo_.quantTbl.emplace_back(CodecJpegQuantTable {
272                  .quantVal = std::vector<uint16_t>(quantStart, quantStart + DCTSIZE2),
273                  .tableFlag = true
274              });
275          } else {
276              decodeInfo_.quantTbl.emplace_back(CodecJpegQuantTable {
277                  .quantVal = {},
278                  .tableFlag = false
279              });
280          }
281      }
282  }
283  
AssembleJpegImgHeader(jpeg_decompress_struct * jpegCompressInfo)284  bool JpegHardwareDecoder::AssembleJpegImgHeader(jpeg_decompress_struct* jpegCompressInfo)
285  {
286      decodeInfo_.imageWidth = jpegCompressInfo->image_width;
287      decodeInfo_.imageHeight = jpegCompressInfo->image_height;
288      decodeInfo_.dataPrecision = jpegCompressInfo->data_precision;
289      decodeInfo_.restartInterval = jpegCompressInfo->restart_interval;
290      decodeInfo_.arithCode = jpegCompressInfo->arith_code;
291      decodeInfo_.progressiveMode = jpegCompressInfo->progressive_mode;
292      // no crop as default
293      decodeInfo_.region.flag = 0;
294      if (!AssembleComponentInfo(jpegCompressInfo)) {
295          return false;
296      }
297      AssembleHuffmanTable(jpegCompressInfo);
298      AssembleQuantizationTable(jpegCompressInfo);
299      return true;
300  }
301  
CopySrcImgToDecodeInputBuffer(ImagePlugin::InputDataStream * srcStream)302  bool JpegHardwareDecoder::CopySrcImgToDecodeInputBuffer(ImagePlugin::InputDataStream *srcStream)
303  {
304      size_t fileSize = srcStream->GetStreamSize();
305      uint32_t positionRecord = srcStream->Tell();
306      JPEG_HW_LOGI("input stream, size=%{public}zu, curPos=%{public}u", fileSize, positionRecord);
307      int32_t ret = hwDecoder_->AllocateInBuffer(inputBuffer_, static_cast<uint32_t>(fileSize), CODEC_IMAGE_JPEG);
308      if (ret != HDF_SUCCESS) {
309          JPEG_HW_LOGE("failed to allocate input buffer, err=%{public}d", ret);
310          return false;
311      }
312      BufferHandle *inputBufferHandle = inputBuffer_.buffer->GetBufferHandle();
313      if (inputBufferHandle == nullptr) {
314          JPEG_HW_LOGE("inputBufferHandle is nullptr");
315          return false;
316      }
317      if (bufferMgr_->Mmap(*inputBufferHandle) == nullptr) {
318          JPEG_HW_LOGE("failed to map input buffer");
319          return false;
320      }
321      (void)bufferMgr_->InvalidateCache(*inputBufferHandle);
322      srcStream->Seek(0);
323      uint32_t readSize = 0;
324      bool flag = srcStream->Read(static_cast<uint32_t>(fileSize), static_cast<uint8_t*>(inputBufferHandle->virAddr),
325                                  static_cast<uint32_t>(inputBufferHandle->size), readSize);
326      (void)bufferMgr_->FlushCache(*inputBufferHandle);
327      ret = bufferMgr_->Unmap(*inputBufferHandle);
328      if (ret != 0) {
329          JPEG_HW_LOGE("failed to unmap input buffer, err=%{public}d", ret);
330      }
331      srcStream->Seek(positionRecord);
332      if (!flag || readSize != static_cast<uint32_t>(fileSize)) {
333          JPEG_HW_LOGE("failed to read input data, readSize=%{public}u", readSize);
334          return false;
335      }
336      return true;
337  }
338  
IsStandAloneJpegMarker(uint16_t marker)339  bool JpegHardwareDecoder::IsStandAloneJpegMarker(uint16_t marker)
340  {
341      auto iter = std::find(JpegMarker::STAND_ALONE_MARKER.begin(), JpegMarker::STAND_ALONE_MARKER.end(), marker);
342      return (iter != JpegMarker::STAND_ALONE_MARKER.end());
343  }
344  
JumpOverCurrentJpegMarker(const uint8_t * data,unsigned int & curPos,unsigned int totalLen,uint16_t marker)345  bool JpegHardwareDecoder::JumpOverCurrentJpegMarker(const uint8_t* data, unsigned int& curPos,
346                                                      unsigned int totalLen, uint16_t marker)
347  {
348      curPos += JpegMarker::MARKER_LEN;
349      if (curPos + JpegMarker::MARKER_LEN > totalLen) {
350          JPEG_HW_LOGE("invalid pos(cur=%{public}u, total=%{public}u) after jump over marker(%{public}u)",
351                       curPos, totalLen, marker);
352          return false;
353      }
354      if (IsStandAloneJpegMarker(marker)) {
355          return true;
356      }
357      // jump over related parameters for those who are not stand alone markers
358      curPos += static_cast<unsigned int>(CombineTwoBytes(data, curPos));
359      if (curPos > totalLen) {
360          JPEG_HW_LOGE("invalid pos(cur=%{public}u, total=%{public}u) after jump over related parameters " \
361                       "for marker(%{public}u)", curPos, totalLen, marker);
362          return false;
363      }
364      return true;
365  }
366  
GetCompressedDataStart()367  bool JpegHardwareDecoder::GetCompressedDataStart()
368  {
369      BufferHandle *inputBufferHandle = inputBuffer_.buffer->GetBufferHandle();
370      bufferMgr_->Mmap(*inputBufferHandle);
371      (void)bufferMgr_->InvalidateCache(*inputBufferHandle);
372      const uint8_t* data = static_cast<const uint8_t*>(inputBufferHandle->virAddr);
373      if (data == nullptr) {
374          JPEG_HW_LOGE("map inputBufferHandle failed");
375          return false;
376      }
377      unsigned int totalLen = static_cast<unsigned int>(inputBufferHandle->size);
378      unsigned int curPos = 0;
379      bool findFlag = false;
380      while (curPos + JpegMarker::MARKER_LEN <= totalLen) {
381          uint16_t potentialMarker = CombineTwoBytes(data, curPos);
382          if (potentialMarker < JpegMarker::MIN_MARKER || potentialMarker > JpegMarker::MAX_MARKER) {
383              ++curPos;
384              continue;
385          }
386          if (!JumpOverCurrentJpegMarker(data, curPos, totalLen, potentialMarker)) {
387              break;
388          }
389          if (potentialMarker == JpegMarker::SOS) {
390              findFlag = true;
391              decodeInfo_.compressPos = curPos;
392              break;
393          }
394      }
395      (void)bufferMgr_->FlushCache(*inputBufferHandle);
396      int32_t ret = bufferMgr_->Unmap(*inputBufferHandle);
397      if (ret != 0) {
398          JPEG_HW_LOGE("failed to unmap input buffer, err=%{public}d", ret);
399      }
400      return findFlag;
401  }
402  
PrepareInputData(SkCodec * codec,ImagePlugin::InputDataStream * srcStream)403  bool JpegHardwareDecoder::PrepareInputData(SkCodec *codec, ImagePlugin::InputDataStream *srcStream)
404  {
405      LifeSpanTimer decodeTimer("prepare input data");
406      jpeg_decompress_struct* jpegCompressInfo = GetJpegCompressInfo(codec);
407      if (jpegCompressInfo == nullptr || srcStream == nullptr) {
408          JPEG_HW_LOGE("failed to get jpeg compress info or invalid input stream");
409          return false;
410      }
411      bool ret = AssembleJpegImgHeader(jpegCompressInfo);
412      ret = ret && CopySrcImgToDecodeInputBuffer(srcStream);
413      ret = ret && GetCompressedDataStart();
414      return ret;
415  }
416  
InitDecoder()417  bool JpegHardwareDecoder::InitDecoder()
418  {
419      LifeSpanTimer decodeTimer("init decoder");
420      int32_t ret = hwDecoder_->Init(CODEC_IMAGE_JPEG);
421      if (ret != HDF_SUCCESS) {
422          JPEG_HW_LOGE("failed to init decoder, err=%{public}d", ret);
423          return false;
424      }
425      return true;
426  }
427  
DoDecode(CodecImageBuffer & outputBuffer)428  bool JpegHardwareDecoder::DoDecode(CodecImageBuffer& outputBuffer)
429  {
430      LifeSpanTimer decodeTimer("do decode");
431      int32_t ret = hwDecoder_->DoJpegDecode(inputBuffer_, outputBuffer, decodeInfo_);
432      if (ret != HDF_SUCCESS) {
433          JPEG_HW_LOGE("failed to do decode, err=%{public}d", ret);
434          return false;
435      }
436      return true;
437  }
438  
RecycleAllocatedResource()439  void JpegHardwareDecoder::RecycleAllocatedResource()
440  {
441      LifeSpanTimer decodeTimer("recycle resource");
442      int32_t ret = hwDecoder_->FreeInBuffer(inputBuffer_);
443      if (ret != HDF_SUCCESS) {
444          JPEG_HW_LOGE("failed to free input buffer, err=%{public}d", ret);
445      }
446  
447      ret = hwDecoder_->DeInit(CODEC_IMAGE_JPEG);
448      if (ret != HDF_SUCCESS) {
449          JPEG_HW_LOGE("failed to deinit decoder, err=%{public}d", ret);
450      }
451  }
452  
GetBufferMgr()453  OHOS::HDI::Display::Buffer::V1_0::IDisplayBuffer* JpegHardwareDecoder::GetBufferMgr()
454  {
455      static OHOS::HDI::Display::Buffer::V1_0::IDisplayBuffer* staticBufferMgr = IDisplayBuffer::Get();
456      return staticBufferMgr;
457  }
458  } // namespace OHOS::ImagePlugin
459