• 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         if (marker == JPEG_MARKER_APP1) {
100             const auto [sizeBuf, size] = ReadSegmentLength(marker);
101             if (size >= APP1_EXIF_LENGTH) {
102                 blob.Resize(size - SEGMENT_LENGTH_SIZE);
103             }
104             if (size >= APP1_EXIF_LENGTH && (imageStream_->Read(blob.Data(), (size - SEGMENT_LENGTH_SIZE)) == -1)) {
105                 return false;
106             }
107             if (size >= APP1_EXIF_LENGTH && (blob.CmpBytes(0, EXIF_ID, EXIF_ID_SIZE) == 0)) {
108                 tiffOffset_ = imageStream_->Tell() - static_cast<long>(blob.Size()) + EXIF_ID_SIZE;
109                 return true;
110             }
111         }
112 
113         marker = FindNextMarker();
114         if (marker == EOF) {
115             IMAGE_LOGE("Failed to find marker 0xff in image stream.");
116             return false;
117         }
118     }
119     IMAGE_LOGD("Failed to find APP1 in image stream.");
120     return false;
121 }
122 
Write()123 uint32_t JpegExifMetadataAccessor::Write()
124 {
125     uint8_t *dataBlob = nullptr;
126     uint32_t size = 0;
127     if (!GetExifEncodeBlob(&dataBlob, size)) {
128         IMAGE_LOGE("Failed to encode metadata. Size: %{public}u", size);
129         return ERR_MEDIA_VALUE_INVALID;
130     }
131 
132     uint32_t result = UpdateData(dataBlob, size);
133 
134     if (dataBlob != nullptr) {
135         free(dataBlob);
136         dataBlob = nullptr;
137     }
138 
139     return result;
140 }
141 
WriteBlob(DataBuf & blob)142 uint32_t JpegExifMetadataAccessor::WriteBlob(DataBuf &blob)
143 {
144     byte *dataBlob = nullptr;
145     uint32_t size = 0;
146     if (!GetExifBlob(blob, &dataBlob, size)) {
147         IMAGE_LOGE("Blob data is empty. Size: %{public}u", size);
148         return ERR_MEDIA_VALUE_INVALID;
149     }
150 
151     return UpdateData(dataBlob, size);
152 }
153 
FindNextMarker() const154 int JpegExifMetadataAccessor::FindNextMarker() const
155 {
156     int marker = -1;
157     do {
158         marker = imageStream_->ReadByte();
159         if (marker == EOF) {
160             return marker;
161         }
162     } while (marker != JPEG_MARKER_HEADER);
163 
164     do {
165         marker = imageStream_->ReadByte();
166         if (marker == EOF) {
167             return marker;
168         }
169     } while (marker == JPEG_MARKER_HEADER);
170 
171     return marker;
172 }
173 
HasLength(byte marker)174 bool HasLength(byte marker)
175 {
176     if ((marker >= JPEG_MARKER_RST1) && (marker <= JPEG_MARKER_EOI)) {
177         return false;
178     }
179     return true;
180 }
181 
ReadSegmentLength(uint8_t marker) const182 std::pair<std::array<byte, 2>, uint16_t> JpegExifMetadataAccessor::ReadSegmentLength(uint8_t marker) const
183 {
184     std::array<byte, READ_BYTES> buf { 0, 0 };
185     uint16_t size { 0 };
186     if (HasLength(marker)) {
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         size = GetUShort(buf.data(), bigEndian);
192     }
193     return { buf, size };
194 }
195 
ReadNextSegment(byte marker)196 DataBuf JpegExifMetadataAccessor::ReadNextSegment(byte marker)
197 {
198     const auto [sizeBuf, size] = ReadSegmentLength(marker);
199     DataBuf buf(size);
200     if (size > SEGMENT_LENGTH_SIZE &&
201         (imageStream_->Read(buf.Data(SEGMENT_LENGTH_SIZE), (size - SEGMENT_LENGTH_SIZE)) != -1)) {
202         std::copy(sizeBuf.begin(), sizeBuf.end(), buf.Begin());
203     }
204 
205     return buf;
206 }
207 
GetExifEncodeBlob(uint8_t ** dataBlob,uint32_t & size)208 bool JpegExifMetadataAccessor::GetExifEncodeBlob(uint8_t **dataBlob, uint32_t &size)
209 {
210     if (this->Get() == nullptr) {
211         IMAGE_LOGE("EXIF metadata is empty.");
212         return false;
213     }
214 
215     ExifData *exifData = this->Get()->GetExifData();
216     TiffParser::EncodeJpegExif(dataBlob, size, exifData);
217 
218     if (dataBlob == nullptr || *dataBlob == nullptr) {
219         IMAGE_LOGE("Failed to encode JPEG data.");
220         return false;
221     }
222 
223     return (size > 0);
224 }
225 
GetExifBlob(const DataBuf & blob,uint8_t ** dataBlob,uint32_t & size)226 bool JpegExifMetadataAccessor::GetExifBlob(const DataBuf &blob, uint8_t **dataBlob, uint32_t &size)
227 {
228     if (blob.Empty()) {
229         IMAGE_LOGE("EXIF blob data is empty.");
230         return false;
231     }
232 
233     *dataBlob = const_cast<byte *>(blob.CData());
234     size = blob.Size();
235 
236     return true;
237 }
238 
WriteHeader(BufferMetadataStream & bufStream)239 bool JpegExifMetadataAccessor::WriteHeader(BufferMetadataStream &bufStream)
240 {
241     byte tmpBuf[JPEG_HEADER_LENGTH];
242     tmpBuf[0] = JPEG_MARKER_HEADER;
243     tmpBuf[1] = JPEG_MARKER_SOI;
244     if (bufStream.Write(tmpBuf, JPEG_HEADER_LENGTH) != JPEG_HEADER_LENGTH) {
245         return false;
246     }
247 
248     return true;
249 }
250 
GetInsertPosAndMarkerAPP1()251 std::tuple<size_t, size_t> JpegExifMetadataAccessor::GetInsertPosAndMarkerAPP1()
252 {
253     size_t markerCount = 0;
254     size_t skipExifSeqNum = -1;
255     size_t insertPos = 0;
256     size_t insertPosApp0 = 0;
257     size_t insertPosApp1 = 0;
258 
259     imageStream_->Seek(0, SeekPos::BEGIN);
260 
261     int ret = FindNextMarker();
262     if (ret == EOF) {
263         IMAGE_LOGE("GetInsertPosAndMarkerAPP1 failed to find marker 0xff in image stream.");
264         return std::make_tuple(insertPos, skipExifSeqNum);
265     }
266     byte marker = static_cast<byte>(ret);
267     while ((marker != JPEG_MARKER_SOS) && (marker != JPEG_MARKER_EOI)) {
268         if (marker == JPEG_MARKER_APP0) {
269             insertPosApp0 = markerCount + 1;
270         } else if (marker == JPEG_MARKER_APP1) {
271             DataBuf buf = ReadNextSegment(marker);
272             if ((buf.Size() >= APP1_EXIF_LENGTH) && (buf.CmpBytes(EXIF_BLOB_OFFSET, EXIF_ID, EXIF_ID_SIZE) == 0)) {
273                 skipExifSeqNum = markerCount;
274                 insertPosApp1 = markerCount;
275             }
276         }
277 
278         if (HasLength(marker) && marker != JPEG_MARKER_APP1) {
279             const auto [sizeBuf, size] = ReadSegmentLength(marker);
280             if (size < SEGMENT_LENGTH_SIZE || imageStream_->Seek(size - SEGMENT_LENGTH_SIZE, SeekPos::CURRENT) == -1) {
281                 break;
282             }
283         }
284 
285         int ret = FindNextMarker();
286         if (ret == EOF) {
287             break;
288         }
289         marker = static_cast<byte>(ret);
290         ++markerCount;
291     }
292     insertPos = insertPosApp1 == 0 ? insertPosApp0 : insertPosApp1;
293     return std::make_tuple(insertPos, skipExifSeqNum);
294 }
295 
WriteData(BufferMetadataStream & bufStream,uint8_t * dataBlob,uint32_t size)296 bool JpegExifMetadataAccessor::WriteData(BufferMetadataStream &bufStream, uint8_t *dataBlob, uint32_t size)
297 {
298     std::array<byte, APP1_HEADER_LENGTH> tmpBuf;
299     tmpBuf[0] = JPEG_MARKER_HEADER;
300     tmpBuf[1] = JPEG_MARKER_APP1;
301 
302     if (size > (JPEG_DATA_MAX_SIZE - APP1_EXIF_LENGTH)) {
303         IMAGE_LOGE("JPEG EXIF size exceeds maximum limit. Size: %{public}u", size);
304         return false;
305     }
306 
307     ssize_t writeHeaderLength = MARKER_LENGTH_SIZE;
308     ssize_t exifHeaderLength = EXIF_ID_LENGTH;
309 
310     if (dataBlob == nullptr) {
311         IMAGE_LOGE("Failed to write data blob. dataBlob is nullptr");
312         return false;
313     }
314     if (size >= EXIF_ID_SIZE && memcmp(reinterpret_cast<char *>(dataBlob), EXIF_ID, EXIF_ID_SIZE) != 0) {
315         writeHeaderLength = APP1_HEADER_LENGTH;
316         exifHeaderLength = APP1_EXIF_LENGTH;
317         std::copy_n(EXIF_ID, EXIF_ID_SIZE, tmpBuf.data() + MARKER_LENGTH_SIZE);
318     }
319 
320     US2Data(tmpBuf.data() + EXIF_BLOB_OFFSET, static_cast<uint16_t>(size + exifHeaderLength), bigEndian);
321     if (bufStream.Write(tmpBuf.data(), writeHeaderLength) != writeHeaderLength) {
322         IMAGE_LOGE("Failed to write EXIF_ID to temporary stream. Expected length: %{public}zu", writeHeaderLength);
323         return false;
324     }
325 
326     if (bufStream.Write(dataBlob, size) != size) {
327         IMAGE_LOGE("Failed to write data blob to temporary stream. Expected size: %{public}u", size);
328         return false;
329     }
330 
331     return true;
332 }
333 
WriteSegment(BufferMetadataStream & bufStream,uint8_t marker,const DataBuf & buf)334 bool JpegExifMetadataAccessor::WriteSegment(BufferMetadataStream &bufStream, uint8_t marker, const DataBuf &buf)
335 {
336     std::array<byte, SEGMENT_LENGTH_SIZE> tmpBuf;
337     tmpBuf[0] = JPEG_MARKER_HEADER;
338     tmpBuf[1] = marker;
339     if (bufStream.Write(tmpBuf.data(), SEGMENT_LENGTH_SIZE) != SEGMENT_LENGTH_SIZE) {
340         IMAGE_LOGE("Failed to write marker and segment. Marker: %{public}u", marker);
341         return false;
342     }
343     if (bufStream.Write(const_cast<byte *>(buf.CData()), buf.Size()) != static_cast<int>(buf.Size())) {
344         IMAGE_LOGE("Failed to write buffer. Buffer size: %{public}zu", buf.Size());
345         return false;
346     }
347 
348     return true;
349 }
350 
WriteTail(BufferMetadataStream & bufStream)351 bool JpegExifMetadataAccessor::WriteTail(BufferMetadataStream &bufStream)
352 {
353     std::array<byte, SEGMENT_LENGTH_SIZE> tmpBuf;
354     tmpBuf[0] = JPEG_MARKER_HEADER;
355     tmpBuf[1] = JPEG_MARKER_SOS;
356     if (bufStream.Write(tmpBuf.data(), SEGMENT_LENGTH_SIZE) != SEGMENT_LENGTH_SIZE) {
357         IMAGE_LOGE("Failed to write the final marker. Expected length: %{public}d",
358                    static_cast<int>(SEGMENT_LENGTH_SIZE));
359         return false;
360     }
361 
362     return true;
363 }
364 
CopyRestData(BufferMetadataStream & bufStream)365 bool JpegExifMetadataAccessor::CopyRestData(BufferMetadataStream &bufStream)
366 {
367     DataBuf buf(READ_WRITE_BLOCK_SIZE * READ_WRITE_BLOCK_SIZE_NUM);
368     ssize_t readSize = imageStream_->Read(buf.Data(), buf.Size());
369     while (readSize > 0) {
370         if (bufStream.Write(const_cast<byte *>(buf.CData()), readSize) != readSize) {
371             IMAGE_LOGE("Failed to write block data to temporary stream. Expected size: %{public}zd", readSize);
372             return false;
373         }
374         readSize = imageStream_->Read(buf.Data(), buf.Size());
375     }
376 
377     return true;
378 }
379 
UpdateExifMetadata(BufferMetadataStream & bufStream,uint8_t * dataBlob,uint32_t size)380 bool JpegExifMetadataAccessor::UpdateExifMetadata(BufferMetadataStream &bufStream, uint8_t *dataBlob, uint32_t size)
381 {
382     size_t markerCount = 0;
383     auto [insertPos, skipExifSeqNum] = GetInsertPosAndMarkerAPP1();
384 
385     // Write Exif data segment after 0xFFD8
386     insertPos = 0;
387 
388     if (!WriteHeader(bufStream)) {
389         IMAGE_LOGE("Failed to write header to output image stream");
390         return false;
391     }
392 
393     imageStream_->Seek(0, SeekPos::BEGIN);
394 
395     int ret = FindNextMarker();
396     if (ret == EOF) {
397         IMAGE_LOGE("UpdateExifMetadata failed to find marker 0xff in image stream.");
398         return false;
399     }
400     byte marker = static_cast<byte>(ret);
401     while (marker != JPEG_MARKER_SOS) {
402         DataBuf buf = ReadNextSegment(marker);
403         if (markerCount == insertPos) {
404             WriteData(bufStream, dataBlob, size);
405         }
406 
407         if (marker == JPEG_MARKER_EOI) {
408             break;
409         }
410 
411         if ((markerCount != skipExifSeqNum) && (marker != JPEG_MARKER_SOI)) {
412             WriteSegment(bufStream, marker, buf);
413         } else {
414             IMAGE_LOGD("Skipping existing exifApp segment number.");
415         }
416 
417         int ret = FindNextMarker();
418         if (ret == EOF) {
419             break;
420         }
421         marker = static_cast<byte>(ret);
422         ++markerCount;
423     }
424 
425     WriteTail(bufStream);
426 
427     return CopyRestData(bufStream);
428 }
429 
UpdateData(uint8_t * dataBlob,uint32_t size)430 uint32_t JpegExifMetadataAccessor::UpdateData(uint8_t *dataBlob, uint32_t size)
431 {
432     BufferMetadataStream tmpBufStream;
433     if (!tmpBufStream.Open(OpenMode::ReadWrite)) {
434         IMAGE_LOGE("Failed to open temporary image stream");
435         return ERR_IMAGE_SOURCE_DATA;
436     }
437 
438     if (!imageStream_->IsOpen()) {
439         IMAGE_LOGE("The output image stream is not open");
440         return ERR_IMAGE_SOURCE_DATA;
441     }
442 
443     if (!UpdateExifMetadata(tmpBufStream, dataBlob, size)) {
444         IMAGE_LOGE("Failed to write to temporary image stream");
445         return ERROR;
446     }
447 
448     imageStream_->Seek(0, SeekPos::BEGIN);
449     imageStream_->CopyFrom(tmpBufStream);
450 
451     return SUCCESS;
452 }
453 } // namespace Media
454 } // namespace OHOS