blob: b476c752eb90c72bf5673836d63e6bce55ce82c4 [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>
Chong Zhangb51ca282017-07-26 16:25:28 -070021#include <media/stagefright/DataSource.h>
22#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
43 ImageItem() : ImageItem(0) {}
44 ImageItem(uint32_t _type) : type(_type),
45 rows(0), columns(0), width(0), height(0), rotation(0),
46 offset(0), size(0), nextTileIndex(0) {}
47
48 bool isGrid() const {
49 return type == FOURCC('g', 'r', 'i', 'd');
50 }
51
52 status_t getNextTileItemId(uint32_t *nextTileItemId, bool reset) {
53 if (reset) {
54 nextTileIndex = 0;
55 }
56 if (nextTileIndex >= dimgRefs.size()) {
57 return ERROR_END_OF_STREAM;
58 }
59 *nextTileItemId = dimgRefs[nextTileIndex++];
60 return OK;
61 }
62
63 uint32_t type;
64 int32_t rows;
65 int32_t columns;
66 int32_t width;
67 int32_t height;
68 int32_t rotation;
69 off64_t offset;
70 size_t size;
71 sp<ABuffer> hvcc;
72 sp<ABuffer> icc;
73
74 Vector<uint32_t> thumbnails;
75 Vector<uint32_t> dimgRefs;
76 size_t nextTileIndex;
77};
78
79
80/////////////////////////////////////////////////////////////////////
81//
82// ISO boxes
83//
84
85struct Box {
86protected:
87 Box(const sp<DataSource> source, uint32_t type) :
88 mDataSource(source), mType(type) {}
89
90 virtual ~Box() {}
91
92 virtual status_t onChunkData(
93 uint32_t /*type*/, off64_t /*offset*/, size_t /*size*/) {
94 return OK;
95 }
96
97 inline uint32_t type() const { return mType; }
98
99 inline sp<DataSource> source() const { return mDataSource; }
100
101 status_t parseChunk(off64_t *offset);
102
103 status_t parseChunks(off64_t offset, size_t size);
104
105private:
106 sp<DataSource> mDataSource;
107 uint32_t mType;
108};
109
110status_t Box::parseChunk(off64_t *offset) {
111 if (*offset < 0) {
112 ALOGE("b/23540914");
113 return ERROR_MALFORMED;
114 }
115 uint32_t hdr[2];
116 if (mDataSource->readAt(*offset, hdr, 8) < 8) {
117 return ERROR_IO;
118 }
119 uint64_t chunk_size = ntohl(hdr[0]);
120 int32_t chunk_type = ntohl(hdr[1]);
121 off64_t data_offset = *offset + 8;
122
123 if (chunk_size == 1) {
124 if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) {
125 return ERROR_IO;
126 }
127 chunk_size = ntoh64(chunk_size);
128 data_offset += 8;
129
130 if (chunk_size < 16) {
131 // The smallest valid chunk is 16 bytes long in this case.
132 return ERROR_MALFORMED;
133 }
134 } else if (chunk_size == 0) {
135 // This shouldn't happen since we should never be top level
136 ALOGE("invalid chunk size 0 for non-top level box");
137 return ERROR_MALFORMED;
138 } else if (chunk_size < 8) {
139 // The smallest valid chunk is 8 bytes long.
140 ALOGE("invalid chunk size: %lld", (long long)chunk_size);
141 return ERROR_MALFORMED;
142 }
143
144 char chunk[5];
145 MakeFourCCString(chunk_type, chunk);
146 ALOGV("chunk: %s @ %lld", chunk, (long long)*offset);
147
148 off64_t chunk_data_size = chunk_size - (data_offset - *offset);
149 if (chunk_data_size < 0) {
150 ALOGE("b/23540914");
151 return ERROR_MALFORMED;
152 }
153
154 status_t err = onChunkData(chunk_type, data_offset, chunk_data_size);
155
156 if (err != OK) {
157 return err;
158 }
159 *offset += chunk_size;
160 return OK;
161}
162
163status_t Box::parseChunks(off64_t offset, size_t size) {
164 off64_t stopOffset = offset + size;
165 while (offset < stopOffset) {
166 status_t err = parseChunk(&offset);
167 if (err != OK) {
168 return err;
169 }
170 }
171 if (offset != stopOffset) {
172 return ERROR_MALFORMED;
173 }
174 return OK;
175}
176
177///////////////////////////////////////////////////////////////////////
178
179struct FullBox : public Box {
180protected:
181 FullBox(const sp<DataSource> source, uint32_t type) :
182 Box(source, type), mVersion(0), mFlags(0) {}
183
184 inline uint8_t version() const { return mVersion; }
185
186 inline uint32_t flags() const { return mFlags; }
187
188 status_t parseFullBoxHeader(off64_t *offset, size_t *size);
189
190private:
191 uint8_t mVersion;
192 uint32_t mFlags;
193};
194
195status_t FullBox::parseFullBoxHeader(off64_t *offset, size_t *size) {
196 if (*size < 4) {
197 return ERROR_MALFORMED;
198 }
199 if (!source()->readAt(*offset, &mVersion, 1)) {
200 return ERROR_IO;
201 }
202 if (!source()->getUInt24(*offset + 1, &mFlags)) {
203 return ERROR_IO;
204 }
205 *offset += 4;
206 *size -= 4;
207 return OK;
208}
209
210/////////////////////////////////////////////////////////////////////
211//
212// PrimaryImage box
213//
214
215struct PitmBox : public FullBox {
216 PitmBox(const sp<DataSource> source) :
217 FullBox(source, FOURCC('p', 'i', 't', 'm')) {}
218
219 status_t parse(off64_t offset, size_t size, uint32_t *primaryItemId);
220};
221
222status_t PitmBox::parse(off64_t offset, size_t size, uint32_t *primaryItemId) {
223 status_t err = parseFullBoxHeader(&offset, &size);
224 if (err != OK) {
225 return err;
226 }
227
228 size_t itemIdSize = (version() == 0) ? 2 : 4;
229 if (size < itemIdSize) {
230 return ERROR_MALFORMED;
231 }
232 uint32_t itemId;
233 if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) {
234 return ERROR_IO;
235 }
236
237 ALOGV("primary id %d", itemId);
238 *primaryItemId = itemId;
239
240 return OK;
241}
242
243/////////////////////////////////////////////////////////////////////
244//
245// ItemLocation related boxes
246//
247
248struct ExtentEntry {
249 uint64_t extentIndex;
250 uint64_t extentOffset;
251 uint64_t extentLength;
252};
253
254struct ItemLoc {
255 ItemLoc() : ItemLoc(0, 0, 0, 0) {}
256 ItemLoc(uint32_t item_id, uint16_t construction_method,
257 uint16_t data_reference_index, uint64_t base_offset) :
258 itemId(item_id),
259 constructionMethod(construction_method),
260 dataReferenceIndex(data_reference_index),
261 baseOffset(base_offset) {}
262
263 void addExtent(const ExtentEntry& extent) {
264 extents.push_back(extent);
265 }
266
267 status_t getLoc(off64_t *offset, size_t *size,
268 off64_t idatOffset, size_t idatSize) const {
269 // TODO: fix extent handling, fix constructionMethod = 2
270 CHECK(extents.size() == 1);
271 if (constructionMethod == 0) {
272 *offset = baseOffset + extents[0].extentOffset;
273 *size = extents[0].extentLength;
274 return OK;
275 } else if (constructionMethod == 1) {
276 if (baseOffset + extents[0].extentOffset + extents[0].extentLength
277 > idatSize) {
278 return ERROR_MALFORMED;
279 }
280 *offset = baseOffset + extents[0].extentOffset + idatOffset;
281 *size = extents[0].extentLength;
282 return OK;
283 }
284 return ERROR_UNSUPPORTED;
285 }
286
287 // parsed info
288 uint32_t itemId;
289 uint16_t constructionMethod;
290 uint16_t dataReferenceIndex;
291 off64_t baseOffset;
292 Vector<ExtentEntry> extents;
293};
294
295struct IlocBox : public FullBox {
296 IlocBox(const sp<DataSource> source, KeyedVector<uint32_t, ItemLoc> *itemLocs) :
297 FullBox(source, FOURCC('i', 'l', 'o', 'c')),
298 mItemLocs(itemLocs), mHasConstructMethod1(false) {}
299
300 status_t parse(off64_t offset, size_t size);
301
302 bool hasConstructMethod1() { return mHasConstructMethod1; }
303
304private:
305 static bool isSizeFieldValid(uint32_t offset_size) {
306 return offset_size == 0 || offset_size == 4 || offset_size == 8;
307 }
308 KeyedVector<uint32_t, ItemLoc> *mItemLocs;
309 bool mHasConstructMethod1;
310};
311
312status_t IlocBox::parse(off64_t offset, size_t size) {
313 status_t err = parseFullBoxHeader(&offset, &size);
314 if (err != OK) {
315 return err;
316 }
317 if (version() > 2) {
318 ALOGE("%s: invalid version %d", __FUNCTION__, version());
319 return ERROR_MALFORMED;
320 }
321
322 if (size < 2) {
323 return ERROR_MALFORMED;
324 }
325 uint8_t offset_size;
326 if (!source()->readAt(offset++, &offset_size, 1)) {
327 return ERROR_IO;
328 }
329 uint8_t length_size = (offset_size & 0xF);
330 offset_size >>= 4;
331
332 uint8_t base_offset_size;
333 if (!source()->readAt(offset++, &base_offset_size, 1)) {
334 return ERROR_IO;
335 }
336 uint8_t index_size = 0;
337 if (version() == 1 || version() == 2) {
338 index_size = (base_offset_size & 0xF);
339 }
340 base_offset_size >>= 4;
341 size -= 2;
342
343 if (!isSizeFieldValid(offset_size)
344 || !isSizeFieldValid(length_size)
345 || !isSizeFieldValid(base_offset_size)
346 || !isSizeFieldValid((index_size))) {
347 ALOGE("%s: offset size not valid: %d, %d, %d, %d", __FUNCTION__,
348 offset_size, length_size, base_offset_size, index_size);
349 return ERROR_MALFORMED;
350 }
351
352 uint32_t item_count;
353 size_t itemFieldSize = version() < 2 ? 2 : 4;
354 if (size < itemFieldSize) {
355 return ERROR_MALFORMED;
356 }
357 if (!source()->getUInt32Var(offset, &item_count, itemFieldSize)) {
358 return ERROR_IO;
359 }
360
361 ALOGV("item_count %lld", (long long) item_count);
362 offset += itemFieldSize;
363 size -= itemFieldSize;
364
365 for (size_t i = 0; i < item_count; i++) {
366 uint32_t item_id;
367 if (!source()->getUInt32Var(offset, &item_id, itemFieldSize)) {
368 return ERROR_IO;
369 }
370 ALOGV("item[%zu]: id %lld", i, (long long)item_id);
371 offset += itemFieldSize;
372
373 uint8_t construction_method = 0;
374 if (version() == 1 || version() == 2) {
375 uint8_t buf[2];
376 if (!source()->readAt(offset, buf, 2)) {
377 return ERROR_IO;
378 }
379 construction_method = (buf[1] & 0xF);
380 ALOGV("construction_method %d", construction_method);
381 if (construction_method == 1) {
382 mHasConstructMethod1 = true;
383 }
384
385 offset += 2;
386 }
387
388 uint16_t data_reference_index;
389 if (!source()->getUInt16(offset, &data_reference_index)) {
390 return ERROR_IO;
391 }
392 ALOGV("data_reference_index %d", data_reference_index);
393 if (data_reference_index != 0) {
394 // we don't support reference to other files
395 return ERROR_UNSUPPORTED;
396 }
397 offset += 2;
398
399 uint64_t base_offset = 0;
400 if (base_offset_size != 0) {
401 if (!source()->getUInt64Var(offset, &base_offset, base_offset_size)) {
402 return ERROR_IO;
403 }
404 offset += base_offset_size;
405 }
406 ALOGV("base_offset %lld", (long long) base_offset);
407
408 ssize_t index = mItemLocs->add(item_id, ItemLoc(
409 item_id, construction_method, data_reference_index, base_offset));
410 ItemLoc &item = mItemLocs->editValueAt(index);
411
412 uint16_t extent_count;
413 if (!source()->getUInt16(offset, &extent_count)) {
414 return ERROR_IO;
415 }
416 ALOGV("extent_count %d", extent_count);
417
418 if (extent_count > 1 && (offset_size == 0 || length_size == 0)) {
419 // if the item is dividec into more than one extents, offset and
420 // length must be present.
421 return ERROR_MALFORMED;
422 }
423 offset += 2;
424
425 for (size_t j = 0; j < extent_count; j++) {
426 uint64_t extent_index = 1; // default=1
427 if ((version() == 1 || version() == 2) && (index_size > 0)) {
428 if (!source()->getUInt64Var(offset, &extent_index, index_size)) {
429 return ERROR_IO;
430 }
431 // TODO: add support for this mode
432 offset += index_size;
433 ALOGV("extent_index %lld", (long long)extent_index);
434 }
435
436 uint64_t extent_offset = 0; // default=0
437 if (offset_size > 0) {
438 if (!source()->getUInt64Var(offset, &extent_offset, offset_size)) {
439 return ERROR_IO;
440 }
441 offset += offset_size;
442 }
443 ALOGV("extent_offset %lld", (long long)extent_offset);
444
445 uint64_t extent_length = 0; // this indicates full length of file
446 if (length_size > 0) {
447 if (!source()->getUInt64Var(offset, &extent_length, length_size)) {
448 return ERROR_IO;
449 }
450 offset += length_size;
451 }
452 ALOGV("extent_length %lld", (long long)extent_length);
453
454 item.addExtent({ extent_index, extent_offset, extent_length });
455 }
456 }
457 return OK;
458}
459
460/////////////////////////////////////////////////////////////////////
461//
462// ItemReference related boxes
463//
464
465struct ItemReference : public Box, public RefBase {
466 ItemReference(const sp<DataSource> source, uint32_t type, uint32_t itemIdSize) :
467 Box(source, type), mItemId(0), mRefIdSize(itemIdSize) {}
468
469 status_t parse(off64_t offset, size_t size);
470
471 uint32_t itemId() { return mItemId; }
472
Chong Zhangecd08132017-10-05 16:09:29 -0700473 void apply(KeyedVector<uint32_t, ImageItem> &itemIdToItemMap) const;
Chong Zhangb51ca282017-07-26 16:25:28 -0700474
475private:
476 uint32_t mItemId;
477 uint32_t mRefIdSize;
478 Vector<uint32_t> mRefs;
479
480 DISALLOW_EVIL_CONSTRUCTORS(ItemReference);
481};
482
Chong Zhangecd08132017-10-05 16:09:29 -0700483void ItemReference::apply(KeyedVector<uint32_t, ImageItem> &itemIdToItemMap) const {
484 ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
485
486 // ignore non-image items
487 if (itemIndex < 0) {
488 return;
489 }
490
491 ALOGV("attach reference type 0x%x to item id %d)", type(), mItemId);
492
493 if (type() == FOURCC('d', 'i', 'm', 'g')) {
494 ImageItem &derivedImage = itemIdToItemMap.editValueAt(itemIndex);
495 if (!derivedImage.dimgRefs.empty()) {
496 ALOGW("dimgRefs if not clean!");
497 }
498 derivedImage.dimgRefs.appendVector(mRefs);
499 } else if (type() == FOURCC('t', 'h', 'm', 'b')) {
500 for (size_t i = 0; i < mRefs.size(); i++) {
501 itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]);
502
503 // ignore non-image items
504 if (itemIndex < 0) {
505 continue;
506 }
507 ALOGV("Image item id %d uses thumbnail item id %d", mRefs[i], mItemId);
508 ImageItem &masterImage = itemIdToItemMap.editValueAt(itemIndex);
509 if (!masterImage.thumbnails.empty()) {
510 ALOGW("already has thumbnails!");
511 }
512 masterImage.thumbnails.push_back(mItemId);
513 }
514 } else {
515 ALOGW("ignoring unsupported ref type 0x%x", type());
516 }
517}
518
Chong Zhangb51ca282017-07-26 16:25:28 -0700519status_t ItemReference::parse(off64_t offset, size_t size) {
520 if (size < mRefIdSize + 2) {
521 return ERROR_MALFORMED;
522 }
523 if (!source()->getUInt32Var(offset, &mItemId, mRefIdSize)) {
524 return ERROR_IO;
525 }
526 offset += mRefIdSize;
527
528 uint16_t count;
529 if (!source()->getUInt16(offset, &count)) {
530 return ERROR_IO;
531 }
532 offset += 2;
533 size -= (mRefIdSize + 2);
534
535 if (size < count * mRefIdSize) {
536 return ERROR_MALFORMED;
537 }
538
539 for (size_t i = 0; i < count; i++) {
540 uint32_t refItemId;
541 if (!source()->getUInt32Var(offset, &refItemId, mRefIdSize)) {
542 return ERROR_IO;
543 }
544 offset += mRefIdSize;
545 mRefs.push_back(refItemId);
546 ALOGV("item id %d: referencing item id %d", mItemId, refItemId);
547 }
548
549 return OK;
550}
551
552struct IrefBox : public FullBox {
553 IrefBox(const sp<DataSource> source, Vector<sp<ItemReference> > *itemRefs) :
554 FullBox(source, FOURCC('i', 'r', 'e', 'f')), mRefIdSize(0), mItemRefs(itemRefs) {}
555
556 status_t parse(off64_t offset, size_t size);
557
558protected:
559 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
560
561private:
562 uint32_t mRefIdSize;
563 Vector<sp<ItemReference> > *mItemRefs;
564};
565
566status_t IrefBox::parse(off64_t offset, size_t size) {
567 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
568 status_t err = parseFullBoxHeader(&offset, &size);
569 if (err != OK) {
570 return err;
571 }
572
573 mRefIdSize = (version() == 0) ? 2 : 4;
574 return parseChunks(offset, size);
575}
576
577status_t IrefBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
578 sp<ItemReference> itemRef = new ItemReference(source(), type, mRefIdSize);
579
580 status_t err = itemRef->parse(offset, size);
581 if (err != OK) {
582 return err;
583 }
584 mItemRefs->push_back(itemRef);
585 return OK;
586}
587
588/////////////////////////////////////////////////////////////////////
589//
590// ItemProperty related boxes
591//
592
593struct AssociationEntry {
594 uint32_t itemId;
595 bool essential;
596 uint16_t index;
597};
598
599struct ItemProperty : public RefBase {
600 ItemProperty() {}
601
602 virtual void attachTo(ImageItem &/*image*/) const {
603 ALOGW("Unrecognized property");
604 }
605 virtual status_t parse(off64_t /*offset*/, size_t /*size*/) {
606 ALOGW("Unrecognized property");
607 return OK;
608 }
609
610private:
611 DISALLOW_EVIL_CONSTRUCTORS(ItemProperty);
612};
613
614struct IspeBox : public FullBox, public ItemProperty {
615 IspeBox(const sp<DataSource> source) :
616 FullBox(source, FOURCC('i', 's', 'p', 'e')), mWidth(0), mHeight(0) {}
617
618 status_t parse(off64_t offset, size_t size) override;
619
620 void attachTo(ImageItem &image) const override {
621 image.width = mWidth;
622 image.height = mHeight;
623 }
624
625private:
626 uint32_t mWidth;
627 uint32_t mHeight;
628};
629
630status_t IspeBox::parse(off64_t offset, size_t size) {
631 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
632
633 status_t err = parseFullBoxHeader(&offset, &size);
634 if (err != OK) {
635 return err;
636 }
637
638 if (size < 8) {
639 return ERROR_MALFORMED;
640 }
641 if (!source()->getUInt32(offset, &mWidth)
642 || !source()->getUInt32(offset + 4, &mHeight)) {
643 return ERROR_IO;
644 }
645 ALOGV("property ispe: %dx%d", mWidth, mHeight);
646
647 return OK;
648}
649
650struct HvccBox : public Box, public ItemProperty {
651 HvccBox(const sp<DataSource> source) :
652 Box(source, FOURCC('h', 'v', 'c', 'C')) {}
653
654 status_t parse(off64_t offset, size_t size) override;
655
656 void attachTo(ImageItem &image) const override {
657 image.hvcc = mHVCC;
658 }
659
660private:
661 sp<ABuffer> mHVCC;
662};
663
664status_t HvccBox::parse(off64_t offset, size_t size) {
665 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
666
667 mHVCC = new ABuffer(size);
668
669 if (mHVCC->data() == NULL) {
670 ALOGE("b/28471206");
671 return NO_MEMORY;
672 }
673
674 if (source()->readAt(offset, mHVCC->data(), size) < (ssize_t)size) {
675 return ERROR_IO;
676 }
677
678 ALOGV("property hvcC");
679
680 return OK;
681}
682
683struct IrotBox : public Box, public ItemProperty {
684 IrotBox(const sp<DataSource> source) :
685 Box(source, FOURCC('i', 'r', 'o', 't')), mAngle(0) {}
686
687 status_t parse(off64_t offset, size_t size) override;
688
689 void attachTo(ImageItem &image) const override {
690 image.rotation = mAngle * 90;
691 }
692
693private:
694 uint8_t mAngle;
695};
696
697status_t IrotBox::parse(off64_t offset, size_t size) {
698 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
699
700 if (size < 1) {
701 return ERROR_MALFORMED;
702 }
703 if (source()->readAt(offset, &mAngle, 1) != 1) {
704 return ERROR_IO;
705 }
706 mAngle &= 0x3;
707 ALOGV("property irot: %d", mAngle);
708
709 return OK;
710}
711
712struct ColrBox : public Box, public ItemProperty {
713 ColrBox(const sp<DataSource> source) :
714 Box(source, FOURCC('c', 'o', 'l', 'r')) {}
715
716 status_t parse(off64_t offset, size_t size) override;
717
718 void attachTo(ImageItem &image) const override {
719 image.icc = mICCData;
720 }
721
722private:
723 sp<ABuffer> mICCData;
724};
725
726status_t ColrBox::parse(off64_t offset, size_t size) {
727 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
728
729 if (size < 4) {
730 return ERROR_MALFORMED;
731 }
732 uint32_t colour_type;
733 if (!source()->getUInt32(offset, &colour_type)) {
734 return ERROR_IO;
735 }
736 offset += 4;
737 size -= 4;
738 if (colour_type == FOURCC('n', 'c', 'l', 'x')) {
739 return OK;
740 }
741 if ((colour_type != FOURCC('r', 'I', 'C', 'C')) &&
742 (colour_type != FOURCC('p', 'r', 'o', 'f'))) {
743 return ERROR_MALFORMED;
744 }
745
746 mICCData = new ABuffer(size);
747 if (mICCData->data() == NULL) {
748 ALOGE("b/28471206");
749 return NO_MEMORY;
750 }
751
752 if (source()->readAt(offset, mICCData->data(), size) != (ssize_t)size) {
753 return ERROR_IO;
754 }
755
756 ALOGV("property Colr: size %zd", size);
757 return OK;
758}
759
760struct IpmaBox : public FullBox {
761 IpmaBox(const sp<DataSource> source, Vector<AssociationEntry> *associations) :
762 FullBox(source, FOURCC('i', 'p', 'm', 'a')), mAssociations(associations) {}
763
764 status_t parse(off64_t offset, size_t size);
765private:
766 Vector<AssociationEntry> *mAssociations;
767};
768
769status_t IpmaBox::parse(off64_t offset, size_t size) {
770 status_t err = parseFullBoxHeader(&offset, &size);
771 if (err != OK) {
772 return err;
773 }
774
775 if (size < 4) {
776 return ERROR_MALFORMED;
777 }
778 uint32_t entryCount;
779 if (!source()->getUInt32(offset, &entryCount)) {
780 return ERROR_IO;
781 }
782 offset += 4;
783 size -= 4;
784
785 for (size_t k = 0; k < entryCount; ++k) {
786 uint32_t itemId = 0;
787 size_t itemIdSize = (version() < 1) ? 2 : 4;
788
789 if (size < itemIdSize + 1) {
790 return ERROR_MALFORMED;
791 }
792
793 if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) {
794 return ERROR_IO;
795 }
796 offset += itemIdSize;
797 size -= itemIdSize;
798
799 uint8_t associationCount;
800 if (!source()->readAt(offset, &associationCount, 1)) {
801 return ERROR_IO;
802 }
803 offset++;
804 size--;
805
806 for (size_t i = 0; i < associationCount; ++i) {
807 size_t propIndexSize = (flags() & 1) ? 2 : 1;
808 if (size < propIndexSize) {
809 return ERROR_MALFORMED;
810 }
811 uint16_t propIndex;
812 if (!source()->getUInt16Var(offset, &propIndex, propIndexSize)) {
813 return ERROR_IO;
814 }
815 offset += propIndexSize;
816 size -= propIndexSize;
817 uint16_t bitmask = (1 << (8 * propIndexSize - 1));
818 AssociationEntry entry = {
819 .itemId = itemId,
820 .essential = !!(propIndex & bitmask),
821 .index = (uint16_t) (propIndex & ~bitmask)
822 };
823
824 ALOGV("item id %d associated to property %d (essential %d)",
825 itemId, entry.index, entry.essential);
826
827 mAssociations->push_back(entry);
828 }
829 }
830
831 return OK;
832}
833
834struct IpcoBox : public Box {
835 IpcoBox(const sp<DataSource> source, Vector<sp<ItemProperty> > *properties) :
836 Box(source, FOURCC('i', 'p', 'c', 'o')), mItemProperties(properties) {}
837
838 status_t parse(off64_t offset, size_t size);
839protected:
840 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
841
842private:
843 Vector<sp<ItemProperty> > *mItemProperties;
844};
845
846status_t IpcoBox::parse(off64_t offset, size_t size) {
847 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
848 // push dummy as the index is 1-based
849 mItemProperties->push_back(new ItemProperty());
850 return parseChunks(offset, size);
851}
852
853status_t IpcoBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
854 sp<ItemProperty> itemProperty;
855 switch(type) {
856 case FOURCC('h', 'v', 'c', 'C'):
857 {
858 itemProperty = new HvccBox(source());
859 break;
860 }
861 case FOURCC('i', 's', 'p', 'e'):
862 {
863 itemProperty = new IspeBox(source());
864 break;
865 }
866 case FOURCC('i', 'r', 'o', 't'):
867 {
868 itemProperty = new IrotBox(source());
869 break;
870 }
871 case FOURCC('c', 'o', 'l', 'r'):
872 {
873 itemProperty = new ColrBox(source());
874 break;
875 }
876 default:
877 {
878 // push dummy to maintain correct item property index
879 itemProperty = new ItemProperty();
880 break;
881 }
882 }
883 status_t err = itemProperty->parse(offset, size);
884 if (err != OK) {
885 return err;
886 }
887 mItemProperties->push_back(itemProperty);
888 return OK;
889}
890
891struct IprpBox : public Box {
892 IprpBox(const sp<DataSource> source,
893 Vector<sp<ItemProperty> > *properties,
894 Vector<AssociationEntry> *associations) :
895 Box(source, FOURCC('i', 'p', 'r', 'p')),
896 mProperties(properties), mAssociations(associations) {}
897
898 status_t parse(off64_t offset, size_t size);
899protected:
900 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
901
902private:
903 Vector<sp<ItemProperty> > *mProperties;
904 Vector<AssociationEntry> *mAssociations;
905};
906
907status_t IprpBox::parse(off64_t offset, size_t size) {
908 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
909
910 status_t err = parseChunks(offset, size);
911 if (err != OK) {
912 return err;
913 }
914 return OK;
915}
916
917status_t IprpBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
918 switch(type) {
919 case FOURCC('i', 'p', 'c', 'o'):
920 {
921 IpcoBox ipcoBox(source(), mProperties);
922 return ipcoBox.parse(offset, size);
923 }
924 case FOURCC('i', 'p', 'm', 'a'):
925 {
926 IpmaBox ipmaBox(source(), mAssociations);
927 return ipmaBox.parse(offset, size);
928 }
929 default:
930 {
931 ALOGW("Unrecognized box.");
932 break;
933 }
934 }
935 return OK;
936}
937
938/////////////////////////////////////////////////////////////////////
939//
940// ItemInfo related boxes
941//
942struct ItemInfo {
943 uint32_t itemId;
944 uint32_t itemType;
945};
946
947struct InfeBox : public FullBox {
948 InfeBox(const sp<DataSource> source) :
949 FullBox(source, FOURCC('i', 'n', 'f', 'e')) {}
950
951 status_t parse(off64_t offset, size_t size, ItemInfo *itemInfo);
952
953private:
954 bool parseNullTerminatedString(off64_t *offset, size_t *size, String8 *out);
955};
956
957bool InfeBox::parseNullTerminatedString(
958 off64_t *offset, size_t *size, String8 *out) {
959 char tmp[256];
960 size_t len = 0;
961 off64_t newOffset = *offset;
962 off64_t stopOffset = *offset + *size;
963 while (newOffset < stopOffset) {
964 if (!source()->readAt(newOffset++, &tmp[len], 1)) {
965 return false;
966 }
967 if (tmp[len] == 0) {
968 out->append(tmp, len);
969
970 *offset = newOffset;
971 *size = stopOffset - newOffset;
972
973 return true;
974 }
975 if (++len >= sizeof(tmp)) {
976 out->append(tmp, len);
977 len = 0;
978 }
979 }
980 return false;
981}
982
983status_t InfeBox::parse(off64_t offset, size_t size, ItemInfo *itemInfo) {
984 status_t err = parseFullBoxHeader(&offset, &size);
985 if (err != OK) {
986 return err;
987 }
988
989 if (version() == 0 || version() == 1) {
Chong Zhangecd08132017-10-05 16:09:29 -0700990 return ERROR_UNSUPPORTED;
Chong Zhangb51ca282017-07-26 16:25:28 -0700991 } else { // version >= 2
992 uint32_t item_id;
993 size_t itemIdSize = (version() == 2) ? 2 : 4;
994 if (size < itemIdSize + 6) {
995 return ERROR_MALFORMED;
996 }
997 if (!source()->getUInt32Var(offset, &item_id, itemIdSize)) {
998 return ERROR_IO;
999 }
1000 ALOGV("item_id %d", item_id);
1001 offset += itemIdSize;
1002 uint16_t item_protection_index;
1003 if (!source()->getUInt16(offset, &item_protection_index)) {
1004 return ERROR_IO;
1005 }
1006 ALOGV("item_protection_index %d", item_protection_index);
1007 offset += 2;
1008 uint32_t item_type;
1009 if (!source()->getUInt32(offset, &item_type)) {
1010 return ERROR_IO;
1011 }
1012
1013 itemInfo->itemId = item_id;
1014 itemInfo->itemType = item_type;
1015
1016 char itemTypeString[5];
1017 MakeFourCCString(item_type, itemTypeString);
1018 ALOGV("item_type %s", itemTypeString);
1019 offset += 4;
1020 size -= itemIdSize + 6;
1021
1022 String8 item_name;
1023 if (!parseNullTerminatedString(&offset, &size, &item_name)) {
1024 return ERROR_MALFORMED;
1025 }
1026 ALOGV("item_name %s", item_name.c_str());
1027
1028 if (item_type == FOURCC('m', 'i', 'm', 'e')) {
1029 String8 content_type;
1030 if (!parseNullTerminatedString(&offset, &size, &content_type)) {
1031 return ERROR_MALFORMED;
1032 }
1033
1034 String8 content_encoding;
1035 if (!parseNullTerminatedString(&offset, &size, &content_encoding)) {
1036 return ERROR_MALFORMED;
1037 }
1038 } else if (item_type == FOURCC('u', 'r', 'i', ' ')) {
1039 String8 item_uri_type;
1040 if (!parseNullTerminatedString(&offset, &size, &item_uri_type)) {
1041 return ERROR_MALFORMED;
1042 }
1043 }
1044 }
1045 return OK;
1046}
1047
1048struct IinfBox : public FullBox {
1049 IinfBox(const sp<DataSource> source, Vector<ItemInfo> *itemInfos) :
1050 FullBox(source, FOURCC('i', 'i', 'n', 'f')),
1051 mItemInfos(itemInfos), mHasGrids(false) {}
1052
1053 status_t parse(off64_t offset, size_t size);
1054
1055 bool hasGrids() { return mHasGrids; }
1056
1057protected:
1058 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
1059
1060private:
1061 Vector<ItemInfo> *mItemInfos;
1062 bool mHasGrids;
1063};
1064
1065status_t IinfBox::parse(off64_t offset, size_t size) {
1066 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1067
1068 status_t err = parseFullBoxHeader(&offset, &size);
1069 if (err != OK) {
1070 return err;
1071 }
1072
1073 size_t entryCountSize = version() == 0 ? 2 : 4;
1074 if (size < entryCountSize) {
1075 return ERROR_MALFORMED;
1076 }
1077 uint32_t entry_count;
1078 if (!source()->getUInt32Var(offset, &entry_count, entryCountSize)) {
1079 return ERROR_IO;
1080 }
1081 ALOGV("entry_count %d", entry_count);
1082
1083 off64_t stopOffset = offset + size;
1084 offset += entryCountSize;
1085 for (size_t i = 0; i < entry_count && offset < stopOffset; i++) {
1086 ALOGV("entry %zu", i);
1087 status_t err = parseChunk(&offset);
1088 if (err != OK) {
1089 return err;
1090 }
1091 }
1092 if (offset != stopOffset) {
1093 return ERROR_MALFORMED;
1094 }
1095
1096 return OK;
1097}
1098
1099status_t IinfBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
1100 if (type != FOURCC('i', 'n', 'f', 'e')) {
1101 return OK;
1102 }
1103
1104 InfeBox infeBox(source());
1105 ItemInfo itemInfo;
1106 status_t err = infeBox.parse(offset, size, &itemInfo);
Chong Zhangecd08132017-10-05 16:09:29 -07001107 if (err == OK) {
1108 mItemInfos->push_back(itemInfo);
1109 mHasGrids |= (itemInfo.itemType == FOURCC('g', 'r', 'i', 'd'));
Chong Zhangb51ca282017-07-26 16:25:28 -07001110 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001111 return OK;
1112}
1113
1114//////////////////////////////////////////////////////////////////
1115
1116ItemTable::ItemTable(const sp<DataSource> &source)
1117 : mDataSource(source),
1118 mPrimaryItemId(0),
1119 mIdatOffset(0),
1120 mIdatSize(0),
1121 mImageItemsValid(false),
Chong Zhangecd08132017-10-05 16:09:29 -07001122 mCurrentItemIndex(0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001123 mRequiredBoxes.insert('iprp');
1124 mRequiredBoxes.insert('iloc');
1125 mRequiredBoxes.insert('pitm');
1126 mRequiredBoxes.insert('iinf');
1127}
1128
1129ItemTable::~ItemTable() {}
1130
1131status_t ItemTable::parse(uint32_t type, off64_t data_offset, size_t chunk_data_size) {
1132 switch(type) {
1133 case FOURCC('i', 'l', 'o', 'c'):
1134 {
1135 return parseIlocBox(data_offset, chunk_data_size);
1136 }
1137 case FOURCC('i', 'i', 'n', 'f'):
1138 {
1139 return parseIinfBox(data_offset, chunk_data_size);
1140 }
1141 case FOURCC('i', 'p', 'r', 'p'):
1142 {
1143 return parseIprpBox(data_offset, chunk_data_size);
1144 }
1145 case FOURCC('p', 'i', 't', 'm'):
1146 {
1147 return parsePitmBox(data_offset, chunk_data_size);
1148 }
1149 case FOURCC('i', 'd', 'a', 't'):
1150 {
1151 return parseIdatBox(data_offset, chunk_data_size);
1152 }
1153 case FOURCC('i', 'r', 'e', 'f'):
1154 {
1155 return parseIrefBox(data_offset, chunk_data_size);
1156 }
1157 case FOURCC('i', 'p', 'r', 'o'):
1158 {
1159 ALOGW("ipro box not supported!");
1160 break;
1161 }
1162 default:
1163 {
1164 ALOGW("unrecognized box type: 0x%x", type);
1165 break;
1166 }
1167 }
1168 return ERROR_UNSUPPORTED;
1169}
1170
1171status_t ItemTable::parseIlocBox(off64_t offset, size_t size) {
1172 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1173
1174 IlocBox ilocBox(mDataSource, &mItemLocs);
1175 status_t err = ilocBox.parse(offset, size);
1176 if (err != OK) {
1177 return err;
1178 }
1179
1180 if (ilocBox.hasConstructMethod1()) {
1181 mRequiredBoxes.insert('idat');
1182 }
1183
1184 return buildImageItemsIfPossible('iloc');
1185}
1186
1187status_t ItemTable::parseIinfBox(off64_t offset, size_t size) {
1188 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1189
1190 IinfBox iinfBox(mDataSource, &mItemInfos);
1191 status_t err = iinfBox.parse(offset, size);
1192 if (err != OK) {
1193 return err;
1194 }
1195
1196 if (iinfBox.hasGrids()) {
1197 mRequiredBoxes.insert('iref');
1198 }
1199
1200 return buildImageItemsIfPossible('iinf');
1201}
1202
1203status_t ItemTable::parsePitmBox(off64_t offset, size_t size) {
1204 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1205
1206 PitmBox pitmBox(mDataSource);
1207 status_t err = pitmBox.parse(offset, size, &mPrimaryItemId);
1208 if (err != OK) {
1209 return err;
1210 }
1211
1212 return buildImageItemsIfPossible('pitm');
1213}
1214
1215status_t ItemTable::parseIprpBox(off64_t offset, size_t size) {
1216 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1217
1218 IprpBox iprpBox(mDataSource, &mItemProperties, &mAssociations);
1219 status_t err = iprpBox.parse(offset, size);
1220 if (err != OK) {
1221 return err;
1222 }
1223
1224 return buildImageItemsIfPossible('iprp');
1225}
1226
1227status_t ItemTable::parseIdatBox(off64_t offset, size_t size) {
1228 ALOGV("%s: idat offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1229
1230 // only remember the offset and size of idat box for later use
1231 mIdatOffset = offset;
1232 mIdatSize = size;
1233
1234 return buildImageItemsIfPossible('idat');
1235}
1236
1237status_t ItemTable::parseIrefBox(off64_t offset, size_t size) {
1238 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1239
1240 IrefBox irefBox(mDataSource, &mItemReferences);
1241 status_t err = irefBox.parse(offset, size);
1242 if (err != OK) {
1243 return err;
1244 }
1245
1246 return buildImageItemsIfPossible('iref');
1247}
1248
1249status_t ItemTable::buildImageItemsIfPossible(uint32_t type) {
1250 if (mImageItemsValid) {
1251 return OK;
1252 }
1253
1254 mBoxesSeen.insert(type);
1255
1256 // need at least 'iprp', 'iloc', 'pitm', 'iinf';
1257 // need 'idat' if any items used construction_method of 2;
1258 // need 'iref' if there are grids.
1259 if (!std::includes(
1260 mBoxesSeen.begin(), mBoxesSeen.end(),
1261 mRequiredBoxes.begin(), mRequiredBoxes.end())) {
1262 return OK;
1263 }
1264
1265 ALOGV("building image table...");
1266
1267 for (size_t i = 0; i < mItemInfos.size(); i++) {
1268 const ItemInfo &info = mItemInfos[i];
1269
1270
1271 // ignore non-image items
1272 if (info.itemType != FOURCC('g', 'r', 'i', 'd') &&
1273 info.itemType != FOURCC('h', 'v', 'c', '1')) {
1274 continue;
1275 }
1276
Chong Zhangecd08132017-10-05 16:09:29 -07001277 ssize_t itemIndex = mItemIdToItemMap.indexOfKey(info.itemId);
1278 if (itemIndex >= 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001279 ALOGW("ignoring duplicate image item id %d", info.itemId);
1280 continue;
1281 }
1282
1283 ssize_t ilocIndex = mItemLocs.indexOfKey(info.itemId);
1284 if (ilocIndex < 0) {
1285 ALOGE("iloc missing for image item id %d", info.itemId);
1286 continue;
1287 }
1288 const ItemLoc &iloc = mItemLocs[ilocIndex];
1289
1290 off64_t offset;
1291 size_t size;
1292 if (iloc.getLoc(&offset, &size, mIdatOffset, mIdatSize) != OK) {
1293 return ERROR_MALFORMED;
1294 }
1295
1296 ImageItem image(info.itemType);
1297
1298 ALOGV("adding %s: itemId %d", image.isGrid() ? "grid" : "image", info.itemId);
1299
1300 if (image.isGrid()) {
1301 if (size > 12) {
1302 return ERROR_MALFORMED;
1303 }
1304 uint8_t buf[12];
1305 if (!mDataSource->readAt(offset, buf, size)) {
1306 return ERROR_IO;
1307 }
1308
1309 image.rows = buf[2] + 1;
1310 image.columns = buf[3] + 1;
1311
1312 ALOGV("rows %d, columans %d", image.rows, image.columns);
1313 } else {
1314 image.offset = offset;
1315 image.size = size;
1316 }
Chong Zhangecd08132017-10-05 16:09:29 -07001317 mItemIdToItemMap.add(info.itemId, image);
Chong Zhangb51ca282017-07-26 16:25:28 -07001318 }
1319
1320 for (size_t i = 0; i < mAssociations.size(); i++) {
1321 attachProperty(mAssociations[i]);
1322 }
1323
1324 for (size_t i = 0; i < mItemReferences.size(); i++) {
Chong Zhangecd08132017-10-05 16:09:29 -07001325 mItemReferences[i]->apply(mItemIdToItemMap);
Chong Zhangb51ca282017-07-26 16:25:28 -07001326 }
1327
1328 mImageItemsValid = true;
1329 return OK;
1330}
1331
1332void ItemTable::attachProperty(const AssociationEntry &association) {
Chong Zhangecd08132017-10-05 16:09:29 -07001333 ssize_t itemIndex = mItemIdToItemMap.indexOfKey(association.itemId);
Chong Zhangb51ca282017-07-26 16:25:28 -07001334
1335 // ignore non-image items
Chong Zhangecd08132017-10-05 16:09:29 -07001336 if (itemIndex < 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001337 return;
1338 }
1339
1340 uint16_t propertyIndex = association.index;
1341 if (propertyIndex >= mItemProperties.size()) {
1342 ALOGW("Ignoring invalid property index %d", propertyIndex);
1343 return;
1344 }
1345
1346 ALOGV("attach property %d to item id %d)",
1347 propertyIndex, association.itemId);
1348
1349 mItemProperties[propertyIndex]->attachTo(
Chong Zhangecd08132017-10-05 16:09:29 -07001350 mItemIdToItemMap.editValueAt(itemIndex));
Chong Zhangb51ca282017-07-26 16:25:28 -07001351}
1352
1353sp<MetaData> ItemTable::getImageMeta() {
1354 if (!mImageItemsValid) {
1355 return NULL;
1356 }
1357
Chong Zhangecd08132017-10-05 16:09:29 -07001358 ssize_t itemIndex = mItemIdToItemMap.indexOfKey(mPrimaryItemId);
1359 if (itemIndex < 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001360 ALOGE("Primary item id %d not found!", mPrimaryItemId);
1361 return NULL;
1362 }
1363
Chong Zhangecd08132017-10-05 16:09:29 -07001364 ALOGV("primary item index %zu", itemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001365
Chong Zhangecd08132017-10-05 16:09:29 -07001366 const ImageItem *image = &mItemIdToItemMap[itemIndex];
Chong Zhangb51ca282017-07-26 16:25:28 -07001367
1368 sp<MetaData> meta = new MetaData;
1369 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_HEVC);
1370
1371 ALOGV("setting image size %dx%d", image->width, image->height);
1372 meta->setInt32(kKeyWidth, image->width);
1373 meta->setInt32(kKeyHeight, image->height);
1374 if (image->rotation != 0) {
Chong Zhang686c23b2017-10-05 15:16:59 -07001375 // Rotation angle in HEIF is CCW, convert to CW here to be
1376 // consistent with the other media formats.
1377 switch(image->rotation) {
1378 case 90: meta->setInt32(kKeyRotation, 270); break;
1379 case 180: meta->setInt32(kKeyRotation, 180); break;
1380 case 270: meta->setInt32(kKeyRotation, 90); break;
1381 default: break; // don't set if invalid
1382 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001383 }
1384 meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5);
1385
1386 if (!image->thumbnails.empty()) {
Chong Zhangecd08132017-10-05 16:09:29 -07001387 ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(image->thumbnails[0]);
1388 if (thumbItemIndex >= 0) {
1389 const ImageItem &thumbnail = mItemIdToItemMap[thumbItemIndex];
Chong Zhangb51ca282017-07-26 16:25:28 -07001390
1391 meta->setInt32(kKeyThumbnailWidth, thumbnail.width);
1392 meta->setInt32(kKeyThumbnailHeight, thumbnail.height);
1393 meta->setData(kKeyThumbnailHVCC, kTypeHVCC,
1394 thumbnail.hvcc->data(), thumbnail.hvcc->size());
Chong Zhangecd08132017-10-05 16:09:29 -07001395 ALOGV("thumbnail meta: %dx%d, item index %zd",
1396 thumbnail.width, thumbnail.height, thumbItemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001397 } else {
Chong Zhangecd08132017-10-05 16:09:29 -07001398 ALOGW("%s: Referenced thumbnail does not exist!", __FUNCTION__);
Chong Zhangb51ca282017-07-26 16:25:28 -07001399 }
1400 }
1401
1402 if (image->isGrid()) {
Chong Zhangecd08132017-10-05 16:09:29 -07001403 ssize_t tileItemIndex = mItemIdToItemMap.indexOfKey(image->dimgRefs[0]);
1404 if (tileItemIndex < 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001405 return NULL;
1406 }
Chong Zhangee079fe2017-08-23 13:51:17 -07001407 // when there are tiles, (kKeyWidth, kKeyHeight) is the full tiled area,
1408 // and (kKeyDisplayWidth, kKeyDisplayHeight) may be smaller than that.
1409 meta->setInt32(kKeyDisplayWidth, image->width);
1410 meta->setInt32(kKeyDisplayHeight, image->height);
1411 int32_t gridRows = image->rows, gridCols = image->columns;
Chong Zhangb51ca282017-07-26 16:25:28 -07001412
Chong Zhangee079fe2017-08-23 13:51:17 -07001413 // point image to the first tile for grid size and HVCC
Chong Zhangecd08132017-10-05 16:09:29 -07001414 image = &mItemIdToItemMap.editValueAt(tileItemIndex);
Chong Zhangee079fe2017-08-23 13:51:17 -07001415 meta->setInt32(kKeyWidth, image->width * gridCols);
1416 meta->setInt32(kKeyHeight, image->height * gridRows);
1417 meta->setInt32(kKeyGridWidth, image->width);
1418 meta->setInt32(kKeyGridHeight, image->height);
1419 meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5);
Chong Zhangb51ca282017-07-26 16:25:28 -07001420 }
1421
1422 if (image->hvcc == NULL) {
Chong Zhangecd08132017-10-05 16:09:29 -07001423 ALOGE("%s: hvcc is missing for item index %zd!", __FUNCTION__, itemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001424 return NULL;
1425 }
1426 meta->setData(kKeyHVCC, kTypeHVCC, image->hvcc->data(), image->hvcc->size());
1427
1428 if (image->icc != NULL) {
1429 meta->setData(kKeyIccProfile, 0, image->icc->data(), image->icc->size());
1430 }
1431 return meta;
1432}
1433
1434uint32_t ItemTable::countImages() const {
Chong Zhangecd08132017-10-05 16:09:29 -07001435 return mImageItemsValid ? mItemIdToItemMap.size() : 0;
Chong Zhangb51ca282017-07-26 16:25:28 -07001436}
1437
Chong Zhangecd08132017-10-05 16:09:29 -07001438status_t ItemTable::findPrimaryImage(uint32_t *itemIndex) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001439 if (!mImageItemsValid) {
1440 return INVALID_OPERATION;
1441 }
1442
Chong Zhangecd08132017-10-05 16:09:29 -07001443 ssize_t index = mItemIdToItemMap.indexOfKey(mPrimaryItemId);
Chong Zhangb51ca282017-07-26 16:25:28 -07001444 if (index < 0) {
1445 return ERROR_MALFORMED;
1446 }
1447
Chong Zhangecd08132017-10-05 16:09:29 -07001448 *itemIndex = index;
Chong Zhangb51ca282017-07-26 16:25:28 -07001449 return OK;
1450}
1451
Chong Zhangecd08132017-10-05 16:09:29 -07001452status_t ItemTable::findThumbnail(uint32_t *itemIndex) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001453 if (!mImageItemsValid) {
1454 return INVALID_OPERATION;
1455 }
1456
Chong Zhangecd08132017-10-05 16:09:29 -07001457 ssize_t primaryItemIndex = mItemIdToItemMap.indexOfKey(mPrimaryItemId);
1458 if (primaryItemIndex < 0) {
1459 ALOGE("%s: Primary item id %d not found!", __FUNCTION__, mPrimaryItemId);
Chong Zhangb51ca282017-07-26 16:25:28 -07001460 return ERROR_MALFORMED;
1461 }
1462
Chong Zhangecd08132017-10-05 16:09:29 -07001463 const ImageItem &primaryImage = mItemIdToItemMap[primaryItemIndex];
Chong Zhangb51ca282017-07-26 16:25:28 -07001464 if (primaryImage.thumbnails.empty()) {
Chong Zhangecd08132017-10-05 16:09:29 -07001465 ALOGW("%s: Using primary in place of thumbnail.", __FUNCTION__);
1466 *itemIndex = primaryItemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001467 return OK;
1468 }
1469
Chong Zhangecd08132017-10-05 16:09:29 -07001470 ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(
Chong Zhangb51ca282017-07-26 16:25:28 -07001471 primaryImage.thumbnails[0]);
Chong Zhangecd08132017-10-05 16:09:29 -07001472 if (thumbItemIndex < 0) {
1473 ALOGE("%s: Thumbnail item id %d not found!",
1474 __FUNCTION__, primaryImage.thumbnails[0]);
Chong Zhangb51ca282017-07-26 16:25:28 -07001475 return ERROR_MALFORMED;
1476 }
1477
Chong Zhangecd08132017-10-05 16:09:29 -07001478 *itemIndex = thumbItemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001479 return OK;
1480}
1481
1482status_t ItemTable::getImageOffsetAndSize(
Chong Zhangecd08132017-10-05 16:09:29 -07001483 uint32_t *itemIndex, off64_t *offset, size_t *size) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001484 if (!mImageItemsValid) {
1485 return INVALID_OPERATION;
1486 }
1487
Chong Zhangecd08132017-10-05 16:09:29 -07001488 if (itemIndex != NULL) {
1489 if (*itemIndex >= mItemIdToItemMap.size()) {
1490 ALOGE("%s: Bad item index!", __FUNCTION__);
Chong Zhangb51ca282017-07-26 16:25:28 -07001491 return BAD_VALUE;
1492 }
Chong Zhangecd08132017-10-05 16:09:29 -07001493 mCurrentItemIndex = *itemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001494 }
1495
Chong Zhangecd08132017-10-05 16:09:29 -07001496 ImageItem &image = mItemIdToItemMap.editValueAt(mCurrentItemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001497 if (image.isGrid()) {
1498 uint32_t tileItemId;
Chong Zhangecd08132017-10-05 16:09:29 -07001499 status_t err = image.getNextTileItemId(&tileItemId, itemIndex != NULL);
Chong Zhangb51ca282017-07-26 16:25:28 -07001500 if (err != OK) {
1501 return err;
1502 }
Chong Zhangecd08132017-10-05 16:09:29 -07001503 ssize_t tileItemIndex = mItemIdToItemMap.indexOfKey(tileItemId);
1504 if (tileItemIndex < 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001505 return ERROR_END_OF_STREAM;
1506 }
Chong Zhangecd08132017-10-05 16:09:29 -07001507 *offset = mItemIdToItemMap[tileItemIndex].offset;
1508 *size = mItemIdToItemMap[tileItemIndex].size;
Chong Zhangb51ca282017-07-26 16:25:28 -07001509 } else {
Chong Zhangecd08132017-10-05 16:09:29 -07001510 if (itemIndex == NULL) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001511 // For single images, we only allow it to be read once, after that
Chong Zhangecd08132017-10-05 16:09:29 -07001512 // it's EOS. New item index must be requested each time.
Chong Zhangb51ca282017-07-26 16:25:28 -07001513 return ERROR_END_OF_STREAM;
1514 }
Chong Zhangecd08132017-10-05 16:09:29 -07001515 *offset = mItemIdToItemMap[mCurrentItemIndex].offset;
1516 *size = mItemIdToItemMap[mCurrentItemIndex].size;
Chong Zhangb51ca282017-07-26 16:25:28 -07001517 }
1518
1519 return OK;
1520}
1521
1522} // namespace heif
1523
1524} // namespace android