1 /*
2 * Copyright (C) 2024 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 "jpeg_mpf_parser.h"
17
18 #include <vector>
19 #include "hilog/log_cpp.h"
20 #include "image_log.h"
21 #include "image_utils.h"
22 #include "media_errors.h"
23
24 #undef LOG_DOMAIN
25 #define LOG_DOMAIN LOG_TAG_DOMAIN_ID_IMAGE
26
27 #undef LOG_TAG
28 #define LOG_TAG "JpegMpfParser"
29
30 namespace OHOS {
31 namespace Media {
32
33 using namespace std;
34
35 constexpr uint8_t MP_INDEX_IFD_BYTE_SIZE = 12;
36 constexpr uint8_t MP_ENTRY_BYTE_SIZE = 16;
37 constexpr uint8_t UINT16_BYTE_SIZE = 2;
38 constexpr uint8_t UINT32_BYTE_SIZE = 4;
39 constexpr uint16_t TAG_TYPE_UNDEFINED = 0x07;
40 constexpr uint16_t TAG_TYPE_LONG = 0x04;
41 constexpr uint16_t HDR_MULTI_PICTURE_APP_LENGTH = 90;
42 constexpr uint16_t FRAGMENT_METADATA_LENGTH = 20;
43 constexpr uint16_t AUXILIARY_TAG_NAME_LENGTH = 8;
44
45 constexpr uint8_t JPEG_MARKER_PREFIX = 0xFF;
46 constexpr uint8_t JPEG_MARKER_APP2 = 0xE2;
47
48 constexpr uint8_t MAX_IMAGE_NUM = 32;
49
50 static constexpr uint8_t MULTI_PICTURE_HEADER_FLAG[] = {
51 'M', 'P', 'F', '\0'
52 };
53 static constexpr uint8_t BIG_ENDIAN_FLAG[] = {
54 0x4D, 0x4D, 0x00, 0x2A
55 };
56 static constexpr uint8_t LITTLE_ENDIAN_FLAG[] = {
57 0x49, 0x49, 0x2A, 0x00
58 };
59
60 static constexpr uint8_t MPF_VERSION_DEFAULT[] = {
61 '0', '1', '0', '0'
62 };
63
64 static constexpr uint8_t FRAGMENT_META_FLAG[] = {
65 0xFF, 0xEC, 0x00, 0x12
66 };
67
68 enum MpfIFDTag : uint16_t {
69 MPF_VERSION_TAG = 45056,
70 NUMBERS_OF_IMAGES_TAG = 45057,
71 MP_ENTRY_TAG = 45058,
72 IMAGE_UID_LIST_TAG = 45059,
73 TOTAL_FRAMES_TAG = 45060,
74 };
75
76 static const std::map<std::string, AuxiliaryPictureType> AUXILIARY_TAG_TYPE_MAP = {
77 {AUXILIARY_TAG_DEPTH_MAP_BACK, AuxiliaryPictureType::DEPTH_MAP},
78 {AUXILIARY_TAG_DEPTH_MAP_FRONT, AuxiliaryPictureType::DEPTH_MAP},
79 {AUXILIARY_TAG_UNREFOCUS_MAP, AuxiliaryPictureType::UNREFOCUS_MAP},
80 {AUXILIARY_TAG_LINEAR_MAP, AuxiliaryPictureType::LINEAR_MAP},
81 {AUXILIARY_TAG_FRAGMENT_MAP, AuxiliaryPictureType::FRAGMENT_MAP}
82 };
83
CheckMpfOffset(uint8_t * data,uint32_t size,uint32_t & offset)84 bool JpegMpfParser::CheckMpfOffset(uint8_t* data, uint32_t size, uint32_t& offset)
85 {
86 if (data == nullptr) {
87 return false;
88 }
89 for (offset = 0; offset < size; offset++) {
90 if (data[offset] == JPEG_MARKER_PREFIX && (data[offset + 1] == JPEG_MARKER_APP2)) {
91 offset += UINT32_BYTE_SIZE;
92 return true;
93 }
94 }
95 return false;
96 }
97
Parsing(uint8_t * data,uint32_t size)98 bool JpegMpfParser::Parsing(uint8_t* data, uint32_t size)
99 {
100 if (data == nullptr || size == 0) {
101 return false;
102 }
103 if (memcmp(data, MULTI_PICTURE_HEADER_FLAG, sizeof(MULTI_PICTURE_HEADER_FLAG)) != 0) {
104 return false;
105 }
106 data += UINT32_BYTE_SIZE;
107 size -= UINT32_BYTE_SIZE;
108 uint32_t dataOffset = 0;
109 bool isBigEndian = false;
110 if (memcmp(data, BIG_ENDIAN_FLAG, sizeof(BIG_ENDIAN_FLAG)) == 0) {
111 isBigEndian = true;
112 } else if (memcmp(data, LITTLE_ENDIAN_FLAG, sizeof(LITTLE_ENDIAN_FLAG)) == 0) {
113 isBigEndian = false;
114 } else {
115 return false;
116 }
117 dataOffset += UINT32_BYTE_SIZE;
118 uint32_t ifdOffset = ImageUtils::BytesToUint32(data, dataOffset, size, isBigEndian);
119 if (ifdOffset < dataOffset || ifdOffset > size) {
120 IMAGE_LOGD("get ifd offset error");
121 return false;
122 }
123 dataOffset = ifdOffset;
124 return ParsingMpIndexIFD(data, size, dataOffset, isBigEndian);
125 }
126
ParsingMpIndexIFD(uint8_t * data,uint32_t size,uint32_t dataOffset,bool isBigEndian)127 bool JpegMpfParser::ParsingMpIndexIFD(uint8_t* data, uint32_t size, uint32_t dataOffset, bool isBigEndian)
128 {
129 uint16_t tagCount = ImageUtils::BytesToUint16(data, dataOffset, size, isBigEndian);
130 if (dataOffset + MP_INDEX_IFD_BYTE_SIZE * tagCount > size) {
131 return false;
132 }
133 uint16_t previousTag = 0;
134 for (uint16_t i = 0; i < tagCount; i++) {
135 uint16_t tag = ImageUtils::BytesToUint16(data, dataOffset, size, isBigEndian);
136 if (tag <= previousTag) {
137 return false;
138 }
139 previousTag = tag;
140 uint16_t type = ImageUtils::BytesToUint16(data, dataOffset, size, isBigEndian);
141 uint32_t count = ImageUtils::BytesToUint32(data, dataOffset, size, isBigEndian);
142 uint32_t value = ImageUtils::BytesToUint32(data, dataOffset, size, isBigEndian);
143 IMAGE_LOGD("mpf tag=%{public}d,type=%{public}d,count=%{public}d,value=%{public}d", tag, type, count, value);
144 switch (tag) {
145 case MpfIFDTag::MPF_VERSION_TAG:
146 if (memcmp(data + (dataOffset - UINT32_BYTE_SIZE), MPF_VERSION_DEFAULT,
147 sizeof(MPF_VERSION_DEFAULT)) != 0) {
148 return false;
149 }
150 break;
151 case MpfIFDTag::NUMBERS_OF_IMAGES_TAG:
152 imageNums_ = value;
153 break;
154 case MpfIFDTag::MP_ENTRY_TAG:
155 if (count != MP_ENTRY_BYTE_SIZE * imageNums_ || value < dataOffset || value > size) {
156 return false;
157 }
158 if (!ParsingMpEntry(data + value, size - value, isBigEndian, imageNums_)) {
159 IMAGE_LOGD("mpf parse entry failed");
160 return false;
161 }
162 break;
163 default:
164 break;
165 }
166 }
167 uint32_t mpAttrIFDOffset = ImageUtils::BytesToUint32(data, dataOffset, size, isBigEndian);
168 if (mpAttrIFDOffset > 0 && dataOffset > mpAttrIFDOffset) {
169 return false;
170 }
171 return true;
172 }
173
ParsingMpEntry(uint8_t * data,uint32_t size,bool isBigEndian,uint32_t imageNums)174 bool JpegMpfParser::ParsingMpEntry(uint8_t* data, uint32_t size, bool isBigEndian, uint32_t imageNums)
175 {
176 uint32_t dataOffset = 0;
177 if (imageNums == 0 || imageNums * MP_ENTRY_BYTE_SIZE > size || imageNums > MAX_IMAGE_NUM) {
178 IMAGE_LOGE("Parsing imageNums error");
179 return false;
180 }
181 images_.resize(imageNums);
182 for (uint32_t i = 0; i < imageNums; i++) {
183 uint32_t imageAttr = ImageUtils::BytesToUint32(data, dataOffset, size, isBigEndian);
184 images_[i].size = ImageUtils::BytesToUint32(data, dataOffset, size, isBigEndian);
185 images_[i].offset = ImageUtils::BytesToUint32(data, dataOffset, size, isBigEndian);
186 uint16_t image1EntryNum = ImageUtils::BytesToUint16(data, dataOffset, size, isBigEndian);
187 uint16_t image2EntryNum = ImageUtils::BytesToUint16(data, dataOffset, size, isBigEndian);
188 IMAGE_LOGD("index=%{public}d, imageAttr=%{public}d, image1entrynum=%{public}d, image2entryNum=%{public}d",
189 i, imageAttr, image1EntryNum, image2EntryNum);
190 }
191 return true;
192 }
193
FindAuxiliaryTags(const uint8_t * data,uint32_t size,std::string & foundTag)194 static bool FindAuxiliaryTags(const uint8_t* data, uint32_t size, std::string& foundTag)
195 {
196 if (data == nullptr || size < AUXILIARY_TAG_NAME_LENGTH) {
197 return false;
198 }
199 for (const auto &[tagName, _] : AUXILIARY_TAG_TYPE_MAP) {
200 if (memcmp(data, tagName.c_str(), tagName.size()) == 0) {
201 foundTag = tagName;
202 return true;
203 }
204 }
205 return false;
206 }
207
208 // |<------------------ Auxiliary picture structure ----------------->|
209 // |<- Image data ->|<- Image size(4 Bytes) ->|<- Tag name(8 Bytes) ->|
GetLastAuxiliaryTagOffset(const uint8_t * data,uint32_t size,std::string & foundTag)210 static int32_t GetLastAuxiliaryTagOffset(const uint8_t* data, uint32_t size, std::string& foundTag)
211 {
212 if (data == nullptr || size < AUXILIARY_TAG_NAME_LENGTH) {
213 return ERR_MEDIA_INVALID_VALUE;
214 }
215 uint32_t offset = size - AUXILIARY_TAG_NAME_LENGTH;
216 while (offset > 0) {
217 if (FindAuxiliaryTags(data + offset, size - offset, foundTag)) {
218 return static_cast<int32_t>(offset);
219 }
220 --offset;
221 }
222 return ERR_MEDIA_INVALID_VALUE;
223 }
224
225 // Parse the following types of auxiliary pictures: DEPTH_MAP, UNREFOCUS_MAP, LINEAR_MAP, FRAGMENT_MAP
ParsingAuxiliaryPictures(uint8_t * data,uint32_t dataSize,bool isBigEndian)226 bool JpegMpfParser::ParsingAuxiliaryPictures(uint8_t* data, uint32_t dataSize, bool isBigEndian)
227 {
228 if (data == nullptr || dataSize == 0) {
229 return false;
230 }
231
232 uint32_t offset = dataSize;
233 while (offset > 0) {
234 std::string foundTag("");
235 int32_t matchedPos = GetLastAuxiliaryTagOffset(data, offset, foundTag);
236 if (matchedPos == ERR_MEDIA_INVALID_VALUE) {
237 IMAGE_LOGI("%{public}s no more auxiliary pictures", __func__);
238 break;
239 }
240 offset = static_cast<uint32_t>(matchedPos);
241 auto it = AUXILIARY_TAG_TYPE_MAP.find(foundTag);
242 if (it == AUXILIARY_TAG_TYPE_MAP.end()) {
243 IMAGE_LOGW("%{public}s unknown auxiliary tag: %{public}s", __func__, foundTag.c_str());
244 continue;
245 }
246
247 if (offset < UINT32_BYTE_SIZE) {
248 IMAGE_LOGW("%{public}s invalid offset: %{public}u, auxiliary tag: %{public}s",
249 __func__, offset, foundTag.c_str());
250 continue;
251 }
252 offset -= UINT32_BYTE_SIZE;
253 // tag and image size before this position
254 uint32_t imageSize = ImageUtils::BytesToUint32(data, offset, dataSize, isBigEndian);
255 if (offset < imageSize + UINT32_BYTE_SIZE) {
256 IMAGE_LOGW("%{public}s invalid image size: %{public}u, offset: %{public}u, auxiliary tag: %{public}s",
257 __func__, imageSize, offset, foundTag.c_str());
258 continue;
259 }
260 offset = offset - imageSize - UINT32_BYTE_SIZE;
261 SingleJpegImage auxImage = {
262 .offset = offset,
263 .size = imageSize,
264 .auxType = it->second,
265 .auxTagName = it->first,
266 };
267 images_.push_back(auxImage);
268 IMAGE_LOGD("[%{public}s] auxType=%{public}d, offset=%{public}u, size=%{public}u, tagName=%{public}s",
269 __func__, auxImage.auxType, auxImage.offset, auxImage.size, auxImage.auxTagName.c_str());
270 }
271 return true;
272 }
273
ParsingFragmentMetadata(uint8_t * data,uint32_t size,Rect & fragmentRect,bool isBigEndian)274 bool JpegMpfParser::ParsingFragmentMetadata(uint8_t* data, uint32_t size, Rect& fragmentRect, bool isBigEndian)
275 {
276 if (data == nullptr || size == 0) {
277 return false;
278 }
279
280 for (uint32_t offset = 0; offset < size; offset++) {
281 if (offset + FRAGMENT_METADATA_LENGTH + sizeof(FRAGMENT_META_FLAG)> size) {
282 return false;
283 }
284 if (memcmp(data + offset, FRAGMENT_META_FLAG, sizeof(FRAGMENT_META_FLAG)) == 0) {
285 offset += UINT32_BYTE_SIZE;
286 fragmentRect.left = ImageUtils::BytesToInt32(data, offset, size, isBigEndian);
287 fragmentRect.top = ImageUtils::BytesToInt32(data, offset, size, isBigEndian);
288 fragmentRect.width = ImageUtils::BytesToInt32(data, offset, size, isBigEndian);
289 fragmentRect.height = ImageUtils::BytesToInt32(data, offset, size, isBigEndian);
290 IMAGE_LOGD("[%{public}s] left=%{public}d, top=%{public}d, width=%{public}d, height=%{public}d",
291 __func__, fragmentRect.left, fragmentRect.top, fragmentRect.width, fragmentRect.height);
292 return true;
293 }
294 }
295 return false;
296 }
297
WriteMPEntryToBytes(vector<uint8_t> & bytes,uint32_t & offset,std::vector<SingleJpegImage> images)298 static void WriteMPEntryToBytes(vector<uint8_t>& bytes, uint32_t& offset, std::vector<SingleJpegImage> images)
299 {
300 for (uint32_t i = 0; i < images.size(); i++) {
301 uint32_t attributeData = 0;
302 if (i == 0) {
303 // 0x20: representative image flag / 0x03: primary image type code;
304 attributeData = 0x20030000;
305 }
306 ImageUtils::Uint32ToBytes(attributeData, bytes, offset);
307 ImageUtils::Uint32ToBytes(images[i].size, bytes, offset);
308 ImageUtils::Uint32ToBytes(images[i].offset, bytes, offset);
309 const uint16_t dependentImage1EntryNumber = 0;
310 const uint16_t dependentImage2EntryNumber = 0;
311 ImageUtils::Uint16ToBytes(dependentImage1EntryNumber, bytes, offset);
312 ImageUtils::Uint16ToBytes(dependentImage2EntryNumber, bytes, offset);
313 }
314 }
315
WriteMpIndexIFD(vector<uint8_t> & bytes,uint32_t & offset,uint8_t imageNum)316 static void WriteMpIndexIFD(vector<uint8_t>& bytes, uint32_t& offset, uint8_t imageNum)
317 {
318 // tag count is three(MPF_VERSION_TAG, NUMBERS_OF_IMAGES_TAG, MP_ENTRY_TAG)
319 const uint16_t tagCount = 3;
320 ImageUtils::Uint16ToBytes(tagCount, bytes, offset);
321
322 // tag MPF_VERSION_TAG
323 const uint16_t versionTagCount = 4;
324 ImageUtils::Uint16ToBytes(MPF_VERSION_TAG, bytes, offset);
325 ImageUtils::Uint16ToBytes(TAG_TYPE_UNDEFINED, bytes, offset);
326 ImageUtils::Uint32ToBytes(versionTagCount, bytes, offset);
327 ImageUtils::ArrayToBytes(MPF_VERSION_DEFAULT, UINT32_BYTE_SIZE, bytes, offset);
328
329 // tag NUMBERS_OF_IMAGES_TAG
330 const uint16_t imageNumTagCount = 1;
331 ImageUtils::Uint16ToBytes(NUMBERS_OF_IMAGES_TAG, bytes, offset);
332 ImageUtils::Uint16ToBytes(TAG_TYPE_LONG, bytes, offset);
333 ImageUtils::Uint32ToBytes(imageNumTagCount, bytes, offset);
334 ImageUtils::Uint32ToBytes(imageNum, bytes, offset);
335
336 // tag MP_ENTRY_TAG
337 const uint32_t mpEntryCount = static_cast<uint32_t>(MP_ENTRY_BYTE_SIZE) * static_cast<uint32_t>(imageNum);
338 ImageUtils::Uint16ToBytes(MP_ENTRY_TAG, bytes, offset);
339 ImageUtils::Uint16ToBytes(TAG_TYPE_UNDEFINED, bytes, offset);
340 ImageUtils::Uint32ToBytes(mpEntryCount, bytes, offset);
341
342 // offset-markerSize(2)-lengthSize(2)-MULTI_PICTURE_FLAG size(4)+mpEntryOffsetSize(4)+attributeIfdOffset(4)
343 uint32_t mpEntryOffset = offset - UINT16_BYTE_SIZE - UINT16_BYTE_SIZE - UINT32_BYTE_SIZE +
344 UINT32_BYTE_SIZE + UINT32_BYTE_SIZE;
345 ImageUtils::Uint32ToBytes(mpEntryOffset, bytes, offset);
346 }
347
PackHdrJpegMpfMarker(SingleJpegImage base,SingleJpegImage gainmap)348 std::vector<uint8_t> JpegMpfPacker::PackHdrJpegMpfMarker(SingleJpegImage base, SingleJpegImage gainmap)
349 {
350 vector<uint8_t> bytes(HDR_MULTI_PICTURE_APP_LENGTH);
351 uint32_t index = 0;
352 bytes[index++] = 0xFF;
353 bytes[index++] = 0xE2;
354
355 // length dont combine marker(0xFFE2)
356 ImageUtils::Uint16ToBytes(HDR_MULTI_PICTURE_APP_LENGTH - UINT16_BYTE_SIZE, bytes, index);
357 ImageUtils::ArrayToBytes(MULTI_PICTURE_HEADER_FLAG, UINT32_BYTE_SIZE, bytes, index);
358 ImageUtils::ArrayToBytes(BIG_ENDIAN_FLAG, UINT32_BYTE_SIZE, bytes, index);
359
360 // BIG_ENDIAN_FLAG size + IFDOffset size
361 const uint32_t IFDOffset = UINT32_BYTE_SIZE + UINT32_BYTE_SIZE;
362 ImageUtils::Uint32ToBytes(IFDOffset, bytes, index);
363 std::vector<SingleJpegImage> images = {base, gainmap};
364 WriteMpIndexIFD(bytes, index, images.size());
365 const uint32_t attributeIfdOffset = 0;
366 ImageUtils::Uint32ToBytes(attributeIfdOffset, bytes, index);
367 WriteMPEntryToBytes(bytes, index, images);
368 return bytes;
369 }
370
PackFragmentMetadata(Rect & fragmentRect,bool isBigEndian)371 std::vector<uint8_t> JpegMpfPacker::PackFragmentMetadata(Rect& fragmentRect, bool isBigEndian)
372 {
373 std::vector<uint8_t> bytes(FRAGMENT_METADATA_LENGTH);
374 uint32_t offset = 0;
375 ImageUtils::ArrayToBytes(FRAGMENT_META_FLAG, UINT32_BYTE_SIZE, bytes, offset);
376 ImageUtils::Int32ToBytes(fragmentRect.left, bytes, offset, isBigEndian);
377 ImageUtils::Int32ToBytes(fragmentRect.top, bytes, offset, isBigEndian);
378 ImageUtils::Int32ToBytes(fragmentRect.width, bytes, offset, isBigEndian);
379 ImageUtils::Int32ToBytes(fragmentRect.height, bytes, offset, isBigEndian);
380 return bytes;
381 }
382
PackDataSize(uint32_t size,bool isBigEndian)383 std::vector<uint8_t> JpegMpfPacker::PackDataSize(uint32_t size, bool isBigEndian)
384 {
385 std::vector<uint8_t> bytes(UINT32_BYTE_SIZE);
386 uint32_t offset = 0;
387 ImageUtils::Uint32ToBytes(size, bytes, offset, isBigEndian);
388 return bytes;
389 }
390
PackAuxiliaryTagName(std::string & tagName)391 std::vector<uint8_t> JpegMpfPacker::PackAuxiliaryTagName(std::string& tagName)
392 {
393 std::vector<uint8_t> bytes(AUXILIARY_TAG_NAME_LENGTH, 0x00);
394 std::copy(tagName.begin(), tagName.end(), bytes.begin());
395 return bytes;
396 }
397 }
398 }
399