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