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