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