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