blob: 0b1cfa094d321d14ffdd45f780a30d0100e8ad29 [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;
Vignesh Venkatasubramanianf89bf3c2020-10-05 17:28:45 -070079 sp<ABuffer> av1c;
Chong Zhangb51ca282017-07-26 16:25:28 -070080
81 Vector<uint32_t> thumbnails;
82 Vector<uint32_t> dimgRefs;
Chong Zhang01a76012018-03-14 18:19:49 -070083 Vector<uint32_t> cdscRefs;
Chong Zhangb51ca282017-07-26 16:25:28 -070084 size_t nextTileIndex;
85};
86
Chong Zhang01a76012018-03-14 18:19:49 -070087struct ExifItem {
88 off64_t offset;
89 size_t size;
90};
Chong Zhangb51ca282017-07-26 16:25:28 -070091
92/////////////////////////////////////////////////////////////////////
93//
94// ISO boxes
95//
96
97struct Box {
98protected:
Marco Nelissencec44d02018-06-17 22:21:09 -070099 Box(DataSourceHelper *source, uint32_t type) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700100 mDataSource(source), mType(type) {}
101
102 virtual ~Box() {}
103
104 virtual status_t onChunkData(
105 uint32_t /*type*/, off64_t /*offset*/, size_t /*size*/) {
106 return OK;
107 }
108
109 inline uint32_t type() const { return mType; }
110
Marco Nelissencec44d02018-06-17 22:21:09 -0700111 inline DataSourceHelper *source() const { return mDataSource; }
Chong Zhangb51ca282017-07-26 16:25:28 -0700112
113 status_t parseChunk(off64_t *offset);
114
115 status_t parseChunks(off64_t offset, size_t size);
116
117private:
Marco Nelissencec44d02018-06-17 22:21:09 -0700118 DataSourceHelper *mDataSource;
Chong Zhangb51ca282017-07-26 16:25:28 -0700119 uint32_t mType;
120};
121
122status_t Box::parseChunk(off64_t *offset) {
123 if (*offset < 0) {
124 ALOGE("b/23540914");
125 return ERROR_MALFORMED;
126 }
127 uint32_t hdr[2];
128 if (mDataSource->readAt(*offset, hdr, 8) < 8) {
129 return ERROR_IO;
130 }
131 uint64_t chunk_size = ntohl(hdr[0]);
132 int32_t chunk_type = ntohl(hdr[1]);
133 off64_t data_offset = *offset + 8;
134
135 if (chunk_size == 1) {
136 if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) {
137 return ERROR_IO;
138 }
139 chunk_size = ntoh64(chunk_size);
140 data_offset += 8;
141
142 if (chunk_size < 16) {
143 // The smallest valid chunk is 16 bytes long in this case.
144 return ERROR_MALFORMED;
145 }
146 } else if (chunk_size == 0) {
147 // This shouldn't happen since we should never be top level
148 ALOGE("invalid chunk size 0 for non-top level box");
149 return ERROR_MALFORMED;
150 } else if (chunk_size < 8) {
151 // The smallest valid chunk is 8 bytes long.
152 ALOGE("invalid chunk size: %lld", (long long)chunk_size);
153 return ERROR_MALFORMED;
154 }
155
156 char chunk[5];
157 MakeFourCCString(chunk_type, chunk);
158 ALOGV("chunk: %s @ %lld", chunk, (long long)*offset);
159
160 off64_t chunk_data_size = chunk_size - (data_offset - *offset);
161 if (chunk_data_size < 0) {
162 ALOGE("b/23540914");
163 return ERROR_MALFORMED;
164 }
165
166 status_t err = onChunkData(chunk_type, data_offset, chunk_data_size);
167
168 if (err != OK) {
169 return err;
170 }
171 *offset += chunk_size;
172 return OK;
173}
174
175status_t Box::parseChunks(off64_t offset, size_t size) {
176 off64_t stopOffset = offset + size;
177 while (offset < stopOffset) {
178 status_t err = parseChunk(&offset);
179 if (err != OK) {
180 return err;
181 }
182 }
183 if (offset != stopOffset) {
184 return ERROR_MALFORMED;
185 }
186 return OK;
187}
188
189///////////////////////////////////////////////////////////////////////
190
191struct FullBox : public Box {
192protected:
Marco Nelissencec44d02018-06-17 22:21:09 -0700193 FullBox(DataSourceHelper *source, uint32_t type) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700194 Box(source, type), mVersion(0), mFlags(0) {}
195
196 inline uint8_t version() const { return mVersion; }
197
198 inline uint32_t flags() const { return mFlags; }
199
200 status_t parseFullBoxHeader(off64_t *offset, size_t *size);
201
202private:
203 uint8_t mVersion;
204 uint32_t mFlags;
205};
206
207status_t FullBox::parseFullBoxHeader(off64_t *offset, size_t *size) {
208 if (*size < 4) {
209 return ERROR_MALFORMED;
210 }
211 if (!source()->readAt(*offset, &mVersion, 1)) {
212 return ERROR_IO;
213 }
214 if (!source()->getUInt24(*offset + 1, &mFlags)) {
215 return ERROR_IO;
216 }
217 *offset += 4;
218 *size -= 4;
219 return OK;
220}
221
222/////////////////////////////////////////////////////////////////////
223//
224// PrimaryImage box
225//
226
227struct PitmBox : public FullBox {
Marco Nelissencec44d02018-06-17 22:21:09 -0700228 PitmBox(DataSourceHelper *source) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800229 FullBox(source, FOURCC("pitm")) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700230
231 status_t parse(off64_t offset, size_t size, uint32_t *primaryItemId);
232};
233
234status_t PitmBox::parse(off64_t offset, size_t size, uint32_t *primaryItemId) {
235 status_t err = parseFullBoxHeader(&offset, &size);
236 if (err != OK) {
237 return err;
238 }
239
240 size_t itemIdSize = (version() == 0) ? 2 : 4;
241 if (size < itemIdSize) {
242 return ERROR_MALFORMED;
243 }
244 uint32_t itemId;
245 if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) {
246 return ERROR_IO;
247 }
248
249 ALOGV("primary id %d", itemId);
250 *primaryItemId = itemId;
251
252 return OK;
253}
254
255/////////////////////////////////////////////////////////////////////
256//
257// ItemLocation related boxes
258//
259
260struct ExtentEntry {
261 uint64_t extentIndex;
262 uint64_t extentOffset;
263 uint64_t extentLength;
264};
265
266struct ItemLoc {
267 ItemLoc() : ItemLoc(0, 0, 0, 0) {}
268 ItemLoc(uint32_t item_id, uint16_t construction_method,
269 uint16_t data_reference_index, uint64_t base_offset) :
270 itemId(item_id),
271 constructionMethod(construction_method),
272 dataReferenceIndex(data_reference_index),
273 baseOffset(base_offset) {}
274
275 void addExtent(const ExtentEntry& extent) {
276 extents.push_back(extent);
277 }
278
279 status_t getLoc(off64_t *offset, size_t *size,
280 off64_t idatOffset, size_t idatSize) const {
281 // TODO: fix extent handling, fix constructionMethod = 2
282 CHECK(extents.size() == 1);
283 if (constructionMethod == 0) {
284 *offset = baseOffset + extents[0].extentOffset;
285 *size = extents[0].extentLength;
286 return OK;
287 } else if (constructionMethod == 1) {
288 if (baseOffset + extents[0].extentOffset + extents[0].extentLength
289 > idatSize) {
290 return ERROR_MALFORMED;
291 }
292 *offset = baseOffset + extents[0].extentOffset + idatOffset;
293 *size = extents[0].extentLength;
294 return OK;
295 }
296 return ERROR_UNSUPPORTED;
297 }
298
299 // parsed info
300 uint32_t itemId;
301 uint16_t constructionMethod;
302 uint16_t dataReferenceIndex;
303 off64_t baseOffset;
304 Vector<ExtentEntry> extents;
305};
306
307struct IlocBox : public FullBox {
Marco Nelissencec44d02018-06-17 22:21:09 -0700308 IlocBox(DataSourceHelper *source, KeyedVector<uint32_t, ItemLoc> *itemLocs) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800309 FullBox(source, FOURCC("iloc")),
Chong Zhangb51ca282017-07-26 16:25:28 -0700310 mItemLocs(itemLocs), mHasConstructMethod1(false) {}
311
312 status_t parse(off64_t offset, size_t size);
313
314 bool hasConstructMethod1() { return mHasConstructMethod1; }
315
316private:
317 static bool isSizeFieldValid(uint32_t offset_size) {
318 return offset_size == 0 || offset_size == 4 || offset_size == 8;
319 }
320 KeyedVector<uint32_t, ItemLoc> *mItemLocs;
321 bool mHasConstructMethod1;
322};
323
324status_t IlocBox::parse(off64_t offset, size_t size) {
325 status_t err = parseFullBoxHeader(&offset, &size);
326 if (err != OK) {
327 return err;
328 }
329 if (version() > 2) {
330 ALOGE("%s: invalid version %d", __FUNCTION__, version());
331 return ERROR_MALFORMED;
332 }
333
334 if (size < 2) {
335 return ERROR_MALFORMED;
336 }
337 uint8_t offset_size;
338 if (!source()->readAt(offset++, &offset_size, 1)) {
339 return ERROR_IO;
340 }
341 uint8_t length_size = (offset_size & 0xF);
342 offset_size >>= 4;
343
344 uint8_t base_offset_size;
345 if (!source()->readAt(offset++, &base_offset_size, 1)) {
346 return ERROR_IO;
347 }
348 uint8_t index_size = 0;
349 if (version() == 1 || version() == 2) {
350 index_size = (base_offset_size & 0xF);
351 }
352 base_offset_size >>= 4;
353 size -= 2;
354
355 if (!isSizeFieldValid(offset_size)
356 || !isSizeFieldValid(length_size)
357 || !isSizeFieldValid(base_offset_size)
358 || !isSizeFieldValid((index_size))) {
359 ALOGE("%s: offset size not valid: %d, %d, %d, %d", __FUNCTION__,
360 offset_size, length_size, base_offset_size, index_size);
361 return ERROR_MALFORMED;
362 }
363
364 uint32_t item_count;
365 size_t itemFieldSize = version() < 2 ? 2 : 4;
366 if (size < itemFieldSize) {
367 return ERROR_MALFORMED;
368 }
369 if (!source()->getUInt32Var(offset, &item_count, itemFieldSize)) {
370 return ERROR_IO;
371 }
372
373 ALOGV("item_count %lld", (long long) item_count);
374 offset += itemFieldSize;
375 size -= itemFieldSize;
376
377 for (size_t i = 0; i < item_count; i++) {
378 uint32_t item_id;
379 if (!source()->getUInt32Var(offset, &item_id, itemFieldSize)) {
380 return ERROR_IO;
381 }
382 ALOGV("item[%zu]: id %lld", i, (long long)item_id);
383 offset += itemFieldSize;
384
385 uint8_t construction_method = 0;
386 if (version() == 1 || version() == 2) {
387 uint8_t buf[2];
388 if (!source()->readAt(offset, buf, 2)) {
389 return ERROR_IO;
390 }
391 construction_method = (buf[1] & 0xF);
392 ALOGV("construction_method %d", construction_method);
393 if (construction_method == 1) {
394 mHasConstructMethod1 = true;
395 }
396
397 offset += 2;
398 }
399
400 uint16_t data_reference_index;
401 if (!source()->getUInt16(offset, &data_reference_index)) {
402 return ERROR_IO;
403 }
404 ALOGV("data_reference_index %d", data_reference_index);
405 if (data_reference_index != 0) {
406 // we don't support reference to other files
407 return ERROR_UNSUPPORTED;
408 }
409 offset += 2;
410
411 uint64_t base_offset = 0;
412 if (base_offset_size != 0) {
413 if (!source()->getUInt64Var(offset, &base_offset, base_offset_size)) {
414 return ERROR_IO;
415 }
416 offset += base_offset_size;
417 }
418 ALOGV("base_offset %lld", (long long) base_offset);
419
420 ssize_t index = mItemLocs->add(item_id, ItemLoc(
421 item_id, construction_method, data_reference_index, base_offset));
422 ItemLoc &item = mItemLocs->editValueAt(index);
423
424 uint16_t extent_count;
425 if (!source()->getUInt16(offset, &extent_count)) {
426 return ERROR_IO;
427 }
428 ALOGV("extent_count %d", extent_count);
429
Dongwon Kang69b6ab32019-05-08 17:11:39 -0700430 if (extent_count > 1) {
431 return ERROR_UNSUPPORTED;
Chong Zhangb51ca282017-07-26 16:25:28 -0700432 }
433 offset += 2;
434
435 for (size_t j = 0; j < extent_count; j++) {
436 uint64_t extent_index = 1; // default=1
437 if ((version() == 1 || version() == 2) && (index_size > 0)) {
438 if (!source()->getUInt64Var(offset, &extent_index, index_size)) {
439 return ERROR_IO;
440 }
441 // TODO: add support for this mode
442 offset += index_size;
443 ALOGV("extent_index %lld", (long long)extent_index);
444 }
445
446 uint64_t extent_offset = 0; // default=0
447 if (offset_size > 0) {
448 if (!source()->getUInt64Var(offset, &extent_offset, offset_size)) {
449 return ERROR_IO;
450 }
451 offset += offset_size;
452 }
453 ALOGV("extent_offset %lld", (long long)extent_offset);
454
455 uint64_t extent_length = 0; // this indicates full length of file
456 if (length_size > 0) {
457 if (!source()->getUInt64Var(offset, &extent_length, length_size)) {
458 return ERROR_IO;
459 }
460 offset += length_size;
461 }
462 ALOGV("extent_length %lld", (long long)extent_length);
463
464 item.addExtent({ extent_index, extent_offset, extent_length });
465 }
466 }
467 return OK;
468}
469
470/////////////////////////////////////////////////////////////////////
471//
472// ItemReference related boxes
473//
474
475struct ItemReference : public Box, public RefBase {
Marco Nelissencec44d02018-06-17 22:21:09 -0700476 ItemReference(DataSourceHelper *source, uint32_t type, uint32_t itemIdSize) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700477 Box(source, type), mItemId(0), mRefIdSize(itemIdSize) {}
478
479 status_t parse(off64_t offset, size_t size);
480
481 uint32_t itemId() { return mItemId; }
482
Chong Zhang01a76012018-03-14 18:19:49 -0700483 void apply(
484 KeyedVector<uint32_t, ImageItem> &itemIdToItemMap,
485 KeyedVector<uint32_t, ExifItem> &itemIdToExifMap) const;
Chong Zhangb51ca282017-07-26 16:25:28 -0700486
487private:
488 uint32_t mItemId;
489 uint32_t mRefIdSize;
490 Vector<uint32_t> mRefs;
491
492 DISALLOW_EVIL_CONSTRUCTORS(ItemReference);
493};
494
Chong Zhang01a76012018-03-14 18:19:49 -0700495void ItemReference::apply(
496 KeyedVector<uint32_t, ImageItem> &itemIdToItemMap,
497 KeyedVector<uint32_t, ExifItem> &itemIdToExifMap) const {
Chong Zhangecd08132017-10-05 16:09:29 -0700498 ALOGV("attach reference type 0x%x to item id %d)", type(), mItemId);
499
Chong Zhang01a76012018-03-14 18:19:49 -0700500 switch(type()) {
Marco Nelissen51087de2019-01-22 15:39:07 -0800501 case FOURCC("dimg"): {
Chong Zhang01a76012018-03-14 18:19:49 -0700502 ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
503
504 // ignore non-image items
505 if (itemIndex < 0) {
506 return;
507 }
508
Chong Zhangecd08132017-10-05 16:09:29 -0700509 ImageItem &derivedImage = itemIdToItemMap.editValueAt(itemIndex);
510 if (!derivedImage.dimgRefs.empty()) {
Chong Zhang99745d12018-05-15 09:50:52 -0700511 ALOGW("dimgRefs not clean!");
Chong Zhangecd08132017-10-05 16:09:29 -0700512 }
513 derivedImage.dimgRefs.appendVector(mRefs);
Chong Zhangd3e0d862017-10-03 13:17:13 -0700514
515 for (size_t i = 0; i < mRefs.size(); i++) {
516 itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]);
517
518 // ignore non-image items
519 if (itemIndex < 0) {
520 continue;
521 }
522 ImageItem &sourceImage = itemIdToItemMap.editValueAt(itemIndex);
523
524 // mark the source image of the derivation as hidden
525 sourceImage.hidden = true;
526 }
Chong Zhang01a76012018-03-14 18:19:49 -0700527 break;
528 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800529 case FOURCC("thmb"): {
Chong Zhang01a76012018-03-14 18:19:49 -0700530 ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
531
532 // ignore non-image items
533 if (itemIndex < 0) {
534 return;
535 }
536
Chong Zhangd3e0d862017-10-03 13:17:13 -0700537 // mark thumbnail image as hidden, these can be retrieved if the client
538 // request thumbnail explicitly, but won't be exposed as displayables.
539 ImageItem &thumbImage = itemIdToItemMap.editValueAt(itemIndex);
540 thumbImage.hidden = true;
541
Chong Zhangecd08132017-10-05 16:09:29 -0700542 for (size_t i = 0; i < mRefs.size(); i++) {
543 itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]);
544
545 // ignore non-image items
546 if (itemIndex < 0) {
547 continue;
548 }
549 ALOGV("Image item id %d uses thumbnail item id %d", mRefs[i], mItemId);
Chong Zhangb3ca2592020-07-27 15:31:35 -0700550 ImageItem &imageItem = itemIdToItemMap.editValueAt(itemIndex);
551 if (!imageItem.thumbnails.empty()) {
Chong Zhangecd08132017-10-05 16:09:29 -0700552 ALOGW("already has thumbnails!");
553 }
Chong Zhangb3ca2592020-07-27 15:31:35 -0700554 imageItem.thumbnails.push_back(mItemId);
Chong Zhangecd08132017-10-05 16:09:29 -0700555 }
Chong Zhang01a76012018-03-14 18:19:49 -0700556 break;
557 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800558 case FOURCC("cdsc"): {
Chong Zhang01a76012018-03-14 18:19:49 -0700559 ssize_t itemIndex = itemIdToExifMap.indexOfKey(mItemId);
560
561 // ignore non-exif block items
562 if (itemIndex < 0) {
563 return;
564 }
565
566 for (size_t i = 0; i < mRefs.size(); i++) {
567 itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]);
568
569 // ignore non-image items
570 if (itemIndex < 0) {
571 continue;
572 }
573 ALOGV("Image item id %d uses metadata item id %d", mRefs[i], mItemId);
574 ImageItem &image = itemIdToItemMap.editValueAt(itemIndex);
575 image.cdscRefs.push_back(mItemId);
576 }
577 break;
578 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800579 case FOURCC("auxl"): {
Chong Zhang01a76012018-03-14 18:19:49 -0700580 ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
581
582 // ignore non-image items
583 if (itemIndex < 0) {
584 return;
585 }
586
Chong Zhangd3e0d862017-10-03 13:17:13 -0700587 // mark auxiliary image as hidden
588 ImageItem &auxImage = itemIdToItemMap.editValueAt(itemIndex);
589 auxImage.hidden = true;
Chong Zhang01a76012018-03-14 18:19:49 -0700590 break;
591 }
592 default:
Chong Zhangecd08132017-10-05 16:09:29 -0700593 ALOGW("ignoring unsupported ref type 0x%x", type());
594 }
595}
596
Chong Zhangb51ca282017-07-26 16:25:28 -0700597status_t ItemReference::parse(off64_t offset, size_t size) {
598 if (size < mRefIdSize + 2) {
599 return ERROR_MALFORMED;
600 }
601 if (!source()->getUInt32Var(offset, &mItemId, mRefIdSize)) {
602 return ERROR_IO;
603 }
604 offset += mRefIdSize;
605
606 uint16_t count;
607 if (!source()->getUInt16(offset, &count)) {
608 return ERROR_IO;
609 }
610 offset += 2;
611 size -= (mRefIdSize + 2);
612
613 if (size < count * mRefIdSize) {
614 return ERROR_MALFORMED;
615 }
616
617 for (size_t i = 0; i < count; i++) {
618 uint32_t refItemId;
619 if (!source()->getUInt32Var(offset, &refItemId, mRefIdSize)) {
620 return ERROR_IO;
621 }
622 offset += mRefIdSize;
623 mRefs.push_back(refItemId);
624 ALOGV("item id %d: referencing item id %d", mItemId, refItemId);
625 }
626
627 return OK;
628}
629
630struct IrefBox : public FullBox {
Marco Nelissencec44d02018-06-17 22:21:09 -0700631 IrefBox(DataSourceHelper *source, Vector<sp<ItemReference> > *itemRefs) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800632 FullBox(source, FOURCC("iref")), mRefIdSize(0), mItemRefs(itemRefs) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700633
634 status_t parse(off64_t offset, size_t size);
635
636protected:
637 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
638
639private:
640 uint32_t mRefIdSize;
641 Vector<sp<ItemReference> > *mItemRefs;
642};
643
644status_t IrefBox::parse(off64_t offset, size_t size) {
645 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
646 status_t err = parseFullBoxHeader(&offset, &size);
647 if (err != OK) {
648 return err;
649 }
650
651 mRefIdSize = (version() == 0) ? 2 : 4;
652 return parseChunks(offset, size);
653}
654
655status_t IrefBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
656 sp<ItemReference> itemRef = new ItemReference(source(), type, mRefIdSize);
657
658 status_t err = itemRef->parse(offset, size);
659 if (err != OK) {
660 return err;
661 }
662 mItemRefs->push_back(itemRef);
663 return OK;
664}
665
666/////////////////////////////////////////////////////////////////////
667//
668// ItemProperty related boxes
669//
670
671struct AssociationEntry {
672 uint32_t itemId;
673 bool essential;
674 uint16_t index;
675};
676
677struct ItemProperty : public RefBase {
678 ItemProperty() {}
679
680 virtual void attachTo(ImageItem &/*image*/) const {
681 ALOGW("Unrecognized property");
682 }
683 virtual status_t parse(off64_t /*offset*/, size_t /*size*/) {
684 ALOGW("Unrecognized property");
685 return OK;
686 }
687
688private:
689 DISALLOW_EVIL_CONSTRUCTORS(ItemProperty);
690};
691
692struct IspeBox : public FullBox, public ItemProperty {
Marco Nelissencec44d02018-06-17 22:21:09 -0700693 IspeBox(DataSourceHelper *source) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800694 FullBox(source, FOURCC("ispe")), mWidth(0), mHeight(0) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700695
696 status_t parse(off64_t offset, size_t size) override;
697
698 void attachTo(ImageItem &image) const override {
699 image.width = mWidth;
700 image.height = mHeight;
701 }
702
703private:
Chong Zhang242a9252019-12-09 16:38:39 -0800704 int32_t mWidth;
705 int32_t mHeight;
Chong Zhangb51ca282017-07-26 16:25:28 -0700706};
707
708status_t IspeBox::parse(off64_t offset, size_t size) {
709 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
710
711 status_t err = parseFullBoxHeader(&offset, &size);
712 if (err != OK) {
713 return err;
714 }
715
716 if (size < 8) {
717 return ERROR_MALFORMED;
718 }
Chong Zhang242a9252019-12-09 16:38:39 -0800719 if (!source()->getUInt32(offset, (uint32_t *)&mWidth)
720 || !source()->getUInt32(offset + 4, (uint32_t *)&mHeight)) {
Chong Zhangb51ca282017-07-26 16:25:28 -0700721 return ERROR_IO;
722 }
Chong Zhangb51ca282017-07-26 16:25:28 -0700723
Chong Zhang242a9252019-12-09 16:38:39 -0800724 // Validate that the dimension doesn't cause overflow on calculated max input size.
725 // Max input size is width*height*1.5, restrict width*height to 1<<29 so that
726 // we don't need to cast to int64_t when doing mults.
727 if (mWidth <= 0 || mHeight <= 0 || mWidth > (1 << 29) / mHeight) {
728 return ERROR_MALFORMED;
729 }
730
731 ALOGV("property ispe: %dx%d", mWidth, mHeight);
Chong Zhangb51ca282017-07-26 16:25:28 -0700732 return OK;
733}
734
735struct HvccBox : public Box, public ItemProperty {
Marco Nelissencec44d02018-06-17 22:21:09 -0700736 HvccBox(DataSourceHelper *source) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800737 Box(source, FOURCC("hvcC")) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700738
739 status_t parse(off64_t offset, size_t size) override;
740
741 void attachTo(ImageItem &image) const override {
742 image.hvcc = mHVCC;
743 }
744
745private:
746 sp<ABuffer> mHVCC;
747};
748
749status_t HvccBox::parse(off64_t offset, size_t size) {
750 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
751
752 mHVCC = new ABuffer(size);
753
754 if (mHVCC->data() == NULL) {
755 ALOGE("b/28471206");
756 return NO_MEMORY;
757 }
758
759 if (source()->readAt(offset, mHVCC->data(), size) < (ssize_t)size) {
760 return ERROR_IO;
761 }
762
763 ALOGV("property hvcC");
764
765 return OK;
766}
767
Vignesh Venkatasubramanianf89bf3c2020-10-05 17:28:45 -0700768struct Av1cBox : public Box, public ItemProperty {
769 Av1cBox(DataSourceHelper *source) :
770 Box(source, FOURCC("av1C")) {}
771
772 status_t parse(off64_t offset, size_t size) override;
773
774 void attachTo(ImageItem &image) const override {
775 image.av1c = mAv1c;
776 }
777
778private:
779 sp<ABuffer> mAv1c;
780};
781
782status_t Av1cBox::parse(off64_t offset, size_t size) {
783 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
784
785 mAv1c = new ABuffer(size);
786
787 if (mAv1c->data() == NULL) {
788 ALOGE("b/28471206");
789 return NO_MEMORY;
790 }
791
792 if (source()->readAt(offset, mAv1c->data(), size) < (ssize_t)size) {
793 return ERROR_IO;
794 }
795
796 ALOGV("property av1C");
797
798 return OK;
799}
800
Chong Zhangb51ca282017-07-26 16:25:28 -0700801struct IrotBox : public Box, public ItemProperty {
Marco Nelissencec44d02018-06-17 22:21:09 -0700802 IrotBox(DataSourceHelper *source) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800803 Box(source, FOURCC("irot")), mAngle(0) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700804
805 status_t parse(off64_t offset, size_t size) override;
806
807 void attachTo(ImageItem &image) const override {
808 image.rotation = mAngle * 90;
809 }
810
811private:
812 uint8_t mAngle;
813};
814
815status_t IrotBox::parse(off64_t offset, size_t size) {
816 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
817
818 if (size < 1) {
819 return ERROR_MALFORMED;
820 }
821 if (source()->readAt(offset, &mAngle, 1) != 1) {
822 return ERROR_IO;
823 }
824 mAngle &= 0x3;
825 ALOGV("property irot: %d", mAngle);
826
827 return OK;
828}
829
830struct ColrBox : public Box, public ItemProperty {
Marco Nelissencec44d02018-06-17 22:21:09 -0700831 ColrBox(DataSourceHelper *source) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800832 Box(source, FOURCC("colr")) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700833
834 status_t parse(off64_t offset, size_t size) override;
835
836 void attachTo(ImageItem &image) const override {
837 image.icc = mICCData;
838 }
839
840private:
841 sp<ABuffer> mICCData;
842};
843
844status_t ColrBox::parse(off64_t offset, size_t size) {
845 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
846
847 if (size < 4) {
848 return ERROR_MALFORMED;
849 }
850 uint32_t colour_type;
851 if (!source()->getUInt32(offset, &colour_type)) {
852 return ERROR_IO;
853 }
854 offset += 4;
855 size -= 4;
Marco Nelissen51087de2019-01-22 15:39:07 -0800856 if (colour_type == FOURCC("nclx")) {
Chong Zhangb51ca282017-07-26 16:25:28 -0700857 return OK;
858 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800859 if ((colour_type != FOURCC("rICC")) &&
860 (colour_type != FOURCC("prof"))) {
Chong Zhangb51ca282017-07-26 16:25:28 -0700861 return ERROR_MALFORMED;
862 }
863
864 mICCData = new ABuffer(size);
865 if (mICCData->data() == NULL) {
866 ALOGE("b/28471206");
867 return NO_MEMORY;
868 }
869
870 if (source()->readAt(offset, mICCData->data(), size) != (ssize_t)size) {
871 return ERROR_IO;
872 }
873
874 ALOGV("property Colr: size %zd", size);
875 return OK;
876}
877
878struct IpmaBox : public FullBox {
Marco Nelissencec44d02018-06-17 22:21:09 -0700879 IpmaBox(DataSourceHelper *source, Vector<AssociationEntry> *associations) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800880 FullBox(source, FOURCC("ipma")), mAssociations(associations) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700881
882 status_t parse(off64_t offset, size_t size);
883private:
884 Vector<AssociationEntry> *mAssociations;
885};
886
887status_t IpmaBox::parse(off64_t offset, size_t size) {
888 status_t err = parseFullBoxHeader(&offset, &size);
889 if (err != OK) {
890 return err;
891 }
892
893 if (size < 4) {
894 return ERROR_MALFORMED;
895 }
896 uint32_t entryCount;
897 if (!source()->getUInt32(offset, &entryCount)) {
898 return ERROR_IO;
899 }
900 offset += 4;
901 size -= 4;
902
903 for (size_t k = 0; k < entryCount; ++k) {
904 uint32_t itemId = 0;
905 size_t itemIdSize = (version() < 1) ? 2 : 4;
906
907 if (size < itemIdSize + 1) {
908 return ERROR_MALFORMED;
909 }
910
911 if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) {
912 return ERROR_IO;
913 }
914 offset += itemIdSize;
915 size -= itemIdSize;
916
917 uint8_t associationCount;
918 if (!source()->readAt(offset, &associationCount, 1)) {
919 return ERROR_IO;
920 }
921 offset++;
922 size--;
923
924 for (size_t i = 0; i < associationCount; ++i) {
925 size_t propIndexSize = (flags() & 1) ? 2 : 1;
926 if (size < propIndexSize) {
927 return ERROR_MALFORMED;
928 }
929 uint16_t propIndex;
930 if (!source()->getUInt16Var(offset, &propIndex, propIndexSize)) {
931 return ERROR_IO;
932 }
933 offset += propIndexSize;
934 size -= propIndexSize;
935 uint16_t bitmask = (1 << (8 * propIndexSize - 1));
936 AssociationEntry entry = {
937 .itemId = itemId,
938 .essential = !!(propIndex & bitmask),
939 .index = (uint16_t) (propIndex & ~bitmask)
940 };
941
942 ALOGV("item id %d associated to property %d (essential %d)",
943 itemId, entry.index, entry.essential);
944
945 mAssociations->push_back(entry);
946 }
947 }
948
949 return OK;
950}
951
952struct IpcoBox : public Box {
Marco Nelissencec44d02018-06-17 22:21:09 -0700953 IpcoBox(DataSourceHelper *source, Vector<sp<ItemProperty> > *properties) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800954 Box(source, FOURCC("ipco")), mItemProperties(properties) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700955
956 status_t parse(off64_t offset, size_t size);
957protected:
958 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
959
960private:
961 Vector<sp<ItemProperty> > *mItemProperties;
962};
963
964status_t IpcoBox::parse(off64_t offset, size_t size) {
965 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
Chong Zhangb3ca2592020-07-27 15:31:35 -0700966 // push a placeholder as the index is 1-based
Chong Zhangb51ca282017-07-26 16:25:28 -0700967 mItemProperties->push_back(new ItemProperty());
968 return parseChunks(offset, size);
969}
970
971status_t IpcoBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
972 sp<ItemProperty> itemProperty;
973 switch(type) {
Marco Nelissen51087de2019-01-22 15:39:07 -0800974 case FOURCC("hvcC"):
Chong Zhangb51ca282017-07-26 16:25:28 -0700975 {
976 itemProperty = new HvccBox(source());
977 break;
978 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800979 case FOURCC("ispe"):
Chong Zhangb51ca282017-07-26 16:25:28 -0700980 {
981 itemProperty = new IspeBox(source());
982 break;
983 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800984 case FOURCC("irot"):
Chong Zhangb51ca282017-07-26 16:25:28 -0700985 {
986 itemProperty = new IrotBox(source());
987 break;
988 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800989 case FOURCC("colr"):
Chong Zhangb51ca282017-07-26 16:25:28 -0700990 {
991 itemProperty = new ColrBox(source());
992 break;
993 }
Vignesh Venkatasubramanianf89bf3c2020-10-05 17:28:45 -0700994 case FOURCC("av1C"):
995 {
996 itemProperty = new Av1cBox(source());
997 break;
998 }
Chong Zhangb51ca282017-07-26 16:25:28 -0700999 default:
1000 {
1001 // push dummy to maintain correct item property index
1002 itemProperty = new ItemProperty();
1003 break;
1004 }
1005 }
1006 status_t err = itemProperty->parse(offset, size);
1007 if (err != OK) {
1008 return err;
1009 }
1010 mItemProperties->push_back(itemProperty);
1011 return OK;
1012}
1013
1014struct IprpBox : public Box {
Marco Nelissencec44d02018-06-17 22:21:09 -07001015 IprpBox(DataSourceHelper *source,
Chong Zhangb51ca282017-07-26 16:25:28 -07001016 Vector<sp<ItemProperty> > *properties,
1017 Vector<AssociationEntry> *associations) :
Marco Nelissen51087de2019-01-22 15:39:07 -08001018 Box(source, FOURCC("iprp")),
Chong Zhangb51ca282017-07-26 16:25:28 -07001019 mProperties(properties), mAssociations(associations) {}
1020
1021 status_t parse(off64_t offset, size_t size);
1022protected:
1023 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
1024
1025private:
1026 Vector<sp<ItemProperty> > *mProperties;
1027 Vector<AssociationEntry> *mAssociations;
1028};
1029
1030status_t IprpBox::parse(off64_t offset, size_t size) {
1031 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1032
1033 status_t err = parseChunks(offset, size);
1034 if (err != OK) {
1035 return err;
1036 }
1037 return OK;
1038}
1039
1040status_t IprpBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
1041 switch(type) {
Marco Nelissen51087de2019-01-22 15:39:07 -08001042 case FOURCC("ipco"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001043 {
1044 IpcoBox ipcoBox(source(), mProperties);
1045 return ipcoBox.parse(offset, size);
1046 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001047 case FOURCC("ipma"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001048 {
1049 IpmaBox ipmaBox(source(), mAssociations);
1050 return ipmaBox.parse(offset, size);
1051 }
1052 default:
1053 {
1054 ALOGW("Unrecognized box.");
1055 break;
1056 }
1057 }
1058 return OK;
1059}
1060
1061/////////////////////////////////////////////////////////////////////
1062//
1063// ItemInfo related boxes
1064//
1065struct ItemInfo {
1066 uint32_t itemId;
1067 uint32_t itemType;
Chong Zhangd3e0d862017-10-03 13:17:13 -07001068 bool hidden;
Chong Zhangb51ca282017-07-26 16:25:28 -07001069};
1070
1071struct InfeBox : public FullBox {
Marco Nelissencec44d02018-06-17 22:21:09 -07001072 InfeBox(DataSourceHelper *source) :
Marco Nelissen51087de2019-01-22 15:39:07 -08001073 FullBox(source, FOURCC("infe")) {}
Chong Zhangb51ca282017-07-26 16:25:28 -07001074
1075 status_t parse(off64_t offset, size_t size, ItemInfo *itemInfo);
1076
1077private:
1078 bool parseNullTerminatedString(off64_t *offset, size_t *size, String8 *out);
1079};
1080
1081bool InfeBox::parseNullTerminatedString(
1082 off64_t *offset, size_t *size, String8 *out) {
Chong Zhang70601852018-03-08 15:23:43 -08001083 char tmp;
1084 Vector<char> buf;
1085 buf.setCapacity(256);
Chong Zhangb51ca282017-07-26 16:25:28 -07001086 off64_t newOffset = *offset;
1087 off64_t stopOffset = *offset + *size;
1088 while (newOffset < stopOffset) {
Chong Zhang70601852018-03-08 15:23:43 -08001089 if (!source()->readAt(newOffset++, &tmp, 1)) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001090 return false;
1091 }
Chong Zhang70601852018-03-08 15:23:43 -08001092 buf.push_back(tmp);
1093 if (tmp == 0) {
1094 out->setTo(buf.array());
Chong Zhangb51ca282017-07-26 16:25:28 -07001095
1096 *offset = newOffset;
1097 *size = stopOffset - newOffset;
1098
1099 return true;
1100 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001101 }
1102 return false;
1103}
1104
1105status_t InfeBox::parse(off64_t offset, size_t size, ItemInfo *itemInfo) {
1106 status_t err = parseFullBoxHeader(&offset, &size);
1107 if (err != OK) {
1108 return err;
1109 }
1110
1111 if (version() == 0 || version() == 1) {
Chong Zhangecd08132017-10-05 16:09:29 -07001112 return ERROR_UNSUPPORTED;
Chong Zhangb51ca282017-07-26 16:25:28 -07001113 } else { // version >= 2
1114 uint32_t item_id;
1115 size_t itemIdSize = (version() == 2) ? 2 : 4;
1116 if (size < itemIdSize + 6) {
1117 return ERROR_MALFORMED;
1118 }
1119 if (!source()->getUInt32Var(offset, &item_id, itemIdSize)) {
1120 return ERROR_IO;
1121 }
1122 ALOGV("item_id %d", item_id);
1123 offset += itemIdSize;
1124 uint16_t item_protection_index;
1125 if (!source()->getUInt16(offset, &item_protection_index)) {
1126 return ERROR_IO;
1127 }
1128 ALOGV("item_protection_index %d", item_protection_index);
1129 offset += 2;
1130 uint32_t item_type;
1131 if (!source()->getUInt32(offset, &item_type)) {
1132 return ERROR_IO;
1133 }
1134
1135 itemInfo->itemId = item_id;
1136 itemInfo->itemType = item_type;
Chong Zhangd3e0d862017-10-03 13:17:13 -07001137 // According to HEIF spec, (flags & 1) indicates the image is hidden
1138 // and not supposed to be displayed.
1139 itemInfo->hidden = (flags() & 1);
Chong Zhangb51ca282017-07-26 16:25:28 -07001140
1141 char itemTypeString[5];
1142 MakeFourCCString(item_type, itemTypeString);
1143 ALOGV("item_type %s", itemTypeString);
1144 offset += 4;
1145 size -= itemIdSize + 6;
1146
1147 String8 item_name;
1148 if (!parseNullTerminatedString(&offset, &size, &item_name)) {
1149 return ERROR_MALFORMED;
1150 }
1151 ALOGV("item_name %s", item_name.c_str());
1152
Marco Nelissen51087de2019-01-22 15:39:07 -08001153 if (item_type == FOURCC("mime")) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001154 String8 content_type;
1155 if (!parseNullTerminatedString(&offset, &size, &content_type)) {
1156 return ERROR_MALFORMED;
1157 }
1158
Ray Essick46c24cd2018-03-08 13:54:19 -08001159 // content_encoding is optional; can be omitted if would be empty
1160 if (size > 0) {
1161 String8 content_encoding;
1162 if (!parseNullTerminatedString(&offset, &size, &content_encoding)) {
1163 return ERROR_MALFORMED;
1164 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001165 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001166 } else if (item_type == FOURCC("uri ")) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001167 String8 item_uri_type;
1168 if (!parseNullTerminatedString(&offset, &size, &item_uri_type)) {
1169 return ERROR_MALFORMED;
1170 }
1171 }
1172 }
1173 return OK;
1174}
1175
1176struct IinfBox : public FullBox {
Marco Nelissencec44d02018-06-17 22:21:09 -07001177 IinfBox(DataSourceHelper *source, Vector<ItemInfo> *itemInfos) :
Chong Zhang866f0f02019-02-01 14:19:37 -08001178 FullBox(source, FOURCC("iinf")), mItemInfos(itemInfos) {}
Chong Zhangb51ca282017-07-26 16:25:28 -07001179
1180 status_t parse(off64_t offset, size_t size);
1181
Chong Zhang866f0f02019-02-01 14:19:37 -08001182 bool hasFourCC(uint32_t type) { return mFourCCSeen.count(type) > 0; }
Chong Zhangb51ca282017-07-26 16:25:28 -07001183
1184protected:
1185 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
1186
1187private:
1188 Vector<ItemInfo> *mItemInfos;
Chong Zhang866f0f02019-02-01 14:19:37 -08001189 std::unordered_set<uint32_t> mFourCCSeen;
Chong Zhangb51ca282017-07-26 16:25:28 -07001190};
1191
1192status_t IinfBox::parse(off64_t offset, size_t size) {
1193 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1194
1195 status_t err = parseFullBoxHeader(&offset, &size);
1196 if (err != OK) {
1197 return err;
1198 }
1199
1200 size_t entryCountSize = version() == 0 ? 2 : 4;
1201 if (size < entryCountSize) {
1202 return ERROR_MALFORMED;
1203 }
1204 uint32_t entry_count;
1205 if (!source()->getUInt32Var(offset, &entry_count, entryCountSize)) {
1206 return ERROR_IO;
1207 }
1208 ALOGV("entry_count %d", entry_count);
1209
1210 off64_t stopOffset = offset + size;
1211 offset += entryCountSize;
1212 for (size_t i = 0; i < entry_count && offset < stopOffset; i++) {
1213 ALOGV("entry %zu", i);
1214 status_t err = parseChunk(&offset);
1215 if (err != OK) {
1216 return err;
1217 }
1218 }
1219 if (offset != stopOffset) {
1220 return ERROR_MALFORMED;
1221 }
1222
1223 return OK;
1224}
1225
1226status_t IinfBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
Marco Nelissen51087de2019-01-22 15:39:07 -08001227 if (type != FOURCC("infe")) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001228 return OK;
1229 }
1230
1231 InfeBox infeBox(source());
1232 ItemInfo itemInfo;
1233 status_t err = infeBox.parse(offset, size, &itemInfo);
Chong Zhangecd08132017-10-05 16:09:29 -07001234 if (err == OK) {
1235 mItemInfos->push_back(itemInfo);
Chong Zhang866f0f02019-02-01 14:19:37 -08001236 mFourCCSeen.insert(itemInfo.itemType);
Chong Zhangb51ca282017-07-26 16:25:28 -07001237 }
Chong Zhang5518a2c2017-10-13 18:31:25 -07001238 // InfeBox parse returns ERROR_UNSUPPORTED if the box if an unsupported
1239 // version. Ignore this error as it's not fatal.
1240 return (err == ERROR_UNSUPPORTED) ? OK : err;
Chong Zhangb51ca282017-07-26 16:25:28 -07001241}
1242
1243//////////////////////////////////////////////////////////////////
1244
Vignesh Venkatasubramanianf89bf3c2020-10-05 17:28:45 -07001245ItemTable::ItemTable(DataSourceHelper *source, bool isHeif)
Chong Zhangb51ca282017-07-26 16:25:28 -07001246 : mDataSource(source),
Vignesh Venkatasubramanianf89bf3c2020-10-05 17:28:45 -07001247 mIsHeif(isHeif),
Chong Zhangb51ca282017-07-26 16:25:28 -07001248 mPrimaryItemId(0),
1249 mIdatOffset(0),
1250 mIdatSize(0),
1251 mImageItemsValid(false),
Chong Zhangecd08132017-10-05 16:09:29 -07001252 mCurrentItemIndex(0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001253 mRequiredBoxes.insert('iprp');
1254 mRequiredBoxes.insert('iloc');
1255 mRequiredBoxes.insert('pitm');
1256 mRequiredBoxes.insert('iinf');
1257}
1258
1259ItemTable::~ItemTable() {}
1260
1261status_t ItemTable::parse(uint32_t type, off64_t data_offset, size_t chunk_data_size) {
1262 switch(type) {
Marco Nelissen51087de2019-01-22 15:39:07 -08001263 case FOURCC("iloc"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001264 {
1265 return parseIlocBox(data_offset, chunk_data_size);
1266 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001267 case FOURCC("iinf"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001268 {
1269 return parseIinfBox(data_offset, chunk_data_size);
1270 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001271 case FOURCC("iprp"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001272 {
1273 return parseIprpBox(data_offset, chunk_data_size);
1274 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001275 case FOURCC("pitm"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001276 {
1277 return parsePitmBox(data_offset, chunk_data_size);
1278 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001279 case FOURCC("idat"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001280 {
1281 return parseIdatBox(data_offset, chunk_data_size);
1282 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001283 case FOURCC("iref"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001284 {
1285 return parseIrefBox(data_offset, chunk_data_size);
1286 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001287 case FOURCC("ipro"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001288 {
1289 ALOGW("ipro box not supported!");
1290 break;
1291 }
1292 default:
1293 {
1294 ALOGW("unrecognized box type: 0x%x", type);
1295 break;
1296 }
1297 }
1298 return ERROR_UNSUPPORTED;
1299}
1300
1301status_t ItemTable::parseIlocBox(off64_t offset, size_t size) {
1302 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1303
1304 IlocBox ilocBox(mDataSource, &mItemLocs);
1305 status_t err = ilocBox.parse(offset, size);
1306 if (err != OK) {
1307 return err;
1308 }
1309
1310 if (ilocBox.hasConstructMethod1()) {
1311 mRequiredBoxes.insert('idat');
1312 }
1313
1314 return buildImageItemsIfPossible('iloc');
1315}
1316
1317status_t ItemTable::parseIinfBox(off64_t offset, size_t size) {
1318 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1319
1320 IinfBox iinfBox(mDataSource, &mItemInfos);
1321 status_t err = iinfBox.parse(offset, size);
1322 if (err != OK) {
1323 return err;
1324 }
1325
Chong Zhang866f0f02019-02-01 14:19:37 -08001326 if (iinfBox.hasFourCC(FOURCC("grid")) || iinfBox.hasFourCC(FOURCC("Exif"))) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001327 mRequiredBoxes.insert('iref');
1328 }
1329
1330 return buildImageItemsIfPossible('iinf');
1331}
1332
1333status_t ItemTable::parsePitmBox(off64_t offset, size_t size) {
1334 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1335
1336 PitmBox pitmBox(mDataSource);
1337 status_t err = pitmBox.parse(offset, size, &mPrimaryItemId);
1338 if (err != OK) {
1339 return err;
1340 }
1341
1342 return buildImageItemsIfPossible('pitm');
1343}
1344
1345status_t ItemTable::parseIprpBox(off64_t offset, size_t size) {
1346 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1347
1348 IprpBox iprpBox(mDataSource, &mItemProperties, &mAssociations);
1349 status_t err = iprpBox.parse(offset, size);
1350 if (err != OK) {
1351 return err;
1352 }
1353
1354 return buildImageItemsIfPossible('iprp');
1355}
1356
1357status_t ItemTable::parseIdatBox(off64_t offset, size_t size) {
1358 ALOGV("%s: idat offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1359
1360 // only remember the offset and size of idat box for later use
1361 mIdatOffset = offset;
1362 mIdatSize = size;
1363
1364 return buildImageItemsIfPossible('idat');
1365}
1366
1367status_t ItemTable::parseIrefBox(off64_t offset, size_t size) {
1368 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1369
1370 IrefBox irefBox(mDataSource, &mItemReferences);
1371 status_t err = irefBox.parse(offset, size);
1372 if (err != OK) {
1373 return err;
1374 }
1375
1376 return buildImageItemsIfPossible('iref');
1377}
1378
1379status_t ItemTable::buildImageItemsIfPossible(uint32_t type) {
1380 if (mImageItemsValid) {
1381 return OK;
1382 }
1383
1384 mBoxesSeen.insert(type);
1385
1386 // need at least 'iprp', 'iloc', 'pitm', 'iinf';
1387 // need 'idat' if any items used construction_method of 2;
1388 // need 'iref' if there are grids.
1389 if (!std::includes(
1390 mBoxesSeen.begin(), mBoxesSeen.end(),
1391 mRequiredBoxes.begin(), mRequiredBoxes.end())) {
1392 return OK;
1393 }
1394
1395 ALOGV("building image table...");
1396
1397 for (size_t i = 0; i < mItemInfos.size(); i++) {
1398 const ItemInfo &info = mItemInfos[i];
1399
Chong Zhang01a76012018-03-14 18:19:49 -07001400 // Only handle 3 types of items, all others are ignored:
1401 // 'grid': derived image from tiles
1402 // 'hvc1': coded image (or tile)
1403 // 'Exif': EXIF metadata
Marco Nelissen51087de2019-01-22 15:39:07 -08001404 if (info.itemType != FOURCC("grid") &&
1405 info.itemType != FOURCC("hvc1") &&
Vignesh Venkatasubramanianf89bf3c2020-10-05 17:28:45 -07001406 info.itemType != FOURCC("Exif") &&
1407 info.itemType != FOURCC("av01")) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001408 continue;
1409 }
1410
Chong Zhangecd08132017-10-05 16:09:29 -07001411 ssize_t itemIndex = mItemIdToItemMap.indexOfKey(info.itemId);
1412 if (itemIndex >= 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001413 ALOGW("ignoring duplicate image item id %d", info.itemId);
1414 continue;
1415 }
1416
1417 ssize_t ilocIndex = mItemLocs.indexOfKey(info.itemId);
1418 if (ilocIndex < 0) {
1419 ALOGE("iloc missing for image item id %d", info.itemId);
1420 continue;
1421 }
1422 const ItemLoc &iloc = mItemLocs[ilocIndex];
1423
1424 off64_t offset;
1425 size_t size;
1426 if (iloc.getLoc(&offset, &size, mIdatOffset, mIdatSize) != OK) {
1427 return ERROR_MALFORMED;
1428 }
1429
Marco Nelissen51087de2019-01-22 15:39:07 -08001430 if (info.itemType == FOURCC("Exif")) {
Chong Zhang01a76012018-03-14 18:19:49 -07001431 // Only add if the Exif data is non-empty. The first 4 bytes contain
1432 // the offset to TIFF header, which the Exif parser doesn't use.
1433 if (size > 4) {
1434 ExifItem exifItem = {
1435 .offset = offset,
1436 .size = size,
1437 };
1438 mItemIdToExifMap.add(info.itemId, exifItem);
1439 }
1440 continue;
1441 }
1442
Chong Zhangd3e0d862017-10-03 13:17:13 -07001443 ImageItem image(info.itemType, info.itemId, info.hidden);
Chong Zhangb51ca282017-07-26 16:25:28 -07001444
1445 ALOGV("adding %s: itemId %d", image.isGrid() ? "grid" : "image", info.itemId);
1446
1447 if (image.isGrid()) {
Chong Zhanga9879ef2018-05-10 12:32:50 -07001448 // ImageGrid struct is at least 8-byte, at most 12-byte (if flags&1)
1449 if (size < 8 || size > 12) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001450 return ERROR_MALFORMED;
1451 }
1452 uint8_t buf[12];
1453 if (!mDataSource->readAt(offset, buf, size)) {
1454 return ERROR_IO;
1455 }
1456
1457 image.rows = buf[2] + 1;
1458 image.columns = buf[3] + 1;
1459
1460 ALOGV("rows %d, columans %d", image.rows, image.columns);
1461 } else {
1462 image.offset = offset;
1463 image.size = size;
1464 }
Chong Zhangecd08132017-10-05 16:09:29 -07001465 mItemIdToItemMap.add(info.itemId, image);
Chong Zhangb51ca282017-07-26 16:25:28 -07001466 }
1467
1468 for (size_t i = 0; i < mAssociations.size(); i++) {
1469 attachProperty(mAssociations[i]);
1470 }
1471
1472 for (size_t i = 0; i < mItemReferences.size(); i++) {
Chong Zhang01a76012018-03-14 18:19:49 -07001473 mItemReferences[i]->apply(mItemIdToItemMap, mItemIdToExifMap);
Chong Zhangb51ca282017-07-26 16:25:28 -07001474 }
1475
Chong Zhangd3e0d862017-10-03 13:17:13 -07001476 bool foundPrimary = false;
1477 for (size_t i = 0; i < mItemIdToItemMap.size(); i++) {
1478 // add all non-hidden images, also add the primary even if it's marked
1479 // hidden, in case the primary is set to a thumbnail
1480 bool isPrimary = (mItemIdToItemMap[i].itemId == mPrimaryItemId);
1481 if (!mItemIdToItemMap[i].hidden || isPrimary) {
1482 mDisplayables.push_back(i);
1483 }
1484 foundPrimary |= isPrimary;
1485 }
1486
1487 ALOGV("found %zu displayables", mDisplayables.size());
1488
1489 // fail if no displayables are found
1490 if (mDisplayables.empty()) {
1491 return ERROR_MALFORMED;
1492 }
1493
1494 // if the primary item id is invalid, set primary to the first displayable
1495 if (!foundPrimary) {
1496 mPrimaryItemId = mItemIdToItemMap[mDisplayables[0]].itemId;
1497 }
1498
Chong Zhangb51ca282017-07-26 16:25:28 -07001499 mImageItemsValid = true;
1500 return OK;
1501}
1502
1503void ItemTable::attachProperty(const AssociationEntry &association) {
Chong Zhangecd08132017-10-05 16:09:29 -07001504 ssize_t itemIndex = mItemIdToItemMap.indexOfKey(association.itemId);
Chong Zhangb51ca282017-07-26 16:25:28 -07001505
1506 // ignore non-image items
Chong Zhangecd08132017-10-05 16:09:29 -07001507 if (itemIndex < 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001508 return;
1509 }
1510
1511 uint16_t propertyIndex = association.index;
1512 if (propertyIndex >= mItemProperties.size()) {
1513 ALOGW("Ignoring invalid property index %d", propertyIndex);
1514 return;
1515 }
1516
1517 ALOGV("attach property %d to item id %d)",
1518 propertyIndex, association.itemId);
1519
Chong Zhangd3e0d862017-10-03 13:17:13 -07001520 mItemProperties[propertyIndex]->attachTo(mItemIdToItemMap.editValueAt(itemIndex));
Chong Zhangb51ca282017-07-26 16:25:28 -07001521}
1522
Chong Zhangd3e0d862017-10-03 13:17:13 -07001523uint32_t ItemTable::countImages() const {
1524 return mImageItemsValid ? mDisplayables.size() : 0;
1525}
1526
Marco Nelissendba610b2018-11-01 16:07:03 -07001527AMediaFormat *ItemTable::getImageMeta(const uint32_t imageIndex) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001528 if (!mImageItemsValid) {
1529 return NULL;
1530 }
1531
Chong Zhangd3e0d862017-10-03 13:17:13 -07001532 if (imageIndex >= mDisplayables.size()) {
1533 ALOGE("%s: invalid image index %u", __FUNCTION__, imageIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001534 return NULL;
1535 }
Chong Zhangd3e0d862017-10-03 13:17:13 -07001536 const uint32_t itemIndex = mDisplayables[imageIndex];
1537 ALOGV("image[%u]: item index %u", imageIndex, itemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001538
Chong Zhangecd08132017-10-05 16:09:29 -07001539 const ImageItem *image = &mItemIdToItemMap[itemIndex];
Chong Zhangb51ca282017-07-26 16:25:28 -07001540
Chong Zhang99745d12018-05-15 09:50:52 -07001541 ssize_t tileItemIndex = -1;
1542 if (image->isGrid()) {
1543 if (image->dimgRefs.empty()) {
1544 return NULL;
1545 }
1546 tileItemIndex = mItemIdToItemMap.indexOfKey(image->dimgRefs[0]);
1547 if (tileItemIndex < 0) {
1548 return NULL;
1549 }
1550 }
1551
Marco Nelissendba610b2018-11-01 16:07:03 -07001552 AMediaFormat *meta = AMediaFormat_new();
Vignesh Venkatasubramanianf89bf3c2020-10-05 17:28:45 -07001553 AMediaFormat_setString(
1554 meta, AMEDIAFORMAT_KEY_MIME,
1555 mIsHeif ? MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC : MEDIA_MIMETYPE_IMAGE_AVIF);
Chong Zhangb51ca282017-07-26 16:25:28 -07001556
Chong Zhangd3e0d862017-10-03 13:17:13 -07001557 if (image->itemId == mPrimaryItemId) {
Marco Nelissendba610b2018-11-01 16:07:03 -07001558 AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_DEFAULT, 1);
Chong Zhangd3e0d862017-10-03 13:17:13 -07001559 }
1560
1561 ALOGV("image[%u]: size %dx%d", imageIndex, image->width, image->height);
1562
Marco Nelissendba610b2018-11-01 16:07:03 -07001563 AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_WIDTH, image->width);
1564 AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_HEIGHT, image->height);
Chong Zhangb51ca282017-07-26 16:25:28 -07001565 if (image->rotation != 0) {
Chong Zhang686c23b2017-10-05 15:16:59 -07001566 // Rotation angle in HEIF is CCW, convert to CW here to be
1567 // consistent with the other media formats.
1568 switch(image->rotation) {
Marco Nelissendba610b2018-11-01 16:07:03 -07001569 case 90:
1570 case 180:
1571 case 270:
1572 AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_ROTATION, 360 - image->rotation);
1573 break;
Chong Zhang686c23b2017-10-05 15:16:59 -07001574 default: break; // don't set if invalid
1575 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001576 }
Chong Zhang242a9252019-12-09 16:38:39 -08001577 // we validated no overflow in IspeBox::parse()
Marco Nelissendba610b2018-11-01 16:07:03 -07001578 AMediaFormat_setInt32(meta,
Chong Zhang242a9252019-12-09 16:38:39 -08001579 AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 3 / 2);
Chong Zhangb51ca282017-07-26 16:25:28 -07001580
1581 if (!image->thumbnails.empty()) {
Chong Zhangecd08132017-10-05 16:09:29 -07001582 ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(image->thumbnails[0]);
1583 if (thumbItemIndex >= 0) {
1584 const ImageItem &thumbnail = mItemIdToItemMap[thumbItemIndex];
Vignesh Venkatasubramanianf89bf3c2020-10-05 17:28:45 -07001585 // TODO(vigneshv): Handle thumbnail for AVIF.
Dongwon Kangc0fa2712018-06-20 16:39:58 -07001586 if (thumbnail.hvcc != NULL) {
Marco Nelissendba610b2018-11-01 16:07:03 -07001587 AMediaFormat_setInt32(meta,
1588 AMEDIAFORMAT_KEY_THUMBNAIL_WIDTH, thumbnail.width);
1589 AMediaFormat_setInt32(meta,
1590 AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT, thumbnail.height);
1591 AMediaFormat_setBuffer(meta,
Chong Zhang4c1f9582019-03-13 15:15:53 +09001592 AMEDIAFORMAT_KEY_THUMBNAIL_CSD_HEVC,
1593 thumbnail.hvcc->data(), thumbnail.hvcc->size());
Dongwon Kangc0fa2712018-06-20 16:39:58 -07001594 ALOGV("image[%u]: thumbnail: size %dx%d, item index %zd",
1595 imageIndex, thumbnail.width, thumbnail.height, thumbItemIndex);
1596 } else {
1597 ALOGW("%s: thumbnail data is missing for image[%u]!", __FUNCTION__, imageIndex);
1598 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001599 } else {
Chong Zhangecd08132017-10-05 16:09:29 -07001600 ALOGW("%s: Referenced thumbnail does not exist!", __FUNCTION__);
Chong Zhangb51ca282017-07-26 16:25:28 -07001601 }
1602 }
1603
1604 if (image->isGrid()) {
Marco Nelissendba610b2018-11-01 16:07:03 -07001605 AMediaFormat_setInt32(meta,
1606 AMEDIAFORMAT_KEY_GRID_ROWS, image->rows);
1607 AMediaFormat_setInt32(meta,
1608 AMEDIAFORMAT_KEY_GRID_COLUMNS, image->columns);
Chong Zhangee079fe2017-08-23 13:51:17 -07001609 // point image to the first tile for grid size and HVCC
Chong Zhangecd08132017-10-05 16:09:29 -07001610 image = &mItemIdToItemMap.editValueAt(tileItemIndex);
Marco Nelissendba610b2018-11-01 16:07:03 -07001611 AMediaFormat_setInt32(meta,
1612 AMEDIAFORMAT_KEY_TILE_WIDTH, image->width);
1613 AMediaFormat_setInt32(meta,
1614 AMEDIAFORMAT_KEY_TILE_HEIGHT, image->height);
Chong Zhang242a9252019-12-09 16:38:39 -08001615 // we validated no overflow in IspeBox::parse()
Marco Nelissendba610b2018-11-01 16:07:03 -07001616 AMediaFormat_setInt32(meta,
Chong Zhang242a9252019-12-09 16:38:39 -08001617 AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 3 / 2);
Chong Zhangb51ca282017-07-26 16:25:28 -07001618 }
1619
Vignesh Venkatasubramanianf89bf3c2020-10-05 17:28:45 -07001620 if (mIsHeif) {
1621 if (image->hvcc == NULL) {
1622 ALOGE("%s: hvcc is missing for image[%u]!", __FUNCTION__, imageIndex);
1623 return NULL;
1624 }
1625 AMediaFormat_setBuffer(meta,
1626 AMEDIAFORMAT_KEY_CSD_HEVC, image->hvcc->data(), image->hvcc->size());
1627 } else {
1628 if (image->av1c == NULL) {
1629 ALOGE("%s: av1c is missing for image[%u]!", __FUNCTION__, imageIndex);
1630 return NULL;
1631 }
1632 AMediaFormat_setBuffer(meta,
1633 AMEDIAFORMAT_KEY_CSD_0, image->av1c->data(), image->av1c->size());
Chong Zhangb51ca282017-07-26 16:25:28 -07001634 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001635
1636 if (image->icc != NULL) {
Marco Nelissendba610b2018-11-01 16:07:03 -07001637 AMediaFormat_setBuffer(meta,
1638 AMEDIAFORMAT_KEY_ICC_PROFILE, image->icc->data(), image->icc->size());
Chong Zhangb51ca282017-07-26 16:25:28 -07001639 }
1640 return meta;
1641}
1642
Chong Zhangd3e0d862017-10-03 13:17:13 -07001643status_t ItemTable::findImageItem(const uint32_t imageIndex, uint32_t *itemIndex) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001644 if (!mImageItemsValid) {
1645 return INVALID_OPERATION;
1646 }
1647
Chong Zhangd3e0d862017-10-03 13:17:13 -07001648 if (imageIndex >= mDisplayables.size()) {
1649 ALOGE("%s: invalid image index %d", __FUNCTION__, imageIndex);
1650 return BAD_VALUE;
Chong Zhangb51ca282017-07-26 16:25:28 -07001651 }
1652
Chong Zhangd3e0d862017-10-03 13:17:13 -07001653 *itemIndex = mDisplayables[imageIndex];
1654
1655 ALOGV("image[%u]: item index %u", imageIndex, *itemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001656 return OK;
1657}
1658
Chong Zhangd3e0d862017-10-03 13:17:13 -07001659status_t ItemTable::findThumbnailItem(const uint32_t imageIndex, uint32_t *itemIndex) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001660 if (!mImageItemsValid) {
1661 return INVALID_OPERATION;
1662 }
1663
Chong Zhangd3e0d862017-10-03 13:17:13 -07001664 if (imageIndex >= mDisplayables.size()) {
1665 ALOGE("%s: invalid image index %d", __FUNCTION__, imageIndex);
1666 return BAD_VALUE;
Chong Zhangb51ca282017-07-26 16:25:28 -07001667 }
1668
Chong Zhangb3ca2592020-07-27 15:31:35 -07001669 uint32_t imageItemIndex = mDisplayables[imageIndex];
Chong Zhangd3e0d862017-10-03 13:17:13 -07001670
Chong Zhangb3ca2592020-07-27 15:31:35 -07001671 const ImageItem &imageItem = mItemIdToItemMap[imageItemIndex];
1672 if (imageItem.thumbnails.empty()) {
1673 *itemIndex = imageItemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001674 return OK;
1675 }
1676
Chong Zhangb3ca2592020-07-27 15:31:35 -07001677 ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(imageItem.thumbnails[0]);
Chong Zhangecd08132017-10-05 16:09:29 -07001678 if (thumbItemIndex < 0) {
Chong Zhangb3ca2592020-07-27 15:31:35 -07001679 // Do not return the image item in this case, fail it so that the
Chong Zhangd5fa3572018-04-09 19:03:10 -07001680 // thumbnail extraction code knows we really don't have it.
1681 return INVALID_OPERATION;
Chong Zhangb51ca282017-07-26 16:25:28 -07001682 }
1683
Chong Zhangecd08132017-10-05 16:09:29 -07001684 *itemIndex = thumbItemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001685 return OK;
1686}
1687
1688status_t ItemTable::getImageOffsetAndSize(
Chong Zhangecd08132017-10-05 16:09:29 -07001689 uint32_t *itemIndex, off64_t *offset, size_t *size) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001690 if (!mImageItemsValid) {
1691 return INVALID_OPERATION;
1692 }
1693
Chong Zhangecd08132017-10-05 16:09:29 -07001694 if (itemIndex != NULL) {
1695 if (*itemIndex >= mItemIdToItemMap.size()) {
1696 ALOGE("%s: Bad item index!", __FUNCTION__);
Chong Zhangb51ca282017-07-26 16:25:28 -07001697 return BAD_VALUE;
1698 }
Chong Zhangecd08132017-10-05 16:09:29 -07001699 mCurrentItemIndex = *itemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001700 }
1701
Chong Zhangecd08132017-10-05 16:09:29 -07001702 ImageItem &image = mItemIdToItemMap.editValueAt(mCurrentItemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001703 if (image.isGrid()) {
1704 uint32_t tileItemId;
Chong Zhangecd08132017-10-05 16:09:29 -07001705 status_t err = image.getNextTileItemId(&tileItemId, itemIndex != NULL);
Chong Zhangb51ca282017-07-26 16:25:28 -07001706 if (err != OK) {
1707 return err;
1708 }
Chong Zhangecd08132017-10-05 16:09:29 -07001709 ssize_t tileItemIndex = mItemIdToItemMap.indexOfKey(tileItemId);
1710 if (tileItemIndex < 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001711 return ERROR_END_OF_STREAM;
1712 }
Chong Zhangecd08132017-10-05 16:09:29 -07001713 *offset = mItemIdToItemMap[tileItemIndex].offset;
1714 *size = mItemIdToItemMap[tileItemIndex].size;
Chong Zhangb51ca282017-07-26 16:25:28 -07001715 } else {
Chong Zhangecd08132017-10-05 16:09:29 -07001716 if (itemIndex == NULL) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001717 // For single images, we only allow it to be read once, after that
Chong Zhangecd08132017-10-05 16:09:29 -07001718 // it's EOS. New item index must be requested each time.
Chong Zhangb51ca282017-07-26 16:25:28 -07001719 return ERROR_END_OF_STREAM;
1720 }
Chong Zhangecd08132017-10-05 16:09:29 -07001721 *offset = mItemIdToItemMap[mCurrentItemIndex].offset;
1722 *size = mItemIdToItemMap[mCurrentItemIndex].size;
Chong Zhangb51ca282017-07-26 16:25:28 -07001723 }
1724
1725 return OK;
1726}
1727
Chong Zhang01a76012018-03-14 18:19:49 -07001728status_t ItemTable::getExifOffsetAndSize(off64_t *offset, size_t *size) {
1729 if (!mImageItemsValid) {
1730 return INVALID_OPERATION;
1731 }
1732
1733 ssize_t itemIndex = mItemIdToItemMap.indexOfKey(mPrimaryItemId);
1734
1735 // this should not happen, something's seriously wrong.
1736 if (itemIndex < 0) {
1737 return INVALID_OPERATION;
1738 }
1739
1740 const ImageItem &image = mItemIdToItemMap[itemIndex];
1741 if (image.cdscRefs.size() == 0) {
1742 return NAME_NOT_FOUND;
1743 }
1744
1745 ssize_t exifIndex = mItemIdToExifMap.indexOfKey(image.cdscRefs[0]);
1746 if (exifIndex < 0) {
1747 return NAME_NOT_FOUND;
1748 }
1749
1750 // skip the first 4-byte of the offset to TIFF header
Chong Zhang72500a12019-01-11 18:05:12 -08001751 uint32_t tiffOffset;
1752 if (!mDataSource->readAt(
1753 mItemIdToExifMap[exifIndex].offset, &tiffOffset, 4)) {
1754 return ERROR_IO;
1755 }
1756
1757 // We need 'Exif\0\0' before the tiff header
1758 tiffOffset = ntohl(tiffOffset);
1759 if (tiffOffset < 6) {
1760 return ERROR_MALFORMED;
1761 }
1762 // The first 4-byte of the item is the offset of the tiff header within the
1763 // exif data. The size of the item should be > 4 for a non-empty exif (this
1764 // was already checked when the item was added). Also check that the tiff
1765 // header offset is valid.
1766 if (mItemIdToExifMap[exifIndex].size <= 4 ||
1767 tiffOffset > mItemIdToExifMap[exifIndex].size - 4) {
1768 return ERROR_MALFORMED;
1769 }
1770
1771 // Offset of 'Exif\0\0' relative to the beginning of 'Exif' item
1772 // (first 4-byte is the tiff header offset)
1773 uint32_t exifOffset = 4 + tiffOffset - 6;
1774 *offset = mItemIdToExifMap[exifIndex].offset + exifOffset;
1775 *size = mItemIdToExifMap[exifIndex].size - exifOffset;
Chong Zhang01a76012018-03-14 18:19:49 -07001776 return OK;
1777}
1778
Chong Zhangb51ca282017-07-26 16:25:28 -07001779} // namespace heif
1780
1781} // namespace android