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