blob: 74d5f1fa66c398519d81af15fef2cf136a9a664d [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;
79 size_t nextTileIndex;
80};
81
82
83/////////////////////////////////////////////////////////////////////
84//
85// ISO boxes
86//
87
88struct Box {
89protected:
Marco Nelissen2a243f02018-01-30 08:29:57 -080090 Box(DataSourceBase *source, uint32_t type) :
Chong Zhangb51ca282017-07-26 16:25:28 -070091 mDataSource(source), mType(type) {}
92
93 virtual ~Box() {}
94
95 virtual status_t onChunkData(
96 uint32_t /*type*/, off64_t /*offset*/, size_t /*size*/) {
97 return OK;
98 }
99
100 inline uint32_t type() const { return mType; }
101
Marco Nelissen2a243f02018-01-30 08:29:57 -0800102 inline DataSourceBase *source() const { return mDataSource; }
Chong Zhangb51ca282017-07-26 16:25:28 -0700103
104 status_t parseChunk(off64_t *offset);
105
106 status_t parseChunks(off64_t offset, size_t size);
107
108private:
Marco Nelissen2a243f02018-01-30 08:29:57 -0800109 DataSourceBase *mDataSource;
Chong Zhangb51ca282017-07-26 16:25:28 -0700110 uint32_t mType;
111};
112
113status_t Box::parseChunk(off64_t *offset) {
114 if (*offset < 0) {
115 ALOGE("b/23540914");
116 return ERROR_MALFORMED;
117 }
118 uint32_t hdr[2];
119 if (mDataSource->readAt(*offset, hdr, 8) < 8) {
120 return ERROR_IO;
121 }
122 uint64_t chunk_size = ntohl(hdr[0]);
123 int32_t chunk_type = ntohl(hdr[1]);
124 off64_t data_offset = *offset + 8;
125
126 if (chunk_size == 1) {
127 if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) {
128 return ERROR_IO;
129 }
130 chunk_size = ntoh64(chunk_size);
131 data_offset += 8;
132
133 if (chunk_size < 16) {
134 // The smallest valid chunk is 16 bytes long in this case.
135 return ERROR_MALFORMED;
136 }
137 } else if (chunk_size == 0) {
138 // This shouldn't happen since we should never be top level
139 ALOGE("invalid chunk size 0 for non-top level box");
140 return ERROR_MALFORMED;
141 } else if (chunk_size < 8) {
142 // The smallest valid chunk is 8 bytes long.
143 ALOGE("invalid chunk size: %lld", (long long)chunk_size);
144 return ERROR_MALFORMED;
145 }
146
147 char chunk[5];
148 MakeFourCCString(chunk_type, chunk);
149 ALOGV("chunk: %s @ %lld", chunk, (long long)*offset);
150
151 off64_t chunk_data_size = chunk_size - (data_offset - *offset);
152 if (chunk_data_size < 0) {
153 ALOGE("b/23540914");
154 return ERROR_MALFORMED;
155 }
156
157 status_t err = onChunkData(chunk_type, data_offset, chunk_data_size);
158
159 if (err != OK) {
160 return err;
161 }
162 *offset += chunk_size;
163 return OK;
164}
165
166status_t Box::parseChunks(off64_t offset, size_t size) {
167 off64_t stopOffset = offset + size;
168 while (offset < stopOffset) {
169 status_t err = parseChunk(&offset);
170 if (err != OK) {
171 return err;
172 }
173 }
174 if (offset != stopOffset) {
175 return ERROR_MALFORMED;
176 }
177 return OK;
178}
179
180///////////////////////////////////////////////////////////////////////
181
182struct FullBox : public Box {
183protected:
Marco Nelissen2a243f02018-01-30 08:29:57 -0800184 FullBox(DataSourceBase *source, uint32_t type) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700185 Box(source, type), mVersion(0), mFlags(0) {}
186
187 inline uint8_t version() const { return mVersion; }
188
189 inline uint32_t flags() const { return mFlags; }
190
191 status_t parseFullBoxHeader(off64_t *offset, size_t *size);
192
193private:
194 uint8_t mVersion;
195 uint32_t mFlags;
196};
197
198status_t FullBox::parseFullBoxHeader(off64_t *offset, size_t *size) {
199 if (*size < 4) {
200 return ERROR_MALFORMED;
201 }
202 if (!source()->readAt(*offset, &mVersion, 1)) {
203 return ERROR_IO;
204 }
205 if (!source()->getUInt24(*offset + 1, &mFlags)) {
206 return ERROR_IO;
207 }
208 *offset += 4;
209 *size -= 4;
210 return OK;
211}
212
213/////////////////////////////////////////////////////////////////////
214//
215// PrimaryImage box
216//
217
218struct PitmBox : public FullBox {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800219 PitmBox(DataSourceBase *source) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700220 FullBox(source, FOURCC('p', 'i', 't', 'm')) {}
221
222 status_t parse(off64_t offset, size_t size, uint32_t *primaryItemId);
223};
224
225status_t PitmBox::parse(off64_t offset, size_t size, uint32_t *primaryItemId) {
226 status_t err = parseFullBoxHeader(&offset, &size);
227 if (err != OK) {
228 return err;
229 }
230
231 size_t itemIdSize = (version() == 0) ? 2 : 4;
232 if (size < itemIdSize) {
233 return ERROR_MALFORMED;
234 }
235 uint32_t itemId;
236 if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) {
237 return ERROR_IO;
238 }
239
240 ALOGV("primary id %d", itemId);
241 *primaryItemId = itemId;
242
243 return OK;
244}
245
246/////////////////////////////////////////////////////////////////////
247//
248// ItemLocation related boxes
249//
250
251struct ExtentEntry {
252 uint64_t extentIndex;
253 uint64_t extentOffset;
254 uint64_t extentLength;
255};
256
257struct ItemLoc {
258 ItemLoc() : ItemLoc(0, 0, 0, 0) {}
259 ItemLoc(uint32_t item_id, uint16_t construction_method,
260 uint16_t data_reference_index, uint64_t base_offset) :
261 itemId(item_id),
262 constructionMethod(construction_method),
263 dataReferenceIndex(data_reference_index),
264 baseOffset(base_offset) {}
265
266 void addExtent(const ExtentEntry& extent) {
267 extents.push_back(extent);
268 }
269
270 status_t getLoc(off64_t *offset, size_t *size,
271 off64_t idatOffset, size_t idatSize) const {
272 // TODO: fix extent handling, fix constructionMethod = 2
273 CHECK(extents.size() == 1);
274 if (constructionMethod == 0) {
275 *offset = baseOffset + extents[0].extentOffset;
276 *size = extents[0].extentLength;
277 return OK;
278 } else if (constructionMethod == 1) {
279 if (baseOffset + extents[0].extentOffset + extents[0].extentLength
280 > idatSize) {
281 return ERROR_MALFORMED;
282 }
283 *offset = baseOffset + extents[0].extentOffset + idatOffset;
284 *size = extents[0].extentLength;
285 return OK;
286 }
287 return ERROR_UNSUPPORTED;
288 }
289
290 // parsed info
291 uint32_t itemId;
292 uint16_t constructionMethod;
293 uint16_t dataReferenceIndex;
294 off64_t baseOffset;
295 Vector<ExtentEntry> extents;
296};
297
298struct IlocBox : public FullBox {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800299 IlocBox(DataSourceBase *source, KeyedVector<uint32_t, ItemLoc> *itemLocs) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700300 FullBox(source, FOURCC('i', 'l', 'o', 'c')),
301 mItemLocs(itemLocs), mHasConstructMethod1(false) {}
302
303 status_t parse(off64_t offset, size_t size);
304
305 bool hasConstructMethod1() { return mHasConstructMethod1; }
306
307private:
308 static bool isSizeFieldValid(uint32_t offset_size) {
309 return offset_size == 0 || offset_size == 4 || offset_size == 8;
310 }
311 KeyedVector<uint32_t, ItemLoc> *mItemLocs;
312 bool mHasConstructMethod1;
313};
314
315status_t IlocBox::parse(off64_t offset, size_t size) {
316 status_t err = parseFullBoxHeader(&offset, &size);
317 if (err != OK) {
318 return err;
319 }
320 if (version() > 2) {
321 ALOGE("%s: invalid version %d", __FUNCTION__, version());
322 return ERROR_MALFORMED;
323 }
324
325 if (size < 2) {
326 return ERROR_MALFORMED;
327 }
328 uint8_t offset_size;
329 if (!source()->readAt(offset++, &offset_size, 1)) {
330 return ERROR_IO;
331 }
332 uint8_t length_size = (offset_size & 0xF);
333 offset_size >>= 4;
334
335 uint8_t base_offset_size;
336 if (!source()->readAt(offset++, &base_offset_size, 1)) {
337 return ERROR_IO;
338 }
339 uint8_t index_size = 0;
340 if (version() == 1 || version() == 2) {
341 index_size = (base_offset_size & 0xF);
342 }
343 base_offset_size >>= 4;
344 size -= 2;
345
346 if (!isSizeFieldValid(offset_size)
347 || !isSizeFieldValid(length_size)
348 || !isSizeFieldValid(base_offset_size)
349 || !isSizeFieldValid((index_size))) {
350 ALOGE("%s: offset size not valid: %d, %d, %d, %d", __FUNCTION__,
351 offset_size, length_size, base_offset_size, index_size);
352 return ERROR_MALFORMED;
353 }
354
355 uint32_t item_count;
356 size_t itemFieldSize = version() < 2 ? 2 : 4;
357 if (size < itemFieldSize) {
358 return ERROR_MALFORMED;
359 }
360 if (!source()->getUInt32Var(offset, &item_count, itemFieldSize)) {
361 return ERROR_IO;
362 }
363
364 ALOGV("item_count %lld", (long long) item_count);
365 offset += itemFieldSize;
366 size -= itemFieldSize;
367
368 for (size_t i = 0; i < item_count; i++) {
369 uint32_t item_id;
370 if (!source()->getUInt32Var(offset, &item_id, itemFieldSize)) {
371 return ERROR_IO;
372 }
373 ALOGV("item[%zu]: id %lld", i, (long long)item_id);
374 offset += itemFieldSize;
375
376 uint8_t construction_method = 0;
377 if (version() == 1 || version() == 2) {
378 uint8_t buf[2];
379 if (!source()->readAt(offset, buf, 2)) {
380 return ERROR_IO;
381 }
382 construction_method = (buf[1] & 0xF);
383 ALOGV("construction_method %d", construction_method);
384 if (construction_method == 1) {
385 mHasConstructMethod1 = true;
386 }
387
388 offset += 2;
389 }
390
391 uint16_t data_reference_index;
392 if (!source()->getUInt16(offset, &data_reference_index)) {
393 return ERROR_IO;
394 }
395 ALOGV("data_reference_index %d", data_reference_index);
396 if (data_reference_index != 0) {
397 // we don't support reference to other files
398 return ERROR_UNSUPPORTED;
399 }
400 offset += 2;
401
402 uint64_t base_offset = 0;
403 if (base_offset_size != 0) {
404 if (!source()->getUInt64Var(offset, &base_offset, base_offset_size)) {
405 return ERROR_IO;
406 }
407 offset += base_offset_size;
408 }
409 ALOGV("base_offset %lld", (long long) base_offset);
410
411 ssize_t index = mItemLocs->add(item_id, ItemLoc(
412 item_id, construction_method, data_reference_index, base_offset));
413 ItemLoc &item = mItemLocs->editValueAt(index);
414
415 uint16_t extent_count;
416 if (!source()->getUInt16(offset, &extent_count)) {
417 return ERROR_IO;
418 }
419 ALOGV("extent_count %d", extent_count);
420
421 if (extent_count > 1 && (offset_size == 0 || length_size == 0)) {
422 // if the item is dividec into more than one extents, offset and
423 // length must be present.
424 return ERROR_MALFORMED;
425 }
426 offset += 2;
427
428 for (size_t j = 0; j < extent_count; j++) {
429 uint64_t extent_index = 1; // default=1
430 if ((version() == 1 || version() == 2) && (index_size > 0)) {
431 if (!source()->getUInt64Var(offset, &extent_index, index_size)) {
432 return ERROR_IO;
433 }
434 // TODO: add support for this mode
435 offset += index_size;
436 ALOGV("extent_index %lld", (long long)extent_index);
437 }
438
439 uint64_t extent_offset = 0; // default=0
440 if (offset_size > 0) {
441 if (!source()->getUInt64Var(offset, &extent_offset, offset_size)) {
442 return ERROR_IO;
443 }
444 offset += offset_size;
445 }
446 ALOGV("extent_offset %lld", (long long)extent_offset);
447
448 uint64_t extent_length = 0; // this indicates full length of file
449 if (length_size > 0) {
450 if (!source()->getUInt64Var(offset, &extent_length, length_size)) {
451 return ERROR_IO;
452 }
453 offset += length_size;
454 }
455 ALOGV("extent_length %lld", (long long)extent_length);
456
457 item.addExtent({ extent_index, extent_offset, extent_length });
458 }
459 }
460 return OK;
461}
462
463/////////////////////////////////////////////////////////////////////
464//
465// ItemReference related boxes
466//
467
468struct ItemReference : public Box, public RefBase {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800469 ItemReference(DataSourceBase *source, uint32_t type, uint32_t itemIdSize) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700470 Box(source, type), mItemId(0), mRefIdSize(itemIdSize) {}
471
472 status_t parse(off64_t offset, size_t size);
473
474 uint32_t itemId() { return mItemId; }
475
Chong Zhangecd08132017-10-05 16:09:29 -0700476 void apply(KeyedVector<uint32_t, ImageItem> &itemIdToItemMap) const;
Chong Zhangb51ca282017-07-26 16:25:28 -0700477
478private:
479 uint32_t mItemId;
480 uint32_t mRefIdSize;
481 Vector<uint32_t> mRefs;
482
483 DISALLOW_EVIL_CONSTRUCTORS(ItemReference);
484};
485
Chong Zhangecd08132017-10-05 16:09:29 -0700486void ItemReference::apply(KeyedVector<uint32_t, ImageItem> &itemIdToItemMap) const {
487 ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
488
489 // ignore non-image items
490 if (itemIndex < 0) {
491 return;
492 }
493
494 ALOGV("attach reference type 0x%x to item id %d)", type(), mItemId);
495
496 if (type() == FOURCC('d', 'i', 'm', 'g')) {
497 ImageItem &derivedImage = itemIdToItemMap.editValueAt(itemIndex);
498 if (!derivedImage.dimgRefs.empty()) {
499 ALOGW("dimgRefs if not clean!");
500 }
501 derivedImage.dimgRefs.appendVector(mRefs);
Chong Zhangd3e0d862017-10-03 13:17:13 -0700502
503 for (size_t i = 0; i < mRefs.size(); i++) {
504 itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]);
505
506 // ignore non-image items
507 if (itemIndex < 0) {
508 continue;
509 }
510 ImageItem &sourceImage = itemIdToItemMap.editValueAt(itemIndex);
511
512 // mark the source image of the derivation as hidden
513 sourceImage.hidden = true;
514 }
Chong Zhangecd08132017-10-05 16:09:29 -0700515 } else if (type() == FOURCC('t', 'h', 'm', 'b')) {
Chong Zhangd3e0d862017-10-03 13:17:13 -0700516 // mark thumbnail image as hidden, these can be retrieved if the client
517 // request thumbnail explicitly, but won't be exposed as displayables.
518 ImageItem &thumbImage = itemIdToItemMap.editValueAt(itemIndex);
519 thumbImage.hidden = true;
520
Chong Zhangecd08132017-10-05 16:09:29 -0700521 for (size_t i = 0; i < mRefs.size(); i++) {
522 itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]);
523
524 // ignore non-image items
525 if (itemIndex < 0) {
526 continue;
527 }
528 ALOGV("Image item id %d uses thumbnail item id %d", mRefs[i], mItemId);
529 ImageItem &masterImage = itemIdToItemMap.editValueAt(itemIndex);
530 if (!masterImage.thumbnails.empty()) {
531 ALOGW("already has thumbnails!");
532 }
533 masterImage.thumbnails.push_back(mItemId);
534 }
Chong Zhangd3e0d862017-10-03 13:17:13 -0700535 } else if (type() == FOURCC('a', 'u', 'x', 'l')) {
536 // mark auxiliary image as hidden
537 ImageItem &auxImage = itemIdToItemMap.editValueAt(itemIndex);
538 auxImage.hidden = true;
Chong Zhangecd08132017-10-05 16:09:29 -0700539 } else {
540 ALOGW("ignoring unsupported ref type 0x%x", type());
541 }
542}
543
Chong Zhangb51ca282017-07-26 16:25:28 -0700544status_t ItemReference::parse(off64_t offset, size_t size) {
545 if (size < mRefIdSize + 2) {
546 return ERROR_MALFORMED;
547 }
548 if (!source()->getUInt32Var(offset, &mItemId, mRefIdSize)) {
549 return ERROR_IO;
550 }
551 offset += mRefIdSize;
552
553 uint16_t count;
554 if (!source()->getUInt16(offset, &count)) {
555 return ERROR_IO;
556 }
557 offset += 2;
558 size -= (mRefIdSize + 2);
559
560 if (size < count * mRefIdSize) {
561 return ERROR_MALFORMED;
562 }
563
564 for (size_t i = 0; i < count; i++) {
565 uint32_t refItemId;
566 if (!source()->getUInt32Var(offset, &refItemId, mRefIdSize)) {
567 return ERROR_IO;
568 }
569 offset += mRefIdSize;
570 mRefs.push_back(refItemId);
571 ALOGV("item id %d: referencing item id %d", mItemId, refItemId);
572 }
573
574 return OK;
575}
576
577struct IrefBox : public FullBox {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800578 IrefBox(DataSourceBase *source, Vector<sp<ItemReference> > *itemRefs) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700579 FullBox(source, FOURCC('i', 'r', 'e', 'f')), mRefIdSize(0), mItemRefs(itemRefs) {}
580
581 status_t parse(off64_t offset, size_t size);
582
583protected:
584 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
585
586private:
587 uint32_t mRefIdSize;
588 Vector<sp<ItemReference> > *mItemRefs;
589};
590
591status_t IrefBox::parse(off64_t offset, size_t size) {
592 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
593 status_t err = parseFullBoxHeader(&offset, &size);
594 if (err != OK) {
595 return err;
596 }
597
598 mRefIdSize = (version() == 0) ? 2 : 4;
599 return parseChunks(offset, size);
600}
601
602status_t IrefBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
603 sp<ItemReference> itemRef = new ItemReference(source(), type, mRefIdSize);
604
605 status_t err = itemRef->parse(offset, size);
606 if (err != OK) {
607 return err;
608 }
609 mItemRefs->push_back(itemRef);
610 return OK;
611}
612
613/////////////////////////////////////////////////////////////////////
614//
615// ItemProperty related boxes
616//
617
618struct AssociationEntry {
619 uint32_t itemId;
620 bool essential;
621 uint16_t index;
622};
623
624struct ItemProperty : public RefBase {
625 ItemProperty() {}
626
627 virtual void attachTo(ImageItem &/*image*/) const {
628 ALOGW("Unrecognized property");
629 }
630 virtual status_t parse(off64_t /*offset*/, size_t /*size*/) {
631 ALOGW("Unrecognized property");
632 return OK;
633 }
634
635private:
636 DISALLOW_EVIL_CONSTRUCTORS(ItemProperty);
637};
638
639struct IspeBox : public FullBox, public ItemProperty {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800640 IspeBox(DataSourceBase *source) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700641 FullBox(source, FOURCC('i', 's', 'p', 'e')), mWidth(0), mHeight(0) {}
642
643 status_t parse(off64_t offset, size_t size) override;
644
645 void attachTo(ImageItem &image) const override {
646 image.width = mWidth;
647 image.height = mHeight;
648 }
649
650private:
651 uint32_t mWidth;
652 uint32_t mHeight;
653};
654
655status_t IspeBox::parse(off64_t offset, size_t size) {
656 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
657
658 status_t err = parseFullBoxHeader(&offset, &size);
659 if (err != OK) {
660 return err;
661 }
662
663 if (size < 8) {
664 return ERROR_MALFORMED;
665 }
666 if (!source()->getUInt32(offset, &mWidth)
667 || !source()->getUInt32(offset + 4, &mHeight)) {
668 return ERROR_IO;
669 }
670 ALOGV("property ispe: %dx%d", mWidth, mHeight);
671
672 return OK;
673}
674
675struct HvccBox : public Box, public ItemProperty {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800676 HvccBox(DataSourceBase *source) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700677 Box(source, FOURCC('h', 'v', 'c', 'C')) {}
678
679 status_t parse(off64_t offset, size_t size) override;
680
681 void attachTo(ImageItem &image) const override {
682 image.hvcc = mHVCC;
683 }
684
685private:
686 sp<ABuffer> mHVCC;
687};
688
689status_t HvccBox::parse(off64_t offset, size_t size) {
690 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
691
692 mHVCC = new ABuffer(size);
693
694 if (mHVCC->data() == NULL) {
695 ALOGE("b/28471206");
696 return NO_MEMORY;
697 }
698
699 if (source()->readAt(offset, mHVCC->data(), size) < (ssize_t)size) {
700 return ERROR_IO;
701 }
702
703 ALOGV("property hvcC");
704
705 return OK;
706}
707
708struct IrotBox : public Box, public ItemProperty {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800709 IrotBox(DataSourceBase *source) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700710 Box(source, FOURCC('i', 'r', 'o', 't')), mAngle(0) {}
711
712 status_t parse(off64_t offset, size_t size) override;
713
714 void attachTo(ImageItem &image) const override {
715 image.rotation = mAngle * 90;
716 }
717
718private:
719 uint8_t mAngle;
720};
721
722status_t IrotBox::parse(off64_t offset, size_t size) {
723 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
724
725 if (size < 1) {
726 return ERROR_MALFORMED;
727 }
728 if (source()->readAt(offset, &mAngle, 1) != 1) {
729 return ERROR_IO;
730 }
731 mAngle &= 0x3;
732 ALOGV("property irot: %d", mAngle);
733
734 return OK;
735}
736
737struct ColrBox : public Box, public ItemProperty {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800738 ColrBox(DataSourceBase *source) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700739 Box(source, FOURCC('c', 'o', 'l', 'r')) {}
740
741 status_t parse(off64_t offset, size_t size) override;
742
743 void attachTo(ImageItem &image) const override {
744 image.icc = mICCData;
745 }
746
747private:
748 sp<ABuffer> mICCData;
749};
750
751status_t ColrBox::parse(off64_t offset, size_t size) {
752 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
753
754 if (size < 4) {
755 return ERROR_MALFORMED;
756 }
757 uint32_t colour_type;
758 if (!source()->getUInt32(offset, &colour_type)) {
759 return ERROR_IO;
760 }
761 offset += 4;
762 size -= 4;
763 if (colour_type == FOURCC('n', 'c', 'l', 'x')) {
764 return OK;
765 }
766 if ((colour_type != FOURCC('r', 'I', 'C', 'C')) &&
767 (colour_type != FOURCC('p', 'r', 'o', 'f'))) {
768 return ERROR_MALFORMED;
769 }
770
771 mICCData = new ABuffer(size);
772 if (mICCData->data() == NULL) {
773 ALOGE("b/28471206");
774 return NO_MEMORY;
775 }
776
777 if (source()->readAt(offset, mICCData->data(), size) != (ssize_t)size) {
778 return ERROR_IO;
779 }
780
781 ALOGV("property Colr: size %zd", size);
782 return OK;
783}
784
785struct IpmaBox : public FullBox {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800786 IpmaBox(DataSourceBase *source, Vector<AssociationEntry> *associations) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700787 FullBox(source, FOURCC('i', 'p', 'm', 'a')), mAssociations(associations) {}
788
789 status_t parse(off64_t offset, size_t size);
790private:
791 Vector<AssociationEntry> *mAssociations;
792};
793
794status_t IpmaBox::parse(off64_t offset, size_t size) {
795 status_t err = parseFullBoxHeader(&offset, &size);
796 if (err != OK) {
797 return err;
798 }
799
800 if (size < 4) {
801 return ERROR_MALFORMED;
802 }
803 uint32_t entryCount;
804 if (!source()->getUInt32(offset, &entryCount)) {
805 return ERROR_IO;
806 }
807 offset += 4;
808 size -= 4;
809
810 for (size_t k = 0; k < entryCount; ++k) {
811 uint32_t itemId = 0;
812 size_t itemIdSize = (version() < 1) ? 2 : 4;
813
814 if (size < itemIdSize + 1) {
815 return ERROR_MALFORMED;
816 }
817
818 if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) {
819 return ERROR_IO;
820 }
821 offset += itemIdSize;
822 size -= itemIdSize;
823
824 uint8_t associationCount;
825 if (!source()->readAt(offset, &associationCount, 1)) {
826 return ERROR_IO;
827 }
828 offset++;
829 size--;
830
831 for (size_t i = 0; i < associationCount; ++i) {
832 size_t propIndexSize = (flags() & 1) ? 2 : 1;
833 if (size < propIndexSize) {
834 return ERROR_MALFORMED;
835 }
836 uint16_t propIndex;
837 if (!source()->getUInt16Var(offset, &propIndex, propIndexSize)) {
838 return ERROR_IO;
839 }
840 offset += propIndexSize;
841 size -= propIndexSize;
842 uint16_t bitmask = (1 << (8 * propIndexSize - 1));
843 AssociationEntry entry = {
844 .itemId = itemId,
845 .essential = !!(propIndex & bitmask),
846 .index = (uint16_t) (propIndex & ~bitmask)
847 };
848
849 ALOGV("item id %d associated to property %d (essential %d)",
850 itemId, entry.index, entry.essential);
851
852 mAssociations->push_back(entry);
853 }
854 }
855
856 return OK;
857}
858
859struct IpcoBox : public Box {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800860 IpcoBox(DataSourceBase *source, Vector<sp<ItemProperty> > *properties) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700861 Box(source, FOURCC('i', 'p', 'c', 'o')), mItemProperties(properties) {}
862
863 status_t parse(off64_t offset, size_t size);
864protected:
865 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
866
867private:
868 Vector<sp<ItemProperty> > *mItemProperties;
869};
870
871status_t IpcoBox::parse(off64_t offset, size_t size) {
872 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
873 // push dummy as the index is 1-based
874 mItemProperties->push_back(new ItemProperty());
875 return parseChunks(offset, size);
876}
877
878status_t IpcoBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
879 sp<ItemProperty> itemProperty;
880 switch(type) {
881 case FOURCC('h', 'v', 'c', 'C'):
882 {
883 itemProperty = new HvccBox(source());
884 break;
885 }
886 case FOURCC('i', 's', 'p', 'e'):
887 {
888 itemProperty = new IspeBox(source());
889 break;
890 }
891 case FOURCC('i', 'r', 'o', 't'):
892 {
893 itemProperty = new IrotBox(source());
894 break;
895 }
896 case FOURCC('c', 'o', 'l', 'r'):
897 {
898 itemProperty = new ColrBox(source());
899 break;
900 }
901 default:
902 {
903 // push dummy to maintain correct item property index
904 itemProperty = new ItemProperty();
905 break;
906 }
907 }
908 status_t err = itemProperty->parse(offset, size);
909 if (err != OK) {
910 return err;
911 }
912 mItemProperties->push_back(itemProperty);
913 return OK;
914}
915
916struct IprpBox : public Box {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800917 IprpBox(DataSourceBase *source,
Chong Zhangb51ca282017-07-26 16:25:28 -0700918 Vector<sp<ItemProperty> > *properties,
919 Vector<AssociationEntry> *associations) :
920 Box(source, FOURCC('i', 'p', 'r', 'p')),
921 mProperties(properties), mAssociations(associations) {}
922
923 status_t parse(off64_t offset, size_t size);
924protected:
925 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
926
927private:
928 Vector<sp<ItemProperty> > *mProperties;
929 Vector<AssociationEntry> *mAssociations;
930};
931
932status_t IprpBox::parse(off64_t offset, size_t size) {
933 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
934
935 status_t err = parseChunks(offset, size);
936 if (err != OK) {
937 return err;
938 }
939 return OK;
940}
941
942status_t IprpBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
943 switch(type) {
944 case FOURCC('i', 'p', 'c', 'o'):
945 {
946 IpcoBox ipcoBox(source(), mProperties);
947 return ipcoBox.parse(offset, size);
948 }
949 case FOURCC('i', 'p', 'm', 'a'):
950 {
951 IpmaBox ipmaBox(source(), mAssociations);
952 return ipmaBox.parse(offset, size);
953 }
954 default:
955 {
956 ALOGW("Unrecognized box.");
957 break;
958 }
959 }
960 return OK;
961}
962
963/////////////////////////////////////////////////////////////////////
964//
965// ItemInfo related boxes
966//
967struct ItemInfo {
968 uint32_t itemId;
969 uint32_t itemType;
Chong Zhangd3e0d862017-10-03 13:17:13 -0700970 bool hidden;
Chong Zhangb51ca282017-07-26 16:25:28 -0700971};
972
973struct InfeBox : public FullBox {
Marco Nelissen2a243f02018-01-30 08:29:57 -0800974 InfeBox(DataSourceBase *source) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700975 FullBox(source, FOURCC('i', 'n', 'f', 'e')) {}
976
977 status_t parse(off64_t offset, size_t size, ItemInfo *itemInfo);
978
979private:
980 bool parseNullTerminatedString(off64_t *offset, size_t *size, String8 *out);
981};
982
983bool InfeBox::parseNullTerminatedString(
984 off64_t *offset, size_t *size, String8 *out) {
Chong Zhang70601852018-03-08 15:23:43 -0800985 char tmp;
986 Vector<char> buf;
987 buf.setCapacity(256);
Chong Zhangb51ca282017-07-26 16:25:28 -0700988 off64_t newOffset = *offset;
989 off64_t stopOffset = *offset + *size;
990 while (newOffset < stopOffset) {
Chong Zhang70601852018-03-08 15:23:43 -0800991 if (!source()->readAt(newOffset++, &tmp, 1)) {
Chong Zhangb51ca282017-07-26 16:25:28 -0700992 return false;
993 }
Chong Zhang70601852018-03-08 15:23:43 -0800994 buf.push_back(tmp);
995 if (tmp == 0) {
996 out->setTo(buf.array());
Chong Zhangb51ca282017-07-26 16:25:28 -0700997
998 *offset = newOffset;
999 *size = stopOffset - newOffset;
1000
1001 return true;
1002 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001003 }
1004 return false;
1005}
1006
1007status_t InfeBox::parse(off64_t offset, size_t size, ItemInfo *itemInfo) {
1008 status_t err = parseFullBoxHeader(&offset, &size);
1009 if (err != OK) {
1010 return err;
1011 }
1012
1013 if (version() == 0 || version() == 1) {
Chong Zhangecd08132017-10-05 16:09:29 -07001014 return ERROR_UNSUPPORTED;
Chong Zhangb51ca282017-07-26 16:25:28 -07001015 } else { // version >= 2
1016 uint32_t item_id;
1017 size_t itemIdSize = (version() == 2) ? 2 : 4;
1018 if (size < itemIdSize + 6) {
1019 return ERROR_MALFORMED;
1020 }
1021 if (!source()->getUInt32Var(offset, &item_id, itemIdSize)) {
1022 return ERROR_IO;
1023 }
1024 ALOGV("item_id %d", item_id);
1025 offset += itemIdSize;
1026 uint16_t item_protection_index;
1027 if (!source()->getUInt16(offset, &item_protection_index)) {
1028 return ERROR_IO;
1029 }
1030 ALOGV("item_protection_index %d", item_protection_index);
1031 offset += 2;
1032 uint32_t item_type;
1033 if (!source()->getUInt32(offset, &item_type)) {
1034 return ERROR_IO;
1035 }
1036
1037 itemInfo->itemId = item_id;
1038 itemInfo->itemType = item_type;
Chong Zhangd3e0d862017-10-03 13:17:13 -07001039 // According to HEIF spec, (flags & 1) indicates the image is hidden
1040 // and not supposed to be displayed.
1041 itemInfo->hidden = (flags() & 1);
Chong Zhangb51ca282017-07-26 16:25:28 -07001042
1043 char itemTypeString[5];
1044 MakeFourCCString(item_type, itemTypeString);
1045 ALOGV("item_type %s", itemTypeString);
1046 offset += 4;
1047 size -= itemIdSize + 6;
1048
1049 String8 item_name;
1050 if (!parseNullTerminatedString(&offset, &size, &item_name)) {
1051 return ERROR_MALFORMED;
1052 }
1053 ALOGV("item_name %s", item_name.c_str());
1054
1055 if (item_type == FOURCC('m', 'i', 'm', 'e')) {
1056 String8 content_type;
1057 if (!parseNullTerminatedString(&offset, &size, &content_type)) {
1058 return ERROR_MALFORMED;
1059 }
1060
1061 String8 content_encoding;
1062 if (!parseNullTerminatedString(&offset, &size, &content_encoding)) {
1063 return ERROR_MALFORMED;
1064 }
1065 } else if (item_type == FOURCC('u', 'r', 'i', ' ')) {
1066 String8 item_uri_type;
1067 if (!parseNullTerminatedString(&offset, &size, &item_uri_type)) {
1068 return ERROR_MALFORMED;
1069 }
1070 }
1071 }
1072 return OK;
1073}
1074
1075struct IinfBox : public FullBox {
Marco Nelissen2a243f02018-01-30 08:29:57 -08001076 IinfBox(DataSourceBase *source, Vector<ItemInfo> *itemInfos) :
Chong Zhangb51ca282017-07-26 16:25:28 -07001077 FullBox(source, FOURCC('i', 'i', 'n', 'f')),
1078 mItemInfos(itemInfos), mHasGrids(false) {}
1079
1080 status_t parse(off64_t offset, size_t size);
1081
1082 bool hasGrids() { return mHasGrids; }
1083
1084protected:
1085 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
1086
1087private:
1088 Vector<ItemInfo> *mItemInfos;
1089 bool mHasGrids;
1090};
1091
1092status_t IinfBox::parse(off64_t offset, size_t size) {
1093 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1094
1095 status_t err = parseFullBoxHeader(&offset, &size);
1096 if (err != OK) {
1097 return err;
1098 }
1099
1100 size_t entryCountSize = version() == 0 ? 2 : 4;
1101 if (size < entryCountSize) {
1102 return ERROR_MALFORMED;
1103 }
1104 uint32_t entry_count;
1105 if (!source()->getUInt32Var(offset, &entry_count, entryCountSize)) {
1106 return ERROR_IO;
1107 }
1108 ALOGV("entry_count %d", entry_count);
1109
1110 off64_t stopOffset = offset + size;
1111 offset += entryCountSize;
1112 for (size_t i = 0; i < entry_count && offset < stopOffset; i++) {
1113 ALOGV("entry %zu", i);
1114 status_t err = parseChunk(&offset);
1115 if (err != OK) {
1116 return err;
1117 }
1118 }
1119 if (offset != stopOffset) {
1120 return ERROR_MALFORMED;
1121 }
1122
1123 return OK;
1124}
1125
1126status_t IinfBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
1127 if (type != FOURCC('i', 'n', 'f', 'e')) {
1128 return OK;
1129 }
1130
1131 InfeBox infeBox(source());
1132 ItemInfo itemInfo;
1133 status_t err = infeBox.parse(offset, size, &itemInfo);
Chong Zhangecd08132017-10-05 16:09:29 -07001134 if (err == OK) {
1135 mItemInfos->push_back(itemInfo);
1136 mHasGrids |= (itemInfo.itemType == FOURCC('g', 'r', 'i', 'd'));
Chong Zhangb51ca282017-07-26 16:25:28 -07001137 }
Chong Zhang5518a2c2017-10-13 18:31:25 -07001138 // InfeBox parse returns ERROR_UNSUPPORTED if the box if an unsupported
1139 // version. Ignore this error as it's not fatal.
1140 return (err == ERROR_UNSUPPORTED) ? OK : err;
Chong Zhangb51ca282017-07-26 16:25:28 -07001141}
1142
1143//////////////////////////////////////////////////////////////////
1144
Marco Nelissen2a243f02018-01-30 08:29:57 -08001145ItemTable::ItemTable(DataSourceBase *source)
Chong Zhangb51ca282017-07-26 16:25:28 -07001146 : mDataSource(source),
1147 mPrimaryItemId(0),
1148 mIdatOffset(0),
1149 mIdatSize(0),
1150 mImageItemsValid(false),
Chong Zhangecd08132017-10-05 16:09:29 -07001151 mCurrentItemIndex(0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001152 mRequiredBoxes.insert('iprp');
1153 mRequiredBoxes.insert('iloc');
1154 mRequiredBoxes.insert('pitm');
1155 mRequiredBoxes.insert('iinf');
1156}
1157
1158ItemTable::~ItemTable() {}
1159
1160status_t ItemTable::parse(uint32_t type, off64_t data_offset, size_t chunk_data_size) {
1161 switch(type) {
1162 case FOURCC('i', 'l', 'o', 'c'):
1163 {
1164 return parseIlocBox(data_offset, chunk_data_size);
1165 }
1166 case FOURCC('i', 'i', 'n', 'f'):
1167 {
1168 return parseIinfBox(data_offset, chunk_data_size);
1169 }
1170 case FOURCC('i', 'p', 'r', 'p'):
1171 {
1172 return parseIprpBox(data_offset, chunk_data_size);
1173 }
1174 case FOURCC('p', 'i', 't', 'm'):
1175 {
1176 return parsePitmBox(data_offset, chunk_data_size);
1177 }
1178 case FOURCC('i', 'd', 'a', 't'):
1179 {
1180 return parseIdatBox(data_offset, chunk_data_size);
1181 }
1182 case FOURCC('i', 'r', 'e', 'f'):
1183 {
1184 return parseIrefBox(data_offset, chunk_data_size);
1185 }
1186 case FOURCC('i', 'p', 'r', 'o'):
1187 {
1188 ALOGW("ipro box not supported!");
1189 break;
1190 }
1191 default:
1192 {
1193 ALOGW("unrecognized box type: 0x%x", type);
1194 break;
1195 }
1196 }
1197 return ERROR_UNSUPPORTED;
1198}
1199
1200status_t ItemTable::parseIlocBox(off64_t offset, size_t size) {
1201 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1202
1203 IlocBox ilocBox(mDataSource, &mItemLocs);
1204 status_t err = ilocBox.parse(offset, size);
1205 if (err != OK) {
1206 return err;
1207 }
1208
1209 if (ilocBox.hasConstructMethod1()) {
1210 mRequiredBoxes.insert('idat');
1211 }
1212
1213 return buildImageItemsIfPossible('iloc');
1214}
1215
1216status_t ItemTable::parseIinfBox(off64_t offset, size_t size) {
1217 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1218
1219 IinfBox iinfBox(mDataSource, &mItemInfos);
1220 status_t err = iinfBox.parse(offset, size);
1221 if (err != OK) {
1222 return err;
1223 }
1224
1225 if (iinfBox.hasGrids()) {
1226 mRequiredBoxes.insert('iref');
1227 }
1228
1229 return buildImageItemsIfPossible('iinf');
1230}
1231
1232status_t ItemTable::parsePitmBox(off64_t offset, size_t size) {
1233 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1234
1235 PitmBox pitmBox(mDataSource);
1236 status_t err = pitmBox.parse(offset, size, &mPrimaryItemId);
1237 if (err != OK) {
1238 return err;
1239 }
1240
1241 return buildImageItemsIfPossible('pitm');
1242}
1243
1244status_t ItemTable::parseIprpBox(off64_t offset, size_t size) {
1245 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1246
1247 IprpBox iprpBox(mDataSource, &mItemProperties, &mAssociations);
1248 status_t err = iprpBox.parse(offset, size);
1249 if (err != OK) {
1250 return err;
1251 }
1252
1253 return buildImageItemsIfPossible('iprp');
1254}
1255
1256status_t ItemTable::parseIdatBox(off64_t offset, size_t size) {
1257 ALOGV("%s: idat offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1258
1259 // only remember the offset and size of idat box for later use
1260 mIdatOffset = offset;
1261 mIdatSize = size;
1262
1263 return buildImageItemsIfPossible('idat');
1264}
1265
1266status_t ItemTable::parseIrefBox(off64_t offset, size_t size) {
1267 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1268
1269 IrefBox irefBox(mDataSource, &mItemReferences);
1270 status_t err = irefBox.parse(offset, size);
1271 if (err != OK) {
1272 return err;
1273 }
1274
1275 return buildImageItemsIfPossible('iref');
1276}
1277
1278status_t ItemTable::buildImageItemsIfPossible(uint32_t type) {
1279 if (mImageItemsValid) {
1280 return OK;
1281 }
1282
1283 mBoxesSeen.insert(type);
1284
1285 // need at least 'iprp', 'iloc', 'pitm', 'iinf';
1286 // need 'idat' if any items used construction_method of 2;
1287 // need 'iref' if there are grids.
1288 if (!std::includes(
1289 mBoxesSeen.begin(), mBoxesSeen.end(),
1290 mRequiredBoxes.begin(), mRequiredBoxes.end())) {
1291 return OK;
1292 }
1293
1294 ALOGV("building image table...");
1295
1296 for (size_t i = 0; i < mItemInfos.size(); i++) {
1297 const ItemInfo &info = mItemInfos[i];
1298
1299
1300 // ignore non-image items
1301 if (info.itemType != FOURCC('g', 'r', 'i', 'd') &&
1302 info.itemType != FOURCC('h', 'v', 'c', '1')) {
1303 continue;
1304 }
1305
Chong Zhangecd08132017-10-05 16:09:29 -07001306 ssize_t itemIndex = mItemIdToItemMap.indexOfKey(info.itemId);
1307 if (itemIndex >= 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001308 ALOGW("ignoring duplicate image item id %d", info.itemId);
1309 continue;
1310 }
1311
1312 ssize_t ilocIndex = mItemLocs.indexOfKey(info.itemId);
1313 if (ilocIndex < 0) {
1314 ALOGE("iloc missing for image item id %d", info.itemId);
1315 continue;
1316 }
1317 const ItemLoc &iloc = mItemLocs[ilocIndex];
1318
1319 off64_t offset;
1320 size_t size;
1321 if (iloc.getLoc(&offset, &size, mIdatOffset, mIdatSize) != OK) {
1322 return ERROR_MALFORMED;
1323 }
1324
Chong Zhangd3e0d862017-10-03 13:17:13 -07001325 ImageItem image(info.itemType, info.itemId, info.hidden);
Chong Zhangb51ca282017-07-26 16:25:28 -07001326
1327 ALOGV("adding %s: itemId %d", image.isGrid() ? "grid" : "image", info.itemId);
1328
1329 if (image.isGrid()) {
1330 if (size > 12) {
1331 return ERROR_MALFORMED;
1332 }
1333 uint8_t buf[12];
1334 if (!mDataSource->readAt(offset, buf, size)) {
1335 return ERROR_IO;
1336 }
1337
1338 image.rows = buf[2] + 1;
1339 image.columns = buf[3] + 1;
1340
1341 ALOGV("rows %d, columans %d", image.rows, image.columns);
1342 } else {
1343 image.offset = offset;
1344 image.size = size;
1345 }
Chong Zhangecd08132017-10-05 16:09:29 -07001346 mItemIdToItemMap.add(info.itemId, image);
Chong Zhangb51ca282017-07-26 16:25:28 -07001347 }
1348
1349 for (size_t i = 0; i < mAssociations.size(); i++) {
1350 attachProperty(mAssociations[i]);
1351 }
1352
1353 for (size_t i = 0; i < mItemReferences.size(); i++) {
Chong Zhangecd08132017-10-05 16:09:29 -07001354 mItemReferences[i]->apply(mItemIdToItemMap);
Chong Zhangb51ca282017-07-26 16:25:28 -07001355 }
1356
Chong Zhangd3e0d862017-10-03 13:17:13 -07001357 bool foundPrimary = false;
1358 for (size_t i = 0; i < mItemIdToItemMap.size(); i++) {
1359 // add all non-hidden images, also add the primary even if it's marked
1360 // hidden, in case the primary is set to a thumbnail
1361 bool isPrimary = (mItemIdToItemMap[i].itemId == mPrimaryItemId);
1362 if (!mItemIdToItemMap[i].hidden || isPrimary) {
1363 mDisplayables.push_back(i);
1364 }
1365 foundPrimary |= isPrimary;
1366 }
1367
1368 ALOGV("found %zu displayables", mDisplayables.size());
1369
1370 // fail if no displayables are found
1371 if (mDisplayables.empty()) {
1372 return ERROR_MALFORMED;
1373 }
1374
1375 // if the primary item id is invalid, set primary to the first displayable
1376 if (!foundPrimary) {
1377 mPrimaryItemId = mItemIdToItemMap[mDisplayables[0]].itemId;
1378 }
1379
Chong Zhangb51ca282017-07-26 16:25:28 -07001380 mImageItemsValid = true;
1381 return OK;
1382}
1383
1384void ItemTable::attachProperty(const AssociationEntry &association) {
Chong Zhangecd08132017-10-05 16:09:29 -07001385 ssize_t itemIndex = mItemIdToItemMap.indexOfKey(association.itemId);
Chong Zhangb51ca282017-07-26 16:25:28 -07001386
1387 // ignore non-image items
Chong Zhangecd08132017-10-05 16:09:29 -07001388 if (itemIndex < 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001389 return;
1390 }
1391
1392 uint16_t propertyIndex = association.index;
1393 if (propertyIndex >= mItemProperties.size()) {
1394 ALOGW("Ignoring invalid property index %d", propertyIndex);
1395 return;
1396 }
1397
1398 ALOGV("attach property %d to item id %d)",
1399 propertyIndex, association.itemId);
1400
Chong Zhangd3e0d862017-10-03 13:17:13 -07001401 mItemProperties[propertyIndex]->attachTo(mItemIdToItemMap.editValueAt(itemIndex));
Chong Zhangb51ca282017-07-26 16:25:28 -07001402}
1403
Chong Zhangd3e0d862017-10-03 13:17:13 -07001404uint32_t ItemTable::countImages() const {
1405 return mImageItemsValid ? mDisplayables.size() : 0;
1406}
1407
1408sp<MetaData> ItemTable::getImageMeta(const uint32_t imageIndex) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001409 if (!mImageItemsValid) {
1410 return NULL;
1411 }
1412
Chong Zhangd3e0d862017-10-03 13:17:13 -07001413 if (imageIndex >= mDisplayables.size()) {
1414 ALOGE("%s: invalid image index %u", __FUNCTION__, imageIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001415 return NULL;
1416 }
Chong Zhangd3e0d862017-10-03 13:17:13 -07001417 const uint32_t itemIndex = mDisplayables[imageIndex];
1418 ALOGV("image[%u]: item index %u", imageIndex, itemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001419
Chong Zhangecd08132017-10-05 16:09:29 -07001420 const ImageItem *image = &mItemIdToItemMap[itemIndex];
Chong Zhangb51ca282017-07-26 16:25:28 -07001421
1422 sp<MetaData> meta = new MetaData;
Chong Zhangd3e0d862017-10-03 13:17:13 -07001423 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
Chong Zhangb51ca282017-07-26 16:25:28 -07001424
Chong Zhangd3e0d862017-10-03 13:17:13 -07001425 if (image->itemId == mPrimaryItemId) {
Chong Zhangfbc97c52017-11-10 13:49:46 -08001426 meta->setInt32(kKeyTrackIsDefault, 1);
Chong Zhangd3e0d862017-10-03 13:17:13 -07001427 }
1428
1429 ALOGV("image[%u]: size %dx%d", imageIndex, image->width, image->height);
1430
Chong Zhangb51ca282017-07-26 16:25:28 -07001431 meta->setInt32(kKeyWidth, image->width);
1432 meta->setInt32(kKeyHeight, image->height);
1433 if (image->rotation != 0) {
Chong Zhang686c23b2017-10-05 15:16:59 -07001434 // Rotation angle in HEIF is CCW, convert to CW here to be
1435 // consistent with the other media formats.
1436 switch(image->rotation) {
1437 case 90: meta->setInt32(kKeyRotation, 270); break;
1438 case 180: meta->setInt32(kKeyRotation, 180); break;
1439 case 270: meta->setInt32(kKeyRotation, 90); break;
1440 default: break; // don't set if invalid
1441 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001442 }
1443 meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5);
1444
1445 if (!image->thumbnails.empty()) {
Chong Zhangecd08132017-10-05 16:09:29 -07001446 ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(image->thumbnails[0]);
1447 if (thumbItemIndex >= 0) {
1448 const ImageItem &thumbnail = mItemIdToItemMap[thumbItemIndex];
Chong Zhangb51ca282017-07-26 16:25:28 -07001449
1450 meta->setInt32(kKeyThumbnailWidth, thumbnail.width);
1451 meta->setInt32(kKeyThumbnailHeight, thumbnail.height);
1452 meta->setData(kKeyThumbnailHVCC, kTypeHVCC,
1453 thumbnail.hvcc->data(), thumbnail.hvcc->size());
Chong Zhangd3e0d862017-10-03 13:17:13 -07001454 ALOGV("image[%u]: thumbnail: size %dx%d, item index %zd",
1455 imageIndex, thumbnail.width, thumbnail.height, thumbItemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001456 } else {
Chong Zhangecd08132017-10-05 16:09:29 -07001457 ALOGW("%s: Referenced thumbnail does not exist!", __FUNCTION__);
Chong Zhangb51ca282017-07-26 16:25:28 -07001458 }
1459 }
1460
1461 if (image->isGrid()) {
Chong Zhangecd08132017-10-05 16:09:29 -07001462 ssize_t tileItemIndex = mItemIdToItemMap.indexOfKey(image->dimgRefs[0]);
1463 if (tileItemIndex < 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001464 return NULL;
1465 }
Chong Zhangd3e0d862017-10-03 13:17:13 -07001466 meta->setInt32(kKeyGridRows, image->rows);
1467 meta->setInt32(kKeyGridCols, image->columns);
Chong Zhangb51ca282017-07-26 16:25:28 -07001468
Chong Zhangee079fe2017-08-23 13:51:17 -07001469 // point image to the first tile for grid size and HVCC
Chong Zhangecd08132017-10-05 16:09:29 -07001470 image = &mItemIdToItemMap.editValueAt(tileItemIndex);
Chong Zhangee079fe2017-08-23 13:51:17 -07001471 meta->setInt32(kKeyGridWidth, image->width);
1472 meta->setInt32(kKeyGridHeight, image->height);
1473 meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5);
Chong Zhangb51ca282017-07-26 16:25:28 -07001474 }
1475
1476 if (image->hvcc == NULL) {
Chong Zhangd3e0d862017-10-03 13:17:13 -07001477 ALOGE("%s: hvcc is missing for image[%u]!", __FUNCTION__, imageIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001478 return NULL;
1479 }
1480 meta->setData(kKeyHVCC, kTypeHVCC, image->hvcc->data(), image->hvcc->size());
1481
1482 if (image->icc != NULL) {
1483 meta->setData(kKeyIccProfile, 0, image->icc->data(), image->icc->size());
1484 }
1485 return meta;
1486}
1487
Chong Zhangd3e0d862017-10-03 13:17:13 -07001488status_t ItemTable::findImageItem(const uint32_t imageIndex, uint32_t *itemIndex) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001489 if (!mImageItemsValid) {
1490 return INVALID_OPERATION;
1491 }
1492
Chong Zhangd3e0d862017-10-03 13:17:13 -07001493 if (imageIndex >= mDisplayables.size()) {
1494 ALOGE("%s: invalid image index %d", __FUNCTION__, imageIndex);
1495 return BAD_VALUE;
Chong Zhangb51ca282017-07-26 16:25:28 -07001496 }
1497
Chong Zhangd3e0d862017-10-03 13:17:13 -07001498 *itemIndex = mDisplayables[imageIndex];
1499
1500 ALOGV("image[%u]: item index %u", imageIndex, *itemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001501 return OK;
1502}
1503
Chong Zhangd3e0d862017-10-03 13:17:13 -07001504status_t ItemTable::findThumbnailItem(const uint32_t imageIndex, uint32_t *itemIndex) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001505 if (!mImageItemsValid) {
1506 return INVALID_OPERATION;
1507 }
1508
Chong Zhangd3e0d862017-10-03 13:17:13 -07001509 if (imageIndex >= mDisplayables.size()) {
1510 ALOGE("%s: invalid image index %d", __FUNCTION__, imageIndex);
1511 return BAD_VALUE;
Chong Zhangb51ca282017-07-26 16:25:28 -07001512 }
1513
Chong Zhangd3e0d862017-10-03 13:17:13 -07001514 uint32_t masterItemIndex = mDisplayables[imageIndex];
1515
1516 const ImageItem &masterImage = mItemIdToItemMap[masterItemIndex];
1517 if (masterImage.thumbnails.empty()) {
1518 *itemIndex = masterItemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001519 return OK;
1520 }
1521
Chong Zhangd3e0d862017-10-03 13:17:13 -07001522 ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(masterImage.thumbnails[0]);
Chong Zhangecd08132017-10-05 16:09:29 -07001523 if (thumbItemIndex < 0) {
Chong Zhangd3e0d862017-10-03 13:17:13 -07001524 ALOGW("%s: Thumbnail item id %d not found, use master instead",
1525 __FUNCTION__, masterImage.thumbnails[0]);
1526 *itemIndex = masterItemIndex;
1527 return OK;
Chong Zhangb51ca282017-07-26 16:25:28 -07001528 }
1529
Chong Zhangecd08132017-10-05 16:09:29 -07001530 *itemIndex = thumbItemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001531 return OK;
1532}
1533
1534status_t ItemTable::getImageOffsetAndSize(
Chong Zhangecd08132017-10-05 16:09:29 -07001535 uint32_t *itemIndex, off64_t *offset, size_t *size) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001536 if (!mImageItemsValid) {
1537 return INVALID_OPERATION;
1538 }
1539
Chong Zhangecd08132017-10-05 16:09:29 -07001540 if (itemIndex != NULL) {
1541 if (*itemIndex >= mItemIdToItemMap.size()) {
1542 ALOGE("%s: Bad item index!", __FUNCTION__);
Chong Zhangb51ca282017-07-26 16:25:28 -07001543 return BAD_VALUE;
1544 }
Chong Zhangecd08132017-10-05 16:09:29 -07001545 mCurrentItemIndex = *itemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001546 }
1547
Chong Zhangecd08132017-10-05 16:09:29 -07001548 ImageItem &image = mItemIdToItemMap.editValueAt(mCurrentItemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001549 if (image.isGrid()) {
1550 uint32_t tileItemId;
Chong Zhangecd08132017-10-05 16:09:29 -07001551 status_t err = image.getNextTileItemId(&tileItemId, itemIndex != NULL);
Chong Zhangb51ca282017-07-26 16:25:28 -07001552 if (err != OK) {
1553 return err;
1554 }
Chong Zhangecd08132017-10-05 16:09:29 -07001555 ssize_t tileItemIndex = mItemIdToItemMap.indexOfKey(tileItemId);
1556 if (tileItemIndex < 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001557 return ERROR_END_OF_STREAM;
1558 }
Chong Zhangecd08132017-10-05 16:09:29 -07001559 *offset = mItemIdToItemMap[tileItemIndex].offset;
1560 *size = mItemIdToItemMap[tileItemIndex].size;
Chong Zhangb51ca282017-07-26 16:25:28 -07001561 } else {
Chong Zhangecd08132017-10-05 16:09:29 -07001562 if (itemIndex == NULL) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001563 // For single images, we only allow it to be read once, after that
Chong Zhangecd08132017-10-05 16:09:29 -07001564 // it's EOS. New item index must be requested each time.
Chong Zhangb51ca282017-07-26 16:25:28 -07001565 return ERROR_END_OF_STREAM;
1566 }
Chong Zhangecd08132017-10-05 16:09:29 -07001567 *offset = mItemIdToItemMap[mCurrentItemIndex].offset;
1568 *size = mItemIdToItemMap[mCurrentItemIndex].size;
Chong Zhangb51ca282017-07-26 16:25:28 -07001569 }
1570
1571 return OK;
1572}
1573
1574} // namespace heif
1575
1576} // namespace android