• 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 <array>
17 #include "jpeg_exif_metadata_accessor.h"
18 
19 #include <libexif/exif-data.h>
20 #include <array>
21 #include "file_metadata_stream.h"
22 #include "image_log.h"
23 #include "media_errors.h"
24 #include "tiff_parser.h"
25 
26 #undef LOG_DOMAIN
27 #define LOG_DOMAIN LOG_TAG_DOMAIN_ID_IMAGE
28 
29 #undef LOG_TAG
30 #define LOG_TAG "JpegExifMetadataAccessor"
31 
32 namespace OHOS {
33 namespace Media {
34 namespace {
35 using uint_8 = byte;
36 constexpr byte JPEG_MARKER_APP0 = 0xe0;
37 constexpr byte JPEG_MARKER_APP1 = 0xe1;
38 constexpr byte JPEG_MARKER_SOI = 0xd8;
39 constexpr byte JPEG_MARKER_EOI = 0xd9;
40 constexpr byte JPEG_MARKER_RST1 = 0xd0;
41 constexpr byte JPEG_MARKER_SOS = 0xda;
42 constexpr auto EXIF_ID = "Exif\0\0";
43 constexpr auto EXIF_BLOB_OFFSET = 2;
44 constexpr auto EXIF_ID_LENGTH = 2;
45 constexpr auto SEGMENT_LENGTH_SIZE = 2;
46 constexpr auto READ_BYTES = 2;
47 constexpr auto JPEG_HEADER_LENGTH = 2;
48 constexpr auto EXIF_ID_SIZE = 6;
49 constexpr auto APP1_EXIF_LENGTH = 8;
50 constexpr auto APP1_HEADER_LENGTH = 10;
51 constexpr auto MARKER_LENGTH_SIZE = 4;
52 constexpr auto READ_WRITE_BLOCK_SIZE = 4096;
53 constexpr auto READ_WRITE_BLOCK_SIZE_NUM = 32;
54 constexpr auto JPEG_MARKER_HEADER = 0xff;
55 constexpr auto JPEG_DATA_MAX_SIZE = 0xffff;
56 }
57 
JpegExifMetadataAccessor(std::shared_ptr<MetadataStream> & stream)58 JpegExifMetadataAccessor::JpegExifMetadataAccessor(std::shared_ptr<MetadataStream> &stream)
59     : AbstractExifMetadataAccessor(stream)
60 {}
61 
~JpegExifMetadataAccessor()62 JpegExifMetadataAccessor::~JpegExifMetadataAccessor() {}
63 
Read()64 uint32_t JpegExifMetadataAccessor::Read()
65 {
66     DataBuf dataBuf;
67     if (!ReadBlob(dataBuf)) {
68         IMAGE_LOGD("Failed to read data buffer from image stream.");
69         return ERR_IMAGE_SOURCE_DATA;
70     }
71 
72     ExifData *exifData;
73     TiffParser::DecodeJpegExif(reinterpret_cast<const unsigned char *>(dataBuf.CData()), dataBuf.Size(), &exifData);
74     if (exifData == nullptr) {
75         IMAGE_LOGE("Failed to decode EXIF data from image stream.");
76         return ERR_EXIF_DECODE_FAILED;
77     }
78 
79     exifMetadata_ = std::make_shared<OHOS::Media::ExifMetadata>(exifData);
80 
81     return SUCCESS;
82 }
83 
ReadBlob(DataBuf & blob)84 bool JpegExifMetadataAccessor::ReadBlob(DataBuf &blob)
85 {
86     if (!imageStream_->IsOpen()) {
87         IMAGE_LOGE("Output image stream is not open.");
88         return false;
89     }
90     imageStream_->Seek(0, SeekPos::BEGIN);
91 
92     int marker = FindNextMarker();
93     if (marker == EOF) {
94         IMAGE_LOGE("Failed to find marker 0xff in image stream.");
95         return false;
96     }
97 
98     while ((marker != JPEG_MARKER_SOS) && (marker != JPEG_MARKER_EOI)) {
99         const auto [sizeBuf, size] = ReadSegmentLength(marker);
100 
101         if ((marker == JPEG_MARKER_APP1) && (size >= APP1_EXIF_LENGTH)) {
102             blob.Resize(size - SEGMENT_LENGTH_SIZE);
103             if (imageStream_->Read(blob.Data(), (size - SEGMENT_LENGTH_SIZE)) == -1) {
104                 return false;
105             }
106             if (blob.CmpBytes(0, EXIF_ID, EXIF_ID_SIZE) == 0) {
107                 tiffOffset_ = imageStream_->Tell() - static_cast<long>(blob.Size()) + EXIF_ID_SIZE;
108                 return true;
109             }
110         }
111 
112         marker = FindNextMarker();
113         if (marker == EOF) {
114             IMAGE_LOGE("Failed to find marker 0xff in image stream.");
115             return false;
116         }
117     }
118     IMAGE_LOGD("Failed to find APP1 in image stream.");
119     return false;
120 }
121 
Write()122 uint32_t JpegExifMetadataAccessor::Write()
123 {
124     uint8_t *dataBlob = nullptr;
125     uint32_t size = 0;
126     if (!GetExifEncodeBlob(&dataBlob, size)) {
127         IMAGE_LOGE("Failed to encode metadata. Size: %{public}u", size);
128         return ERR_MEDIA_VALUE_INVALID;
129     }
130 
131     uint32_t result = UpdateData(dataBlob, size);
132 
133     if (dataBlob != nullptr) {
134         free(dataBlob);
135         dataBlob = nullptr;
136     }
137 
138     return result;
139 }
140 
WriteBlob(DataBuf & blob)141 uint32_t JpegExifMetadataAccessor::WriteBlob(DataBuf &blob)
142 {
143     byte *dataBlob = nullptr;
144     uint32_t size = 0;
145     if (!GetExifBlob(blob, &dataBlob, size)) {
146         IMAGE_LOGE("Blob data is empty. Size: %{public}u", size);
147         return ERR_MEDIA_VALUE_INVALID;
148     }
149 
150     return UpdateData(dataBlob, size);
151 }
152 
FindNextMarker() const153 int JpegExifMetadataAccessor::FindNextMarker() const
154 {
155     int marker = -1;
156     do {
157         marker = imageStream_->ReadByte();
158         if (marker == EOF) {
159             return marker;
160         }
161     } while (marker != JPEG_MARKER_HEADER);
162 
163     do {
164         marker = imageStream_->ReadByte();
165         if (marker == EOF) {
166             return marker;
167         }
168     } while (marker == JPEG_MARKER_HEADER);
169 
170     return marker;
171 }
172 
HasLength(byte marker)173 bool HasLength(byte marker)
174 {
175     if ((marker >= JPEG_MARKER_RST1) && (marker <= JPEG_MARKER_EOI)) {
176         return false;
177     }
178     return true;
179 }
180 
ReadSegmentLength(uint8_t marker) const181 std::pair<std::array<byte, 2>, uint16_t> JpegExifMetadataAccessor::ReadSegmentLength(uint8_t marker) const
182 {
183     std::array<byte, READ_BYTES> buf { 0, 0 };
184     uint16_t size { 0 };
185     if (HasLength(marker)) {
186         long currentPos = imageStream_->Tell();
187         if (imageStream_->Read(buf.data(), buf.size()) == -1) {
188             IMAGE_LOGE("Failed to read from image stream. Marker: %{public}u", marker);
189             return { buf, size };
190         }
191         if (buf[0] == JPEG_MARKER_HEADER && HasLength(buf[1])) {
192             IMAGE_LOGD("ReadSegmentLength wrong marker, marker: %{public}u", marker);
193             imageStream_->Seek(currentPos, SeekPos::BEGIN);
194             return { buf, 0 };
195         }
196         size = GetUShort(buf.data(), bigEndian);
197     }
198     return { buf, size };
199 }
200 
ReadNextSegment(byte marker)201 DataBuf JpegExifMetadataAccessor::ReadNextSegment(byte marker)
202 {
203     const auto [sizeBuf, size] = ReadSegmentLength(marker);
204     DataBuf buf(size);
205     if (size > SEGMENT_LENGTH_SIZE &&
206         (imageStream_->Read(buf.Data(SEGMENT_LENGTH_SIZE), (size - SEGMENT_LENGTH_SIZE)) != -1)) {
207         std::copy(sizeBuf.begin(), sizeBuf.end(), buf.Begin());
208     }
209 
210     return buf;
211 }
212 
GetExifEncodeBlob(uint8_t ** dataBlob,uint32_t & size)213 bool JpegExifMetadataAccessor::GetExifEncodeBlob(uint8_t **dataBlob, uint32_t &size)
214 {
215     if (this->Get() == nullptr) {
216         IMAGE_LOGE("EXIF metadata is empty.");
217         return false;
218     }
219 
220     ExifData *exifData = this->Get()->GetExifData();
221     TiffParser::EncodeJpegExif(dataBlob, size, exifData);
222 
223     if (dataBlob == nullptr || *dataBlob == nullptr) {
224         IMAGE_LOGE("Failed to encode JPEG data.");
225         return false;
226     }
227 
228     return (size > 0);
229 }
230 
GetExifBlob(const DataBuf & blob,uint8_t ** dataBlob,uint32_t & size)231 bool JpegExifMetadataAccessor::GetExifBlob(const DataBuf &blob, uint8_t **dataBlob, uint32_t &size)
232 {
233     if (blob.Empty()) {
234         IMAGE_LOGE("EXIF blob data is empty.");
235         return false;
236     }
237 
238     *dataBlob = const_cast<byte *>(blob.CData());
239     size = blob.Size();
240 
241     return true;
242 }
243 
WriteHeader(BufferMetadataStream & bufStream)244 bool JpegExifMetadataAccessor::WriteHeader(BufferMetadataStream &bufStream)
245 {
246     byte tmpBuf[JPEG_HEADER_LENGTH];
247     tmpBuf[0] = JPEG_MARKER_HEADER;
248     tmpBuf[1] = JPEG_MARKER_SOI;
249     if (bufStream.Write(tmpBuf, JPEG_HEADER_LENGTH) != JPEG_HEADER_LENGTH) {
250         return false;
251     }
252 
253     return true;
254 }
255 
GetInsertPosAndMarkerAPP1()256 std::tuple<size_t, size_t> JpegExifMetadataAccessor::GetInsertPosAndMarkerAPP1()
257 {
258     size_t markerCount = 0;
259     size_t skipExifSeqNum = -1;
260     size_t insertPos = 0;
261     size_t insertPosApp0 = 0;
262     size_t insertPosApp1 = 0;
263 
264     imageStream_->Seek(0, SeekPos::BEGIN);
265 
266     int ret = FindNextMarker();
267     if (ret == EOF) {
268         IMAGE_LOGE("GetInsertPosAndMarkerAPP1 failed to find marker 0xff in image stream.");
269         return std::make_tuple(insertPos, skipExifSeqNum);
270     }
271     byte marker = static_cast<byte>(ret);
272     while ((marker != JPEG_MARKER_SOS) && (marker != JPEG_MARKER_EOI)) {
273         DataBuf buf = ReadNextSegment(marker);
274         if (marker == JPEG_MARKER_APP0) {
275             insertPosApp0 = markerCount + 1;
276         } else if ((marker == JPEG_MARKER_APP1) && (buf.Size() >= APP1_EXIF_LENGTH) &&
277             (buf.CmpBytes(EXIF_BLOB_OFFSET, EXIF_ID, EXIF_ID_SIZE) == 0)) {
278             skipExifSeqNum = markerCount;
279             insertPosApp1 = markerCount;
280         }
281 
282         int ret = FindNextMarker();
283         if (ret == EOF) {
284             break;
285         }
286         marker = static_cast<byte>(ret);
287         ++markerCount;
288     }
289     insertPos = insertPosApp1 == 0 ? insertPosApp0 : insertPosApp1;
290     return std::make_tuple(insertPos, skipExifSeqNum);
291 }
292 
WriteData(BufferMetadataStream & bufStream,uint8_t * dataBlob,uint32_t size)293 bool JpegExifMetadataAccessor::WriteData(BufferMetadataStream &bufStream, uint8_t *dataBlob, uint32_t size)
294 {
295     std::array<byte, APP1_HEADER_LENGTH> tmpBuf;
296     tmpBuf[0] = JPEG_MARKER_HEADER;
297     tmpBuf[1] = JPEG_MARKER_APP1;
298 
299     if (size > (JPEG_DATA_MAX_SIZE - APP1_EXIF_LENGTH)) {
300         IMAGE_LOGE("JPEG EXIF size exceeds maximum limit. Size: %{public}u", size);
301         return false;
302     }
303 
304     ssize_t writeHeaderLength = MARKER_LENGTH_SIZE;
305     ssize_t exifHeaderLength = EXIF_ID_LENGTH;
306 
307     if (dataBlob == nullptr) {
308         IMAGE_LOGE("Failed to write data blob. dataBlob is nullptr");
309         return false;
310     }
311     if (size >= EXIF_ID_SIZE && memcmp(reinterpret_cast<char *>(dataBlob), EXIF_ID, EXIF_ID_SIZE) != 0) {
312         writeHeaderLength = APP1_HEADER_LENGTH;
313         exifHeaderLength = APP1_EXIF_LENGTH;
314         std::copy_n(EXIF_ID, EXIF_ID_SIZE, tmpBuf.data() + MARKER_LENGTH_SIZE);
315     }
316 
317     US2Data(tmpBuf.data() + EXIF_BLOB_OFFSET, static_cast<uint16_t>(size + exifHeaderLength), bigEndian);
318     if (bufStream.Write(tmpBuf.data(), writeHeaderLength) != writeHeaderLength) {
319         IMAGE_LOGE("Failed to write EXIF_ID to temporary stream. Expected length: %{public}zu", writeHeaderLength);
320         return false;
321     }
322 
323     if (bufStream.Write(dataBlob, size) != size) {
324         IMAGE_LOGE("Failed to write data blob to temporary stream. Expected size: %{public}u", size);
325         return false;
326     }
327 
328     return true;
329 }
330 
WriteSegment(BufferMetadataStream & bufStream,uint8_t marker,const DataBuf & buf)331 bool JpegExifMetadataAccessor::WriteSegment(BufferMetadataStream &bufStream, uint8_t marker, const DataBuf &buf)
332 {
333     std::array<byte, SEGMENT_LENGTH_SIZE> tmpBuf;
334     tmpBuf[0] = JPEG_MARKER_HEADER;
335     tmpBuf[1] = marker;
336     if (bufStream.Write(tmpBuf.data(), SEGMENT_LENGTH_SIZE) != SEGMENT_LENGTH_SIZE) {
337         IMAGE_LOGE("Failed to write marker and segment. Marker: %{public}u", marker);
338         return false;
339     }
340     if (bufStream.Write(const_cast<byte *>(buf.CData()), buf.Size()) != static_cast<int>(buf.Size())) {
341         IMAGE_LOGE("Failed to write buffer. Buffer size: %{public}zu", buf.Size());
342         return false;
343     }
344 
345     return true;
346 }
347 
WriteTail(BufferMetadataStream & bufStream)348 bool JpegExifMetadataAccessor::WriteTail(BufferMetadataStream &bufStream)
349 {
350     std::array<byte, SEGMENT_LENGTH_SIZE> tmpBuf;
351     tmpBuf[0] = JPEG_MARKER_HEADER;
352     tmpBuf[1] = JPEG_MARKER_SOS;
353     if (bufStream.Write(tmpBuf.data(), SEGMENT_LENGTH_SIZE) != SEGMENT_LENGTH_SIZE) {
354         IMAGE_LOGE("Failed to write the final marker. Expected length: %{public}d",
355                    static_cast<int>(SEGMENT_LENGTH_SIZE));
356         return false;
357     }
358 
359     return true;
360 }
361 
CopyRestData(BufferMetadataStream & bufStream)362 bool JpegExifMetadataAccessor::CopyRestData(BufferMetadataStream &bufStream)
363 {
364     DataBuf buf(READ_WRITE_BLOCK_SIZE * READ_WRITE_BLOCK_SIZE_NUM);
365     ssize_t readSize = imageStream_->Read(buf.Data(), buf.Size());
366     while (readSize > 0) {
367         if (bufStream.Write(const_cast<byte *>(buf.CData()), readSize) != readSize) {
368             IMAGE_LOGE("Failed to write block data to temporary stream. Expected size: %{public}zd", readSize);
369             return false;
370         }
371         readSize = imageStream_->Read(buf.Data(), buf.Size());
372     }
373 
374     return true;
375 }
376 
UpdateExifMetadata(BufferMetadataStream & bufStream,uint8_t * dataBlob,uint32_t size)377 bool JpegExifMetadataAccessor::UpdateExifMetadata(BufferMetadataStream &bufStream, uint8_t *dataBlob, uint32_t size)
378 {
379     size_t markerCount = 0;
380     auto [insertPos, skipExifSeqNum] = GetInsertPosAndMarkerAPP1();
381 
382     if (!WriteHeader(bufStream)) {
383         IMAGE_LOGE("Failed to write header to output image stream");
384         return false;
385     }
386 
387     imageStream_->Seek(0, SeekPos::BEGIN);
388 
389     int ret = FindNextMarker();
390     if (ret == EOF) {
391         IMAGE_LOGE("UpdateExifMetadata failed to find marker 0xff in image stream.");
392         return false;
393     }
394     byte marker = static_cast<byte>(ret);
395     while (marker != JPEG_MARKER_SOS) {
396         DataBuf buf = ReadNextSegment(marker);
397         if (markerCount == insertPos) {
398             WriteData(bufStream, dataBlob, size);
399         }
400 
401         if (marker == JPEG_MARKER_EOI) {
402             break;
403         }
404 
405         if ((markerCount != skipExifSeqNum) && (marker != JPEG_MARKER_SOI)) {
406             WriteSegment(bufStream, marker, buf);
407         } else {
408             IMAGE_LOGD("Skipping existing exifApp segment number.");
409         }
410 
411         int ret = FindNextMarker();
412         if (ret == EOF) {
413             break;
414         }
415         marker = static_cast<byte>(ret);
416         ++markerCount;
417     }
418 
419     WriteTail(bufStream);
420 
421     return CopyRestData(bufStream);
422 }
423 
UpdateData(uint8_t * dataBlob,uint32_t size)424 uint32_t JpegExifMetadataAccessor::UpdateData(uint8_t *dataBlob, uint32_t size)
425 {
426     BufferMetadataStream tmpBufStream;
427     if (!tmpBufStream.Open(OpenMode::ReadWrite)) {
428         IMAGE_LOGE("Failed to open temporary image stream");
429         return ERR_IMAGE_SOURCE_DATA;
430     }
431 
432     if (!imageStream_->IsOpen()) {
433         IMAGE_LOGE("The output image stream is not open");
434         return ERR_IMAGE_SOURCE_DATA;
435     }
436 
437     if (!UpdateExifMetadata(tmpBufStream, dataBlob, size)) {
438         IMAGE_LOGE("Failed to write to temporary image stream");
439         return ERROR;
440     }
441 
442     imageStream_->Seek(0, SeekPos::BEGIN);
443     imageStream_->CopyFrom(tmpBufStream);
444 
445     return SUCCESS;
446 }
447 } // namespace Media
448 } // namespace OHOS