blob: d13f0dd631fcc960411e918d67ff60ce49022a86 [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) {
985 char tmp[256];
986 size_t len = 0;
987 off64_t newOffset = *offset;
988 off64_t stopOffset = *offset + *size;
989 while (newOffset < stopOffset) {
990 if (!source()->readAt(newOffset++, &tmp[len], 1)) {
991 return false;
992 }
993 if (tmp[len] == 0) {
994 out->append(tmp, len);
995
996 *offset = newOffset;
997 *size = stopOffset - newOffset;
998
999 return true;
1000 }
1001 if (++len >= sizeof(tmp)) {
1002 out->append(tmp, len);
1003 len = 0;
1004 }
1005 }
1006 return false;
1007}
1008
1009status_t InfeBox::parse(off64_t offset, size_t size, ItemInfo *itemInfo) {
1010 status_t err = parseFullBoxHeader(&offset, &size);
1011 if (err != OK) {
1012 return err;
1013 }
1014
1015 if (version() == 0 || version() == 1) {
Chong Zhangecd08132017-10-05 16:09:29 -07001016 return ERROR_UNSUPPORTED;
Chong Zhangb51ca282017-07-26 16:25:28 -07001017 } else { // version >= 2
1018 uint32_t item_id;
1019 size_t itemIdSize = (version() == 2) ? 2 : 4;
1020 if (size < itemIdSize + 6) {
1021 return ERROR_MALFORMED;
1022 }
1023 if (!source()->getUInt32Var(offset, &item_id, itemIdSize)) {
1024 return ERROR_IO;
1025 }
1026 ALOGV("item_id %d", item_id);
1027 offset += itemIdSize;
1028 uint16_t item_protection_index;
1029 if (!source()->getUInt16(offset, &item_protection_index)) {
1030 return ERROR_IO;
1031 }
1032 ALOGV("item_protection_index %d", item_protection_index);
1033 offset += 2;
1034 uint32_t item_type;
1035 if (!source()->getUInt32(offset, &item_type)) {
1036 return ERROR_IO;
1037 }
1038
1039 itemInfo->itemId = item_id;
1040 itemInfo->itemType = item_type;
Chong Zhangd3e0d862017-10-03 13:17:13 -07001041 // According to HEIF spec, (flags & 1) indicates the image is hidden
1042 // and not supposed to be displayed.
1043 itemInfo->hidden = (flags() & 1);
Chong Zhangb51ca282017-07-26 16:25:28 -07001044
1045 char itemTypeString[5];
1046 MakeFourCCString(item_type, itemTypeString);
1047 ALOGV("item_type %s", itemTypeString);
1048 offset += 4;
1049 size -= itemIdSize + 6;
1050
1051 String8 item_name;
1052 if (!parseNullTerminatedString(&offset, &size, &item_name)) {
1053 return ERROR_MALFORMED;
1054 }
1055 ALOGV("item_name %s", item_name.c_str());
1056
1057 if (item_type == FOURCC('m', 'i', 'm', 'e')) {
1058 String8 content_type;
1059 if (!parseNullTerminatedString(&offset, &size, &content_type)) {
1060 return ERROR_MALFORMED;
1061 }
1062
Ray Essick46c24cd2018-03-08 13:54:19 -08001063 // content_encoding is optional; can be omitted if would be empty
1064 if (size > 0) {
1065 String8 content_encoding;
1066 if (!parseNullTerminatedString(&offset, &size, &content_encoding)) {
1067 return ERROR_MALFORMED;
1068 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001069 }
1070 } else if (item_type == FOURCC('u', 'r', 'i', ' ')) {
1071 String8 item_uri_type;
1072 if (!parseNullTerminatedString(&offset, &size, &item_uri_type)) {
1073 return ERROR_MALFORMED;
1074 }
1075 }
1076 }
1077 return OK;
1078}
1079
1080struct IinfBox : public FullBox {
Marco Nelissen2a243f02018-01-30 08:29:57 -08001081 IinfBox(DataSourceBase *source, Vector<ItemInfo> *itemInfos) :
Chong Zhangb51ca282017-07-26 16:25:28 -07001082 FullBox(source, FOURCC('i', 'i', 'n', 'f')),
1083 mItemInfos(itemInfos), mHasGrids(false) {}
1084
1085 status_t parse(off64_t offset, size_t size);
1086
1087 bool hasGrids() { return mHasGrids; }
1088
1089protected:
1090 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
1091
1092private:
1093 Vector<ItemInfo> *mItemInfos;
1094 bool mHasGrids;
1095};
1096
1097status_t IinfBox::parse(off64_t offset, size_t size) {
1098 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1099
1100 status_t err = parseFullBoxHeader(&offset, &size);
1101 if (err != OK) {
1102 return err;
1103 }
1104
1105 size_t entryCountSize = version() == 0 ? 2 : 4;
1106 if (size < entryCountSize) {
1107 return ERROR_MALFORMED;
1108 }
1109 uint32_t entry_count;
1110 if (!source()->getUInt32Var(offset, &entry_count, entryCountSize)) {
1111 return ERROR_IO;
1112 }
1113 ALOGV("entry_count %d", entry_count);
1114
1115 off64_t stopOffset = offset + size;
1116 offset += entryCountSize;
1117 for (size_t i = 0; i < entry_count && offset < stopOffset; i++) {
1118 ALOGV("entry %zu", i);
1119 status_t err = parseChunk(&offset);
1120 if (err != OK) {
1121 return err;
1122 }
1123 }
1124 if (offset != stopOffset) {
1125 return ERROR_MALFORMED;
1126 }
1127
1128 return OK;
1129}
1130
1131status_t IinfBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
1132 if (type != FOURCC('i', 'n', 'f', 'e')) {
1133 return OK;
1134 }
1135
1136 InfeBox infeBox(source());
1137 ItemInfo itemInfo;
1138 status_t err = infeBox.parse(offset, size, &itemInfo);
Chong Zhangecd08132017-10-05 16:09:29 -07001139 if (err == OK) {
1140 mItemInfos->push_back(itemInfo);
1141 mHasGrids |= (itemInfo.itemType == FOURCC('g', 'r', 'i', 'd'));
Chong Zhangb51ca282017-07-26 16:25:28 -07001142 }
Chong Zhang5518a2c2017-10-13 18:31:25 -07001143 // InfeBox parse returns ERROR_UNSUPPORTED if the box if an unsupported
1144 // version. Ignore this error as it's not fatal.
1145 return (err == ERROR_UNSUPPORTED) ? OK : err;
Chong Zhangb51ca282017-07-26 16:25:28 -07001146}
1147
1148//////////////////////////////////////////////////////////////////
1149
Marco Nelissen2a243f02018-01-30 08:29:57 -08001150ItemTable::ItemTable(DataSourceBase *source)
Chong Zhangb51ca282017-07-26 16:25:28 -07001151 : mDataSource(source),
1152 mPrimaryItemId(0),
1153 mIdatOffset(0),
1154 mIdatSize(0),
1155 mImageItemsValid(false),
Chong Zhangecd08132017-10-05 16:09:29 -07001156 mCurrentItemIndex(0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001157 mRequiredBoxes.insert('iprp');
1158 mRequiredBoxes.insert('iloc');
1159 mRequiredBoxes.insert('pitm');
1160 mRequiredBoxes.insert('iinf');
1161}
1162
1163ItemTable::~ItemTable() {}
1164
1165status_t ItemTable::parse(uint32_t type, off64_t data_offset, size_t chunk_data_size) {
1166 switch(type) {
1167 case FOURCC('i', 'l', 'o', 'c'):
1168 {
1169 return parseIlocBox(data_offset, chunk_data_size);
1170 }
1171 case FOURCC('i', 'i', 'n', 'f'):
1172 {
1173 return parseIinfBox(data_offset, chunk_data_size);
1174 }
1175 case FOURCC('i', 'p', 'r', 'p'):
1176 {
1177 return parseIprpBox(data_offset, chunk_data_size);
1178 }
1179 case FOURCC('p', 'i', 't', 'm'):
1180 {
1181 return parsePitmBox(data_offset, chunk_data_size);
1182 }
1183 case FOURCC('i', 'd', 'a', 't'):
1184 {
1185 return parseIdatBox(data_offset, chunk_data_size);
1186 }
1187 case FOURCC('i', 'r', 'e', 'f'):
1188 {
1189 return parseIrefBox(data_offset, chunk_data_size);
1190 }
1191 case FOURCC('i', 'p', 'r', 'o'):
1192 {
1193 ALOGW("ipro box not supported!");
1194 break;
1195 }
1196 default:
1197 {
1198 ALOGW("unrecognized box type: 0x%x", type);
1199 break;
1200 }
1201 }
1202 return ERROR_UNSUPPORTED;
1203}
1204
1205status_t ItemTable::parseIlocBox(off64_t offset, size_t size) {
1206 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1207
1208 IlocBox ilocBox(mDataSource, &mItemLocs);
1209 status_t err = ilocBox.parse(offset, size);
1210 if (err != OK) {
1211 return err;
1212 }
1213
1214 if (ilocBox.hasConstructMethod1()) {
1215 mRequiredBoxes.insert('idat');
1216 }
1217
1218 return buildImageItemsIfPossible('iloc');
1219}
1220
1221status_t ItemTable::parseIinfBox(off64_t offset, size_t size) {
1222 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1223
1224 IinfBox iinfBox(mDataSource, &mItemInfos);
1225 status_t err = iinfBox.parse(offset, size);
1226 if (err != OK) {
1227 return err;
1228 }
1229
1230 if (iinfBox.hasGrids()) {
1231 mRequiredBoxes.insert('iref');
1232 }
1233
1234 return buildImageItemsIfPossible('iinf');
1235}
1236
1237status_t ItemTable::parsePitmBox(off64_t offset, size_t size) {
1238 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1239
1240 PitmBox pitmBox(mDataSource);
1241 status_t err = pitmBox.parse(offset, size, &mPrimaryItemId);
1242 if (err != OK) {
1243 return err;
1244 }
1245
1246 return buildImageItemsIfPossible('pitm');
1247}
1248
1249status_t ItemTable::parseIprpBox(off64_t offset, size_t size) {
1250 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1251
1252 IprpBox iprpBox(mDataSource, &mItemProperties, &mAssociations);
1253 status_t err = iprpBox.parse(offset, size);
1254 if (err != OK) {
1255 return err;
1256 }
1257
1258 return buildImageItemsIfPossible('iprp');
1259}
1260
1261status_t ItemTable::parseIdatBox(off64_t offset, size_t size) {
1262 ALOGV("%s: idat offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1263
1264 // only remember the offset and size of idat box for later use
1265 mIdatOffset = offset;
1266 mIdatSize = size;
1267
1268 return buildImageItemsIfPossible('idat');
1269}
1270
1271status_t ItemTable::parseIrefBox(off64_t offset, size_t size) {
1272 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1273
1274 IrefBox irefBox(mDataSource, &mItemReferences);
1275 status_t err = irefBox.parse(offset, size);
1276 if (err != OK) {
1277 return err;
1278 }
1279
1280 return buildImageItemsIfPossible('iref');
1281}
1282
1283status_t ItemTable::buildImageItemsIfPossible(uint32_t type) {
1284 if (mImageItemsValid) {
1285 return OK;
1286 }
1287
1288 mBoxesSeen.insert(type);
1289
1290 // need at least 'iprp', 'iloc', 'pitm', 'iinf';
1291 // need 'idat' if any items used construction_method of 2;
1292 // need 'iref' if there are grids.
1293 if (!std::includes(
1294 mBoxesSeen.begin(), mBoxesSeen.end(),
1295 mRequiredBoxes.begin(), mRequiredBoxes.end())) {
1296 return OK;
1297 }
1298
1299 ALOGV("building image table...");
1300
1301 for (size_t i = 0; i < mItemInfos.size(); i++) {
1302 const ItemInfo &info = mItemInfos[i];
1303
1304
1305 // ignore non-image items
1306 if (info.itemType != FOURCC('g', 'r', 'i', 'd') &&
1307 info.itemType != FOURCC('h', 'v', 'c', '1')) {
1308 continue;
1309 }
1310
Chong Zhangecd08132017-10-05 16:09:29 -07001311 ssize_t itemIndex = mItemIdToItemMap.indexOfKey(info.itemId);
1312 if (itemIndex >= 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001313 ALOGW("ignoring duplicate image item id %d", info.itemId);
1314 continue;
1315 }
1316
1317 ssize_t ilocIndex = mItemLocs.indexOfKey(info.itemId);
1318 if (ilocIndex < 0) {
1319 ALOGE("iloc missing for image item id %d", info.itemId);
1320 continue;
1321 }
1322 const ItemLoc &iloc = mItemLocs[ilocIndex];
1323
1324 off64_t offset;
1325 size_t size;
1326 if (iloc.getLoc(&offset, &size, mIdatOffset, mIdatSize) != OK) {
1327 return ERROR_MALFORMED;
1328 }
1329
Chong Zhangd3e0d862017-10-03 13:17:13 -07001330 ImageItem image(info.itemType, info.itemId, info.hidden);
Chong Zhangb51ca282017-07-26 16:25:28 -07001331
1332 ALOGV("adding %s: itemId %d", image.isGrid() ? "grid" : "image", info.itemId);
1333
1334 if (image.isGrid()) {
1335 if (size > 12) {
1336 return ERROR_MALFORMED;
1337 }
1338 uint8_t buf[12];
1339 if (!mDataSource->readAt(offset, buf, size)) {
1340 return ERROR_IO;
1341 }
1342
1343 image.rows = buf[2] + 1;
1344 image.columns = buf[3] + 1;
1345
1346 ALOGV("rows %d, columans %d", image.rows, image.columns);
1347 } else {
1348 image.offset = offset;
1349 image.size = size;
1350 }
Chong Zhangecd08132017-10-05 16:09:29 -07001351 mItemIdToItemMap.add(info.itemId, image);
Chong Zhangb51ca282017-07-26 16:25:28 -07001352 }
1353
1354 for (size_t i = 0; i < mAssociations.size(); i++) {
1355 attachProperty(mAssociations[i]);
1356 }
1357
1358 for (size_t i = 0; i < mItemReferences.size(); i++) {
Chong Zhangecd08132017-10-05 16:09:29 -07001359 mItemReferences[i]->apply(mItemIdToItemMap);
Chong Zhangb51ca282017-07-26 16:25:28 -07001360 }
1361
Chong Zhangd3e0d862017-10-03 13:17:13 -07001362 bool foundPrimary = false;
1363 for (size_t i = 0; i < mItemIdToItemMap.size(); i++) {
1364 // add all non-hidden images, also add the primary even if it's marked
1365 // hidden, in case the primary is set to a thumbnail
1366 bool isPrimary = (mItemIdToItemMap[i].itemId == mPrimaryItemId);
1367 if (!mItemIdToItemMap[i].hidden || isPrimary) {
1368 mDisplayables.push_back(i);
1369 }
1370 foundPrimary |= isPrimary;
1371 }
1372
1373 ALOGV("found %zu displayables", mDisplayables.size());
1374
1375 // fail if no displayables are found
1376 if (mDisplayables.empty()) {
1377 return ERROR_MALFORMED;
1378 }
1379
1380 // if the primary item id is invalid, set primary to the first displayable
1381 if (!foundPrimary) {
1382 mPrimaryItemId = mItemIdToItemMap[mDisplayables[0]].itemId;
1383 }
1384
Chong Zhangb51ca282017-07-26 16:25:28 -07001385 mImageItemsValid = true;
1386 return OK;
1387}
1388
1389void ItemTable::attachProperty(const AssociationEntry &association) {
Chong Zhangecd08132017-10-05 16:09:29 -07001390 ssize_t itemIndex = mItemIdToItemMap.indexOfKey(association.itemId);
Chong Zhangb51ca282017-07-26 16:25:28 -07001391
1392 // ignore non-image items
Chong Zhangecd08132017-10-05 16:09:29 -07001393 if (itemIndex < 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001394 return;
1395 }
1396
1397 uint16_t propertyIndex = association.index;
1398 if (propertyIndex >= mItemProperties.size()) {
1399 ALOGW("Ignoring invalid property index %d", propertyIndex);
1400 return;
1401 }
1402
1403 ALOGV("attach property %d to item id %d)",
1404 propertyIndex, association.itemId);
1405
Chong Zhangd3e0d862017-10-03 13:17:13 -07001406 mItemProperties[propertyIndex]->attachTo(mItemIdToItemMap.editValueAt(itemIndex));
Chong Zhangb51ca282017-07-26 16:25:28 -07001407}
1408
Chong Zhangd3e0d862017-10-03 13:17:13 -07001409uint32_t ItemTable::countImages() const {
1410 return mImageItemsValid ? mDisplayables.size() : 0;
1411}
1412
1413sp<MetaData> ItemTable::getImageMeta(const uint32_t imageIndex) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001414 if (!mImageItemsValid) {
1415 return NULL;
1416 }
1417
Chong Zhangd3e0d862017-10-03 13:17:13 -07001418 if (imageIndex >= mDisplayables.size()) {
1419 ALOGE("%s: invalid image index %u", __FUNCTION__, imageIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001420 return NULL;
1421 }
Chong Zhangd3e0d862017-10-03 13:17:13 -07001422 const uint32_t itemIndex = mDisplayables[imageIndex];
1423 ALOGV("image[%u]: item index %u", imageIndex, itemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001424
Chong Zhangecd08132017-10-05 16:09:29 -07001425 const ImageItem *image = &mItemIdToItemMap[itemIndex];
Chong Zhangb51ca282017-07-26 16:25:28 -07001426
1427 sp<MetaData> meta = new MetaData;
Chong Zhangd3e0d862017-10-03 13:17:13 -07001428 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
Chong Zhangb51ca282017-07-26 16:25:28 -07001429
Chong Zhangd3e0d862017-10-03 13:17:13 -07001430 if (image->itemId == mPrimaryItemId) {
Chong Zhangfbc97c52017-11-10 13:49:46 -08001431 meta->setInt32(kKeyTrackIsDefault, 1);
Chong Zhangd3e0d862017-10-03 13:17:13 -07001432 }
1433
1434 ALOGV("image[%u]: size %dx%d", imageIndex, image->width, image->height);
1435
Chong Zhangb51ca282017-07-26 16:25:28 -07001436 meta->setInt32(kKeyWidth, image->width);
1437 meta->setInt32(kKeyHeight, image->height);
1438 if (image->rotation != 0) {
Chong Zhang686c23b2017-10-05 15:16:59 -07001439 // Rotation angle in HEIF is CCW, convert to CW here to be
1440 // consistent with the other media formats.
1441 switch(image->rotation) {
1442 case 90: meta->setInt32(kKeyRotation, 270); break;
1443 case 180: meta->setInt32(kKeyRotation, 180); break;
1444 case 270: meta->setInt32(kKeyRotation, 90); break;
1445 default: break; // don't set if invalid
1446 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001447 }
1448 meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5);
1449
1450 if (!image->thumbnails.empty()) {
Chong Zhangecd08132017-10-05 16:09:29 -07001451 ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(image->thumbnails[0]);
1452 if (thumbItemIndex >= 0) {
1453 const ImageItem &thumbnail = mItemIdToItemMap[thumbItemIndex];
Chong Zhangb51ca282017-07-26 16:25:28 -07001454
1455 meta->setInt32(kKeyThumbnailWidth, thumbnail.width);
1456 meta->setInt32(kKeyThumbnailHeight, thumbnail.height);
1457 meta->setData(kKeyThumbnailHVCC, kTypeHVCC,
1458 thumbnail.hvcc->data(), thumbnail.hvcc->size());
Chong Zhangd3e0d862017-10-03 13:17:13 -07001459 ALOGV("image[%u]: thumbnail: size %dx%d, item index %zd",
1460 imageIndex, thumbnail.width, thumbnail.height, thumbItemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001461 } else {
Chong Zhangecd08132017-10-05 16:09:29 -07001462 ALOGW("%s: Referenced thumbnail does not exist!", __FUNCTION__);
Chong Zhangb51ca282017-07-26 16:25:28 -07001463 }
1464 }
1465
1466 if (image->isGrid()) {
Chong Zhangecd08132017-10-05 16:09:29 -07001467 ssize_t tileItemIndex = mItemIdToItemMap.indexOfKey(image->dimgRefs[0]);
1468 if (tileItemIndex < 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001469 return NULL;
1470 }
Chong Zhangd3e0d862017-10-03 13:17:13 -07001471 meta->setInt32(kKeyGridRows, image->rows);
1472 meta->setInt32(kKeyGridCols, image->columns);
Chong Zhangb51ca282017-07-26 16:25:28 -07001473
Chong Zhangee079fe2017-08-23 13:51:17 -07001474 // point image to the first tile for grid size and HVCC
Chong Zhangecd08132017-10-05 16:09:29 -07001475 image = &mItemIdToItemMap.editValueAt(tileItemIndex);
Chong Zhangee079fe2017-08-23 13:51:17 -07001476 meta->setInt32(kKeyGridWidth, image->width);
1477 meta->setInt32(kKeyGridHeight, image->height);
1478 meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5);
Chong Zhangb51ca282017-07-26 16:25:28 -07001479 }
1480
1481 if (image->hvcc == NULL) {
Chong Zhangd3e0d862017-10-03 13:17:13 -07001482 ALOGE("%s: hvcc is missing for image[%u]!", __FUNCTION__, imageIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001483 return NULL;
1484 }
1485 meta->setData(kKeyHVCC, kTypeHVCC, image->hvcc->data(), image->hvcc->size());
1486
1487 if (image->icc != NULL) {
1488 meta->setData(kKeyIccProfile, 0, image->icc->data(), image->icc->size());
1489 }
1490 return meta;
1491}
1492
Chong Zhangd3e0d862017-10-03 13:17:13 -07001493status_t ItemTable::findImageItem(const uint32_t imageIndex, uint32_t *itemIndex) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001494 if (!mImageItemsValid) {
1495 return INVALID_OPERATION;
1496 }
1497
Chong Zhangd3e0d862017-10-03 13:17:13 -07001498 if (imageIndex >= mDisplayables.size()) {
1499 ALOGE("%s: invalid image index %d", __FUNCTION__, imageIndex);
1500 return BAD_VALUE;
Chong Zhangb51ca282017-07-26 16:25:28 -07001501 }
1502
Chong Zhangd3e0d862017-10-03 13:17:13 -07001503 *itemIndex = mDisplayables[imageIndex];
1504
1505 ALOGV("image[%u]: item index %u", imageIndex, *itemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001506 return OK;
1507}
1508
Chong Zhangd3e0d862017-10-03 13:17:13 -07001509status_t ItemTable::findThumbnailItem(const uint32_t imageIndex, uint32_t *itemIndex) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001510 if (!mImageItemsValid) {
1511 return INVALID_OPERATION;
1512 }
1513
Chong Zhangd3e0d862017-10-03 13:17:13 -07001514 if (imageIndex >= mDisplayables.size()) {
1515 ALOGE("%s: invalid image index %d", __FUNCTION__, imageIndex);
1516 return BAD_VALUE;
Chong Zhangb51ca282017-07-26 16:25:28 -07001517 }
1518
Chong Zhangd3e0d862017-10-03 13:17:13 -07001519 uint32_t masterItemIndex = mDisplayables[imageIndex];
1520
1521 const ImageItem &masterImage = mItemIdToItemMap[masterItemIndex];
1522 if (masterImage.thumbnails.empty()) {
1523 *itemIndex = masterItemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001524 return OK;
1525 }
1526
Chong Zhangd3e0d862017-10-03 13:17:13 -07001527 ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(masterImage.thumbnails[0]);
Chong Zhangecd08132017-10-05 16:09:29 -07001528 if (thumbItemIndex < 0) {
Chong Zhangd3e0d862017-10-03 13:17:13 -07001529 ALOGW("%s: Thumbnail item id %d not found, use master instead",
1530 __FUNCTION__, masterImage.thumbnails[0]);
1531 *itemIndex = masterItemIndex;
1532 return OK;
Chong Zhangb51ca282017-07-26 16:25:28 -07001533 }
1534
Chong Zhangecd08132017-10-05 16:09:29 -07001535 *itemIndex = thumbItemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001536 return OK;
1537}
1538
1539status_t ItemTable::getImageOffsetAndSize(
Chong Zhangecd08132017-10-05 16:09:29 -07001540 uint32_t *itemIndex, off64_t *offset, size_t *size) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001541 if (!mImageItemsValid) {
1542 return INVALID_OPERATION;
1543 }
1544
Chong Zhangecd08132017-10-05 16:09:29 -07001545 if (itemIndex != NULL) {
1546 if (*itemIndex >= mItemIdToItemMap.size()) {
1547 ALOGE("%s: Bad item index!", __FUNCTION__);
Chong Zhangb51ca282017-07-26 16:25:28 -07001548 return BAD_VALUE;
1549 }
Chong Zhangecd08132017-10-05 16:09:29 -07001550 mCurrentItemIndex = *itemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001551 }
1552
Chong Zhangecd08132017-10-05 16:09:29 -07001553 ImageItem &image = mItemIdToItemMap.editValueAt(mCurrentItemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001554 if (image.isGrid()) {
1555 uint32_t tileItemId;
Chong Zhangecd08132017-10-05 16:09:29 -07001556 status_t err = image.getNextTileItemId(&tileItemId, itemIndex != NULL);
Chong Zhangb51ca282017-07-26 16:25:28 -07001557 if (err != OK) {
1558 return err;
1559 }
Chong Zhangecd08132017-10-05 16:09:29 -07001560 ssize_t tileItemIndex = mItemIdToItemMap.indexOfKey(tileItemId);
1561 if (tileItemIndex < 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001562 return ERROR_END_OF_STREAM;
1563 }
Chong Zhangecd08132017-10-05 16:09:29 -07001564 *offset = mItemIdToItemMap[tileItemIndex].offset;
1565 *size = mItemIdToItemMap[tileItemIndex].size;
Chong Zhangb51ca282017-07-26 16:25:28 -07001566 } else {
Chong Zhangecd08132017-10-05 16:09:29 -07001567 if (itemIndex == NULL) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001568 // For single images, we only allow it to be read once, after that
Chong Zhangecd08132017-10-05 16:09:29 -07001569 // it's EOS. New item index must be requested each time.
Chong Zhangb51ca282017-07-26 16:25:28 -07001570 return ERROR_END_OF_STREAM;
1571 }
Chong Zhangecd08132017-10-05 16:09:29 -07001572 *offset = mItemIdToItemMap[mCurrentItemIndex].offset;
1573 *size = mItemIdToItemMap[mCurrentItemIndex].size;
Chong Zhangb51ca282017-07-26 16:25:28 -07001574 }
1575
1576 return OK;
1577}
1578
1579} // namespace heif
1580
1581} // namespace android