blob: b6787afe6f733a75c998dc9f2710d84d0c4e45a0 [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
Marco Nelissen75226172016-11-16 14:10:52 -080020#include <ItemTable.h>
Marco Nelissen2a243f02018-01-30 08:29:57 -080021#include <media/DataSourceBase.h>
Chong Zhangb51ca282017-07-26 16:25:28 -070022#include <media/stagefright/MetaData.h>
23#include <media/stagefright/MediaErrors.h>
Chong Zhangb51ca282017-07-26 16:25:28 -070024#include <media/stagefright/foundation/ABuffer.h>
Dongwon Kang60761282017-10-09 11:16:48 -070025#include <media/stagefright/foundation/ByteUtils.h>
Chong Zhangb51ca282017-07-26 16:25:28 -070026#include <media/stagefright/foundation/hexdump.h>
Dongwon Kange7a8a632017-10-09 14:52:58 -070027#include <media/stagefright/foundation/MediaDefs.h>
Chong Zhangb51ca282017-07-26 16:25:28 -070028#include <utils/Log.h>
29
30namespace android {
31
32namespace heif {
33
34/////////////////////////////////////////////////////////////////////
35//
36// struct to keep track of one image item
37//
38
39struct ImageItem {
40 friend struct ItemReference;
41 friend struct ItemProperty;
42
Chong Zhangd3e0d862017-10-03 13:17:13 -070043 ImageItem() : ImageItem(0, 0, false) {}
44 ImageItem(uint32_t _type, uint32_t _id, bool _hidden) :
45 type(_type), itemId(_id), hidden(_hidden),
Chong Zhangb51ca282017-07-26 16:25:28 -070046 rows(0), columns(0), width(0), height(0), rotation(0),
47 offset(0), size(0), nextTileIndex(0) {}
48
49 bool isGrid() const {
50 return type == FOURCC('g', 'r', 'i', 'd');
51 }
52
53 status_t getNextTileItemId(uint32_t *nextTileItemId, bool reset) {
54 if (reset) {
55 nextTileIndex = 0;
56 }
57 if (nextTileIndex >= dimgRefs.size()) {
58 return ERROR_END_OF_STREAM;
59 }
60 *nextTileItemId = dimgRefs[nextTileIndex++];
61 return OK;
62 }
63
64 uint32_t type;
Chong Zhangd3e0d862017-10-03 13:17:13 -070065 uint32_t itemId;
66 bool hidden;
Chong Zhangb51ca282017-07-26 16:25:28 -070067 int32_t rows;
68 int32_t columns;
69 int32_t width;
70 int32_t height;
71 int32_t rotation;
72 off64_t offset;
73 size_t size;
74 sp<ABuffer> hvcc;
75 sp<ABuffer> icc;
76
77 Vector<uint32_t> thumbnails;
78 Vector<uint32_t> dimgRefs;
Chong Zhang01a76012018-03-14 18:19:49 -070079 Vector<uint32_t> cdscRefs;
Chong Zhangb51ca282017-07-26 16:25:28 -070080 size_t nextTileIndex;
81};
82
Chong Zhang01a76012018-03-14 18:19:49 -070083struct ExifItem {
84 off64_t offset;
85 size_t size;
86};
Chong Zhangb51ca282017-07-26 16:25:28 -070087
88/////////////////////////////////////////////////////////////////////
89//
90// ISO boxes
91//
92
93struct Box {
94protected:
Marco Nelissen2a243f02018-01-30 08:29:57 -080095 Box(DataSourceBase *source, uint32_t type) :
Chong Zhangb51ca282017-07-26 16:25:28 -070096 mDataSource(source), mType(type) {}
97
98 virtual ~Box() {}
99
100 virtual status_t onChunkData(
101 uint32_t /*type*/, off64_t /*offset*/, size_t /*size*/) {
102 return OK;
103 }
104
105 inline uint32_t type() const { return mType; }
106
Marco Nelissen2a243f02018-01-30 08:29:57 -0800107 inline DataSourceBase *source() const { return mDataSource; }
Chong Zhangb51ca282017-07-26 16:25:28 -0700108
109 status_t parseChunk(off64_t *offset);
110
111 status_t parseChunks(off64_t offset, size_t size);
112
113private:
Marco Nelissen2a243f02018-01-30 08:29:57 -0800114 DataSourceBase *mDataSource;
Chong Zhangb51ca282017-07-26 16:25:28 -0700115 uint32_t mType;
116};
117
118status_t Box::parseChunk(off64_t *offset) {
119 if (*offset < 0) {
120 ALOGE("b/23540914");
121 return ERROR_MALFORMED;
122 }
123 uint32_t hdr[2];
124 if (mDataSource->readAt(*offset, hdr, 8) < 8) {
125 return ERROR_IO;
126 }
127 uint64_t chunk_size = ntohl(hdr[0]);
128 int32_t chunk_type = ntohl(hdr[1]);
129 off64_t data_offset = *offset + 8;
130
131 if (chunk_size == 1) {
132 if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) {
133 return ERROR_IO;
134 }
135 chunk_size = ntoh64(chunk_size);
136 data_offset += 8;
137
138 if (chunk_size < 16) {
139 // The smallest valid chunk is 16 bytes long in this case.
140 return ERROR_MALFORMED;
141 }
142 } else if (chunk_size == 0) {
143 // This shouldn't happen since we should never be top level
144 ALOGE("invalid chunk size 0 for non-top level box");
145 return ERROR_MALFORMED;
146 } else if (chunk_size < 8) {
147 // The smallest valid chunk is 8 bytes long.
148 ALOGE("invalid chunk size: %lld", (long long)chunk_size);
149 return ERROR_MALFORMED;
150 }
151
152 char chunk[5];
153 MakeFourCCString(chunk_type, chunk);
154 ALOGV("chunk: %s @ %lld", chunk, (long long)*offset);
155
156 off64_t chunk_data_size = chunk_size - (data_offset - *offset);
157 if (chunk_data_size < 0) {
158 ALOGE("b/23540914");
159 return ERROR_MALFORMED;
160 }
161
162 status_t err = onChunkData(chunk_type, data_offset, chunk_data_size);
163
164 if (err != OK) {
165 return err;
166 }
167 *offset += chunk_size;
168 return OK;
169}
170
171status_t Box::parseChunks(off64_t offset, size_t size) {
172 off64_t stopOffset = offset + size;
173 while (offset < stopOffset) {
174 status_t err = parseChunk(&offset);
175 if (err != OK) {
176 return err;
177 }
178 }
179 if (offset != stopOffset) {
180 return ERROR_MALFORMED;
181 }
182 return OK;
183}
184
185///////////////////////////////////////////////////////////////////////
186
187struct FullBox : public Box {
188protected:
Marco Nelissen2a243f02018-01-30 08:29:57 -0800189 FullBox(DataSourceBase *source, uint32_t type) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700190 Box(source, type), mVersion(0), mFlags(0) {}
191
192 inline uint8_t version() const { return mVersion; }
193
194 inline uint32_t flags() const { return mFlags; }
195
196 status_t parseFullBoxHeader(off64_t *offset, size_t *size);
197
198private:
199 uint8_t mVersion;
200 uint32_t mFlags;
201};
202
203status_t FullBox::parseFullBoxHeader(off64_t *offset, size_t *size) {
204 if (*size < 4) {
205 return ERROR_MALFORMED;
206 }
207 if (!source()->readAt(*offset, &mVersion, 1)) {
208 return ERROR_IO;
209 }
210 if (!source()->getUInt24(*offset + 1, &mFlags)) {
211 return ERROR_IO;
212 }
213 *offset += 4;
214 *size -= 4;
215 return OK;
216}
217
218/////////////////////////////////////////////////////////////////////
219//
220// PrimaryImage box
221//
222
223struct PitmBox : public FullBox {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800224 PitmBox(DataSourceBase *source) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700225 FullBox(source, FOURCC('p', 'i', 't', 'm')) {}
226
227 status_t parse(off64_t offset, size_t size, uint32_t *primaryItemId);
228};
229
230status_t PitmBox::parse(off64_t offset, size_t size, uint32_t *primaryItemId) {
231 status_t err = parseFullBoxHeader(&offset, &size);
232 if (err != OK) {
233 return err;
234 }
235
236 size_t itemIdSize = (version() == 0) ? 2 : 4;
237 if (size < itemIdSize) {
238 return ERROR_MALFORMED;
239 }
240 uint32_t itemId;
241 if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) {
242 return ERROR_IO;
243 }
244
245 ALOGV("primary id %d", itemId);
246 *primaryItemId = itemId;
247
248 return OK;
249}
250
251/////////////////////////////////////////////////////////////////////
252//
253// ItemLocation related boxes
254//
255
256struct ExtentEntry {
257 uint64_t extentIndex;
258 uint64_t extentOffset;
259 uint64_t extentLength;
260};
261
262struct ItemLoc {
263 ItemLoc() : ItemLoc(0, 0, 0, 0) {}
264 ItemLoc(uint32_t item_id, uint16_t construction_method,
265 uint16_t data_reference_index, uint64_t base_offset) :
266 itemId(item_id),
267 constructionMethod(construction_method),
268 dataReferenceIndex(data_reference_index),
269 baseOffset(base_offset) {}
270
271 void addExtent(const ExtentEntry& extent) {
272 extents.push_back(extent);
273 }
274
275 status_t getLoc(off64_t *offset, size_t *size,
276 off64_t idatOffset, size_t idatSize) const {
277 // TODO: fix extent handling, fix constructionMethod = 2
278 CHECK(extents.size() == 1);
279 if (constructionMethod == 0) {
280 *offset = baseOffset + extents[0].extentOffset;
281 *size = extents[0].extentLength;
282 return OK;
283 } else if (constructionMethod == 1) {
284 if (baseOffset + extents[0].extentOffset + extents[0].extentLength
285 > idatSize) {
286 return ERROR_MALFORMED;
287 }
288 *offset = baseOffset + extents[0].extentOffset + idatOffset;
289 *size = extents[0].extentLength;
290 return OK;
291 }
292 return ERROR_UNSUPPORTED;
293 }
294
295 // parsed info
296 uint32_t itemId;
297 uint16_t constructionMethod;
298 uint16_t dataReferenceIndex;
299 off64_t baseOffset;
300 Vector<ExtentEntry> extents;
301};
302
303struct IlocBox : public FullBox {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800304 IlocBox(DataSourceBase *source, KeyedVector<uint32_t, ItemLoc> *itemLocs) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700305 FullBox(source, FOURCC('i', 'l', 'o', 'c')),
306 mItemLocs(itemLocs), mHasConstructMethod1(false) {}
307
308 status_t parse(off64_t offset, size_t size);
309
310 bool hasConstructMethod1() { return mHasConstructMethod1; }
311
312private:
313 static bool isSizeFieldValid(uint32_t offset_size) {
314 return offset_size == 0 || offset_size == 4 || offset_size == 8;
315 }
316 KeyedVector<uint32_t, ItemLoc> *mItemLocs;
317 bool mHasConstructMethod1;
318};
319
320status_t IlocBox::parse(off64_t offset, size_t size) {
321 status_t err = parseFullBoxHeader(&offset, &size);
322 if (err != OK) {
323 return err;
324 }
325 if (version() > 2) {
326 ALOGE("%s: invalid version %d", __FUNCTION__, version());
327 return ERROR_MALFORMED;
328 }
329
330 if (size < 2) {
331 return ERROR_MALFORMED;
332 }
333 uint8_t offset_size;
334 if (!source()->readAt(offset++, &offset_size, 1)) {
335 return ERROR_IO;
336 }
337 uint8_t length_size = (offset_size & 0xF);
338 offset_size >>= 4;
339
340 uint8_t base_offset_size;
341 if (!source()->readAt(offset++, &base_offset_size, 1)) {
342 return ERROR_IO;
343 }
344 uint8_t index_size = 0;
345 if (version() == 1 || version() == 2) {
346 index_size = (base_offset_size & 0xF);
347 }
348 base_offset_size >>= 4;
349 size -= 2;
350
351 if (!isSizeFieldValid(offset_size)
352 || !isSizeFieldValid(length_size)
353 || !isSizeFieldValid(base_offset_size)
354 || !isSizeFieldValid((index_size))) {
355 ALOGE("%s: offset size not valid: %d, %d, %d, %d", __FUNCTION__,
356 offset_size, length_size, base_offset_size, index_size);
357 return ERROR_MALFORMED;
358 }
359
360 uint32_t item_count;
361 size_t itemFieldSize = version() < 2 ? 2 : 4;
362 if (size < itemFieldSize) {
363 return ERROR_MALFORMED;
364 }
365 if (!source()->getUInt32Var(offset, &item_count, itemFieldSize)) {
366 return ERROR_IO;
367 }
368
369 ALOGV("item_count %lld", (long long) item_count);
370 offset += itemFieldSize;
371 size -= itemFieldSize;
372
373 for (size_t i = 0; i < item_count; i++) {
374 uint32_t item_id;
375 if (!source()->getUInt32Var(offset, &item_id, itemFieldSize)) {
376 return ERROR_IO;
377 }
378 ALOGV("item[%zu]: id %lld", i, (long long)item_id);
379 offset += itemFieldSize;
380
381 uint8_t construction_method = 0;
382 if (version() == 1 || version() == 2) {
383 uint8_t buf[2];
384 if (!source()->readAt(offset, buf, 2)) {
385 return ERROR_IO;
386 }
387 construction_method = (buf[1] & 0xF);
388 ALOGV("construction_method %d", construction_method);
389 if (construction_method == 1) {
390 mHasConstructMethod1 = true;
391 }
392
393 offset += 2;
394 }
395
396 uint16_t data_reference_index;
397 if (!source()->getUInt16(offset, &data_reference_index)) {
398 return ERROR_IO;
399 }
400 ALOGV("data_reference_index %d", data_reference_index);
401 if (data_reference_index != 0) {
402 // we don't support reference to other files
403 return ERROR_UNSUPPORTED;
404 }
405 offset += 2;
406
407 uint64_t base_offset = 0;
408 if (base_offset_size != 0) {
409 if (!source()->getUInt64Var(offset, &base_offset, base_offset_size)) {
410 return ERROR_IO;
411 }
412 offset += base_offset_size;
413 }
414 ALOGV("base_offset %lld", (long long) base_offset);
415
416 ssize_t index = mItemLocs->add(item_id, ItemLoc(
417 item_id, construction_method, data_reference_index, base_offset));
418 ItemLoc &item = mItemLocs->editValueAt(index);
419
420 uint16_t extent_count;
421 if (!source()->getUInt16(offset, &extent_count)) {
422 return ERROR_IO;
423 }
424 ALOGV("extent_count %d", extent_count);
425
426 if (extent_count > 1 && (offset_size == 0 || length_size == 0)) {
427 // if the item is dividec into more than one extents, offset and
428 // length must be present.
429 return ERROR_MALFORMED;
430 }
431 offset += 2;
432
433 for (size_t j = 0; j < extent_count; j++) {
434 uint64_t extent_index = 1; // default=1
435 if ((version() == 1 || version() == 2) && (index_size > 0)) {
436 if (!source()->getUInt64Var(offset, &extent_index, index_size)) {
437 return ERROR_IO;
438 }
439 // TODO: add support for this mode
440 offset += index_size;
441 ALOGV("extent_index %lld", (long long)extent_index);
442 }
443
444 uint64_t extent_offset = 0; // default=0
445 if (offset_size > 0) {
446 if (!source()->getUInt64Var(offset, &extent_offset, offset_size)) {
447 return ERROR_IO;
448 }
449 offset += offset_size;
450 }
451 ALOGV("extent_offset %lld", (long long)extent_offset);
452
453 uint64_t extent_length = 0; // this indicates full length of file
454 if (length_size > 0) {
455 if (!source()->getUInt64Var(offset, &extent_length, length_size)) {
456 return ERROR_IO;
457 }
458 offset += length_size;
459 }
460 ALOGV("extent_length %lld", (long long)extent_length);
461
462 item.addExtent({ extent_index, extent_offset, extent_length });
463 }
464 }
465 return OK;
466}
467
468/////////////////////////////////////////////////////////////////////
469//
470// ItemReference related boxes
471//
472
473struct ItemReference : public Box, public RefBase {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800474 ItemReference(DataSourceBase *source, uint32_t type, uint32_t itemIdSize) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700475 Box(source, type), mItemId(0), mRefIdSize(itemIdSize) {}
476
477 status_t parse(off64_t offset, size_t size);
478
479 uint32_t itemId() { return mItemId; }
480
Chong Zhang01a76012018-03-14 18:19:49 -0700481 void apply(
482 KeyedVector<uint32_t, ImageItem> &itemIdToItemMap,
483 KeyedVector<uint32_t, ExifItem> &itemIdToExifMap) const;
Chong Zhangb51ca282017-07-26 16:25:28 -0700484
485private:
486 uint32_t mItemId;
487 uint32_t mRefIdSize;
488 Vector<uint32_t> mRefs;
489
490 DISALLOW_EVIL_CONSTRUCTORS(ItemReference);
491};
492
Chong Zhang01a76012018-03-14 18:19:49 -0700493void ItemReference::apply(
494 KeyedVector<uint32_t, ImageItem> &itemIdToItemMap,
495 KeyedVector<uint32_t, ExifItem> &itemIdToExifMap) const {
Chong Zhangecd08132017-10-05 16:09:29 -0700496 ALOGV("attach reference type 0x%x to item id %d)", type(), mItemId);
497
Chong Zhang01a76012018-03-14 18:19:49 -0700498 switch(type()) {
499 case FOURCC('d', 'i', 'm', 'g'): {
500 ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
501
502 // ignore non-image items
503 if (itemIndex < 0) {
504 return;
505 }
506
Chong Zhangecd08132017-10-05 16:09:29 -0700507 ImageItem &derivedImage = itemIdToItemMap.editValueAt(itemIndex);
508 if (!derivedImage.dimgRefs.empty()) {
509 ALOGW("dimgRefs if not clean!");
510 }
511 derivedImage.dimgRefs.appendVector(mRefs);
Chong Zhangd3e0d862017-10-03 13:17:13 -0700512
513 for (size_t i = 0; i < mRefs.size(); i++) {
514 itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]);
515
516 // ignore non-image items
517 if (itemIndex < 0) {
518 continue;
519 }
520 ImageItem &sourceImage = itemIdToItemMap.editValueAt(itemIndex);
521
522 // mark the source image of the derivation as hidden
523 sourceImage.hidden = true;
524 }
Chong Zhang01a76012018-03-14 18:19:49 -0700525 break;
526 }
527 case FOURCC('t', 'h', 'm', 'b'): {
528 ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
529
530 // ignore non-image items
531 if (itemIndex < 0) {
532 return;
533 }
534
Chong Zhangd3e0d862017-10-03 13:17:13 -0700535 // mark thumbnail image as hidden, these can be retrieved if the client
536 // request thumbnail explicitly, but won't be exposed as displayables.
537 ImageItem &thumbImage = itemIdToItemMap.editValueAt(itemIndex);
538 thumbImage.hidden = true;
539
Chong Zhangecd08132017-10-05 16:09:29 -0700540 for (size_t i = 0; i < mRefs.size(); i++) {
541 itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]);
542
543 // ignore non-image items
544 if (itemIndex < 0) {
545 continue;
546 }
547 ALOGV("Image item id %d uses thumbnail item id %d", mRefs[i], mItemId);
548 ImageItem &masterImage = itemIdToItemMap.editValueAt(itemIndex);
549 if (!masterImage.thumbnails.empty()) {
550 ALOGW("already has thumbnails!");
551 }
552 masterImage.thumbnails.push_back(mItemId);
553 }
Chong Zhang01a76012018-03-14 18:19:49 -0700554 break;
555 }
556 case FOURCC('c', 'd', 's', 'c'): {
557 ssize_t itemIndex = itemIdToExifMap.indexOfKey(mItemId);
558
559 // ignore non-exif block items
560 if (itemIndex < 0) {
561 return;
562 }
563
564 for (size_t i = 0; i < mRefs.size(); i++) {
565 itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]);
566
567 // ignore non-image items
568 if (itemIndex < 0) {
569 continue;
570 }
571 ALOGV("Image item id %d uses metadata item id %d", mRefs[i], mItemId);
572 ImageItem &image = itemIdToItemMap.editValueAt(itemIndex);
573 image.cdscRefs.push_back(mItemId);
574 }
575 break;
576 }
577 case FOURCC('a', 'u', 'x', 'l'): {
578 ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
579
580 // ignore non-image items
581 if (itemIndex < 0) {
582 return;
583 }
584
Chong Zhangd3e0d862017-10-03 13:17:13 -0700585 // mark auxiliary image as hidden
586 ImageItem &auxImage = itemIdToItemMap.editValueAt(itemIndex);
587 auxImage.hidden = true;
Chong Zhang01a76012018-03-14 18:19:49 -0700588 break;
589 }
590 default:
Chong Zhangecd08132017-10-05 16:09:29 -0700591 ALOGW("ignoring unsupported ref type 0x%x", type());
592 }
593}
594
Chong Zhangb51ca282017-07-26 16:25:28 -0700595status_t ItemReference::parse(off64_t offset, size_t size) {
596 if (size < mRefIdSize + 2) {
597 return ERROR_MALFORMED;
598 }
599 if (!source()->getUInt32Var(offset, &mItemId, mRefIdSize)) {
600 return ERROR_IO;
601 }
602 offset += mRefIdSize;
603
604 uint16_t count;
605 if (!source()->getUInt16(offset, &count)) {
606 return ERROR_IO;
607 }
608 offset += 2;
609 size -= (mRefIdSize + 2);
610
611 if (size < count * mRefIdSize) {
612 return ERROR_MALFORMED;
613 }
614
615 for (size_t i = 0; i < count; i++) {
616 uint32_t refItemId;
617 if (!source()->getUInt32Var(offset, &refItemId, mRefIdSize)) {
618 return ERROR_IO;
619 }
620 offset += mRefIdSize;
621 mRefs.push_back(refItemId);
622 ALOGV("item id %d: referencing item id %d", mItemId, refItemId);
623 }
624
625 return OK;
626}
627
628struct IrefBox : public FullBox {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800629 IrefBox(DataSourceBase *source, Vector<sp<ItemReference> > *itemRefs) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700630 FullBox(source, FOURCC('i', 'r', 'e', 'f')), mRefIdSize(0), mItemRefs(itemRefs) {}
631
632 status_t parse(off64_t offset, size_t size);
633
634protected:
635 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
636
637private:
638 uint32_t mRefIdSize;
639 Vector<sp<ItemReference> > *mItemRefs;
640};
641
642status_t IrefBox::parse(off64_t offset, size_t size) {
643 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
644 status_t err = parseFullBoxHeader(&offset, &size);
645 if (err != OK) {
646 return err;
647 }
648
649 mRefIdSize = (version() == 0) ? 2 : 4;
650 return parseChunks(offset, size);
651}
652
653status_t IrefBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
654 sp<ItemReference> itemRef = new ItemReference(source(), type, mRefIdSize);
655
656 status_t err = itemRef->parse(offset, size);
657 if (err != OK) {
658 return err;
659 }
660 mItemRefs->push_back(itemRef);
661 return OK;
662}
663
664/////////////////////////////////////////////////////////////////////
665//
666// ItemProperty related boxes
667//
668
669struct AssociationEntry {
670 uint32_t itemId;
671 bool essential;
672 uint16_t index;
673};
674
675struct ItemProperty : public RefBase {
676 ItemProperty() {}
677
678 virtual void attachTo(ImageItem &/*image*/) const {
679 ALOGW("Unrecognized property");
680 }
681 virtual status_t parse(off64_t /*offset*/, size_t /*size*/) {
682 ALOGW("Unrecognized property");
683 return OK;
684 }
685
686private:
687 DISALLOW_EVIL_CONSTRUCTORS(ItemProperty);
688};
689
690struct IspeBox : public FullBox, public ItemProperty {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800691 IspeBox(DataSourceBase *source) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700692 FullBox(source, FOURCC('i', 's', 'p', 'e')), mWidth(0), mHeight(0) {}
693
694 status_t parse(off64_t offset, size_t size) override;
695
696 void attachTo(ImageItem &image) const override {
697 image.width = mWidth;
698 image.height = mHeight;
699 }
700
701private:
702 uint32_t mWidth;
703 uint32_t mHeight;
704};
705
706status_t IspeBox::parse(off64_t offset, size_t size) {
707 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
708
709 status_t err = parseFullBoxHeader(&offset, &size);
710 if (err != OK) {
711 return err;
712 }
713
714 if (size < 8) {
715 return ERROR_MALFORMED;
716 }
717 if (!source()->getUInt32(offset, &mWidth)
718 || !source()->getUInt32(offset + 4, &mHeight)) {
719 return ERROR_IO;
720 }
721 ALOGV("property ispe: %dx%d", mWidth, mHeight);
722
723 return OK;
724}
725
726struct HvccBox : public Box, public ItemProperty {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800727 HvccBox(DataSourceBase *source) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700728 Box(source, FOURCC('h', 'v', 'c', 'C')) {}
729
730 status_t parse(off64_t offset, size_t size) override;
731
732 void attachTo(ImageItem &image) const override {
733 image.hvcc = mHVCC;
734 }
735
736private:
737 sp<ABuffer> mHVCC;
738};
739
740status_t HvccBox::parse(off64_t offset, size_t size) {
741 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
742
743 mHVCC = new ABuffer(size);
744
745 if (mHVCC->data() == NULL) {
746 ALOGE("b/28471206");
747 return NO_MEMORY;
748 }
749
750 if (source()->readAt(offset, mHVCC->data(), size) < (ssize_t)size) {
751 return ERROR_IO;
752 }
753
754 ALOGV("property hvcC");
755
756 return OK;
757}
758
759struct IrotBox : public Box, public ItemProperty {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800760 IrotBox(DataSourceBase *source) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700761 Box(source, FOURCC('i', 'r', 'o', 't')), mAngle(0) {}
762
763 status_t parse(off64_t offset, size_t size) override;
764
765 void attachTo(ImageItem &image) const override {
766 image.rotation = mAngle * 90;
767 }
768
769private:
770 uint8_t mAngle;
771};
772
773status_t IrotBox::parse(off64_t offset, size_t size) {
774 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
775
776 if (size < 1) {
777 return ERROR_MALFORMED;
778 }
779 if (source()->readAt(offset, &mAngle, 1) != 1) {
780 return ERROR_IO;
781 }
782 mAngle &= 0x3;
783 ALOGV("property irot: %d", mAngle);
784
785 return OK;
786}
787
788struct ColrBox : public Box, public ItemProperty {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800789 ColrBox(DataSourceBase *source) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700790 Box(source, FOURCC('c', 'o', 'l', 'r')) {}
791
792 status_t parse(off64_t offset, size_t size) override;
793
794 void attachTo(ImageItem &image) const override {
795 image.icc = mICCData;
796 }
797
798private:
799 sp<ABuffer> mICCData;
800};
801
802status_t ColrBox::parse(off64_t offset, size_t size) {
803 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
804
805 if (size < 4) {
806 return ERROR_MALFORMED;
807 }
808 uint32_t colour_type;
809 if (!source()->getUInt32(offset, &colour_type)) {
810 return ERROR_IO;
811 }
812 offset += 4;
813 size -= 4;
814 if (colour_type == FOURCC('n', 'c', 'l', 'x')) {
815 return OK;
816 }
817 if ((colour_type != FOURCC('r', 'I', 'C', 'C')) &&
818 (colour_type != FOURCC('p', 'r', 'o', 'f'))) {
819 return ERROR_MALFORMED;
820 }
821
822 mICCData = new ABuffer(size);
823 if (mICCData->data() == NULL) {
824 ALOGE("b/28471206");
825 return NO_MEMORY;
826 }
827
828 if (source()->readAt(offset, mICCData->data(), size) != (ssize_t)size) {
829 return ERROR_IO;
830 }
831
832 ALOGV("property Colr: size %zd", size);
833 return OK;
834}
835
836struct IpmaBox : public FullBox {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800837 IpmaBox(DataSourceBase *source, Vector<AssociationEntry> *associations) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700838 FullBox(source, FOURCC('i', 'p', 'm', 'a')), mAssociations(associations) {}
839
840 status_t parse(off64_t offset, size_t size);
841private:
842 Vector<AssociationEntry> *mAssociations;
843};
844
845status_t IpmaBox::parse(off64_t offset, size_t size) {
846 status_t err = parseFullBoxHeader(&offset, &size);
847 if (err != OK) {
848 return err;
849 }
850
851 if (size < 4) {
852 return ERROR_MALFORMED;
853 }
854 uint32_t entryCount;
855 if (!source()->getUInt32(offset, &entryCount)) {
856 return ERROR_IO;
857 }
858 offset += 4;
859 size -= 4;
860
861 for (size_t k = 0; k < entryCount; ++k) {
862 uint32_t itemId = 0;
863 size_t itemIdSize = (version() < 1) ? 2 : 4;
864
865 if (size < itemIdSize + 1) {
866 return ERROR_MALFORMED;
867 }
868
869 if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) {
870 return ERROR_IO;
871 }
872 offset += itemIdSize;
873 size -= itemIdSize;
874
875 uint8_t associationCount;
876 if (!source()->readAt(offset, &associationCount, 1)) {
877 return ERROR_IO;
878 }
879 offset++;
880 size--;
881
882 for (size_t i = 0; i < associationCount; ++i) {
883 size_t propIndexSize = (flags() & 1) ? 2 : 1;
884 if (size < propIndexSize) {
885 return ERROR_MALFORMED;
886 }
887 uint16_t propIndex;
888 if (!source()->getUInt16Var(offset, &propIndex, propIndexSize)) {
889 return ERROR_IO;
890 }
891 offset += propIndexSize;
892 size -= propIndexSize;
893 uint16_t bitmask = (1 << (8 * propIndexSize - 1));
894 AssociationEntry entry = {
895 .itemId = itemId,
896 .essential = !!(propIndex & bitmask),
897 .index = (uint16_t) (propIndex & ~bitmask)
898 };
899
900 ALOGV("item id %d associated to property %d (essential %d)",
901 itemId, entry.index, entry.essential);
902
903 mAssociations->push_back(entry);
904 }
905 }
906
907 return OK;
908}
909
910struct IpcoBox : public Box {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800911 IpcoBox(DataSourceBase *source, Vector<sp<ItemProperty> > *properties) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700912 Box(source, FOURCC('i', 'p', 'c', 'o')), mItemProperties(properties) {}
913
914 status_t parse(off64_t offset, size_t size);
915protected:
916 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
917
918private:
919 Vector<sp<ItemProperty> > *mItemProperties;
920};
921
922status_t IpcoBox::parse(off64_t offset, size_t size) {
923 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
924 // push dummy as the index is 1-based
925 mItemProperties->push_back(new ItemProperty());
926 return parseChunks(offset, size);
927}
928
929status_t IpcoBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
930 sp<ItemProperty> itemProperty;
931 switch(type) {
932 case FOURCC('h', 'v', 'c', 'C'):
933 {
934 itemProperty = new HvccBox(source());
935 break;
936 }
937 case FOURCC('i', 's', 'p', 'e'):
938 {
939 itemProperty = new IspeBox(source());
940 break;
941 }
942 case FOURCC('i', 'r', 'o', 't'):
943 {
944 itemProperty = new IrotBox(source());
945 break;
946 }
947 case FOURCC('c', 'o', 'l', 'r'):
948 {
949 itemProperty = new ColrBox(source());
950 break;
951 }
952 default:
953 {
954 // push dummy to maintain correct item property index
955 itemProperty = new ItemProperty();
956 break;
957 }
958 }
959 status_t err = itemProperty->parse(offset, size);
960 if (err != OK) {
961 return err;
962 }
963 mItemProperties->push_back(itemProperty);
964 return OK;
965}
966
967struct IprpBox : public Box {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800968 IprpBox(DataSourceBase *source,
Chong Zhangb51ca282017-07-26 16:25:28 -0700969 Vector<sp<ItemProperty> > *properties,
970 Vector<AssociationEntry> *associations) :
971 Box(source, FOURCC('i', 'p', 'r', 'p')),
972 mProperties(properties), mAssociations(associations) {}
973
974 status_t parse(off64_t offset, size_t size);
975protected:
976 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
977
978private:
979 Vector<sp<ItemProperty> > *mProperties;
980 Vector<AssociationEntry> *mAssociations;
981};
982
983status_t IprpBox::parse(off64_t offset, size_t size) {
984 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
985
986 status_t err = parseChunks(offset, size);
987 if (err != OK) {
988 return err;
989 }
990 return OK;
991}
992
993status_t IprpBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
994 switch(type) {
995 case FOURCC('i', 'p', 'c', 'o'):
996 {
997 IpcoBox ipcoBox(source(), mProperties);
998 return ipcoBox.parse(offset, size);
999 }
1000 case FOURCC('i', 'p', 'm', 'a'):
1001 {
1002 IpmaBox ipmaBox(source(), mAssociations);
1003 return ipmaBox.parse(offset, size);
1004 }
1005 default:
1006 {
1007 ALOGW("Unrecognized box.");
1008 break;
1009 }
1010 }
1011 return OK;
1012}
1013
1014/////////////////////////////////////////////////////////////////////
1015//
1016// ItemInfo related boxes
1017//
1018struct ItemInfo {
1019 uint32_t itemId;
1020 uint32_t itemType;
Chong Zhangd3e0d862017-10-03 13:17:13 -07001021 bool hidden;
Chong Zhangb51ca282017-07-26 16:25:28 -07001022};
1023
1024struct InfeBox : public FullBox {
Marco Nelissen2a243f02018-01-30 08:29:57 -08001025 InfeBox(DataSourceBase *source) :
Chong Zhangb51ca282017-07-26 16:25:28 -07001026 FullBox(source, FOURCC('i', 'n', 'f', 'e')) {}
1027
1028 status_t parse(off64_t offset, size_t size, ItemInfo *itemInfo);
1029
1030private:
1031 bool parseNullTerminatedString(off64_t *offset, size_t *size, String8 *out);
1032};
1033
1034bool InfeBox::parseNullTerminatedString(
1035 off64_t *offset, size_t *size, String8 *out) {
Chong Zhang70601852018-03-08 15:23:43 -08001036 char tmp;
1037 Vector<char> buf;
1038 buf.setCapacity(256);
Chong Zhangb51ca282017-07-26 16:25:28 -07001039 off64_t newOffset = *offset;
1040 off64_t stopOffset = *offset + *size;
1041 while (newOffset < stopOffset) {
Chong Zhang70601852018-03-08 15:23:43 -08001042 if (!source()->readAt(newOffset++, &tmp, 1)) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001043 return false;
1044 }
Chong Zhang70601852018-03-08 15:23:43 -08001045 buf.push_back(tmp);
1046 if (tmp == 0) {
1047 out->setTo(buf.array());
Chong Zhangb51ca282017-07-26 16:25:28 -07001048
1049 *offset = newOffset;
1050 *size = stopOffset - newOffset;
1051
1052 return true;
1053 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001054 }
1055 return false;
1056}
1057
1058status_t InfeBox::parse(off64_t offset, size_t size, ItemInfo *itemInfo) {
1059 status_t err = parseFullBoxHeader(&offset, &size);
1060 if (err != OK) {
1061 return err;
1062 }
1063
1064 if (version() == 0 || version() == 1) {
Chong Zhangecd08132017-10-05 16:09:29 -07001065 return ERROR_UNSUPPORTED;
Chong Zhangb51ca282017-07-26 16:25:28 -07001066 } else { // version >= 2
1067 uint32_t item_id;
1068 size_t itemIdSize = (version() == 2) ? 2 : 4;
1069 if (size < itemIdSize + 6) {
1070 return ERROR_MALFORMED;
1071 }
1072 if (!source()->getUInt32Var(offset, &item_id, itemIdSize)) {
1073 return ERROR_IO;
1074 }
1075 ALOGV("item_id %d", item_id);
1076 offset += itemIdSize;
1077 uint16_t item_protection_index;
1078 if (!source()->getUInt16(offset, &item_protection_index)) {
1079 return ERROR_IO;
1080 }
1081 ALOGV("item_protection_index %d", item_protection_index);
1082 offset += 2;
1083 uint32_t item_type;
1084 if (!source()->getUInt32(offset, &item_type)) {
1085 return ERROR_IO;
1086 }
1087
1088 itemInfo->itemId = item_id;
1089 itemInfo->itemType = item_type;
Chong Zhangd3e0d862017-10-03 13:17:13 -07001090 // According to HEIF spec, (flags & 1) indicates the image is hidden
1091 // and not supposed to be displayed.
1092 itemInfo->hidden = (flags() & 1);
Chong Zhangb51ca282017-07-26 16:25:28 -07001093
1094 char itemTypeString[5];
1095 MakeFourCCString(item_type, itemTypeString);
1096 ALOGV("item_type %s", itemTypeString);
1097 offset += 4;
1098 size -= itemIdSize + 6;
1099
1100 String8 item_name;
1101 if (!parseNullTerminatedString(&offset, &size, &item_name)) {
1102 return ERROR_MALFORMED;
1103 }
1104 ALOGV("item_name %s", item_name.c_str());
1105
1106 if (item_type == FOURCC('m', 'i', 'm', 'e')) {
1107 String8 content_type;
1108 if (!parseNullTerminatedString(&offset, &size, &content_type)) {
1109 return ERROR_MALFORMED;
1110 }
1111
Ray Essick46c24cd2018-03-08 13:54:19 -08001112 // content_encoding is optional; can be omitted if would be empty
1113 if (size > 0) {
1114 String8 content_encoding;
1115 if (!parseNullTerminatedString(&offset, &size, &content_encoding)) {
1116 return ERROR_MALFORMED;
1117 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001118 }
1119 } else if (item_type == FOURCC('u', 'r', 'i', ' ')) {
1120 String8 item_uri_type;
1121 if (!parseNullTerminatedString(&offset, &size, &item_uri_type)) {
1122 return ERROR_MALFORMED;
1123 }
1124 }
1125 }
1126 return OK;
1127}
1128
1129struct IinfBox : public FullBox {
Marco Nelissen2a243f02018-01-30 08:29:57 -08001130 IinfBox(DataSourceBase *source, Vector<ItemInfo> *itemInfos) :
Chong Zhangb51ca282017-07-26 16:25:28 -07001131 FullBox(source, FOURCC('i', 'i', 'n', 'f')),
1132 mItemInfos(itemInfos), mHasGrids(false) {}
1133
1134 status_t parse(off64_t offset, size_t size);
1135
1136 bool hasGrids() { return mHasGrids; }
1137
1138protected:
1139 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
1140
1141private:
1142 Vector<ItemInfo> *mItemInfos;
1143 bool mHasGrids;
1144};
1145
1146status_t IinfBox::parse(off64_t offset, size_t size) {
1147 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1148
1149 status_t err = parseFullBoxHeader(&offset, &size);
1150 if (err != OK) {
1151 return err;
1152 }
1153
1154 size_t entryCountSize = version() == 0 ? 2 : 4;
1155 if (size < entryCountSize) {
1156 return ERROR_MALFORMED;
1157 }
1158 uint32_t entry_count;
1159 if (!source()->getUInt32Var(offset, &entry_count, entryCountSize)) {
1160 return ERROR_IO;
1161 }
1162 ALOGV("entry_count %d", entry_count);
1163
1164 off64_t stopOffset = offset + size;
1165 offset += entryCountSize;
1166 for (size_t i = 0; i < entry_count && offset < stopOffset; i++) {
1167 ALOGV("entry %zu", i);
1168 status_t err = parseChunk(&offset);
1169 if (err != OK) {
1170 return err;
1171 }
1172 }
1173 if (offset != stopOffset) {
1174 return ERROR_MALFORMED;
1175 }
1176
1177 return OK;
1178}
1179
1180status_t IinfBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
1181 if (type != FOURCC('i', 'n', 'f', 'e')) {
1182 return OK;
1183 }
1184
1185 InfeBox infeBox(source());
1186 ItemInfo itemInfo;
1187 status_t err = infeBox.parse(offset, size, &itemInfo);
Chong Zhangecd08132017-10-05 16:09:29 -07001188 if (err == OK) {
1189 mItemInfos->push_back(itemInfo);
1190 mHasGrids |= (itemInfo.itemType == FOURCC('g', 'r', 'i', 'd'));
Chong Zhangb51ca282017-07-26 16:25:28 -07001191 }
Chong Zhang5518a2c2017-10-13 18:31:25 -07001192 // InfeBox parse returns ERROR_UNSUPPORTED if the box if an unsupported
1193 // version. Ignore this error as it's not fatal.
1194 return (err == ERROR_UNSUPPORTED) ? OK : err;
Chong Zhangb51ca282017-07-26 16:25:28 -07001195}
1196
1197//////////////////////////////////////////////////////////////////
1198
Marco Nelissen2a243f02018-01-30 08:29:57 -08001199ItemTable::ItemTable(DataSourceBase *source)
Chong Zhangb51ca282017-07-26 16:25:28 -07001200 : mDataSource(source),
1201 mPrimaryItemId(0),
1202 mIdatOffset(0),
1203 mIdatSize(0),
1204 mImageItemsValid(false),
Chong Zhangecd08132017-10-05 16:09:29 -07001205 mCurrentItemIndex(0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001206 mRequiredBoxes.insert('iprp');
1207 mRequiredBoxes.insert('iloc');
1208 mRequiredBoxes.insert('pitm');
1209 mRequiredBoxes.insert('iinf');
1210}
1211
1212ItemTable::~ItemTable() {}
1213
1214status_t ItemTable::parse(uint32_t type, off64_t data_offset, size_t chunk_data_size) {
1215 switch(type) {
1216 case FOURCC('i', 'l', 'o', 'c'):
1217 {
1218 return parseIlocBox(data_offset, chunk_data_size);
1219 }
1220 case FOURCC('i', 'i', 'n', 'f'):
1221 {
1222 return parseIinfBox(data_offset, chunk_data_size);
1223 }
1224 case FOURCC('i', 'p', 'r', 'p'):
1225 {
1226 return parseIprpBox(data_offset, chunk_data_size);
1227 }
1228 case FOURCC('p', 'i', 't', 'm'):
1229 {
1230 return parsePitmBox(data_offset, chunk_data_size);
1231 }
1232 case FOURCC('i', 'd', 'a', 't'):
1233 {
1234 return parseIdatBox(data_offset, chunk_data_size);
1235 }
1236 case FOURCC('i', 'r', 'e', 'f'):
1237 {
1238 return parseIrefBox(data_offset, chunk_data_size);
1239 }
1240 case FOURCC('i', 'p', 'r', 'o'):
1241 {
1242 ALOGW("ipro box not supported!");
1243 break;
1244 }
1245 default:
1246 {
1247 ALOGW("unrecognized box type: 0x%x", type);
1248 break;
1249 }
1250 }
1251 return ERROR_UNSUPPORTED;
1252}
1253
1254status_t ItemTable::parseIlocBox(off64_t offset, size_t size) {
1255 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1256
1257 IlocBox ilocBox(mDataSource, &mItemLocs);
1258 status_t err = ilocBox.parse(offset, size);
1259 if (err != OK) {
1260 return err;
1261 }
1262
1263 if (ilocBox.hasConstructMethod1()) {
1264 mRequiredBoxes.insert('idat');
1265 }
1266
1267 return buildImageItemsIfPossible('iloc');
1268}
1269
1270status_t ItemTable::parseIinfBox(off64_t offset, size_t size) {
1271 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1272
1273 IinfBox iinfBox(mDataSource, &mItemInfos);
1274 status_t err = iinfBox.parse(offset, size);
1275 if (err != OK) {
1276 return err;
1277 }
1278
1279 if (iinfBox.hasGrids()) {
1280 mRequiredBoxes.insert('iref');
1281 }
1282
1283 return buildImageItemsIfPossible('iinf');
1284}
1285
1286status_t ItemTable::parsePitmBox(off64_t offset, size_t size) {
1287 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1288
1289 PitmBox pitmBox(mDataSource);
1290 status_t err = pitmBox.parse(offset, size, &mPrimaryItemId);
1291 if (err != OK) {
1292 return err;
1293 }
1294
1295 return buildImageItemsIfPossible('pitm');
1296}
1297
1298status_t ItemTable::parseIprpBox(off64_t offset, size_t size) {
1299 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1300
1301 IprpBox iprpBox(mDataSource, &mItemProperties, &mAssociations);
1302 status_t err = iprpBox.parse(offset, size);
1303 if (err != OK) {
1304 return err;
1305 }
1306
1307 return buildImageItemsIfPossible('iprp');
1308}
1309
1310status_t ItemTable::parseIdatBox(off64_t offset, size_t size) {
1311 ALOGV("%s: idat offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1312
1313 // only remember the offset and size of idat box for later use
1314 mIdatOffset = offset;
1315 mIdatSize = size;
1316
1317 return buildImageItemsIfPossible('idat');
1318}
1319
1320status_t ItemTable::parseIrefBox(off64_t offset, size_t size) {
1321 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1322
1323 IrefBox irefBox(mDataSource, &mItemReferences);
1324 status_t err = irefBox.parse(offset, size);
1325 if (err != OK) {
1326 return err;
1327 }
1328
1329 return buildImageItemsIfPossible('iref');
1330}
1331
1332status_t ItemTable::buildImageItemsIfPossible(uint32_t type) {
1333 if (mImageItemsValid) {
1334 return OK;
1335 }
1336
1337 mBoxesSeen.insert(type);
1338
1339 // need at least 'iprp', 'iloc', 'pitm', 'iinf';
1340 // need 'idat' if any items used construction_method of 2;
1341 // need 'iref' if there are grids.
1342 if (!std::includes(
1343 mBoxesSeen.begin(), mBoxesSeen.end(),
1344 mRequiredBoxes.begin(), mRequiredBoxes.end())) {
1345 return OK;
1346 }
1347
1348 ALOGV("building image table...");
1349
1350 for (size_t i = 0; i < mItemInfos.size(); i++) {
1351 const ItemInfo &info = mItemInfos[i];
1352
Chong Zhang01a76012018-03-14 18:19:49 -07001353 // Only handle 3 types of items, all others are ignored:
1354 // 'grid': derived image from tiles
1355 // 'hvc1': coded image (or tile)
1356 // 'Exif': EXIF metadata
Chong Zhangb51ca282017-07-26 16:25:28 -07001357 if (info.itemType != FOURCC('g', 'r', 'i', 'd') &&
Chong Zhang01a76012018-03-14 18:19:49 -07001358 info.itemType != FOURCC('h', 'v', 'c', '1') &&
1359 info.itemType != FOURCC('E', 'x', 'i', 'f')) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001360 continue;
1361 }
1362
Chong Zhangecd08132017-10-05 16:09:29 -07001363 ssize_t itemIndex = mItemIdToItemMap.indexOfKey(info.itemId);
1364 if (itemIndex >= 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001365 ALOGW("ignoring duplicate image item id %d", info.itemId);
1366 continue;
1367 }
1368
1369 ssize_t ilocIndex = mItemLocs.indexOfKey(info.itemId);
1370 if (ilocIndex < 0) {
1371 ALOGE("iloc missing for image item id %d", info.itemId);
1372 continue;
1373 }
1374 const ItemLoc &iloc = mItemLocs[ilocIndex];
1375
1376 off64_t offset;
1377 size_t size;
1378 if (iloc.getLoc(&offset, &size, mIdatOffset, mIdatSize) != OK) {
1379 return ERROR_MALFORMED;
1380 }
1381
Chong Zhang01a76012018-03-14 18:19:49 -07001382 if (info.itemType == FOURCC('E', 'x', 'i', 'f')) {
1383 // Only add if the Exif data is non-empty. The first 4 bytes contain
1384 // the offset to TIFF header, which the Exif parser doesn't use.
1385 if (size > 4) {
1386 ExifItem exifItem = {
1387 .offset = offset,
1388 .size = size,
1389 };
1390 mItemIdToExifMap.add(info.itemId, exifItem);
1391 }
1392 continue;
1393 }
1394
Chong Zhangd3e0d862017-10-03 13:17:13 -07001395 ImageItem image(info.itemType, info.itemId, info.hidden);
Chong Zhangb51ca282017-07-26 16:25:28 -07001396
1397 ALOGV("adding %s: itemId %d", image.isGrid() ? "grid" : "image", info.itemId);
1398
1399 if (image.isGrid()) {
Chong Zhanga9879ef2018-05-10 12:32:50 -07001400 // ImageGrid struct is at least 8-byte, at most 12-byte (if flags&1)
1401 if (size < 8 || size > 12) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001402 return ERROR_MALFORMED;
1403 }
1404 uint8_t buf[12];
1405 if (!mDataSource->readAt(offset, buf, size)) {
1406 return ERROR_IO;
1407 }
1408
1409 image.rows = buf[2] + 1;
1410 image.columns = buf[3] + 1;
1411
1412 ALOGV("rows %d, columans %d", image.rows, image.columns);
1413 } else {
1414 image.offset = offset;
1415 image.size = size;
1416 }
Chong Zhangecd08132017-10-05 16:09:29 -07001417 mItemIdToItemMap.add(info.itemId, image);
Chong Zhangb51ca282017-07-26 16:25:28 -07001418 }
1419
1420 for (size_t i = 0; i < mAssociations.size(); i++) {
1421 attachProperty(mAssociations[i]);
1422 }
1423
1424 for (size_t i = 0; i < mItemReferences.size(); i++) {
Chong Zhang01a76012018-03-14 18:19:49 -07001425 mItemReferences[i]->apply(mItemIdToItemMap, mItemIdToExifMap);
Chong Zhangb51ca282017-07-26 16:25:28 -07001426 }
1427
Chong Zhangd3e0d862017-10-03 13:17:13 -07001428 bool foundPrimary = false;
1429 for (size_t i = 0; i < mItemIdToItemMap.size(); i++) {
1430 // add all non-hidden images, also add the primary even if it's marked
1431 // hidden, in case the primary is set to a thumbnail
1432 bool isPrimary = (mItemIdToItemMap[i].itemId == mPrimaryItemId);
1433 if (!mItemIdToItemMap[i].hidden || isPrimary) {
1434 mDisplayables.push_back(i);
1435 }
1436 foundPrimary |= isPrimary;
1437 }
1438
1439 ALOGV("found %zu displayables", mDisplayables.size());
1440
1441 // fail if no displayables are found
1442 if (mDisplayables.empty()) {
1443 return ERROR_MALFORMED;
1444 }
1445
1446 // if the primary item id is invalid, set primary to the first displayable
1447 if (!foundPrimary) {
1448 mPrimaryItemId = mItemIdToItemMap[mDisplayables[0]].itemId;
1449 }
1450
Chong Zhangb51ca282017-07-26 16:25:28 -07001451 mImageItemsValid = true;
1452 return OK;
1453}
1454
1455void ItemTable::attachProperty(const AssociationEntry &association) {
Chong Zhangecd08132017-10-05 16:09:29 -07001456 ssize_t itemIndex = mItemIdToItemMap.indexOfKey(association.itemId);
Chong Zhangb51ca282017-07-26 16:25:28 -07001457
1458 // ignore non-image items
Chong Zhangecd08132017-10-05 16:09:29 -07001459 if (itemIndex < 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001460 return;
1461 }
1462
1463 uint16_t propertyIndex = association.index;
1464 if (propertyIndex >= mItemProperties.size()) {
1465 ALOGW("Ignoring invalid property index %d", propertyIndex);
1466 return;
1467 }
1468
1469 ALOGV("attach property %d to item id %d)",
1470 propertyIndex, association.itemId);
1471
Chong Zhangd3e0d862017-10-03 13:17:13 -07001472 mItemProperties[propertyIndex]->attachTo(mItemIdToItemMap.editValueAt(itemIndex));
Chong Zhangb51ca282017-07-26 16:25:28 -07001473}
1474
Chong Zhangd3e0d862017-10-03 13:17:13 -07001475uint32_t ItemTable::countImages() const {
1476 return mImageItemsValid ? mDisplayables.size() : 0;
1477}
1478
1479sp<MetaData> ItemTable::getImageMeta(const uint32_t imageIndex) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001480 if (!mImageItemsValid) {
1481 return NULL;
1482 }
1483
Chong Zhangd3e0d862017-10-03 13:17:13 -07001484 if (imageIndex >= mDisplayables.size()) {
1485 ALOGE("%s: invalid image index %u", __FUNCTION__, imageIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001486 return NULL;
1487 }
Chong Zhangd3e0d862017-10-03 13:17:13 -07001488 const uint32_t itemIndex = mDisplayables[imageIndex];
1489 ALOGV("image[%u]: item index %u", imageIndex, itemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001490
Chong Zhangecd08132017-10-05 16:09:29 -07001491 const ImageItem *image = &mItemIdToItemMap[itemIndex];
Chong Zhangb51ca282017-07-26 16:25:28 -07001492
1493 sp<MetaData> meta = new MetaData;
Chong Zhangd3e0d862017-10-03 13:17:13 -07001494 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
Chong Zhangb51ca282017-07-26 16:25:28 -07001495
Chong Zhangd3e0d862017-10-03 13:17:13 -07001496 if (image->itemId == mPrimaryItemId) {
Chong Zhangfbc97c52017-11-10 13:49:46 -08001497 meta->setInt32(kKeyTrackIsDefault, 1);
Chong Zhangd3e0d862017-10-03 13:17:13 -07001498 }
1499
1500 ALOGV("image[%u]: size %dx%d", imageIndex, image->width, image->height);
1501
Chong Zhangb51ca282017-07-26 16:25:28 -07001502 meta->setInt32(kKeyWidth, image->width);
1503 meta->setInt32(kKeyHeight, image->height);
1504 if (image->rotation != 0) {
Chong Zhang686c23b2017-10-05 15:16:59 -07001505 // Rotation angle in HEIF is CCW, convert to CW here to be
1506 // consistent with the other media formats.
1507 switch(image->rotation) {
1508 case 90: meta->setInt32(kKeyRotation, 270); break;
1509 case 180: meta->setInt32(kKeyRotation, 180); break;
1510 case 270: meta->setInt32(kKeyRotation, 90); break;
1511 default: break; // don't set if invalid
1512 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001513 }
1514 meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5);
1515
1516 if (!image->thumbnails.empty()) {
Chong Zhangecd08132017-10-05 16:09:29 -07001517 ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(image->thumbnails[0]);
1518 if (thumbItemIndex >= 0) {
1519 const ImageItem &thumbnail = mItemIdToItemMap[thumbItemIndex];
Chong Zhangb51ca282017-07-26 16:25:28 -07001520
1521 meta->setInt32(kKeyThumbnailWidth, thumbnail.width);
1522 meta->setInt32(kKeyThumbnailHeight, thumbnail.height);
1523 meta->setData(kKeyThumbnailHVCC, kTypeHVCC,
1524 thumbnail.hvcc->data(), thumbnail.hvcc->size());
Chong Zhangd3e0d862017-10-03 13:17:13 -07001525 ALOGV("image[%u]: thumbnail: size %dx%d, item index %zd",
1526 imageIndex, thumbnail.width, thumbnail.height, thumbItemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001527 } else {
Chong Zhangecd08132017-10-05 16:09:29 -07001528 ALOGW("%s: Referenced thumbnail does not exist!", __FUNCTION__);
Chong Zhangb51ca282017-07-26 16:25:28 -07001529 }
1530 }
1531
1532 if (image->isGrid()) {
Chong Zhangecd08132017-10-05 16:09:29 -07001533 ssize_t tileItemIndex = mItemIdToItemMap.indexOfKey(image->dimgRefs[0]);
1534 if (tileItemIndex < 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001535 return NULL;
1536 }
Chong Zhangd3e0d862017-10-03 13:17:13 -07001537 meta->setInt32(kKeyGridRows, image->rows);
1538 meta->setInt32(kKeyGridCols, image->columns);
Chong Zhangb51ca282017-07-26 16:25:28 -07001539
Chong Zhangee079fe2017-08-23 13:51:17 -07001540 // point image to the first tile for grid size and HVCC
Chong Zhangecd08132017-10-05 16:09:29 -07001541 image = &mItemIdToItemMap.editValueAt(tileItemIndex);
Chong Zhang67d5b912018-03-14 18:17:40 -07001542 meta->setInt32(kKeyTileWidth, image->width);
1543 meta->setInt32(kKeyTileHeight, image->height);
Chong Zhangee079fe2017-08-23 13:51:17 -07001544 meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5);
Chong Zhangb51ca282017-07-26 16:25:28 -07001545 }
1546
1547 if (image->hvcc == NULL) {
Chong Zhangd3e0d862017-10-03 13:17:13 -07001548 ALOGE("%s: hvcc is missing for image[%u]!", __FUNCTION__, imageIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001549 return NULL;
1550 }
1551 meta->setData(kKeyHVCC, kTypeHVCC, image->hvcc->data(), image->hvcc->size());
1552
1553 if (image->icc != NULL) {
1554 meta->setData(kKeyIccProfile, 0, image->icc->data(), image->icc->size());
1555 }
1556 return meta;
1557}
1558
Chong Zhangd3e0d862017-10-03 13:17:13 -07001559status_t ItemTable::findImageItem(const uint32_t imageIndex, uint32_t *itemIndex) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001560 if (!mImageItemsValid) {
1561 return INVALID_OPERATION;
1562 }
1563
Chong Zhangd3e0d862017-10-03 13:17:13 -07001564 if (imageIndex >= mDisplayables.size()) {
1565 ALOGE("%s: invalid image index %d", __FUNCTION__, imageIndex);
1566 return BAD_VALUE;
Chong Zhangb51ca282017-07-26 16:25:28 -07001567 }
1568
Chong Zhangd3e0d862017-10-03 13:17:13 -07001569 *itemIndex = mDisplayables[imageIndex];
1570
1571 ALOGV("image[%u]: item index %u", imageIndex, *itemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001572 return OK;
1573}
1574
Chong Zhangd3e0d862017-10-03 13:17:13 -07001575status_t ItemTable::findThumbnailItem(const uint32_t imageIndex, uint32_t *itemIndex) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001576 if (!mImageItemsValid) {
1577 return INVALID_OPERATION;
1578 }
1579
Chong Zhangd3e0d862017-10-03 13:17:13 -07001580 if (imageIndex >= mDisplayables.size()) {
1581 ALOGE("%s: invalid image index %d", __FUNCTION__, imageIndex);
1582 return BAD_VALUE;
Chong Zhangb51ca282017-07-26 16:25:28 -07001583 }
1584
Chong Zhangd3e0d862017-10-03 13:17:13 -07001585 uint32_t masterItemIndex = mDisplayables[imageIndex];
1586
1587 const ImageItem &masterImage = mItemIdToItemMap[masterItemIndex];
1588 if (masterImage.thumbnails.empty()) {
1589 *itemIndex = masterItemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001590 return OK;
1591 }
1592
Chong Zhangd3e0d862017-10-03 13:17:13 -07001593 ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(masterImage.thumbnails[0]);
Chong Zhangecd08132017-10-05 16:09:29 -07001594 if (thumbItemIndex < 0) {
Chong Zhangd5fa3572018-04-09 19:03:10 -07001595 // Do not return the master image in this case, fail it so that the
1596 // thumbnail extraction code knows we really don't have it.
1597 return INVALID_OPERATION;
Chong Zhangb51ca282017-07-26 16:25:28 -07001598 }
1599
Chong Zhangecd08132017-10-05 16:09:29 -07001600 *itemIndex = thumbItemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001601 return OK;
1602}
1603
1604status_t ItemTable::getImageOffsetAndSize(
Chong Zhangecd08132017-10-05 16:09:29 -07001605 uint32_t *itemIndex, off64_t *offset, size_t *size) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001606 if (!mImageItemsValid) {
1607 return INVALID_OPERATION;
1608 }
1609
Chong Zhangecd08132017-10-05 16:09:29 -07001610 if (itemIndex != NULL) {
1611 if (*itemIndex >= mItemIdToItemMap.size()) {
1612 ALOGE("%s: Bad item index!", __FUNCTION__);
Chong Zhangb51ca282017-07-26 16:25:28 -07001613 return BAD_VALUE;
1614 }
Chong Zhangecd08132017-10-05 16:09:29 -07001615 mCurrentItemIndex = *itemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001616 }
1617
Chong Zhangecd08132017-10-05 16:09:29 -07001618 ImageItem &image = mItemIdToItemMap.editValueAt(mCurrentItemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001619 if (image.isGrid()) {
1620 uint32_t tileItemId;
Chong Zhangecd08132017-10-05 16:09:29 -07001621 status_t err = image.getNextTileItemId(&tileItemId, itemIndex != NULL);
Chong Zhangb51ca282017-07-26 16:25:28 -07001622 if (err != OK) {
1623 return err;
1624 }
Chong Zhangecd08132017-10-05 16:09:29 -07001625 ssize_t tileItemIndex = mItemIdToItemMap.indexOfKey(tileItemId);
1626 if (tileItemIndex < 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001627 return ERROR_END_OF_STREAM;
1628 }
Chong Zhangecd08132017-10-05 16:09:29 -07001629 *offset = mItemIdToItemMap[tileItemIndex].offset;
1630 *size = mItemIdToItemMap[tileItemIndex].size;
Chong Zhangb51ca282017-07-26 16:25:28 -07001631 } else {
Chong Zhangecd08132017-10-05 16:09:29 -07001632 if (itemIndex == NULL) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001633 // For single images, we only allow it to be read once, after that
Chong Zhangecd08132017-10-05 16:09:29 -07001634 // it's EOS. New item index must be requested each time.
Chong Zhangb51ca282017-07-26 16:25:28 -07001635 return ERROR_END_OF_STREAM;
1636 }
Chong Zhangecd08132017-10-05 16:09:29 -07001637 *offset = mItemIdToItemMap[mCurrentItemIndex].offset;
1638 *size = mItemIdToItemMap[mCurrentItemIndex].size;
Chong Zhangb51ca282017-07-26 16:25:28 -07001639 }
1640
1641 return OK;
1642}
1643
Chong Zhang01a76012018-03-14 18:19:49 -07001644status_t ItemTable::getExifOffsetAndSize(off64_t *offset, size_t *size) {
1645 if (!mImageItemsValid) {
1646 return INVALID_OPERATION;
1647 }
1648
1649 ssize_t itemIndex = mItemIdToItemMap.indexOfKey(mPrimaryItemId);
1650
1651 // this should not happen, something's seriously wrong.
1652 if (itemIndex < 0) {
1653 return INVALID_OPERATION;
1654 }
1655
1656 const ImageItem &image = mItemIdToItemMap[itemIndex];
1657 if (image.cdscRefs.size() == 0) {
1658 return NAME_NOT_FOUND;
1659 }
1660
1661 ssize_t exifIndex = mItemIdToExifMap.indexOfKey(image.cdscRefs[0]);
1662 if (exifIndex < 0) {
1663 return NAME_NOT_FOUND;
1664 }
1665
1666 // skip the first 4-byte of the offset to TIFF header
1667 *offset = mItemIdToExifMap[exifIndex].offset + 4;
1668 *size = mItemIdToExifMap[exifIndex].size - 4;
1669 return OK;
1670}
1671
Chong Zhangb51ca282017-07-26 16:25:28 -07001672} // namespace heif
1673
1674} // namespace android