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 if (imageStream_->Read(buf.data(), buf.size()) == -1) {
187 IMAGE_LOGE("Failed to read from image stream. Marker: %{public}u", marker);
188 return { buf, size };
189 }
190 size = GetUShort(buf.data(), bigEndian);
191 }
192 return { buf, size };
193 }
194
ReadNextSegment(byte marker)195 DataBuf JpegExifMetadataAccessor::ReadNextSegment(byte marker)
196 {
197 const auto [sizeBuf, size] = ReadSegmentLength(marker);
198 DataBuf buf(size);
199 if (size > SEGMENT_LENGTH_SIZE &&
200 (imageStream_->Read(buf.Data(SEGMENT_LENGTH_SIZE), (size - SEGMENT_LENGTH_SIZE)) != -1)) {
201 std::copy(sizeBuf.begin(), sizeBuf.end(), buf.Begin());
202 }
203
204 return buf;
205 }
206
GetExifEncodeBlob(uint8_t ** dataBlob,uint32_t & size)207 bool JpegExifMetadataAccessor::GetExifEncodeBlob(uint8_t **dataBlob, uint32_t &size)
208 {
209 if (this->Get() == nullptr) {
210 IMAGE_LOGE("EXIF metadata is empty.");
211 return false;
212 }
213
214 ExifData *exifData = this->Get()->GetExifData();
215 TiffParser::EncodeJpegExif(dataBlob, size, exifData);
216
217 if (dataBlob == nullptr || *dataBlob == nullptr) {
218 IMAGE_LOGE("Failed to encode JPEG data.");
219 return false;
220 }
221
222 return (size > 0);
223 }
224
GetExifBlob(const DataBuf & blob,uint8_t ** dataBlob,uint32_t & size)225 bool JpegExifMetadataAccessor::GetExifBlob(const DataBuf &blob, uint8_t **dataBlob, uint32_t &size)
226 {
227 if (blob.Empty()) {
228 IMAGE_LOGE("EXIF blob data is empty.");
229 return false;
230 }
231
232 *dataBlob = const_cast<byte *>(blob.CData());
233 size = blob.Size();
234
235 return true;
236 }
237
WriteHeader(BufferMetadataStream & bufStream)238 bool JpegExifMetadataAccessor::WriteHeader(BufferMetadataStream &bufStream)
239 {
240 byte tmpBuf[JPEG_HEADER_LENGTH];
241 tmpBuf[0] = JPEG_MARKER_HEADER;
242 tmpBuf[1] = JPEG_MARKER_SOI;
243 if (bufStream.Write(tmpBuf, JPEG_HEADER_LENGTH) != JPEG_HEADER_LENGTH) {
244 return false;
245 }
246
247 return true;
248 }
249
GetInsertPosAndMarkerAPP1()250 std::tuple<size_t, size_t> JpegExifMetadataAccessor::GetInsertPosAndMarkerAPP1()
251 {
252 size_t markerCount = 0;
253 size_t skipExifSeqNum = -1;
254 size_t insertPos = 0;
255
256 imageStream_->Seek(0, SeekPos::BEGIN);
257
258 int ret = FindNextMarker();
259 if (ret == EOF) {
260 IMAGE_LOGE("GetInsertPosAndMarkerAPP1 failed to find marker 0xff in image stream.");
261 return std::make_tuple(insertPos, skipExifSeqNum);
262 }
263 byte marker = static_cast<byte>(ret);
264 while ((marker != JPEG_MARKER_SOS) && (marker != JPEG_MARKER_EOI)) {
265 DataBuf buf = ReadNextSegment(marker);
266 if (marker == JPEG_MARKER_APP0) {
267 insertPos = markerCount + 1;
268 } else if ((marker == JPEG_MARKER_APP1) && (buf.Size() >= APP1_EXIF_LENGTH) &&
269 (buf.CmpBytes(EXIF_BLOB_OFFSET, EXIF_ID, EXIF_ID_SIZE) == 0)) {
270 skipExifSeqNum = markerCount;
271 }
272
273 int ret = FindNextMarker();
274 if (ret == EOF) {
275 break;
276 }
277 marker = static_cast<byte>(ret);
278 ++markerCount;
279 }
280
281 return std::make_tuple(insertPos, skipExifSeqNum);
282 }
283
WriteData(BufferMetadataStream & bufStream,uint8_t * dataBlob,uint32_t size)284 bool JpegExifMetadataAccessor::WriteData(BufferMetadataStream &bufStream, uint8_t *dataBlob, uint32_t size)
285 {
286 std::array<byte, APP1_HEADER_LENGTH> tmpBuf;
287 tmpBuf[0] = JPEG_MARKER_HEADER;
288 tmpBuf[1] = JPEG_MARKER_APP1;
289
290 if (size > (JPEG_DATA_MAX_SIZE - APP1_EXIF_LENGTH)) {
291 IMAGE_LOGE("JPEG EXIF size exceeds maximum limit. Size: %{public}u", size);
292 return false;
293 }
294
295 ssize_t writeHeaderLength = MARKER_LENGTH_SIZE;
296 ssize_t exifHeaderLength = EXIF_ID_LENGTH;
297
298 if (dataBlob == nullptr) {
299 IMAGE_LOGE("Failed to write data blob. dataBlob is nullptr");
300 return false;
301 }
302 if (size >= EXIF_ID_SIZE && memcmp(reinterpret_cast<char *>(dataBlob), EXIF_ID, EXIF_ID_SIZE) != 0) {
303 writeHeaderLength = APP1_HEADER_LENGTH;
304 exifHeaderLength = APP1_EXIF_LENGTH;
305 std::copy_n(EXIF_ID, EXIF_ID_SIZE, tmpBuf.data() + MARKER_LENGTH_SIZE);
306 }
307
308 US2Data(tmpBuf.data() + EXIF_BLOB_OFFSET, static_cast<uint16_t>(size + exifHeaderLength), bigEndian);
309 if (bufStream.Write(tmpBuf.data(), writeHeaderLength) != writeHeaderLength) {
310 IMAGE_LOGE("Failed to write EXIF_ID to temporary stream. Expected length: %{public}zu", writeHeaderLength);
311 return false;
312 }
313
314 if (bufStream.Write(dataBlob, size) != size) {
315 IMAGE_LOGE("Failed to write data blob to temporary stream. Expected size: %{public}u", size);
316 return false;
317 }
318
319 return true;
320 }
321
WriteSegment(BufferMetadataStream & bufStream,uint8_t marker,const DataBuf & buf)322 bool JpegExifMetadataAccessor::WriteSegment(BufferMetadataStream &bufStream, uint8_t marker, const DataBuf &buf)
323 {
324 std::array<byte, SEGMENT_LENGTH_SIZE> tmpBuf;
325 tmpBuf[0] = JPEG_MARKER_HEADER;
326 tmpBuf[1] = marker;
327 if (bufStream.Write(tmpBuf.data(), SEGMENT_LENGTH_SIZE) != SEGMENT_LENGTH_SIZE) {
328 IMAGE_LOGE("Failed to write marker and segment. Marker: %{public}u", marker);
329 return false;
330 }
331 if (bufStream.Write(const_cast<byte *>(buf.CData()), buf.Size()) != static_cast<int>(buf.Size())) {
332 IMAGE_LOGE("Failed to write buffer. Buffer size: %{public}zu", buf.Size());
333 return false;
334 }
335
336 return true;
337 }
338
WriteTail(BufferMetadataStream & bufStream)339 bool JpegExifMetadataAccessor::WriteTail(BufferMetadataStream &bufStream)
340 {
341 std::array<byte, SEGMENT_LENGTH_SIZE> tmpBuf;
342 tmpBuf[0] = JPEG_MARKER_HEADER;
343 tmpBuf[1] = JPEG_MARKER_SOS;
344 if (bufStream.Write(tmpBuf.data(), SEGMENT_LENGTH_SIZE) != SEGMENT_LENGTH_SIZE) {
345 IMAGE_LOGE("Failed to write the final marker. Expected length: %{public}d",
346 static_cast<int>(SEGMENT_LENGTH_SIZE));
347 return false;
348 }
349
350 return true;
351 }
352
CopyRestData(BufferMetadataStream & bufStream)353 bool JpegExifMetadataAccessor::CopyRestData(BufferMetadataStream &bufStream)
354 {
355 DataBuf buf(READ_WRITE_BLOCK_SIZE * READ_WRITE_BLOCK_SIZE_NUM);
356 ssize_t readSize = imageStream_->Read(buf.Data(), buf.Size());
357 while (readSize > 0) {
358 if (bufStream.Write(const_cast<byte *>(buf.CData()), readSize) != readSize) {
359 IMAGE_LOGE("Failed to write block data to temporary stream. Expected size: %{public}zd", readSize);
360 return false;
361 }
362 readSize = imageStream_->Read(buf.Data(), buf.Size());
363 }
364
365 return true;
366 }
367
UpdateExifMetadata(BufferMetadataStream & bufStream,uint8_t * dataBlob,uint32_t size)368 bool JpegExifMetadataAccessor::UpdateExifMetadata(BufferMetadataStream &bufStream, uint8_t *dataBlob, uint32_t size)
369 {
370 size_t markerCount = 0;
371 auto [insertPos, skipExifSeqNum] = GetInsertPosAndMarkerAPP1();
372
373 if (!WriteHeader(bufStream)) {
374 IMAGE_LOGE("Failed to write header to output image stream");
375 return false;
376 }
377
378 imageStream_->Seek(0, SeekPos::BEGIN);
379
380 int ret = FindNextMarker();
381 if (ret == EOF) {
382 IMAGE_LOGE("UpdateExifMetadata failed to find marker 0xff in image stream.");
383 return false;
384 }
385 byte marker = static_cast<byte>(ret);
386 while (marker != JPEG_MARKER_SOS) {
387 DataBuf buf = ReadNextSegment(marker);
388 if (markerCount == insertPos) {
389 WriteData(bufStream, dataBlob, size);
390 }
391
392 if (marker == JPEG_MARKER_EOI) {
393 break;
394 }
395
396 if ((markerCount != skipExifSeqNum) && (marker != JPEG_MARKER_SOI)) {
397 WriteSegment(bufStream, marker, buf);
398 } else {
399 IMAGE_LOGD("Skipping existing exifApp segment number.");
400 }
401
402 int ret = FindNextMarker();
403 if (ret == EOF) {
404 break;
405 }
406 marker = static_cast<byte>(ret);
407 ++markerCount;
408 }
409
410 WriteTail(bufStream);
411
412 return CopyRestData(bufStream);
413 }
414
UpdateData(uint8_t * dataBlob,uint32_t size)415 uint32_t JpegExifMetadataAccessor::UpdateData(uint8_t *dataBlob, uint32_t size)
416 {
417 BufferMetadataStream tmpBufStream;
418 if (!tmpBufStream.Open(OpenMode::ReadWrite)) {
419 IMAGE_LOGE("Failed to open temporary image stream");
420 return ERR_IMAGE_SOURCE_DATA;
421 }
422
423 if (!imageStream_->IsOpen()) {
424 IMAGE_LOGE("The output image stream is not open");
425 return ERR_IMAGE_SOURCE_DATA;
426 }
427
428 if (!UpdateExifMetadata(tmpBufStream, dataBlob, size)) {
429 IMAGE_LOGE("Failed to write to temporary image stream");
430 return ERROR;
431 }
432
433 imageStream_->Seek(0, SeekPos::BEGIN);
434 imageStream_->CopyFrom(tmpBufStream);
435
436 return SUCCESS;
437 }
438 } // namespace Media
439 } // namespace OHOS