• 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 "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