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