blob: a72e589b59a72eaecc4e5aa707bf26ea15626b47 [file] [log] [blame]
Chong Zhangb51ca282017-07-26 16:25:28 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Chong Zhangb51ca282017-07-26 16:25:28 -070017//#define LOG_NDEBUG 0
Chong Zhangee079fe2017-08-23 13:51:17 -070018#define LOG_TAG "ItemTable"
Chong Zhangb51ca282017-07-26 16:25:28 -070019
Chong Zhang866f0f02019-02-01 14:19:37 -080020#include <unordered_set>
21
Marco Nelissen75226172016-11-16 14:10:52 -080022#include <ItemTable.h>
Marco Nelissencec44d02018-06-17 22:21:09 -070023#include <media/MediaExtractorPluginApi.h>
24#include <media/MediaExtractorPluginHelper.h>
Chong Zhangb51ca282017-07-26 16:25:28 -070025#include <media/stagefright/MetaData.h>
26#include <media/stagefright/MediaErrors.h>
Chong Zhangb51ca282017-07-26 16:25:28 -070027#include <media/stagefright/foundation/ABuffer.h>
Dongwon Kang60761282017-10-09 11:16:48 -070028#include <media/stagefright/foundation/ByteUtils.h>
Chong Zhangb51ca282017-07-26 16:25:28 -070029#include <media/stagefright/foundation/hexdump.h>
Dongwon Kange7a8a632017-10-09 14:52:58 -070030#include <media/stagefright/foundation/MediaDefs.h>
Chong Zhangb51ca282017-07-26 16:25:28 -070031#include <utils/Log.h>
32
33namespace android {
34
35namespace heif {
36
37/////////////////////////////////////////////////////////////////////
38//
39// struct to keep track of one image item
40//
41
42struct ImageItem {
43 friend struct ItemReference;
44 friend struct ItemProperty;
45
Chong Zhangd3e0d862017-10-03 13:17:13 -070046 ImageItem() : ImageItem(0, 0, false) {}
47 ImageItem(uint32_t _type, uint32_t _id, bool _hidden) :
48 type(_type), itemId(_id), hidden(_hidden),
Chong Zhangb51ca282017-07-26 16:25:28 -070049 rows(0), columns(0), width(0), height(0), rotation(0),
50 offset(0), size(0), nextTileIndex(0) {}
51
52 bool isGrid() const {
Marco Nelissen51087de2019-01-22 15:39:07 -080053 return type == FOURCC("grid");
Chong Zhangb51ca282017-07-26 16:25:28 -070054 }
55
56 status_t getNextTileItemId(uint32_t *nextTileItemId, bool reset) {
57 if (reset) {
58 nextTileIndex = 0;
59 }
60 if (nextTileIndex >= dimgRefs.size()) {
61 return ERROR_END_OF_STREAM;
62 }
63 *nextTileItemId = dimgRefs[nextTileIndex++];
64 return OK;
65 }
66
67 uint32_t type;
Chong Zhangd3e0d862017-10-03 13:17:13 -070068 uint32_t itemId;
69 bool hidden;
Chong Zhangb51ca282017-07-26 16:25:28 -070070 int32_t rows;
71 int32_t columns;
72 int32_t width;
73 int32_t height;
74 int32_t rotation;
75 off64_t offset;
76 size_t size;
77 sp<ABuffer> hvcc;
78 sp<ABuffer> icc;
79
80 Vector<uint32_t> thumbnails;
81 Vector<uint32_t> dimgRefs;
Chong Zhang01a76012018-03-14 18:19:49 -070082 Vector<uint32_t> cdscRefs;
Chong Zhangb51ca282017-07-26 16:25:28 -070083 size_t nextTileIndex;
84};
85
Chong Zhang01a76012018-03-14 18:19:49 -070086struct ExifItem {
87 off64_t offset;
88 size_t size;
89};
Chong Zhangb51ca282017-07-26 16:25:28 -070090
91/////////////////////////////////////////////////////////////////////
92//
93// ISO boxes
94//
95
96struct Box {
97protected:
Marco Nelissencec44d02018-06-17 22:21:09 -070098 Box(DataSourceHelper *source, uint32_t type) :
Chong Zhangb51ca282017-07-26 16:25:28 -070099 mDataSource(source), mType(type) {}
100
101 virtual ~Box() {}
102
103 virtual status_t onChunkData(
104 uint32_t /*type*/, off64_t /*offset*/, size_t /*size*/) {
105 return OK;
106 }
107
108 inline uint32_t type() const { return mType; }
109
Marco Nelissencec44d02018-06-17 22:21:09 -0700110 inline DataSourceHelper *source() const { return mDataSource; }
Chong Zhangb51ca282017-07-26 16:25:28 -0700111
112 status_t parseChunk(off64_t *offset);
113
114 status_t parseChunks(off64_t offset, size_t size);
115
116private:
Marco Nelissencec44d02018-06-17 22:21:09 -0700117 DataSourceHelper *mDataSource;
Chong Zhangb51ca282017-07-26 16:25:28 -0700118 uint32_t mType;
119};
120
121status_t Box::parseChunk(off64_t *offset) {
122 if (*offset < 0) {
123 ALOGE("b/23540914");
124 return ERROR_MALFORMED;
125 }
126 uint32_t hdr[2];
127 if (mDataSource->readAt(*offset, hdr, 8) < 8) {
128 return ERROR_IO;
129 }
130 uint64_t chunk_size = ntohl(hdr[0]);
131 int32_t chunk_type = ntohl(hdr[1]);
132 off64_t data_offset = *offset + 8;
133
134 if (chunk_size == 1) {
135 if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) {
136 return ERROR_IO;
137 }
138 chunk_size = ntoh64(chunk_size);
139 data_offset += 8;
140
141 if (chunk_size < 16) {
142 // The smallest valid chunk is 16 bytes long in this case.
143 return ERROR_MALFORMED;
144 }
145 } else if (chunk_size == 0) {
146 // This shouldn't happen since we should never be top level
147 ALOGE("invalid chunk size 0 for non-top level box");
148 return ERROR_MALFORMED;
149 } else if (chunk_size < 8) {
150 // The smallest valid chunk is 8 bytes long.
151 ALOGE("invalid chunk size: %lld", (long long)chunk_size);
152 return ERROR_MALFORMED;
153 }
154
155 char chunk[5];
156 MakeFourCCString(chunk_type, chunk);
157 ALOGV("chunk: %s @ %lld", chunk, (long long)*offset);
158
159 off64_t chunk_data_size = chunk_size - (data_offset - *offset);
160 if (chunk_data_size < 0) {
161 ALOGE("b/23540914");
162 return ERROR_MALFORMED;
163 }
164
165 status_t err = onChunkData(chunk_type, data_offset, chunk_data_size);
166
167 if (err != OK) {
168 return err;
169 }
170 *offset += chunk_size;
171 return OK;
172}
173
174status_t Box::parseChunks(off64_t offset, size_t size) {
175 off64_t stopOffset = offset + size;
176 while (offset < stopOffset) {
177 status_t err = parseChunk(&offset);
178 if (err != OK) {
179 return err;
180 }
181 }
182 if (offset != stopOffset) {
183 return ERROR_MALFORMED;
184 }
185 return OK;
186}
187
188///////////////////////////////////////////////////////////////////////
189
190struct FullBox : public Box {
191protected:
Marco Nelissencec44d02018-06-17 22:21:09 -0700192 FullBox(DataSourceHelper *source, uint32_t type) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700193 Box(source, type), mVersion(0), mFlags(0) {}
194
195 inline uint8_t version() const { return mVersion; }
196
197 inline uint32_t flags() const { return mFlags; }
198
199 status_t parseFullBoxHeader(off64_t *offset, size_t *size);
200
201private:
202 uint8_t mVersion;
203 uint32_t mFlags;
204};
205
206status_t FullBox::parseFullBoxHeader(off64_t *offset, size_t *size) {
207 if (*size < 4) {
208 return ERROR_MALFORMED;
209 }
210 if (!source()->readAt(*offset, &mVersion, 1)) {
211 return ERROR_IO;
212 }
213 if (!source()->getUInt24(*offset + 1, &mFlags)) {
214 return ERROR_IO;
215 }
216 *offset += 4;
217 *size -= 4;
218 return OK;
219}
220
221/////////////////////////////////////////////////////////////////////
222//
223// PrimaryImage box
224//
225
226struct PitmBox : public FullBox {
Marco Nelissencec44d02018-06-17 22:21:09 -0700227 PitmBox(DataSourceHelper *source) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800228 FullBox(source, FOURCC("pitm")) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700229
230 status_t parse(off64_t offset, size_t size, uint32_t *primaryItemId);
231};
232
233status_t PitmBox::parse(off64_t offset, size_t size, uint32_t *primaryItemId) {
234 status_t err = parseFullBoxHeader(&offset, &size);
235 if (err != OK) {
236 return err;
237 }
238
239 size_t itemIdSize = (version() == 0) ? 2 : 4;
240 if (size < itemIdSize) {
241 return ERROR_MALFORMED;
242 }
243 uint32_t itemId;
244 if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) {
245 return ERROR_IO;
246 }
247
248 ALOGV("primary id %d", itemId);
249 *primaryItemId = itemId;
250
251 return OK;
252}
253
254/////////////////////////////////////////////////////////////////////
255//
256// ItemLocation related boxes
257//
258
259struct ExtentEntry {
260 uint64_t extentIndex;
261 uint64_t extentOffset;
262 uint64_t extentLength;
263};
264
265struct ItemLoc {
266 ItemLoc() : ItemLoc(0, 0, 0, 0) {}
267 ItemLoc(uint32_t item_id, uint16_t construction_method,
268 uint16_t data_reference_index, uint64_t base_offset) :
269 itemId(item_id),
270 constructionMethod(construction_method),
271 dataReferenceIndex(data_reference_index),
272 baseOffset(base_offset) {}
273
274 void addExtent(const ExtentEntry& extent) {
275 extents.push_back(extent);
276 }
277
278 status_t getLoc(off64_t *offset, size_t *size,
279 off64_t idatOffset, size_t idatSize) const {
280 // TODO: fix extent handling, fix constructionMethod = 2
281 CHECK(extents.size() == 1);
282 if (constructionMethod == 0) {
283 *offset = baseOffset + extents[0].extentOffset;
284 *size = extents[0].extentLength;
285 return OK;
286 } else if (constructionMethod == 1) {
287 if (baseOffset + extents[0].extentOffset + extents[0].extentLength
288 > idatSize) {
289 return ERROR_MALFORMED;
290 }
291 *offset = baseOffset + extents[0].extentOffset + idatOffset;
292 *size = extents[0].extentLength;
293 return OK;
294 }
295 return ERROR_UNSUPPORTED;
296 }
297
298 // parsed info
299 uint32_t itemId;
300 uint16_t constructionMethod;
301 uint16_t dataReferenceIndex;
302 off64_t baseOffset;
303 Vector<ExtentEntry> extents;
304};
305
306struct IlocBox : public FullBox {
Marco Nelissencec44d02018-06-17 22:21:09 -0700307 IlocBox(DataSourceHelper *source, KeyedVector<uint32_t, ItemLoc> *itemLocs) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800308 FullBox(source, FOURCC("iloc")),
Chong Zhangb51ca282017-07-26 16:25:28 -0700309 mItemLocs(itemLocs), mHasConstructMethod1(false) {}
310
311 status_t parse(off64_t offset, size_t size);
312
313 bool hasConstructMethod1() { return mHasConstructMethod1; }
314
315private:
316 static bool isSizeFieldValid(uint32_t offset_size) {
317 return offset_size == 0 || offset_size == 4 || offset_size == 8;
318 }
319 KeyedVector<uint32_t, ItemLoc> *mItemLocs;
320 bool mHasConstructMethod1;
321};
322
323status_t IlocBox::parse(off64_t offset, size_t size) {
324 status_t err = parseFullBoxHeader(&offset, &size);
325 if (err != OK) {
326 return err;
327 }
328 if (version() > 2) {
329 ALOGE("%s: invalid version %d", __FUNCTION__, version());
330 return ERROR_MALFORMED;
331 }
332
333 if (size < 2) {
334 return ERROR_MALFORMED;
335 }
336 uint8_t offset_size;
337 if (!source()->readAt(offset++, &offset_size, 1)) {
338 return ERROR_IO;
339 }
340 uint8_t length_size = (offset_size & 0xF);
341 offset_size >>= 4;
342
343 uint8_t base_offset_size;
344 if (!source()->readAt(offset++, &base_offset_size, 1)) {
345 return ERROR_IO;
346 }
347 uint8_t index_size = 0;
348 if (version() == 1 || version() == 2) {
349 index_size = (base_offset_size & 0xF);
350 }
351 base_offset_size >>= 4;
352 size -= 2;
353
354 if (!isSizeFieldValid(offset_size)
355 || !isSizeFieldValid(length_size)
356 || !isSizeFieldValid(base_offset_size)
357 || !isSizeFieldValid((index_size))) {
358 ALOGE("%s: offset size not valid: %d, %d, %d, %d", __FUNCTION__,
359 offset_size, length_size, base_offset_size, index_size);
360 return ERROR_MALFORMED;
361 }
362
363 uint32_t item_count;
364 size_t itemFieldSize = version() < 2 ? 2 : 4;
365 if (size < itemFieldSize) {
366 return ERROR_MALFORMED;
367 }
368 if (!source()->getUInt32Var(offset, &item_count, itemFieldSize)) {
369 return ERROR_IO;
370 }
371
372 ALOGV("item_count %lld", (long long) item_count);
373 offset += itemFieldSize;
374 size -= itemFieldSize;
375
376 for (size_t i = 0; i < item_count; i++) {
377 uint32_t item_id;
378 if (!source()->getUInt32Var(offset, &item_id, itemFieldSize)) {
379 return ERROR_IO;
380 }
381 ALOGV("item[%zu]: id %lld", i, (long long)item_id);
382 offset += itemFieldSize;
383
384 uint8_t construction_method = 0;
385 if (version() == 1 || version() == 2) {
386 uint8_t buf[2];
387 if (!source()->readAt(offset, buf, 2)) {
388 return ERROR_IO;
389 }
390 construction_method = (buf[1] & 0xF);
391 ALOGV("construction_method %d", construction_method);
392 if (construction_method == 1) {
393 mHasConstructMethod1 = true;
394 }
395
396 offset += 2;
397 }
398
399 uint16_t data_reference_index;
400 if (!source()->getUInt16(offset, &data_reference_index)) {
401 return ERROR_IO;
402 }
403 ALOGV("data_reference_index %d", data_reference_index);
404 if (data_reference_index != 0) {
405 // we don't support reference to other files
406 return ERROR_UNSUPPORTED;
407 }
408 offset += 2;
409
410 uint64_t base_offset = 0;
411 if (base_offset_size != 0) {
412 if (!source()->getUInt64Var(offset, &base_offset, base_offset_size)) {
413 return ERROR_IO;
414 }
415 offset += base_offset_size;
416 }
417 ALOGV("base_offset %lld", (long long) base_offset);
418
419 ssize_t index = mItemLocs->add(item_id, ItemLoc(
420 item_id, construction_method, data_reference_index, base_offset));
421 ItemLoc &item = mItemLocs->editValueAt(index);
422
423 uint16_t extent_count;
424 if (!source()->getUInt16(offset, &extent_count)) {
425 return ERROR_IO;
426 }
427 ALOGV("extent_count %d", extent_count);
428
429 if (extent_count > 1 && (offset_size == 0 || length_size == 0)) {
430 // if the item is dividec into more than one extents, offset and
431 // length must be present.
432 return ERROR_MALFORMED;
433 }
434 offset += 2;
435
436 for (size_t j = 0; j < extent_count; j++) {
437 uint64_t extent_index = 1; // default=1
438 if ((version() == 1 || version() == 2) && (index_size > 0)) {
439 if (!source()->getUInt64Var(offset, &extent_index, index_size)) {
440 return ERROR_IO;
441 }
442 // TODO: add support for this mode
443 offset += index_size;
444 ALOGV("extent_index %lld", (long long)extent_index);
445 }
446
447 uint64_t extent_offset = 0; // default=0
448 if (offset_size > 0) {
449 if (!source()->getUInt64Var(offset, &extent_offset, offset_size)) {
450 return ERROR_IO;
451 }
452 offset += offset_size;
453 }
454 ALOGV("extent_offset %lld", (long long)extent_offset);
455
456 uint64_t extent_length = 0; // this indicates full length of file
457 if (length_size > 0) {
458 if (!source()->getUInt64Var(offset, &extent_length, length_size)) {
459 return ERROR_IO;
460 }
461 offset += length_size;
462 }
463 ALOGV("extent_length %lld", (long long)extent_length);
464
465 item.addExtent({ extent_index, extent_offset, extent_length });
466 }
467 }
468 return OK;
469}
470
471/////////////////////////////////////////////////////////////////////
472//
473// ItemReference related boxes
474//
475
476struct ItemReference : public Box, public RefBase {
Marco Nelissencec44d02018-06-17 22:21:09 -0700477 ItemReference(DataSourceHelper *source, uint32_t type, uint32_t itemIdSize) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700478 Box(source, type), mItemId(0), mRefIdSize(itemIdSize) {}
479
480 status_t parse(off64_t offset, size_t size);
481
482 uint32_t itemId() { return mItemId; }
483
Chong Zhang01a76012018-03-14 18:19:49 -0700484 void apply(
485 KeyedVector<uint32_t, ImageItem> &itemIdToItemMap,
486 KeyedVector<uint32_t, ExifItem> &itemIdToExifMap) const;
Chong Zhangb51ca282017-07-26 16:25:28 -0700487
488private:
489 uint32_t mItemId;
490 uint32_t mRefIdSize;
491 Vector<uint32_t> mRefs;
492
493 DISALLOW_EVIL_CONSTRUCTORS(ItemReference);
494};
495
Chong Zhang01a76012018-03-14 18:19:49 -0700496void ItemReference::apply(
497 KeyedVector<uint32_t, ImageItem> &itemIdToItemMap,
498 KeyedVector<uint32_t, ExifItem> &itemIdToExifMap) const {
Chong Zhangecd08132017-10-05 16:09:29 -0700499 ALOGV("attach reference type 0x%x to item id %d)", type(), mItemId);
500
Chong Zhang01a76012018-03-14 18:19:49 -0700501 switch(type()) {
Marco Nelissen51087de2019-01-22 15:39:07 -0800502 case FOURCC("dimg"): {
Chong Zhang01a76012018-03-14 18:19:49 -0700503 ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
504
505 // ignore non-image items
506 if (itemIndex < 0) {
507 return;
508 }
509
Chong Zhangecd08132017-10-05 16:09:29 -0700510 ImageItem &derivedImage = itemIdToItemMap.editValueAt(itemIndex);
511 if (!derivedImage.dimgRefs.empty()) {
Chong Zhang99745d12018-05-15 09:50:52 -0700512 ALOGW("dimgRefs not clean!");
Chong Zhangecd08132017-10-05 16:09:29 -0700513 }
514 derivedImage.dimgRefs.appendVector(mRefs);
Chong Zhangd3e0d862017-10-03 13:17:13 -0700515
516 for (size_t i = 0; i < mRefs.size(); i++) {
517 itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]);
518
519 // ignore non-image items
520 if (itemIndex < 0) {
521 continue;
522 }
523 ImageItem &sourceImage = itemIdToItemMap.editValueAt(itemIndex);
524
525 // mark the source image of the derivation as hidden
526 sourceImage.hidden = true;
527 }
Chong Zhang01a76012018-03-14 18:19:49 -0700528 break;
529 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800530 case FOURCC("thmb"): {
Chong Zhang01a76012018-03-14 18:19:49 -0700531 ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
532
533 // ignore non-image items
534 if (itemIndex < 0) {
535 return;
536 }
537
Chong Zhangd3e0d862017-10-03 13:17:13 -0700538 // mark thumbnail image as hidden, these can be retrieved if the client
539 // request thumbnail explicitly, but won't be exposed as displayables.
540 ImageItem &thumbImage = itemIdToItemMap.editValueAt(itemIndex);
541 thumbImage.hidden = true;
542
Chong Zhangecd08132017-10-05 16:09:29 -0700543 for (size_t i = 0; i < mRefs.size(); i++) {
544 itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]);
545
546 // ignore non-image items
547 if (itemIndex < 0) {
548 continue;
549 }
550 ALOGV("Image item id %d uses thumbnail item id %d", mRefs[i], mItemId);
551 ImageItem &masterImage = itemIdToItemMap.editValueAt(itemIndex);
552 if (!masterImage.thumbnails.empty()) {
553 ALOGW("already has thumbnails!");
554 }
555 masterImage.thumbnails.push_back(mItemId);
556 }
Chong Zhang01a76012018-03-14 18:19:49 -0700557 break;
558 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800559 case FOURCC("cdsc"): {
Chong Zhang01a76012018-03-14 18:19:49 -0700560 ssize_t itemIndex = itemIdToExifMap.indexOfKey(mItemId);
561
562 // ignore non-exif block items
563 if (itemIndex < 0) {
564 return;
565 }
566
567 for (size_t i = 0; i < mRefs.size(); i++) {
568 itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]);
569
570 // ignore non-image items
571 if (itemIndex < 0) {
572 continue;
573 }
574 ALOGV("Image item id %d uses metadata item id %d", mRefs[i], mItemId);
575 ImageItem &image = itemIdToItemMap.editValueAt(itemIndex);
576 image.cdscRefs.push_back(mItemId);
577 }
578 break;
579 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800580 case FOURCC("auxl"): {
Chong Zhang01a76012018-03-14 18:19:49 -0700581 ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
582
583 // ignore non-image items
584 if (itemIndex < 0) {
585 return;
586 }
587
Chong Zhangd3e0d862017-10-03 13:17:13 -0700588 // mark auxiliary image as hidden
589 ImageItem &auxImage = itemIdToItemMap.editValueAt(itemIndex);
590 auxImage.hidden = true;
Chong Zhang01a76012018-03-14 18:19:49 -0700591 break;
592 }
593 default:
Chong Zhangecd08132017-10-05 16:09:29 -0700594 ALOGW("ignoring unsupported ref type 0x%x", type());
595 }
596}
597
Chong Zhangb51ca282017-07-26 16:25:28 -0700598status_t ItemReference::parse(off64_t offset, size_t size) {
599 if (size < mRefIdSize + 2) {
600 return ERROR_MALFORMED;
601 }
602 if (!source()->getUInt32Var(offset, &mItemId, mRefIdSize)) {
603 return ERROR_IO;
604 }
605 offset += mRefIdSize;
606
607 uint16_t count;
608 if (!source()->getUInt16(offset, &count)) {
609 return ERROR_IO;
610 }
611 offset += 2;
612 size -= (mRefIdSize + 2);
613
614 if (size < count * mRefIdSize) {
615 return ERROR_MALFORMED;
616 }
617
618 for (size_t i = 0; i < count; i++) {
619 uint32_t refItemId;
620 if (!source()->getUInt32Var(offset, &refItemId, mRefIdSize)) {
621 return ERROR_IO;
622 }
623 offset += mRefIdSize;
624 mRefs.push_back(refItemId);
625 ALOGV("item id %d: referencing item id %d", mItemId, refItemId);
626 }
627
628 return OK;
629}
630
631struct IrefBox : public FullBox {
Marco Nelissencec44d02018-06-17 22:21:09 -0700632 IrefBox(DataSourceHelper *source, Vector<sp<ItemReference> > *itemRefs) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800633 FullBox(source, FOURCC("iref")), mRefIdSize(0), mItemRefs(itemRefs) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700634
635 status_t parse(off64_t offset, size_t size);
636
637protected:
638 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
639
640private:
641 uint32_t mRefIdSize;
642 Vector<sp<ItemReference> > *mItemRefs;
643};
644
645status_t IrefBox::parse(off64_t offset, size_t size) {
646 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
647 status_t err = parseFullBoxHeader(&offset, &size);
648 if (err != OK) {
649 return err;
650 }
651
652 mRefIdSize = (version() == 0) ? 2 : 4;
653 return parseChunks(offset, size);
654}
655
656status_t IrefBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
657 sp<ItemReference> itemRef = new ItemReference(source(), type, mRefIdSize);
658
659 status_t err = itemRef->parse(offset, size);
660 if (err != OK) {
661 return err;
662 }
663 mItemRefs->push_back(itemRef);
664 return OK;
665}
666
667/////////////////////////////////////////////////////////////////////
668//
669// ItemProperty related boxes
670//
671
672struct AssociationEntry {
673 uint32_t itemId;
674 bool essential;
675 uint16_t index;
676};
677
678struct ItemProperty : public RefBase {
679 ItemProperty() {}
680
681 virtual void attachTo(ImageItem &/*image*/) const {
682 ALOGW("Unrecognized property");
683 }
684 virtual status_t parse(off64_t /*offset*/, size_t /*size*/) {
685 ALOGW("Unrecognized property");
686 return OK;
687 }
688
689private:
690 DISALLOW_EVIL_CONSTRUCTORS(ItemProperty);
691};
692
693struct IspeBox : public FullBox, public ItemProperty {
Marco Nelissencec44d02018-06-17 22:21:09 -0700694 IspeBox(DataSourceHelper *source) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800695 FullBox(source, FOURCC("ispe")), mWidth(0), mHeight(0) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700696
697 status_t parse(off64_t offset, size_t size) override;
698
699 void attachTo(ImageItem &image) const override {
700 image.width = mWidth;
701 image.height = mHeight;
702 }
703
704private:
705 uint32_t mWidth;
706 uint32_t mHeight;
707};
708
709status_t IspeBox::parse(off64_t offset, size_t size) {
710 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
711
712 status_t err = parseFullBoxHeader(&offset, &size);
713 if (err != OK) {
714 return err;
715 }
716
717 if (size < 8) {
718 return ERROR_MALFORMED;
719 }
720 if (!source()->getUInt32(offset, &mWidth)
721 || !source()->getUInt32(offset + 4, &mHeight)) {
722 return ERROR_IO;
723 }
724 ALOGV("property ispe: %dx%d", mWidth, mHeight);
725
726 return OK;
727}
728
729struct HvccBox : public Box, public ItemProperty {
Marco Nelissencec44d02018-06-17 22:21:09 -0700730 HvccBox(DataSourceHelper *source) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800731 Box(source, FOURCC("hvcC")) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700732
733 status_t parse(off64_t offset, size_t size) override;
734
735 void attachTo(ImageItem &image) const override {
736 image.hvcc = mHVCC;
737 }
738
739private:
740 sp<ABuffer> mHVCC;
741};
742
743status_t HvccBox::parse(off64_t offset, size_t size) {
744 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
745
746 mHVCC = new ABuffer(size);
747
748 if (mHVCC->data() == NULL) {
749 ALOGE("b/28471206");
750 return NO_MEMORY;
751 }
752
753 if (source()->readAt(offset, mHVCC->data(), size) < (ssize_t)size) {
754 return ERROR_IO;
755 }
756
757 ALOGV("property hvcC");
758
759 return OK;
760}
761
762struct IrotBox : public Box, public ItemProperty {
Marco Nelissencec44d02018-06-17 22:21:09 -0700763 IrotBox(DataSourceHelper *source) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800764 Box(source, FOURCC("irot")), mAngle(0) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700765
766 status_t parse(off64_t offset, size_t size) override;
767
768 void attachTo(ImageItem &image) const override {
769 image.rotation = mAngle * 90;
770 }
771
772private:
773 uint8_t mAngle;
774};
775
776status_t IrotBox::parse(off64_t offset, size_t size) {
777 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
778
779 if (size < 1) {
780 return ERROR_MALFORMED;
781 }
782 if (source()->readAt(offset, &mAngle, 1) != 1) {
783 return ERROR_IO;
784 }
785 mAngle &= 0x3;
786 ALOGV("property irot: %d", mAngle);
787
788 return OK;
789}
790
791struct ColrBox : public Box, public ItemProperty {
Marco Nelissencec44d02018-06-17 22:21:09 -0700792 ColrBox(DataSourceHelper *source) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800793 Box(source, FOURCC("colr")) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700794
795 status_t parse(off64_t offset, size_t size) override;
796
797 void attachTo(ImageItem &image) const override {
798 image.icc = mICCData;
799 }
800
801private:
802 sp<ABuffer> mICCData;
803};
804
805status_t ColrBox::parse(off64_t offset, size_t size) {
806 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
807
808 if (size < 4) {
809 return ERROR_MALFORMED;
810 }
811 uint32_t colour_type;
812 if (!source()->getUInt32(offset, &colour_type)) {
813 return ERROR_IO;
814 }
815 offset += 4;
816 size -= 4;
Marco Nelissen51087de2019-01-22 15:39:07 -0800817 if (colour_type == FOURCC("nclx")) {
Chong Zhangb51ca282017-07-26 16:25:28 -0700818 return OK;
819 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800820 if ((colour_type != FOURCC("rICC")) &&
821 (colour_type != FOURCC("prof"))) {
Chong Zhangb51ca282017-07-26 16:25:28 -0700822 return ERROR_MALFORMED;
823 }
824
825 mICCData = new ABuffer(size);
826 if (mICCData->data() == NULL) {
827 ALOGE("b/28471206");
828 return NO_MEMORY;
829 }
830
831 if (source()->readAt(offset, mICCData->data(), size) != (ssize_t)size) {
832 return ERROR_IO;
833 }
834
835 ALOGV("property Colr: size %zd", size);
836 return OK;
837}
838
839struct IpmaBox : public FullBox {
Marco Nelissencec44d02018-06-17 22:21:09 -0700840 IpmaBox(DataSourceHelper *source, Vector<AssociationEntry> *associations) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800841 FullBox(source, FOURCC("ipma")), mAssociations(associations) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700842
843 status_t parse(off64_t offset, size_t size);
844private:
845 Vector<AssociationEntry> *mAssociations;
846};
847
848status_t IpmaBox::parse(off64_t offset, size_t size) {
849 status_t err = parseFullBoxHeader(&offset, &size);
850 if (err != OK) {
851 return err;
852 }
853
854 if (size < 4) {
855 return ERROR_MALFORMED;
856 }
857 uint32_t entryCount;
858 if (!source()->getUInt32(offset, &entryCount)) {
859 return ERROR_IO;
860 }
861 offset += 4;
862 size -= 4;
863
864 for (size_t k = 0; k < entryCount; ++k) {
865 uint32_t itemId = 0;
866 size_t itemIdSize = (version() < 1) ? 2 : 4;
867
868 if (size < itemIdSize + 1) {
869 return ERROR_MALFORMED;
870 }
871
872 if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) {
873 return ERROR_IO;
874 }
875 offset += itemIdSize;
876 size -= itemIdSize;
877
878 uint8_t associationCount;
879 if (!source()->readAt(offset, &associationCount, 1)) {
880 return ERROR_IO;
881 }
882 offset++;
883 size--;
884
885 for (size_t i = 0; i < associationCount; ++i) {
886 size_t propIndexSize = (flags() & 1) ? 2 : 1;
887 if (size < propIndexSize) {
888 return ERROR_MALFORMED;
889 }
890 uint16_t propIndex;
891 if (!source()->getUInt16Var(offset, &propIndex, propIndexSize)) {
892 return ERROR_IO;
893 }
894 offset += propIndexSize;
895 size -= propIndexSize;
896 uint16_t bitmask = (1 << (8 * propIndexSize - 1));
897 AssociationEntry entry = {
898 .itemId = itemId,
899 .essential = !!(propIndex & bitmask),
900 .index = (uint16_t) (propIndex & ~bitmask)
901 };
902
903 ALOGV("item id %d associated to property %d (essential %d)",
904 itemId, entry.index, entry.essential);
905
906 mAssociations->push_back(entry);
907 }
908 }
909
910 return OK;
911}
912
913struct IpcoBox : public Box {
Marco Nelissencec44d02018-06-17 22:21:09 -0700914 IpcoBox(DataSourceHelper *source, Vector<sp<ItemProperty> > *properties) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800915 Box(source, FOURCC("ipco")), mItemProperties(properties) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700916
917 status_t parse(off64_t offset, size_t size);
918protected:
919 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
920
921private:
922 Vector<sp<ItemProperty> > *mItemProperties;
923};
924
925status_t IpcoBox::parse(off64_t offset, size_t size) {
926 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
927 // push dummy as the index is 1-based
928 mItemProperties->push_back(new ItemProperty());
929 return parseChunks(offset, size);
930}
931
932status_t IpcoBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
933 sp<ItemProperty> itemProperty;
934 switch(type) {
Marco Nelissen51087de2019-01-22 15:39:07 -0800935 case FOURCC("hvcC"):
Chong Zhangb51ca282017-07-26 16:25:28 -0700936 {
937 itemProperty = new HvccBox(source());
938 break;
939 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800940 case FOURCC("ispe"):
Chong Zhangb51ca282017-07-26 16:25:28 -0700941 {
942 itemProperty = new IspeBox(source());
943 break;
944 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800945 case FOURCC("irot"):
Chong Zhangb51ca282017-07-26 16:25:28 -0700946 {
947 itemProperty = new IrotBox(source());
948 break;
949 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800950 case FOURCC("colr"):
Chong Zhangb51ca282017-07-26 16:25:28 -0700951 {
952 itemProperty = new ColrBox(source());
953 break;
954 }
955 default:
956 {
957 // push dummy to maintain correct item property index
958 itemProperty = new ItemProperty();
959 break;
960 }
961 }
962 status_t err = itemProperty->parse(offset, size);
963 if (err != OK) {
964 return err;
965 }
966 mItemProperties->push_back(itemProperty);
967 return OK;
968}
969
970struct IprpBox : public Box {
Marco Nelissencec44d02018-06-17 22:21:09 -0700971 IprpBox(DataSourceHelper *source,
Chong Zhangb51ca282017-07-26 16:25:28 -0700972 Vector<sp<ItemProperty> > *properties,
973 Vector<AssociationEntry> *associations) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800974 Box(source, FOURCC("iprp")),
Chong Zhangb51ca282017-07-26 16:25:28 -0700975 mProperties(properties), mAssociations(associations) {}
976
977 status_t parse(off64_t offset, size_t size);
978protected:
979 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
980
981private:
982 Vector<sp<ItemProperty> > *mProperties;
983 Vector<AssociationEntry> *mAssociations;
984};
985
986status_t IprpBox::parse(off64_t offset, size_t size) {
987 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
988
989 status_t err = parseChunks(offset, size);
990 if (err != OK) {
991 return err;
992 }
993 return OK;
994}
995
996status_t IprpBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
997 switch(type) {
Marco Nelissen51087de2019-01-22 15:39:07 -0800998 case FOURCC("ipco"):
Chong Zhangb51ca282017-07-26 16:25:28 -0700999 {
1000 IpcoBox ipcoBox(source(), mProperties);
1001 return ipcoBox.parse(offset, size);
1002 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001003 case FOURCC("ipma"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001004 {
1005 IpmaBox ipmaBox(source(), mAssociations);
1006 return ipmaBox.parse(offset, size);
1007 }
1008 default:
1009 {
1010 ALOGW("Unrecognized box.");
1011 break;
1012 }
1013 }
1014 return OK;
1015}
1016
1017/////////////////////////////////////////////////////////////////////
1018//
1019// ItemInfo related boxes
1020//
1021struct ItemInfo {
1022 uint32_t itemId;
1023 uint32_t itemType;
Chong Zhangd3e0d862017-10-03 13:17:13 -07001024 bool hidden;
Chong Zhangb51ca282017-07-26 16:25:28 -07001025};
1026
1027struct InfeBox : public FullBox {
Marco Nelissencec44d02018-06-17 22:21:09 -07001028 InfeBox(DataSourceHelper *source) :
Marco Nelissen51087de2019-01-22 15:39:07 -08001029 FullBox(source, FOURCC("infe")) {}
Chong Zhangb51ca282017-07-26 16:25:28 -07001030
1031 status_t parse(off64_t offset, size_t size, ItemInfo *itemInfo);
1032
1033private:
1034 bool parseNullTerminatedString(off64_t *offset, size_t *size, String8 *out);
1035};
1036
1037bool InfeBox::parseNullTerminatedString(
1038 off64_t *offset, size_t *size, String8 *out) {
Chong Zhang70601852018-03-08 15:23:43 -08001039 char tmp;
1040 Vector<char> buf;
1041 buf.setCapacity(256);
Chong Zhangb51ca282017-07-26 16:25:28 -07001042 off64_t newOffset = *offset;
1043 off64_t stopOffset = *offset + *size;
1044 while (newOffset < stopOffset) {
Chong Zhang70601852018-03-08 15:23:43 -08001045 if (!source()->readAt(newOffset++, &tmp, 1)) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001046 return false;
1047 }
Chong Zhang70601852018-03-08 15:23:43 -08001048 buf.push_back(tmp);
1049 if (tmp == 0) {
1050 out->setTo(buf.array());
Chong Zhangb51ca282017-07-26 16:25:28 -07001051
1052 *offset = newOffset;
1053 *size = stopOffset - newOffset;
1054
1055 return true;
1056 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001057 }
1058 return false;
1059}
1060
1061status_t InfeBox::parse(off64_t offset, size_t size, ItemInfo *itemInfo) {
1062 status_t err = parseFullBoxHeader(&offset, &size);
1063 if (err != OK) {
1064 return err;
1065 }
1066
1067 if (version() == 0 || version() == 1) {
Chong Zhangecd08132017-10-05 16:09:29 -07001068 return ERROR_UNSUPPORTED;
Chong Zhangb51ca282017-07-26 16:25:28 -07001069 } else { // version >= 2
1070 uint32_t item_id;
1071 size_t itemIdSize = (version() == 2) ? 2 : 4;
1072 if (size < itemIdSize + 6) {
1073 return ERROR_MALFORMED;
1074 }
1075 if (!source()->getUInt32Var(offset, &item_id, itemIdSize)) {
1076 return ERROR_IO;
1077 }
1078 ALOGV("item_id %d", item_id);
1079 offset += itemIdSize;
1080 uint16_t item_protection_index;
1081 if (!source()->getUInt16(offset, &item_protection_index)) {
1082 return ERROR_IO;
1083 }
1084 ALOGV("item_protection_index %d", item_protection_index);
1085 offset += 2;
1086 uint32_t item_type;
1087 if (!source()->getUInt32(offset, &item_type)) {
1088 return ERROR_IO;
1089 }
1090
1091 itemInfo->itemId = item_id;
1092 itemInfo->itemType = item_type;
Chong Zhangd3e0d862017-10-03 13:17:13 -07001093 // According to HEIF spec, (flags & 1) indicates the image is hidden
1094 // and not supposed to be displayed.
1095 itemInfo->hidden = (flags() & 1);
Chong Zhangb51ca282017-07-26 16:25:28 -07001096
1097 char itemTypeString[5];
1098 MakeFourCCString(item_type, itemTypeString);
1099 ALOGV("item_type %s", itemTypeString);
1100 offset += 4;
1101 size -= itemIdSize + 6;
1102
1103 String8 item_name;
1104 if (!parseNullTerminatedString(&offset, &size, &item_name)) {
1105 return ERROR_MALFORMED;
1106 }
1107 ALOGV("item_name %s", item_name.c_str());
1108
Marco Nelissen51087de2019-01-22 15:39:07 -08001109 if (item_type == FOURCC("mime")) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001110 String8 content_type;
1111 if (!parseNullTerminatedString(&offset, &size, &content_type)) {
1112 return ERROR_MALFORMED;
1113 }
1114
Ray Essick46c24cd2018-03-08 13:54:19 -08001115 // content_encoding is optional; can be omitted if would be empty
1116 if (size > 0) {
1117 String8 content_encoding;
1118 if (!parseNullTerminatedString(&offset, &size, &content_encoding)) {
1119 return ERROR_MALFORMED;
1120 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001121 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001122 } else if (item_type == FOURCC("uri ")) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001123 String8 item_uri_type;
1124 if (!parseNullTerminatedString(&offset, &size, &item_uri_type)) {
1125 return ERROR_MALFORMED;
1126 }
1127 }
1128 }
1129 return OK;
1130}
1131
1132struct IinfBox : public FullBox {
Marco Nelissencec44d02018-06-17 22:21:09 -07001133 IinfBox(DataSourceHelper *source, Vector<ItemInfo> *itemInfos) :
Chong Zhang866f0f02019-02-01 14:19:37 -08001134 FullBox(source, FOURCC("iinf")), mItemInfos(itemInfos) {}
Chong Zhangb51ca282017-07-26 16:25:28 -07001135
1136 status_t parse(off64_t offset, size_t size);
1137
Chong Zhang866f0f02019-02-01 14:19:37 -08001138 bool hasFourCC(uint32_t type) { return mFourCCSeen.count(type) > 0; }
Chong Zhangb51ca282017-07-26 16:25:28 -07001139
1140protected:
1141 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
1142
1143private:
1144 Vector<ItemInfo> *mItemInfos;
Chong Zhang866f0f02019-02-01 14:19:37 -08001145 std::unordered_set<uint32_t> mFourCCSeen;
Chong Zhangb51ca282017-07-26 16:25:28 -07001146};
1147
1148status_t IinfBox::parse(off64_t offset, size_t size) {
1149 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1150
1151 status_t err = parseFullBoxHeader(&offset, &size);
1152 if (err != OK) {
1153 return err;
1154 }
1155
1156 size_t entryCountSize = version() == 0 ? 2 : 4;
1157 if (size < entryCountSize) {
1158 return ERROR_MALFORMED;
1159 }
1160 uint32_t entry_count;
1161 if (!source()->getUInt32Var(offset, &entry_count, entryCountSize)) {
1162 return ERROR_IO;
1163 }
1164 ALOGV("entry_count %d", entry_count);
1165
1166 off64_t stopOffset = offset + size;
1167 offset += entryCountSize;
1168 for (size_t i = 0; i < entry_count && offset < stopOffset; i++) {
1169 ALOGV("entry %zu", i);
1170 status_t err = parseChunk(&offset);
1171 if (err != OK) {
1172 return err;
1173 }
1174 }
1175 if (offset != stopOffset) {
1176 return ERROR_MALFORMED;
1177 }
1178
1179 return OK;
1180}
1181
1182status_t IinfBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
Marco Nelissen51087de2019-01-22 15:39:07 -08001183 if (type != FOURCC("infe")) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001184 return OK;
1185 }
1186
1187 InfeBox infeBox(source());
1188 ItemInfo itemInfo;
1189 status_t err = infeBox.parse(offset, size, &itemInfo);
Chong Zhangecd08132017-10-05 16:09:29 -07001190 if (err == OK) {
1191 mItemInfos->push_back(itemInfo);
Chong Zhang866f0f02019-02-01 14:19:37 -08001192 mFourCCSeen.insert(itemInfo.itemType);
Chong Zhangb51ca282017-07-26 16:25:28 -07001193 }
Chong Zhang5518a2c2017-10-13 18:31:25 -07001194 // InfeBox parse returns ERROR_UNSUPPORTED if the box if an unsupported
1195 // version. Ignore this error as it's not fatal.
1196 return (err == ERROR_UNSUPPORTED) ? OK : err;
Chong Zhangb51ca282017-07-26 16:25:28 -07001197}
1198
1199//////////////////////////////////////////////////////////////////
1200
Marco Nelissencec44d02018-06-17 22:21:09 -07001201ItemTable::ItemTable(DataSourceHelper *source)
Chong Zhangb51ca282017-07-26 16:25:28 -07001202 : mDataSource(source),
1203 mPrimaryItemId(0),
1204 mIdatOffset(0),
1205 mIdatSize(0),
1206 mImageItemsValid(false),
Chong Zhangecd08132017-10-05 16:09:29 -07001207 mCurrentItemIndex(0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001208 mRequiredBoxes.insert('iprp');
1209 mRequiredBoxes.insert('iloc');
1210 mRequiredBoxes.insert('pitm');
1211 mRequiredBoxes.insert('iinf');
1212}
1213
1214ItemTable::~ItemTable() {}
1215
1216status_t ItemTable::parse(uint32_t type, off64_t data_offset, size_t chunk_data_size) {
1217 switch(type) {
Marco Nelissen51087de2019-01-22 15:39:07 -08001218 case FOURCC("iloc"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001219 {
1220 return parseIlocBox(data_offset, chunk_data_size);
1221 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001222 case FOURCC("iinf"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001223 {
1224 return parseIinfBox(data_offset, chunk_data_size);
1225 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001226 case FOURCC("iprp"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001227 {
1228 return parseIprpBox(data_offset, chunk_data_size);
1229 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001230 case FOURCC("pitm"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001231 {
1232 return parsePitmBox(data_offset, chunk_data_size);
1233 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001234 case FOURCC("idat"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001235 {
1236 return parseIdatBox(data_offset, chunk_data_size);
1237 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001238 case FOURCC("iref"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001239 {
1240 return parseIrefBox(data_offset, chunk_data_size);
1241 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001242 case FOURCC("ipro"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001243 {
1244 ALOGW("ipro box not supported!");
1245 break;
1246 }
1247 default:
1248 {
1249 ALOGW("unrecognized box type: 0x%x", type);
1250 break;
1251 }
1252 }
1253 return ERROR_UNSUPPORTED;
1254}
1255
1256status_t ItemTable::parseIlocBox(off64_t offset, size_t size) {
1257 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1258
1259 IlocBox ilocBox(mDataSource, &mItemLocs);
1260 status_t err = ilocBox.parse(offset, size);
1261 if (err != OK) {
1262 return err;
1263 }
1264
1265 if (ilocBox.hasConstructMethod1()) {
1266 mRequiredBoxes.insert('idat');
1267 }
1268
1269 return buildImageItemsIfPossible('iloc');
1270}
1271
1272status_t ItemTable::parseIinfBox(off64_t offset, size_t size) {
1273 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1274
1275 IinfBox iinfBox(mDataSource, &mItemInfos);
1276 status_t err = iinfBox.parse(offset, size);
1277 if (err != OK) {
1278 return err;
1279 }
1280
Chong Zhang866f0f02019-02-01 14:19:37 -08001281 if (iinfBox.hasFourCC(FOURCC("grid")) || iinfBox.hasFourCC(FOURCC("Exif"))) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001282 mRequiredBoxes.insert('iref');
1283 }
1284
1285 return buildImageItemsIfPossible('iinf');
1286}
1287
1288status_t ItemTable::parsePitmBox(off64_t offset, size_t size) {
1289 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1290
1291 PitmBox pitmBox(mDataSource);
1292 status_t err = pitmBox.parse(offset, size, &mPrimaryItemId);
1293 if (err != OK) {
1294 return err;
1295 }
1296
1297 return buildImageItemsIfPossible('pitm');
1298}
1299
1300status_t ItemTable::parseIprpBox(off64_t offset, size_t size) {
1301 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1302
1303 IprpBox iprpBox(mDataSource, &mItemProperties, &mAssociations);
1304 status_t err = iprpBox.parse(offset, size);
1305 if (err != OK) {
1306 return err;
1307 }
1308
1309 return buildImageItemsIfPossible('iprp');
1310}
1311
1312status_t ItemTable::parseIdatBox(off64_t offset, size_t size) {
1313 ALOGV("%s: idat offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1314
1315 // only remember the offset and size of idat box for later use
1316 mIdatOffset = offset;
1317 mIdatSize = size;
1318
1319 return buildImageItemsIfPossible('idat');
1320}
1321
1322status_t ItemTable::parseIrefBox(off64_t offset, size_t size) {
1323 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1324
1325 IrefBox irefBox(mDataSource, &mItemReferences);
1326 status_t err = irefBox.parse(offset, size);
1327 if (err != OK) {
1328 return err;
1329 }
1330
1331 return buildImageItemsIfPossible('iref');
1332}
1333
1334status_t ItemTable::buildImageItemsIfPossible(uint32_t type) {
1335 if (mImageItemsValid) {
1336 return OK;
1337 }
1338
1339 mBoxesSeen.insert(type);
1340
1341 // need at least 'iprp', 'iloc', 'pitm', 'iinf';
1342 // need 'idat' if any items used construction_method of 2;
1343 // need 'iref' if there are grids.
1344 if (!std::includes(
1345 mBoxesSeen.begin(), mBoxesSeen.end(),
1346 mRequiredBoxes.begin(), mRequiredBoxes.end())) {
1347 return OK;
1348 }
1349
1350 ALOGV("building image table...");
1351
1352 for (size_t i = 0; i < mItemInfos.size(); i++) {
1353 const ItemInfo &info = mItemInfos[i];
1354
Chong Zhang01a76012018-03-14 18:19:49 -07001355 // Only handle 3 types of items, all others are ignored:
1356 // 'grid': derived image from tiles
1357 // 'hvc1': coded image (or tile)
1358 // 'Exif': EXIF metadata
Marco Nelissen51087de2019-01-22 15:39:07 -08001359 if (info.itemType != FOURCC("grid") &&
1360 info.itemType != FOURCC("hvc1") &&
1361 info.itemType != FOURCC("Exif")) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001362 continue;
1363 }
1364
Chong Zhangecd08132017-10-05 16:09:29 -07001365 ssize_t itemIndex = mItemIdToItemMap.indexOfKey(info.itemId);
1366 if (itemIndex >= 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001367 ALOGW("ignoring duplicate image item id %d", info.itemId);
1368 continue;
1369 }
1370
1371 ssize_t ilocIndex = mItemLocs.indexOfKey(info.itemId);
1372 if (ilocIndex < 0) {
1373 ALOGE("iloc missing for image item id %d", info.itemId);
1374 continue;
1375 }
1376 const ItemLoc &iloc = mItemLocs[ilocIndex];
1377
1378 off64_t offset;
1379 size_t size;
1380 if (iloc.getLoc(&offset, &size, mIdatOffset, mIdatSize) != OK) {
1381 return ERROR_MALFORMED;
1382 }
1383
Marco Nelissen51087de2019-01-22 15:39:07 -08001384 if (info.itemType == FOURCC("Exif")) {
Chong Zhang01a76012018-03-14 18:19:49 -07001385 // Only add if the Exif data is non-empty. The first 4 bytes contain
1386 // the offset to TIFF header, which the Exif parser doesn't use.
1387 if (size > 4) {
1388 ExifItem exifItem = {
1389 .offset = offset,
1390 .size = size,
1391 };
1392 mItemIdToExifMap.add(info.itemId, exifItem);
1393 }
1394 continue;
1395 }
1396
Chong Zhangd3e0d862017-10-03 13:17:13 -07001397 ImageItem image(info.itemType, info.itemId, info.hidden);
Chong Zhangb51ca282017-07-26 16:25:28 -07001398
1399 ALOGV("adding %s: itemId %d", image.isGrid() ? "grid" : "image", info.itemId);
1400
1401 if (image.isGrid()) {
Chong Zhanga9879ef2018-05-10 12:32:50 -07001402 // ImageGrid struct is at least 8-byte, at most 12-byte (if flags&1)
1403 if (size < 8 || size > 12) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001404 return ERROR_MALFORMED;
1405 }
1406 uint8_t buf[12];
1407 if (!mDataSource->readAt(offset, buf, size)) {
1408 return ERROR_IO;
1409 }
1410
1411 image.rows = buf[2] + 1;
1412 image.columns = buf[3] + 1;
1413
1414 ALOGV("rows %d, columans %d", image.rows, image.columns);
1415 } else {
1416 image.offset = offset;
1417 image.size = size;
1418 }
Chong Zhangecd08132017-10-05 16:09:29 -07001419 mItemIdToItemMap.add(info.itemId, image);
Chong Zhangb51ca282017-07-26 16:25:28 -07001420 }
1421
1422 for (size_t i = 0; i < mAssociations.size(); i++) {
1423 attachProperty(mAssociations[i]);
1424 }
1425
1426 for (size_t i = 0; i < mItemReferences.size(); i++) {
Chong Zhang01a76012018-03-14 18:19:49 -07001427 mItemReferences[i]->apply(mItemIdToItemMap, mItemIdToExifMap);
Chong Zhangb51ca282017-07-26 16:25:28 -07001428 }
1429
Chong Zhangd3e0d862017-10-03 13:17:13 -07001430 bool foundPrimary = false;
1431 for (size_t i = 0; i < mItemIdToItemMap.size(); i++) {
1432 // add all non-hidden images, also add the primary even if it's marked
1433 // hidden, in case the primary is set to a thumbnail
1434 bool isPrimary = (mItemIdToItemMap[i].itemId == mPrimaryItemId);
1435 if (!mItemIdToItemMap[i].hidden || isPrimary) {
1436 mDisplayables.push_back(i);
1437 }
1438 foundPrimary |= isPrimary;
1439 }
1440
1441 ALOGV("found %zu displayables", mDisplayables.size());
1442
1443 // fail if no displayables are found
1444 if (mDisplayables.empty()) {
1445 return ERROR_MALFORMED;
1446 }
1447
1448 // if the primary item id is invalid, set primary to the first displayable
1449 if (!foundPrimary) {
1450 mPrimaryItemId = mItemIdToItemMap[mDisplayables[0]].itemId;
1451 }
1452
Chong Zhangb51ca282017-07-26 16:25:28 -07001453 mImageItemsValid = true;
1454 return OK;
1455}
1456
1457void ItemTable::attachProperty(const AssociationEntry &association) {
Chong Zhangecd08132017-10-05 16:09:29 -07001458 ssize_t itemIndex = mItemIdToItemMap.indexOfKey(association.itemId);
Chong Zhangb51ca282017-07-26 16:25:28 -07001459
1460 // ignore non-image items
Chong Zhangecd08132017-10-05 16:09:29 -07001461 if (itemIndex < 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001462 return;
1463 }
1464
1465 uint16_t propertyIndex = association.index;
1466 if (propertyIndex >= mItemProperties.size()) {
1467 ALOGW("Ignoring invalid property index %d", propertyIndex);
1468 return;
1469 }
1470
1471 ALOGV("attach property %d to item id %d)",
1472 propertyIndex, association.itemId);
1473
Chong Zhangd3e0d862017-10-03 13:17:13 -07001474 mItemProperties[propertyIndex]->attachTo(mItemIdToItemMap.editValueAt(itemIndex));
Chong Zhangb51ca282017-07-26 16:25:28 -07001475}
1476
Chong Zhangd3e0d862017-10-03 13:17:13 -07001477uint32_t ItemTable::countImages() const {
1478 return mImageItemsValid ? mDisplayables.size() : 0;
1479}
1480
Marco Nelissendba610b2018-11-01 16:07:03 -07001481AMediaFormat *ItemTable::getImageMeta(const uint32_t imageIndex) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001482 if (!mImageItemsValid) {
1483 return NULL;
1484 }
1485
Chong Zhangd3e0d862017-10-03 13:17:13 -07001486 if (imageIndex >= mDisplayables.size()) {
1487 ALOGE("%s: invalid image index %u", __FUNCTION__, imageIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001488 return NULL;
1489 }
Chong Zhangd3e0d862017-10-03 13:17:13 -07001490 const uint32_t itemIndex = mDisplayables[imageIndex];
1491 ALOGV("image[%u]: item index %u", imageIndex, itemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001492
Chong Zhangecd08132017-10-05 16:09:29 -07001493 const ImageItem *image = &mItemIdToItemMap[itemIndex];
Chong Zhangb51ca282017-07-26 16:25:28 -07001494
Chong Zhang99745d12018-05-15 09:50:52 -07001495 ssize_t tileItemIndex = -1;
1496 if (image->isGrid()) {
1497 if (image->dimgRefs.empty()) {
1498 return NULL;
1499 }
1500 tileItemIndex = mItemIdToItemMap.indexOfKey(image->dimgRefs[0]);
1501 if (tileItemIndex < 0) {
1502 return NULL;
1503 }
1504 }
1505
Marco Nelissendba610b2018-11-01 16:07:03 -07001506 AMediaFormat *meta = AMediaFormat_new();
1507 AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
Chong Zhangb51ca282017-07-26 16:25:28 -07001508
Chong Zhangd3e0d862017-10-03 13:17:13 -07001509 if (image->itemId == mPrimaryItemId) {
Marco Nelissendba610b2018-11-01 16:07:03 -07001510 AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_DEFAULT, 1);
Chong Zhangd3e0d862017-10-03 13:17:13 -07001511 }
1512
1513 ALOGV("image[%u]: size %dx%d", imageIndex, image->width, image->height);
1514
Marco Nelissendba610b2018-11-01 16:07:03 -07001515 AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_WIDTH, image->width);
1516 AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_HEIGHT, image->height);
Chong Zhangb51ca282017-07-26 16:25:28 -07001517 if (image->rotation != 0) {
Chong Zhang686c23b2017-10-05 15:16:59 -07001518 // Rotation angle in HEIF is CCW, convert to CW here to be
1519 // consistent with the other media formats.
1520 switch(image->rotation) {
Marco Nelissendba610b2018-11-01 16:07:03 -07001521 case 90:
1522 case 180:
1523 case 270:
1524 AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_ROTATION, 360 - image->rotation);
1525 break;
Chong Zhang686c23b2017-10-05 15:16:59 -07001526 default: break; // don't set if invalid
1527 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001528 }
Marco Nelissendba610b2018-11-01 16:07:03 -07001529 AMediaFormat_setInt32(meta,
1530 AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 1.5);
Chong Zhangb51ca282017-07-26 16:25:28 -07001531
1532 if (!image->thumbnails.empty()) {
Chong Zhangecd08132017-10-05 16:09:29 -07001533 ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(image->thumbnails[0]);
1534 if (thumbItemIndex >= 0) {
1535 const ImageItem &thumbnail = mItemIdToItemMap[thumbItemIndex];
Chong Zhangb51ca282017-07-26 16:25:28 -07001536
Dongwon Kangc0fa2712018-06-20 16:39:58 -07001537 if (thumbnail.hvcc != NULL) {
Marco Nelissendba610b2018-11-01 16:07:03 -07001538 AMediaFormat_setInt32(meta,
1539 AMEDIAFORMAT_KEY_THUMBNAIL_WIDTH, thumbnail.width);
1540 AMediaFormat_setInt32(meta,
1541 AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT, thumbnail.height);
1542 AMediaFormat_setBuffer(meta,
1543 AMEDIAFORMAT_KEY_CSD_HEVC, thumbnail.hvcc->data(), thumbnail.hvcc->size());
Dongwon Kangc0fa2712018-06-20 16:39:58 -07001544 ALOGV("image[%u]: thumbnail: size %dx%d, item index %zd",
1545 imageIndex, thumbnail.width, thumbnail.height, thumbItemIndex);
1546 } else {
1547 ALOGW("%s: thumbnail data is missing for image[%u]!", __FUNCTION__, imageIndex);
1548 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001549 } else {
Chong Zhangecd08132017-10-05 16:09:29 -07001550 ALOGW("%s: Referenced thumbnail does not exist!", __FUNCTION__);
Chong Zhangb51ca282017-07-26 16:25:28 -07001551 }
1552 }
1553
1554 if (image->isGrid()) {
Marco Nelissendba610b2018-11-01 16:07:03 -07001555 AMediaFormat_setInt32(meta,
1556 AMEDIAFORMAT_KEY_GRID_ROWS, image->rows);
1557 AMediaFormat_setInt32(meta,
1558 AMEDIAFORMAT_KEY_GRID_COLUMNS, image->columns);
Chong Zhangee079fe2017-08-23 13:51:17 -07001559 // point image to the first tile for grid size and HVCC
Chong Zhangecd08132017-10-05 16:09:29 -07001560 image = &mItemIdToItemMap.editValueAt(tileItemIndex);
Marco Nelissendba610b2018-11-01 16:07:03 -07001561 AMediaFormat_setInt32(meta,
1562 AMEDIAFORMAT_KEY_TILE_WIDTH, image->width);
1563 AMediaFormat_setInt32(meta,
1564 AMEDIAFORMAT_KEY_TILE_HEIGHT, image->height);
1565 AMediaFormat_setInt32(meta,
1566 AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 1.5);
Chong Zhangb51ca282017-07-26 16:25:28 -07001567 }
1568
1569 if (image->hvcc == NULL) {
Chong Zhangd3e0d862017-10-03 13:17:13 -07001570 ALOGE("%s: hvcc is missing for image[%u]!", __FUNCTION__, imageIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001571 return NULL;
1572 }
Marco Nelissendba610b2018-11-01 16:07:03 -07001573 AMediaFormat_setBuffer(meta,
1574 AMEDIAFORMAT_KEY_CSD_HEVC, image->hvcc->data(), image->hvcc->size());
Chong Zhangb51ca282017-07-26 16:25:28 -07001575
1576 if (image->icc != NULL) {
Marco Nelissendba610b2018-11-01 16:07:03 -07001577 AMediaFormat_setBuffer(meta,
1578 AMEDIAFORMAT_KEY_ICC_PROFILE, image->icc->data(), image->icc->size());
Chong Zhangb51ca282017-07-26 16:25:28 -07001579 }
1580 return meta;
1581}
1582
Chong Zhangd3e0d862017-10-03 13:17:13 -07001583status_t ItemTable::findImageItem(const uint32_t imageIndex, uint32_t *itemIndex) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001584 if (!mImageItemsValid) {
1585 return INVALID_OPERATION;
1586 }
1587
Chong Zhangd3e0d862017-10-03 13:17:13 -07001588 if (imageIndex >= mDisplayables.size()) {
1589 ALOGE("%s: invalid image index %d", __FUNCTION__, imageIndex);
1590 return BAD_VALUE;
Chong Zhangb51ca282017-07-26 16:25:28 -07001591 }
1592
Chong Zhangd3e0d862017-10-03 13:17:13 -07001593 *itemIndex = mDisplayables[imageIndex];
1594
1595 ALOGV("image[%u]: item index %u", imageIndex, *itemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001596 return OK;
1597}
1598
Chong Zhangd3e0d862017-10-03 13:17:13 -07001599status_t ItemTable::findThumbnailItem(const uint32_t imageIndex, uint32_t *itemIndex) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001600 if (!mImageItemsValid) {
1601 return INVALID_OPERATION;
1602 }
1603
Chong Zhangd3e0d862017-10-03 13:17:13 -07001604 if (imageIndex >= mDisplayables.size()) {
1605 ALOGE("%s: invalid image index %d", __FUNCTION__, imageIndex);
1606 return BAD_VALUE;
Chong Zhangb51ca282017-07-26 16:25:28 -07001607 }
1608
Chong Zhangd3e0d862017-10-03 13:17:13 -07001609 uint32_t masterItemIndex = mDisplayables[imageIndex];
1610
1611 const ImageItem &masterImage = mItemIdToItemMap[masterItemIndex];
1612 if (masterImage.thumbnails.empty()) {
1613 *itemIndex = masterItemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001614 return OK;
1615 }
1616
Chong Zhangd3e0d862017-10-03 13:17:13 -07001617 ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(masterImage.thumbnails[0]);
Chong Zhangecd08132017-10-05 16:09:29 -07001618 if (thumbItemIndex < 0) {
Chong Zhangd5fa3572018-04-09 19:03:10 -07001619 // Do not return the master image in this case, fail it so that the
1620 // thumbnail extraction code knows we really don't have it.
1621 return INVALID_OPERATION;
Chong Zhangb51ca282017-07-26 16:25:28 -07001622 }
1623
Chong Zhangecd08132017-10-05 16:09:29 -07001624 *itemIndex = thumbItemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001625 return OK;
1626}
1627
1628status_t ItemTable::getImageOffsetAndSize(
Chong Zhangecd08132017-10-05 16:09:29 -07001629 uint32_t *itemIndex, off64_t *offset, size_t *size) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001630 if (!mImageItemsValid) {
1631 return INVALID_OPERATION;
1632 }
1633
Chong Zhangecd08132017-10-05 16:09:29 -07001634 if (itemIndex != NULL) {
1635 if (*itemIndex >= mItemIdToItemMap.size()) {
1636 ALOGE("%s: Bad item index!", __FUNCTION__);
Chong Zhangb51ca282017-07-26 16:25:28 -07001637 return BAD_VALUE;
1638 }
Chong Zhangecd08132017-10-05 16:09:29 -07001639 mCurrentItemIndex = *itemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001640 }
1641
Chong Zhangecd08132017-10-05 16:09:29 -07001642 ImageItem &image = mItemIdToItemMap.editValueAt(mCurrentItemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001643 if (image.isGrid()) {
1644 uint32_t tileItemId;
Chong Zhangecd08132017-10-05 16:09:29 -07001645 status_t err = image.getNextTileItemId(&tileItemId, itemIndex != NULL);
Chong Zhangb51ca282017-07-26 16:25:28 -07001646 if (err != OK) {
1647 return err;
1648 }
Chong Zhangecd08132017-10-05 16:09:29 -07001649 ssize_t tileItemIndex = mItemIdToItemMap.indexOfKey(tileItemId);
1650 if (tileItemIndex < 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001651 return ERROR_END_OF_STREAM;
1652 }
Chong Zhangecd08132017-10-05 16:09:29 -07001653 *offset = mItemIdToItemMap[tileItemIndex].offset;
1654 *size = mItemIdToItemMap[tileItemIndex].size;
Chong Zhangb51ca282017-07-26 16:25:28 -07001655 } else {
Chong Zhangecd08132017-10-05 16:09:29 -07001656 if (itemIndex == NULL) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001657 // For single images, we only allow it to be read once, after that
Chong Zhangecd08132017-10-05 16:09:29 -07001658 // it's EOS. New item index must be requested each time.
Chong Zhangb51ca282017-07-26 16:25:28 -07001659 return ERROR_END_OF_STREAM;
1660 }
Chong Zhangecd08132017-10-05 16:09:29 -07001661 *offset = mItemIdToItemMap[mCurrentItemIndex].offset;
1662 *size = mItemIdToItemMap[mCurrentItemIndex].size;
Chong Zhangb51ca282017-07-26 16:25:28 -07001663 }
1664
1665 return OK;
1666}
1667
Chong Zhang01a76012018-03-14 18:19:49 -07001668status_t ItemTable::getExifOffsetAndSize(off64_t *offset, size_t *size) {
1669 if (!mImageItemsValid) {
1670 return INVALID_OPERATION;
1671 }
1672
1673 ssize_t itemIndex = mItemIdToItemMap.indexOfKey(mPrimaryItemId);
1674
1675 // this should not happen, something's seriously wrong.
1676 if (itemIndex < 0) {
1677 return INVALID_OPERATION;
1678 }
1679
1680 const ImageItem &image = mItemIdToItemMap[itemIndex];
1681 if (image.cdscRefs.size() == 0) {
1682 return NAME_NOT_FOUND;
1683 }
1684
1685 ssize_t exifIndex = mItemIdToExifMap.indexOfKey(image.cdscRefs[0]);
1686 if (exifIndex < 0) {
1687 return NAME_NOT_FOUND;
1688 }
1689
1690 // skip the first 4-byte of the offset to TIFF header
Chong Zhang72500a12019-01-11 18:05:12 -08001691 uint32_t tiffOffset;
1692 if (!mDataSource->readAt(
1693 mItemIdToExifMap[exifIndex].offset, &tiffOffset, 4)) {
1694 return ERROR_IO;
1695 }
1696
1697 // We need 'Exif\0\0' before the tiff header
1698 tiffOffset = ntohl(tiffOffset);
1699 if (tiffOffset < 6) {
1700 return ERROR_MALFORMED;
1701 }
1702 // The first 4-byte of the item is the offset of the tiff header within the
1703 // exif data. The size of the item should be > 4 for a non-empty exif (this
1704 // was already checked when the item was added). Also check that the tiff
1705 // header offset is valid.
1706 if (mItemIdToExifMap[exifIndex].size <= 4 ||
1707 tiffOffset > mItemIdToExifMap[exifIndex].size - 4) {
1708 return ERROR_MALFORMED;
1709 }
1710
1711 // Offset of 'Exif\0\0' relative to the beginning of 'Exif' item
1712 // (first 4-byte is the tiff header offset)
1713 uint32_t exifOffset = 4 + tiffOffset - 6;
1714 *offset = mItemIdToExifMap[exifIndex].offset + exifOffset;
1715 *size = mItemIdToExifMap[exifIndex].size - exifOffset;
Chong Zhang01a76012018-03-14 18:19:49 -07001716 return OK;
1717}
1718
Chong Zhangb51ca282017-07-26 16:25:28 -07001719} // namespace heif
1720
1721} // namespace android