• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "image_trace.h"
32 
33 namespace OHOS::ImagePlugin {
34 using namespace OHOS::HDI::Codec::Image::V2_0;
35 using namespace OHOS::HDI::Display::Buffer::V1_0;
36 
37 static std::mutex g_codecMtx;
38 static sptr<ICodecImage> g_codecMgr;
39 
40 class CodecJpegDeathRecipient : public IRemoteObject::DeathRecipient {
41 public:
OnRemoteDied(const wptr<IRemoteObject> & object)42     void OnRemoteDied(const wptr<IRemoteObject> &object) override
43     {
44         JPEG_HW_LOGW("codec_image_service died");
45         std::lock_guard<std::mutex> lk(g_codecMtx);
46         g_codecMgr = nullptr;
47     }
48 };
49 
GetCodecManager()50 static sptr<ICodecImage> GetCodecManager()
51 {
52     std::lock_guard<std::mutex> lk(g_codecMtx);
53     if (g_codecMgr) {
54         return g_codecMgr;
55     }
56     JPEG_HW_LOGI("need to get ICodecImage");
57     g_codecMgr = ICodecImage::Get();
58     if (g_codecMgr == nullptr) {
59         return nullptr;
60     }
61     bool isDeathRecipientAdded = false;
62     const sptr<OHOS::IRemoteObject> &remote = OHOS::HDI::hdi_objcast<ICodecImage>(g_codecMgr);
63     if (remote) {
64         sptr<CodecJpegDeathRecipient> deathCallBack(new CodecJpegDeathRecipient());
65         isDeathRecipientAdded = remote->AddDeathRecipient(deathCallBack);
66     }
67     if (!isDeathRecipientAdded) {
68         JPEG_HW_LOGE("failed to add deathRecipient for ICodecImage!");
69     }
70     return g_codecMgr;
71 }
72 
LifeSpanTimer(std::string desc)73 JpegHardwareDecoder::LifeSpanTimer::LifeSpanTimer(std::string desc) : desc_(desc)
74 {
75     startTimeInUs_ = GetCurrentTimeInUs();
76 }
77 
~LifeSpanTimer()78 JpegHardwareDecoder::LifeSpanTimer::~LifeSpanTimer()
79 {
80     static constexpr float MILLISEC_TO_MICROSEC = 1000.0f;
81     int64_t timeSpanInUs = GetCurrentTimeInUs() - startTimeInUs_;
82     JPEG_HW_LOGD("%{public}s cost: %{public}.2f ms",
83                  desc_.c_str(), static_cast<float>(timeSpanInUs / MILLISEC_TO_MICROSEC));
84 }
85 
GetCurrentTimeInUs()86 int64_t JpegHardwareDecoder::LifeSpanTimer::GetCurrentTimeInUs()
87 {
88     auto now = std::chrono::steady_clock::now();
89     return std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count();
90 }
91 
JpegHardwareDecoder()92 JpegHardwareDecoder::JpegHardwareDecoder()
93 {
94     inputBuffer_.fenceFd = -1;
95     hwDecoder_ = GetCodecManager();
96     bufferMgr_ = GetBufferMgr();
97 }
98 
~JpegHardwareDecoder()99 JpegHardwareDecoder::~JpegHardwareDecoder()
100 {
101     hwDecoder_ = nullptr;
102     bufferMgr_ = nullptr;
103 }
104 
IsHardwareDecodeSupported(const std::string & srcImgFormat,OHOS::Media::Size srcImgSize)105 bool JpegHardwareDecoder::IsHardwareDecodeSupported(const std::string& srcImgFormat, OHOS::Media::Size srcImgSize)
106 {
107     if (hwDecoder_ == nullptr) {
108         JPEG_HW_LOGE("failed to get hardware decoder!");
109         return false;
110     }
111     if (srcImgFormat != JPEG_FORMAT_DESC) {
112         JPEG_HW_LOGE("invalid image format: %{public}s", srcImgFormat.c_str());
113         return false;
114     }
115     std::vector<CodecImageCapability> capList;
116     auto ret = hwDecoder_->GetImageCapability(capList);
117     if (ret != HDF_SUCCESS) {
118         JPEG_HW_LOGE("failed to get image capability, err=%{public}d!", ret);
119         return false;
120     }
121 
122     for (const CodecImageCapability& cap : capList) {
123         JPEG_HW_LOGD("cap info: isSoftwareCodec=%{public}d, role=%{public}d, type=%{public}d, " \
124                      "maxSize=[%{public}u*%{public}u], minSize=[%{public}u*%{public}u]",
125                      cap.isSoftwareCodec, cap.role, cap.type, cap.maxWidth, cap.maxHeight,
126                      cap.minWidth, cap.minHeight);
127         if (!cap.isSoftwareCodec && cap.role == CODEC_IMAGE_JPEG && cap.type == CODEC_IMAGE_TYPE_DECODER &&
128             srcImgSize.width >= static_cast<int32_t>(cap.minWidth) && srcImgSize.width <= static_cast<int32_t>(cap.maxWidth)
129             && srcImgSize.height >= static_cast<int32_t>(cap.minHeight) && srcImgSize.height <= static_cast<int32_t>(cap.maxHeight)) {
130             JPEG_HW_LOGD("decoder(%{public}s) selected", cap.name.c_str());
131             return true;
132         }
133     }
134     JPEG_HW_LOGE("no available hardware decoder, img=[%{public}ux%{public}u]", srcImgSize.width, srcImgSize.height);
135     return false;
136 }
137 
GetJpegCompressInfo(SkCodec * codec)138 static jpeg_decompress_struct* GetJpegCompressInfo(SkCodec *codec)
139 {
140     if (codec == nullptr) {
141         JPEG_HW_LOGE("invalid input codec!");
142         return nullptr;
143     }
144     SkJpegCodec* jpegCodec = static_cast<SkJpegCodec*>(codec);
145     if (jpegCodec == nullptr) {
146         JPEG_HW_LOGE("invalid input jpeg codec!");
147         return nullptr;
148     }
149     if (jpegCodec->decoderMgr() == nullptr) {
150         JPEG_HW_LOGE("invalid input jpeg codec mgr!");
151         return nullptr;
152     }
153     return jpegCodec->decoderMgr()->dinfo();
154 }
155 
CheckInputColorFmt(SkCodec * codec)156 bool JpegHardwareDecoder::CheckInputColorFmt(SkCodec *codec)
157 {
158     jpeg_decompress_struct* jpegCompressInfo = GetJpegCompressInfo(codec);
159     if (jpegCompressInfo == nullptr) {
160         JPEG_HW_LOGE("failed to get jpeg info");
161         return false;
162     }
163     if (jpegCompressInfo->jpeg_color_space != JCS_YCbCr &&
164         jpegCompressInfo->jpeg_color_space != JCS_GRAYSCALE) {
165         JPEG_HW_LOGI("unsupported in color: %{public}d", jpegCompressInfo->jpeg_color_space);
166         return false;
167     }
168     return true;
169 }
170 
Decode(SkCodec * codec,ImagePlugin::InputDataStream * srcStream,OHOS::Media::Size srcImgSize,uint32_t sampleSize,CodecImageBuffer & outputBuffer)171 uint32_t JpegHardwareDecoder::Decode(SkCodec *codec, ImagePlugin::InputDataStream *srcStream,
172                                      OHOS::Media::Size srcImgSize, uint32_t sampleSize, CodecImageBuffer& outputBuffer)
173 {
174     Media::ImageTrace imageTrace("jpeg_hw_decoder Decode");
175     LifeSpanTimer decodeTimer("jpeg hardware decode");
176     JPEG_HW_LOGD("img=[%{public}ux%{public}u], sampleSize=%{public}u",
177                  srcImgSize.width, srcImgSize.height, sampleSize);
178     if (hwDecoder_ == nullptr || bufferMgr_ == nullptr) {
179         JPEG_HW_LOGE("failed to get hardware decoder or failed to get buffer manager!");
180         return Media::ERR_IMAGE_DECODE_ABNORMAL;
181     }
182     if (!IsHardwareDecodeSupported(JPEG_FORMAT_DESC, srcImgSize) || !CheckInputColorFmt(codec)) {
183         return Media::ERR_IMAGE_DATA_UNSUPPORT;
184     }
185 
186     decodeInfo_.sampleSize = sampleSize;
187     bool ret = InitDecoder();
188     ret = ret && PrepareInputData(codec, srcStream);
189     ret = ret && DoDecode(outputBuffer);
190     RecycleAllocatedResource();
191     if (ret) {
192         JPEG_HW_LOGD("jpeg hardware decode succeed");
193         return Media::SUCCESS;
194     }
195     return Media::ERR_IMAGE_DECODE_FAILED;
196 }
197 
AssembleComponentInfo(jpeg_decompress_struct * jpegCompressInfo)198 bool JpegHardwareDecoder::AssembleComponentInfo(jpeg_decompress_struct* jpegCompressInfo)
199 {
200     static constexpr int ONE_COMPONENT = 1;
201     static constexpr int THREE_COMPONENTS = 3;
202     if (jpegCompressInfo->num_components != ONE_COMPONENT &&
203         jpegCompressInfo->num_components != THREE_COMPONENTS) {
204         JPEG_HW_LOGE("unsupported component number: %{public}d!", jpegCompressInfo->num_components);
205         return false;
206     }
207     decodeInfo_.numComponents = jpegCompressInfo->num_components;
208     for (int i = 0; i < jpegCompressInfo->num_components; ++i) {
209         decodeInfo_.compInfo.emplace_back(CodecJpegCompInfo {
210             .componentId = jpegCompressInfo->comp_info[i].component_id,
211             .componentIndex = jpegCompressInfo->comp_info[i].component_index,
212             .hSampFactor = jpegCompressInfo->comp_info[i].h_samp_factor,
213             .vSampFactor = jpegCompressInfo->comp_info[i].v_samp_factor,
214             .quantTableNo = jpegCompressInfo->comp_info[i].quant_tbl_no,
215             .dcTableNo = jpegCompressInfo->comp_info[i].dc_tbl_no,
216             .acTableNo = jpegCompressInfo->comp_info[i].ac_tbl_no,
217             .infoFlag = true
218         });
219     }
220     return true;
221 }
222 
HuffmanTblTransform(JHUFF_TBL * huffTbl,CodecJpegHuffTable & tbl)223 bool JpegHardwareDecoder::HuffmanTblTransform(JHUFF_TBL* huffTbl, CodecJpegHuffTable& tbl)
224 {
225     if (huffTbl == nullptr) {
226         return false;
227     }
228     // length of bits is defined as 16 in jpeg specification
229     // bits defined in struct JHUFF_TBL has length of 17, bits[0] is unused
230     static constexpr int LIST_BITS_OFFSET = 1;
231     static constexpr int LIST_BITS_LEN = 16;
232     static constexpr int MAX_LIST_HUFFVAL_LEN = 256;
233     int actualHuffValLen = 0;
234     for (int i = LIST_BITS_OFFSET; i <= LIST_BITS_LEN; ++i) {
235         actualHuffValLen += huffTbl->bits[i];
236     }
237     JPEG_HW_LOGD("actualHuffValLen=%{public}d", actualHuffValLen);
238     if (actualHuffValLen > MAX_LIST_HUFFVAL_LEN) {
239         JPEG_HW_LOGE("invalid huffVal len: %{public}d", actualHuffValLen);
240         return false;
241     }
242     tbl.tableFlag = true;
243     tbl.bits = std::vector<uint8_t>(&huffTbl->bits[LIST_BITS_OFFSET],
244                                     &huffTbl->bits[LIST_BITS_OFFSET] + LIST_BITS_LEN);
245     tbl.huffVal = std::vector<uint8_t>(huffTbl->huffval, &huffTbl->huffval[actualHuffValLen]);
246     return true;
247 }
248 
AssembleHuffmanTable(jpeg_decompress_struct * jpegCompressInfo)249 void JpegHardwareDecoder::AssembleHuffmanTable(jpeg_decompress_struct* jpegCompressInfo)
250 {
251     static constexpr int HUFFMAN_TBL_CNT = 4;
252     for (int i = 0; i < HUFFMAN_TBL_CNT; ++i) {
253         CodecJpegHuffTable dcTbl;
254         if (HuffmanTblTransform(jpegCompressInfo->dc_huff_tbl_ptrs[i], dcTbl)) {
255             decodeInfo_.dcHuffTbl.emplace_back(dcTbl);
256         }
257 
258         CodecJpegHuffTable acTbl;
259         if (HuffmanTblTransform(jpegCompressInfo->ac_huff_tbl_ptrs[i], acTbl)) {
260             decodeInfo_.acHuffTbl.emplace_back(acTbl);
261         }
262     }
263 }
264 
AssembleQuantizationTable(jpeg_decompress_struct * jpegCompressInfo)265 void JpegHardwareDecoder::AssembleQuantizationTable(jpeg_decompress_struct* jpegCompressInfo)
266 {
267     for (int i = 0; i < NUM_QUANT_TBLS; ++i) {
268         if (jpegCompressInfo->quant_tbl_ptrs[i]) {
269             uint16_t* quantStart = reinterpret_cast<uint16_t *>(jpegCompressInfo->quant_tbl_ptrs[i]->quantval);
270             decodeInfo_.quantTbl.emplace_back(CodecJpegQuantTable {
271                 .quantVal = std::vector<uint16_t>(quantStart, quantStart + DCTSIZE2),
272                 .tableFlag = true
273             });
274         } else {
275             decodeInfo_.quantTbl.emplace_back(CodecJpegQuantTable {
276                 .quantVal = {},
277                 .tableFlag = false
278             });
279         }
280     }
281 }
282 
AssembleJpegImgHeader(jpeg_decompress_struct * jpegCompressInfo)283 bool JpegHardwareDecoder::AssembleJpegImgHeader(jpeg_decompress_struct* jpegCompressInfo)
284 {
285     decodeInfo_.imageWidth = jpegCompressInfo->image_width;
286     decodeInfo_.imageHeight = jpegCompressInfo->image_height;
287     decodeInfo_.dataPrecision = jpegCompressInfo->data_precision;
288     decodeInfo_.restartInterval = jpegCompressInfo->restart_interval;
289     decodeInfo_.arithCode = jpegCompressInfo->arith_code;
290     decodeInfo_.progressiveMode = jpegCompressInfo->progressive_mode;
291     // no crop as default
292     decodeInfo_.region.flag = 0;
293     if (!AssembleComponentInfo(jpegCompressInfo)) {
294         return false;
295     }
296     AssembleHuffmanTable(jpegCompressInfo);
297     AssembleQuantizationTable(jpegCompressInfo);
298     return true;
299 }
300 
CopySrcImgToDecodeInputBuffer(ImagePlugin::InputDataStream * srcStream)301 bool JpegHardwareDecoder::CopySrcImgToDecodeInputBuffer(ImagePlugin::InputDataStream *srcStream)
302 {
303     Media::ImageTrace imageTrace("CopySrcImgToDecodeInputBuffer");
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     Media::ImageTrace imageTrace("PrepareInputData");
406     LifeSpanTimer decodeTimer("prepare input data");
407     jpeg_decompress_struct* jpegCompressInfo = GetJpegCompressInfo(codec);
408     if (jpegCompressInfo == nullptr || srcStream == nullptr) {
409         JPEG_HW_LOGE("failed to get jpeg compress info or invalid input stream");
410         return false;
411     }
412     bool ret = AssembleJpegImgHeader(jpegCompressInfo);
413     ret = ret && CopySrcImgToDecodeInputBuffer(srcStream);
414     ret = ret && GetCompressedDataStart();
415     return ret;
416 }
417 
InitDecoder()418 bool JpegHardwareDecoder::InitDecoder()
419 {
420     LifeSpanTimer decodeTimer("init decoder");
421     int32_t ret = hwDecoder_->Init(CODEC_IMAGE_JPEG);
422     if (ret != HDF_SUCCESS) {
423         JPEG_HW_LOGE("failed to init decoder, err=%{public}d", ret);
424         return false;
425     }
426     return true;
427 }
428 
DoDecode(CodecImageBuffer & outputBuffer)429 bool JpegHardwareDecoder::DoDecode(CodecImageBuffer& outputBuffer)
430 {
431     Media::ImageTrace imageTrace("DoDecode");
432     LifeSpanTimer decodeTimer("do decode");
433     int32_t ret = hwDecoder_->DoJpegDecode(inputBuffer_, outputBuffer, decodeInfo_);
434     if (ret != HDF_SUCCESS) {
435         JPEG_HW_LOGE("failed to do decode, err=%{public}d", ret);
436         return false;
437     }
438     return true;
439 }
440 
RecycleAllocatedResource()441 void JpegHardwareDecoder::RecycleAllocatedResource()
442 {
443     Media::ImageTrace imageTrace("RecycleAllocatedResource");
444     LifeSpanTimer decodeTimer("recycle resource");
445     int32_t ret = hwDecoder_->FreeInBuffer(inputBuffer_);
446     if (ret != HDF_SUCCESS) {
447         JPEG_HW_LOGE("failed to free input buffer, err=%{public}d", ret);
448     }
449 
450     ret = hwDecoder_->DeInit(CODEC_IMAGE_JPEG);
451     if (ret != HDF_SUCCESS) {
452         JPEG_HW_LOGE("failed to deinit decoder, err=%{public}d", ret);
453     }
454 }
455 
GetBufferMgr()456 OHOS::HDI::Display::Buffer::V1_0::IDisplayBuffer* JpegHardwareDecoder::GetBufferMgr()
457 {
458     static OHOS::HDI::Display::Buffer::V1_0::IDisplayBuffer* staticBufferMgr = IDisplayBuffer::Get();
459     return staticBufferMgr;
460 }
461 } // namespace OHOS::ImagePlugin
462