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