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