• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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