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 "media_errors.h"
26 #include "src/codec/SkJpegUtility.h"
27 #include "src/codec/SkJpegDecoderMgr.h"
28 #include "src/codec/SkJpegCodec.h"
29
30 namespace OHOS::ImagePlugin {
31 using namespace OHOS::HDI::Codec::Image::V1_0;
32 using namespace OHOS::HDI::Display::Buffer::V1_0;
33
LifeSpanTimer(std::string desc)34 JpegHardwareDecoder::LifeSpanTimer::LifeSpanTimer(std::string desc) : desc_(desc)
35 {
36 startTimeInUs_ = GetCurrentTimeInUs();
37 }
38
~LifeSpanTimer()39 JpegHardwareDecoder::LifeSpanTimer::~LifeSpanTimer()
40 {
41 static constexpr float MILLISEC_TO_MICROSEC = 1000.0f;
42 int64_t timeSpanInUs = GetCurrentTimeInUs() - startTimeInUs_;
43 JPEG_HW_LOGI("%{public}s cost: %{public}.2f ms",
44 desc_.c_str(), static_cast<float>(timeSpanInUs / MILLISEC_TO_MICROSEC));
45 }
46
GetCurrentTimeInUs()47 int64_t JpegHardwareDecoder::LifeSpanTimer::GetCurrentTimeInUs()
48 {
49 auto now = std::chrono::steady_clock::now();
50 return std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count();
51 }
52
JpegHardwareDecoder()53 JpegHardwareDecoder::JpegHardwareDecoder()
54 {
55 inputBuffer_.fenceFd = -1;
56 hwDecoder_ = ICodecImage::Get();
57 bufferMgr_ = IDisplayBuffer::Get();
58 }
59
~JpegHardwareDecoder()60 JpegHardwareDecoder::~JpegHardwareDecoder()
61 {
62 hwDecoder_ = nullptr;
63 bufferMgr_ = nullptr;
64 }
65
IsHardwareDecodeSupported(const std::string & srcImgFormat,PlSize srcImgSize)66 bool JpegHardwareDecoder::IsHardwareDecodeSupported(const std::string& srcImgFormat, PlSize srcImgSize)
67 {
68 if (hwDecoder_ == nullptr) {
69 JPEG_HW_LOGE("failed to get hardware decoder!");
70 return false;
71 }
72 if (srcImgFormat != JPEG_FORMAT_DESC) {
73 JPEG_HW_LOGE("invalid image format: %{public}s", srcImgFormat.c_str());
74 return false;
75 }
76 std::vector<CodecImageCapability> capList;
77 auto ret = hwDecoder_->GetImageCapability(capList);
78 if (ret != HDF_SUCCESS) {
79 JPEG_HW_LOGE("failed to get image capability, err=%{public}d!", ret);
80 return false;
81 }
82
83 for (const CodecImageCapability& cap : capList) {
84 JPEG_HW_LOGD("cap info: isSoftwareCodec=%{public}d, role=%{public}d, type=%{public}d, " \
85 "maxSize=[%{public}u*%{public}u], minSize=[%{public}u*%{public}u]",
86 cap.isSoftwareCodec, cap.role, cap.type, cap.maxWidth, cap.maxHeight,
87 cap.minWidth, cap.minHeight);
88 if (!cap.isSoftwareCodec && cap.role == CODEC_IMAGE_JPEG && cap.type == CODEC_IMAGE_TYPE_DECODER &&
89 srcImgSize.width >= cap.minWidth && srcImgSize.width <= cap.maxWidth &&
90 srcImgSize.height >= cap.minHeight && srcImgSize.height <= cap.maxHeight) {
91 JPEG_HW_LOGD("decoder(%{public}s) selected", cap.name.c_str());
92 return true;
93 }
94 }
95 JPEG_HW_LOGE("no available hardware decoder, img=[%{public}ux%{public}u]", srcImgSize.width, srcImgSize.height);
96 return false;
97 }
98
Decode(SkCodec * codec,ImagePlugin::InputDataStream * srcStream,PlSize srcImgSize,uint32_t sampleSize,CodecImageBuffer & outputBuffer)99 uint32_t JpegHardwareDecoder::Decode(SkCodec *codec, ImagePlugin::InputDataStream *srcStream,
100 PlSize srcImgSize, uint32_t sampleSize, CodecImageBuffer& outputBuffer)
101 {
102 LifeSpanTimer decodeTimer("jpeg hardware decode");
103 JPEG_HW_LOGD("jpeg hardware decode start: img=[%{public}ux%{public}u], sampleSize=%{public}u",
104 srcImgSize.width, srcImgSize.height, sampleSize);
105 if (hwDecoder_ == nullptr || bufferMgr_ == nullptr) {
106 JPEG_HW_LOGE("failed to get hardware decoder or failed to get buffer manager!");
107 return Media::ERR_IMAGE_DECODE_ABNORMAL;
108 }
109 if (!IsHardwareDecodeSupported(JPEG_FORMAT_DESC, srcImgSize)) {
110 return Media::ERR_IMAGE_DATA_UNSUPPORT;
111 }
112 decodeInfo_.sampleSize = sampleSize;
113 bool ret = InitDecoder();
114 ret = ret && PrepareInputData(codec, srcStream);
115 ret = ret && DoDecode(outputBuffer);
116 RecycleAllocatedResource();
117 if (ret) {
118 JPEG_HW_LOGI("jpeg hardware decode succeed");
119 return Media::SUCCESS;
120 }
121 return Media::ERR_IMAGE_DECODE_FAILED;
122 }
123
AssembleComponentInfo(jpeg_decompress_struct * jpegCompressInfo)124 bool JpegHardwareDecoder::AssembleComponentInfo(jpeg_decompress_struct* jpegCompressInfo)
125 {
126 static constexpr int ONE_COMPONENT = 1;
127 static constexpr int THREE_COMPONENTS = 3;
128 if (jpegCompressInfo->num_components != ONE_COMPONENT &&
129 jpegCompressInfo->num_components != THREE_COMPONENTS) {
130 JPEG_HW_LOGE("unsupported component number: %{public}d!", jpegCompressInfo->num_components);
131 return false;
132 }
133 decodeInfo_.numComponents = jpegCompressInfo->num_components;
134 for (int i = 0; i < jpegCompressInfo->num_components; ++i) {
135 decodeInfo_.compInfo.emplace_back(CodecJpegCompInfo {
136 .componentId = jpegCompressInfo->comp_info[i].component_id,
137 .componentIndex = jpegCompressInfo->comp_info[i].component_index,
138 .hSampFactor = jpegCompressInfo->comp_info[i].h_samp_factor,
139 .vSampFactor = jpegCompressInfo->comp_info[i].v_samp_factor,
140 .quantTableNo = jpegCompressInfo->comp_info[i].quant_tbl_no,
141 .dcTableNo = jpegCompressInfo->comp_info[i].dc_tbl_no,
142 .acTableNo = jpegCompressInfo->comp_info[i].ac_tbl_no,
143 .infoFlag = true
144 });
145 }
146 return true;
147 }
148
HuffmanTblTransform(JHUFF_TBL * huffTbl,CodecJpegHuffTable & tbl)149 bool JpegHardwareDecoder::HuffmanTblTransform(JHUFF_TBL* huffTbl, CodecJpegHuffTable& tbl)
150 {
151 if (huffTbl == nullptr) {
152 return false;
153 }
154 // length of bits is defined as 16 in jpeg specification
155 // bits defined in struct JHUFF_TBL has length of 17, bits[0] is unused
156 static constexpr int LIST_BITS_OFFSET = 1;
157 static constexpr int LIST_BITS_LEN = 16;
158 static constexpr int MAX_LIST_HUFFVAL_LEN = 256;
159 int actualHuffValLen = 0;
160 for (int i = LIST_BITS_OFFSET; i <= LIST_BITS_LEN; ++i) {
161 actualHuffValLen += huffTbl->bits[i];
162 }
163 JPEG_HW_LOGD("actualHuffValLen=%{public}d", actualHuffValLen);
164 if (actualHuffValLen > MAX_LIST_HUFFVAL_LEN) {
165 JPEG_HW_LOGE("invalid huffVal len: %{public}d", actualHuffValLen);
166 return false;
167 }
168 tbl.tableFlag = true;
169 tbl.bits = std::vector<uint8_t>(&huffTbl->bits[LIST_BITS_OFFSET],
170 &huffTbl->bits[LIST_BITS_OFFSET] + LIST_BITS_LEN);
171 tbl.huffVal = std::vector<uint8_t>(huffTbl->huffval, &huffTbl->huffval[actualHuffValLen]);
172 return true;
173 }
174
AssembleHuffmanTable(jpeg_decompress_struct * jpegCompressInfo)175 void JpegHardwareDecoder::AssembleHuffmanTable(jpeg_decompress_struct* jpegCompressInfo)
176 {
177 static constexpr int HUFFMAN_TBL_CNT = 4;
178 for (int i = 0; i < HUFFMAN_TBL_CNT; ++i) {
179 CodecJpegHuffTable dcTbl;
180 if (HuffmanTblTransform(jpegCompressInfo->dc_huff_tbl_ptrs[i], dcTbl)) {
181 decodeInfo_.dcHuffTbl.emplace_back(dcTbl);
182 }
183
184 CodecJpegHuffTable acTbl;
185 if (HuffmanTblTransform(jpegCompressInfo->ac_huff_tbl_ptrs[i], acTbl)) {
186 decodeInfo_.acHuffTbl.emplace_back(acTbl);
187 }
188 }
189 }
190
AssembleQuantizationTable(jpeg_decompress_struct * jpegCompressInfo)191 void JpegHardwareDecoder::AssembleQuantizationTable(jpeg_decompress_struct* jpegCompressInfo)
192 {
193 for (int i = 0; i < NUM_QUANT_TBLS; ++i) {
194 if (jpegCompressInfo->quant_tbl_ptrs[i]) {
195 uint16_t* quantStart = jpegCompressInfo->quant_tbl_ptrs[i]->quantval;
196 decodeInfo_.quantTbl.emplace_back(CodecJpegQuantTable {
197 .quantVal = std::vector<uint16_t>(quantStart, quantStart + DCTSIZE2),
198 .tableFlag = true
199 });
200 } else {
201 decodeInfo_.quantTbl.emplace_back(CodecJpegQuantTable {
202 .quantVal = {},
203 .tableFlag = false
204 });
205 }
206 }
207 }
208
GetJpegCompressInfo(SkCodec * codec)209 jpeg_decompress_struct* GetJpegCompressInfo(SkCodec *codec)
210 {
211 if (codec == nullptr) {
212 JPEG_HW_LOGE("invalid input codec!");
213 return nullptr;
214 }
215 SkJpegCodec* jpegCodec = static_cast<SkJpegCodec*>(codec);
216 if (jpegCodec == nullptr) {
217 JPEG_HW_LOGE("invalid input jpeg codec!");
218 return nullptr;
219 }
220 if (jpegCodec->decoderMgr() == nullptr) {
221 JPEG_HW_LOGE("invalid input jpeg codec mgr!");
222 return nullptr;
223 }
224 return jpegCodec->decoderMgr()->dinfo();
225 }
226
AssembleJpegImgHeader(jpeg_decompress_struct * jpegCompressInfo)227 bool JpegHardwareDecoder::AssembleJpegImgHeader(jpeg_decompress_struct* jpegCompressInfo)
228 {
229 decodeInfo_.imageWidth = jpegCompressInfo->image_width;
230 decodeInfo_.imageHeight = jpegCompressInfo->image_height;
231 decodeInfo_.dataPrecision = jpegCompressInfo->data_precision;
232 decodeInfo_.restartInterval = jpegCompressInfo->restart_interval;
233 decodeInfo_.arithCode = jpegCompressInfo->arith_code;
234 decodeInfo_.progressiveMode = jpegCompressInfo->progressive_mode;
235 // no crop as default
236 decodeInfo_.region.flag = 0;
237 if (!AssembleComponentInfo(jpegCompressInfo)) {
238 return false;
239 }
240 AssembleHuffmanTable(jpegCompressInfo);
241 AssembleQuantizationTable(jpegCompressInfo);
242 return true;
243 }
244
CopySrcImgToDecodeInputBuffer(ImagePlugin::InputDataStream * srcStream)245 bool JpegHardwareDecoder::CopySrcImgToDecodeInputBuffer(ImagePlugin::InputDataStream *srcStream)
246 {
247 size_t fileSize = srcStream->GetStreamSize();
248 uint32_t positionRecord = srcStream->Tell();
249 JPEG_HW_LOGI("input stream, size=%{public}zu, curPos=%{public}u", fileSize, positionRecord);
250 int32_t ret = hwDecoder_->AllocateInBuffer(inputBuffer_, static_cast<uint32_t>(fileSize), CODEC_IMAGE_JPEG);
251 if (ret != HDF_SUCCESS) {
252 JPEG_HW_LOGE("failed to allocate input buffer, err=%{public}d", ret);
253 return false;
254 }
255 BufferHandle *inputBufferHandle = inputBuffer_.buffer->GetBufferHandle();
256 if (inputBufferHandle == nullptr) {
257 JPEG_HW_LOGE("inputBufferHandle is nullptr");
258 return false;
259 }
260 bufferMgr_->Mmap(*inputBufferHandle);
261 (void)bufferMgr_->InvalidateCache(*inputBufferHandle);
262 srcStream->Seek(0);
263 uint32_t readSize = 0;
264 bool flag = srcStream->Read(static_cast<uint32_t>(fileSize), static_cast<uint8_t*>(inputBufferHandle->virAddr),
265 static_cast<uint32_t>(inputBufferHandle->size), readSize);
266 (void)bufferMgr_->FlushCache(*inputBufferHandle);
267 ret = bufferMgr_->Unmap(*inputBufferHandle);
268 if (ret != 0) {
269 JPEG_HW_LOGE("failed to unmap input buffer, err=%{public}d", ret);
270 }
271 srcStream->Seek(positionRecord);
272 if (!flag || readSize != static_cast<uint32_t>(fileSize)) {
273 JPEG_HW_LOGE("failed to read input data, readSize=%{public}u", readSize);
274 return false;
275 }
276 return true;
277 }
278
IsStandAloneJpegMarker(uint16_t marker)279 bool JpegHardwareDecoder::IsStandAloneJpegMarker(uint16_t marker)
280 {
281 auto iter = std::find(JpegMarker::STAND_ALONE_MARKER.begin(), JpegMarker::STAND_ALONE_MARKER.end(), marker);
282 return (iter != JpegMarker::STAND_ALONE_MARKER.end());
283 }
284
JumpOverCurrentJpegMarker(const uint8_t * data,unsigned int & curPos,unsigned int totalLen,uint16_t marker)285 bool JpegHardwareDecoder::JumpOverCurrentJpegMarker(const uint8_t* data, unsigned int& curPos,
286 unsigned int totalLen, uint16_t marker)
287 {
288 curPos += JpegMarker::MARKER_LEN;
289 if (curPos + JpegMarker::MARKER_LEN > totalLen) {
290 JPEG_HW_LOGE("invalid pos(cur=%{public}u, total=%{public}u) after jump over marker(%{public}u)",
291 curPos, totalLen, marker);
292 return false;
293 }
294 if (IsStandAloneJpegMarker(marker)) {
295 return true;
296 }
297 // jump over related parameters for those who are not stand alone markers
298 curPos += static_cast<unsigned int>(CombineTwoBytes(data, curPos));
299 if (curPos > totalLen) {
300 JPEG_HW_LOGE("invalid pos(cur=%{public}u, total=%{public}u) after jump over related parameters " \
301 "for marker(%{public}u)", curPos, totalLen, marker);
302 return false;
303 }
304 return true;
305 }
306
GetCompressedDataStart()307 bool JpegHardwareDecoder::GetCompressedDataStart()
308 {
309 BufferHandle *inputBufferHandle = inputBuffer_.buffer->GetBufferHandle();
310 bufferMgr_->Mmap(*inputBufferHandle);
311 (void)bufferMgr_->InvalidateCache(*inputBufferHandle);
312 const uint8_t* data = static_cast<const uint8_t*>(inputBufferHandle->virAddr);
313 unsigned int totalLen = static_cast<unsigned int>(inputBufferHandle->size);
314 unsigned int curPos = 0;
315 bool findFlag = false;
316 while (curPos + JpegMarker::MARKER_LEN <= totalLen) {
317 uint16_t potentialMarker = CombineTwoBytes(data, curPos);
318 if (potentialMarker < JpegMarker::MIN_MARKER || potentialMarker > JpegMarker::MAX_MARKER) {
319 ++curPos;
320 continue;
321 }
322 if (!JumpOverCurrentJpegMarker(data, curPos, totalLen, potentialMarker)) {
323 break;
324 }
325 if (potentialMarker == JpegMarker::SOS) {
326 findFlag = true;
327 decodeInfo_.compressPos = curPos;
328 break;
329 }
330 }
331 (void)bufferMgr_->FlushCache(*inputBufferHandle);
332 int32_t ret = bufferMgr_->Unmap(*inputBufferHandle);
333 if (ret != 0) {
334 JPEG_HW_LOGE("failed to unmap input buffer, err=%{public}d", ret);
335 }
336 return findFlag;
337 }
338
PrepareInputData(SkCodec * codec,ImagePlugin::InputDataStream * srcStream)339 bool JpegHardwareDecoder::PrepareInputData(SkCodec *codec, ImagePlugin::InputDataStream *srcStream)
340 {
341 LifeSpanTimer decodeTimer("prepare input data");
342 jpeg_decompress_struct* jpegCompressInfo = GetJpegCompressInfo(codec);
343 if (jpegCompressInfo == nullptr || srcStream == nullptr) {
344 JPEG_HW_LOGE("failed to get jpeg compress info or invalid input stream");
345 return false;
346 }
347 bool ret = AssembleJpegImgHeader(jpegCompressInfo);
348 ret = ret && CopySrcImgToDecodeInputBuffer(srcStream);
349 ret = ret && GetCompressedDataStart();
350 return ret;
351 }
352
InitDecoder()353 bool JpegHardwareDecoder::InitDecoder()
354 {
355 LifeSpanTimer decodeTimer("init decoder");
356 int32_t ret = hwDecoder_->Init(CODEC_IMAGE_JPEG);
357 if (ret != HDF_SUCCESS) {
358 JPEG_HW_LOGE("failed to init decoder, err=%{public}d", ret);
359 return false;
360 }
361 return true;
362 }
363
DoDecode(CodecImageBuffer & outputBuffer)364 bool JpegHardwareDecoder::DoDecode(CodecImageBuffer& outputBuffer)
365 {
366 LifeSpanTimer decodeTimer("do decode");
367 int32_t ret = hwDecoder_->DoJpegDecode(inputBuffer_, outputBuffer, decodeInfo_);
368 if (ret != HDF_SUCCESS) {
369 JPEG_HW_LOGE("failed to do decode, err=%{public}d", ret);
370 return false;
371 }
372 return true;
373 }
374
RecycleAllocatedResource()375 void JpegHardwareDecoder::RecycleAllocatedResource()
376 {
377 LifeSpanTimer decodeTimer("recycle resource");
378 int32_t ret = hwDecoder_->FreeInBuffer(inputBuffer_);
379 if (ret != HDF_SUCCESS) {
380 JPEG_HW_LOGE("failed to free input buffer, err=%{public}d", ret);
381 }
382
383 ret = hwDecoder_->DeInit(CODEC_IMAGE_JPEG);
384 if (ret != HDF_SUCCESS) {
385 JPEG_HW_LOGE("failed to deinit decoder, err=%{public}d", ret);
386 }
387 }
388 } // namespace OHOS::ImagePlugin
389