blob: 88b4bb71f5db57ae8425f2c6dadb73cc8d33bfd1 [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
473 void apply(KeyedVector<uint32_t, ImageItem> &itemIdToImageMap) const {
474 ssize_t imageIndex = itemIdToImageMap.indexOfKey(mItemId);
475
476 // ignore non-image items
477 if (imageIndex < 0) {
478 return;
479 }
480
481 ALOGV("attach reference type 0x%x to item id %d)", type(), mItemId);
482
483 if (type() == FOURCC('d', 'i', 'm', 'g')) {
484 ImageItem &image = itemIdToImageMap.editValueAt(imageIndex);
485 if (!image.dimgRefs.empty()) {
486 ALOGW("dimgRefs if not clean!");
487 }
488 image.dimgRefs.appendVector(mRefs);
489 } else if (type() == FOURCC('t', 'h', 'm', 'b')) {
490 for (size_t i = 0; i < mRefs.size(); i++) {
491 imageIndex = itemIdToImageMap.indexOfKey(mRefs[i]);
492
493 // ignore non-image items
494 if (imageIndex < 0) {
495 continue;
496 }
497 ALOGV("Image item id %d uses thumbnail item id %d", mRefs[i], mItemId);
498 ImageItem &image = itemIdToImageMap.editValueAt(imageIndex);
499 if (!image.thumbnails.empty()) {
500 ALOGW("already has thumbnails!");
501 }
502 image.thumbnails.push_back(mItemId);
503 }
504 } else {
505 ALOGW("ignoring unsupported ref type 0x%x", type());
506 }
507 }
508
509private:
510 uint32_t mItemId;
511 uint32_t mRefIdSize;
512 Vector<uint32_t> mRefs;
513
514 DISALLOW_EVIL_CONSTRUCTORS(ItemReference);
515};
516
517status_t ItemReference::parse(off64_t offset, size_t size) {
518 if (size < mRefIdSize + 2) {
519 return ERROR_MALFORMED;
520 }
521 if (!source()->getUInt32Var(offset, &mItemId, mRefIdSize)) {
522 return ERROR_IO;
523 }
524 offset += mRefIdSize;
525
526 uint16_t count;
527 if (!source()->getUInt16(offset, &count)) {
528 return ERROR_IO;
529 }
530 offset += 2;
531 size -= (mRefIdSize + 2);
532
533 if (size < count * mRefIdSize) {
534 return ERROR_MALFORMED;
535 }
536
537 for (size_t i = 0; i < count; i++) {
538 uint32_t refItemId;
539 if (!source()->getUInt32Var(offset, &refItemId, mRefIdSize)) {
540 return ERROR_IO;
541 }
542 offset += mRefIdSize;
543 mRefs.push_back(refItemId);
544 ALOGV("item id %d: referencing item id %d", mItemId, refItemId);
545 }
546
547 return OK;
548}
549
550struct IrefBox : public FullBox {
551 IrefBox(const sp<DataSource> source, Vector<sp<ItemReference> > *itemRefs) :
552 FullBox(source, FOURCC('i', 'r', 'e', 'f')), mRefIdSize(0), mItemRefs(itemRefs) {}
553
554 status_t parse(off64_t offset, size_t size);
555
556protected:
557 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
558
559private:
560 uint32_t mRefIdSize;
561 Vector<sp<ItemReference> > *mItemRefs;
562};
563
564status_t IrefBox::parse(off64_t offset, size_t size) {
565 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
566 status_t err = parseFullBoxHeader(&offset, &size);
567 if (err != OK) {
568 return err;
569 }
570
571 mRefIdSize = (version() == 0) ? 2 : 4;
572 return parseChunks(offset, size);
573}
574
575status_t IrefBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
576 sp<ItemReference> itemRef = new ItemReference(source(), type, mRefIdSize);
577
578 status_t err = itemRef->parse(offset, size);
579 if (err != OK) {
580 return err;
581 }
582 mItemRefs->push_back(itemRef);
583 return OK;
584}
585
586/////////////////////////////////////////////////////////////////////
587//
588// ItemProperty related boxes
589//
590
591struct AssociationEntry {
592 uint32_t itemId;
593 bool essential;
594 uint16_t index;
595};
596
597struct ItemProperty : public RefBase {
598 ItemProperty() {}
599
600 virtual void attachTo(ImageItem &/*image*/) const {
601 ALOGW("Unrecognized property");
602 }
603 virtual status_t parse(off64_t /*offset*/, size_t /*size*/) {
604 ALOGW("Unrecognized property");
605 return OK;
606 }
607
608private:
609 DISALLOW_EVIL_CONSTRUCTORS(ItemProperty);
610};
611
612struct IspeBox : public FullBox, public ItemProperty {
613 IspeBox(const sp<DataSource> source) :
614 FullBox(source, FOURCC('i', 's', 'p', 'e')), mWidth(0), mHeight(0) {}
615
616 status_t parse(off64_t offset, size_t size) override;
617
618 void attachTo(ImageItem &image) const override {
619 image.width = mWidth;
620 image.height = mHeight;
621 }
622
623private:
624 uint32_t mWidth;
625 uint32_t mHeight;
626};
627
628status_t IspeBox::parse(off64_t offset, size_t size) {
629 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
630
631 status_t err = parseFullBoxHeader(&offset, &size);
632 if (err != OK) {
633 return err;
634 }
635
636 if (size < 8) {
637 return ERROR_MALFORMED;
638 }
639 if (!source()->getUInt32(offset, &mWidth)
640 || !source()->getUInt32(offset + 4, &mHeight)) {
641 return ERROR_IO;
642 }
643 ALOGV("property ispe: %dx%d", mWidth, mHeight);
644
645 return OK;
646}
647
648struct HvccBox : public Box, public ItemProperty {
649 HvccBox(const sp<DataSource> source) :
650 Box(source, FOURCC('h', 'v', 'c', 'C')) {}
651
652 status_t parse(off64_t offset, size_t size) override;
653
654 void attachTo(ImageItem &image) const override {
655 image.hvcc = mHVCC;
656 }
657
658private:
659 sp<ABuffer> mHVCC;
660};
661
662status_t HvccBox::parse(off64_t offset, size_t size) {
663 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
664
665 mHVCC = new ABuffer(size);
666
667 if (mHVCC->data() == NULL) {
668 ALOGE("b/28471206");
669 return NO_MEMORY;
670 }
671
672 if (source()->readAt(offset, mHVCC->data(), size) < (ssize_t)size) {
673 return ERROR_IO;
674 }
675
676 ALOGV("property hvcC");
677
678 return OK;
679}
680
681struct IrotBox : public Box, public ItemProperty {
682 IrotBox(const sp<DataSource> source) :
683 Box(source, FOURCC('i', 'r', 'o', 't')), mAngle(0) {}
684
685 status_t parse(off64_t offset, size_t size) override;
686
687 void attachTo(ImageItem &image) const override {
688 image.rotation = mAngle * 90;
689 }
690
691private:
692 uint8_t mAngle;
693};
694
695status_t IrotBox::parse(off64_t offset, size_t size) {
696 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
697
698 if (size < 1) {
699 return ERROR_MALFORMED;
700 }
701 if (source()->readAt(offset, &mAngle, 1) != 1) {
702 return ERROR_IO;
703 }
704 mAngle &= 0x3;
705 ALOGV("property irot: %d", mAngle);
706
707 return OK;
708}
709
710struct ColrBox : public Box, public ItemProperty {
711 ColrBox(const sp<DataSource> source) :
712 Box(source, FOURCC('c', 'o', 'l', 'r')) {}
713
714 status_t parse(off64_t offset, size_t size) override;
715
716 void attachTo(ImageItem &image) const override {
717 image.icc = mICCData;
718 }
719
720private:
721 sp<ABuffer> mICCData;
722};
723
724status_t ColrBox::parse(off64_t offset, size_t size) {
725 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
726
727 if (size < 4) {
728 return ERROR_MALFORMED;
729 }
730 uint32_t colour_type;
731 if (!source()->getUInt32(offset, &colour_type)) {
732 return ERROR_IO;
733 }
734 offset += 4;
735 size -= 4;
736 if (colour_type == FOURCC('n', 'c', 'l', 'x')) {
737 return OK;
738 }
739 if ((colour_type != FOURCC('r', 'I', 'C', 'C')) &&
740 (colour_type != FOURCC('p', 'r', 'o', 'f'))) {
741 return ERROR_MALFORMED;
742 }
743
744 mICCData = new ABuffer(size);
745 if (mICCData->data() == NULL) {
746 ALOGE("b/28471206");
747 return NO_MEMORY;
748 }
749
750 if (source()->readAt(offset, mICCData->data(), size) != (ssize_t)size) {
751 return ERROR_IO;
752 }
753
754 ALOGV("property Colr: size %zd", size);
755 return OK;
756}
757
758struct IpmaBox : public FullBox {
759 IpmaBox(const sp<DataSource> source, Vector<AssociationEntry> *associations) :
760 FullBox(source, FOURCC('i', 'p', 'm', 'a')), mAssociations(associations) {}
761
762 status_t parse(off64_t offset, size_t size);
763private:
764 Vector<AssociationEntry> *mAssociations;
765};
766
767status_t IpmaBox::parse(off64_t offset, size_t size) {
768 status_t err = parseFullBoxHeader(&offset, &size);
769 if (err != OK) {
770 return err;
771 }
772
773 if (size < 4) {
774 return ERROR_MALFORMED;
775 }
776 uint32_t entryCount;
777 if (!source()->getUInt32(offset, &entryCount)) {
778 return ERROR_IO;
779 }
780 offset += 4;
781 size -= 4;
782
783 for (size_t k = 0; k < entryCount; ++k) {
784 uint32_t itemId = 0;
785 size_t itemIdSize = (version() < 1) ? 2 : 4;
786
787 if (size < itemIdSize + 1) {
788 return ERROR_MALFORMED;
789 }
790
791 if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) {
792 return ERROR_IO;
793 }
794 offset += itemIdSize;
795 size -= itemIdSize;
796
797 uint8_t associationCount;
798 if (!source()->readAt(offset, &associationCount, 1)) {
799 return ERROR_IO;
800 }
801 offset++;
802 size--;
803
804 for (size_t i = 0; i < associationCount; ++i) {
805 size_t propIndexSize = (flags() & 1) ? 2 : 1;
806 if (size < propIndexSize) {
807 return ERROR_MALFORMED;
808 }
809 uint16_t propIndex;
810 if (!source()->getUInt16Var(offset, &propIndex, propIndexSize)) {
811 return ERROR_IO;
812 }
813 offset += propIndexSize;
814 size -= propIndexSize;
815 uint16_t bitmask = (1 << (8 * propIndexSize - 1));
816 AssociationEntry entry = {
817 .itemId = itemId,
818 .essential = !!(propIndex & bitmask),
819 .index = (uint16_t) (propIndex & ~bitmask)
820 };
821
822 ALOGV("item id %d associated to property %d (essential %d)",
823 itemId, entry.index, entry.essential);
824
825 mAssociations->push_back(entry);
826 }
827 }
828
829 return OK;
830}
831
832struct IpcoBox : public Box {
833 IpcoBox(const sp<DataSource> source, Vector<sp<ItemProperty> > *properties) :
834 Box(source, FOURCC('i', 'p', 'c', 'o')), mItemProperties(properties) {}
835
836 status_t parse(off64_t offset, size_t size);
837protected:
838 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
839
840private:
841 Vector<sp<ItemProperty> > *mItemProperties;
842};
843
844status_t IpcoBox::parse(off64_t offset, size_t size) {
845 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
846 // push dummy as the index is 1-based
847 mItemProperties->push_back(new ItemProperty());
848 return parseChunks(offset, size);
849}
850
851status_t IpcoBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
852 sp<ItemProperty> itemProperty;
853 switch(type) {
854 case FOURCC('h', 'v', 'c', 'C'):
855 {
856 itemProperty = new HvccBox(source());
857 break;
858 }
859 case FOURCC('i', 's', 'p', 'e'):
860 {
861 itemProperty = new IspeBox(source());
862 break;
863 }
864 case FOURCC('i', 'r', 'o', 't'):
865 {
866 itemProperty = new IrotBox(source());
867 break;
868 }
869 case FOURCC('c', 'o', 'l', 'r'):
870 {
871 itemProperty = new ColrBox(source());
872 break;
873 }
874 default:
875 {
876 // push dummy to maintain correct item property index
877 itemProperty = new ItemProperty();
878 break;
879 }
880 }
881 status_t err = itemProperty->parse(offset, size);
882 if (err != OK) {
883 return err;
884 }
885 mItemProperties->push_back(itemProperty);
886 return OK;
887}
888
889struct IprpBox : public Box {
890 IprpBox(const sp<DataSource> source,
891 Vector<sp<ItemProperty> > *properties,
892 Vector<AssociationEntry> *associations) :
893 Box(source, FOURCC('i', 'p', 'r', 'p')),
894 mProperties(properties), mAssociations(associations) {}
895
896 status_t parse(off64_t offset, size_t size);
897protected:
898 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
899
900private:
901 Vector<sp<ItemProperty> > *mProperties;
902 Vector<AssociationEntry> *mAssociations;
903};
904
905status_t IprpBox::parse(off64_t offset, size_t size) {
906 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
907
908 status_t err = parseChunks(offset, size);
909 if (err != OK) {
910 return err;
911 }
912 return OK;
913}
914
915status_t IprpBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
916 switch(type) {
917 case FOURCC('i', 'p', 'c', 'o'):
918 {
919 IpcoBox ipcoBox(source(), mProperties);
920 return ipcoBox.parse(offset, size);
921 }
922 case FOURCC('i', 'p', 'm', 'a'):
923 {
924 IpmaBox ipmaBox(source(), mAssociations);
925 return ipmaBox.parse(offset, size);
926 }
927 default:
928 {
929 ALOGW("Unrecognized box.");
930 break;
931 }
932 }
933 return OK;
934}
935
936/////////////////////////////////////////////////////////////////////
937//
938// ItemInfo related boxes
939//
940struct ItemInfo {
941 uint32_t itemId;
942 uint32_t itemType;
943};
944
945struct InfeBox : public FullBox {
946 InfeBox(const sp<DataSource> source) :
947 FullBox(source, FOURCC('i', 'n', 'f', 'e')) {}
948
949 status_t parse(off64_t offset, size_t size, ItemInfo *itemInfo);
950
951private:
952 bool parseNullTerminatedString(off64_t *offset, size_t *size, String8 *out);
953};
954
955bool InfeBox::parseNullTerminatedString(
956 off64_t *offset, size_t *size, String8 *out) {
957 char tmp[256];
958 size_t len = 0;
959 off64_t newOffset = *offset;
960 off64_t stopOffset = *offset + *size;
961 while (newOffset < stopOffset) {
962 if (!source()->readAt(newOffset++, &tmp[len], 1)) {
963 return false;
964 }
965 if (tmp[len] == 0) {
966 out->append(tmp, len);
967
968 *offset = newOffset;
969 *size = stopOffset - newOffset;
970
971 return true;
972 }
973 if (++len >= sizeof(tmp)) {
974 out->append(tmp, len);
975 len = 0;
976 }
977 }
978 return false;
979}
980
981status_t InfeBox::parse(off64_t offset, size_t size, ItemInfo *itemInfo) {
982 status_t err = parseFullBoxHeader(&offset, &size);
983 if (err != OK) {
984 return err;
985 }
986
987 if (version() == 0 || version() == 1) {
988 if (size < 4) {
989 return ERROR_MALFORMED;
990 }
991 uint16_t item_id;
992 if (!source()->getUInt16(offset, &item_id)) {
993 return ERROR_IO;
994 }
995 ALOGV("item_id %d", item_id);
996 uint16_t item_protection_index;
997 if (!source()->getUInt16(offset + 2, &item_protection_index)) {
998 return ERROR_IO;
999 }
1000 offset += 4;
1001 size -= 4;
1002
1003 String8 item_name;
1004 if (!parseNullTerminatedString(&offset, &size, &item_name)) {
1005 return ERROR_MALFORMED;
1006 }
1007
1008 String8 content_type;
1009 if (!parseNullTerminatedString(&offset, &size, &content_type)) {
1010 return ERROR_MALFORMED;
1011 }
1012
1013 String8 content_encoding;
1014 if (!parseNullTerminatedString(&offset, &size, &content_encoding)) {
1015 return ERROR_MALFORMED;
1016 }
1017
1018 if (version() == 1) {
1019 uint32_t extension_type;
1020 if (!source()->getUInt32(offset, &extension_type)) {
1021 return ERROR_IO;
1022 }
1023 offset++;
1024 size--;
1025 // TODO: handle this case
1026 }
1027 } else { // version >= 2
1028 uint32_t item_id;
1029 size_t itemIdSize = (version() == 2) ? 2 : 4;
1030 if (size < itemIdSize + 6) {
1031 return ERROR_MALFORMED;
1032 }
1033 if (!source()->getUInt32Var(offset, &item_id, itemIdSize)) {
1034 return ERROR_IO;
1035 }
1036 ALOGV("item_id %d", item_id);
1037 offset += itemIdSize;
1038 uint16_t item_protection_index;
1039 if (!source()->getUInt16(offset, &item_protection_index)) {
1040 return ERROR_IO;
1041 }
1042 ALOGV("item_protection_index %d", item_protection_index);
1043 offset += 2;
1044 uint32_t item_type;
1045 if (!source()->getUInt32(offset, &item_type)) {
1046 return ERROR_IO;
1047 }
1048
1049 itemInfo->itemId = item_id;
1050 itemInfo->itemType = item_type;
1051
1052 char itemTypeString[5];
1053 MakeFourCCString(item_type, itemTypeString);
1054 ALOGV("item_type %s", itemTypeString);
1055 offset += 4;
1056 size -= itemIdSize + 6;
1057
1058 String8 item_name;
1059 if (!parseNullTerminatedString(&offset, &size, &item_name)) {
1060 return ERROR_MALFORMED;
1061 }
1062 ALOGV("item_name %s", item_name.c_str());
1063
1064 if (item_type == FOURCC('m', 'i', 'm', 'e')) {
1065 String8 content_type;
1066 if (!parseNullTerminatedString(&offset, &size, &content_type)) {
1067 return ERROR_MALFORMED;
1068 }
1069
1070 String8 content_encoding;
1071 if (!parseNullTerminatedString(&offset, &size, &content_encoding)) {
1072 return ERROR_MALFORMED;
1073 }
1074 } else if (item_type == FOURCC('u', 'r', 'i', ' ')) {
1075 String8 item_uri_type;
1076 if (!parseNullTerminatedString(&offset, &size, &item_uri_type)) {
1077 return ERROR_MALFORMED;
1078 }
1079 }
1080 }
1081 return OK;
1082}
1083
1084struct IinfBox : public FullBox {
1085 IinfBox(const sp<DataSource> source, Vector<ItemInfo> *itemInfos) :
1086 FullBox(source, FOURCC('i', 'i', 'n', 'f')),
1087 mItemInfos(itemInfos), mHasGrids(false) {}
1088
1089 status_t parse(off64_t offset, size_t size);
1090
1091 bool hasGrids() { return mHasGrids; }
1092
1093protected:
1094 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
1095
1096private:
1097 Vector<ItemInfo> *mItemInfos;
1098 bool mHasGrids;
1099};
1100
1101status_t IinfBox::parse(off64_t offset, size_t size) {
1102 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1103
1104 status_t err = parseFullBoxHeader(&offset, &size);
1105 if (err != OK) {
1106 return err;
1107 }
1108
1109 size_t entryCountSize = version() == 0 ? 2 : 4;
1110 if (size < entryCountSize) {
1111 return ERROR_MALFORMED;
1112 }
1113 uint32_t entry_count;
1114 if (!source()->getUInt32Var(offset, &entry_count, entryCountSize)) {
1115 return ERROR_IO;
1116 }
1117 ALOGV("entry_count %d", entry_count);
1118
1119 off64_t stopOffset = offset + size;
1120 offset += entryCountSize;
1121 for (size_t i = 0; i < entry_count && offset < stopOffset; i++) {
1122 ALOGV("entry %zu", i);
1123 status_t err = parseChunk(&offset);
1124 if (err != OK) {
1125 return err;
1126 }
1127 }
1128 if (offset != stopOffset) {
1129 return ERROR_MALFORMED;
1130 }
1131
1132 return OK;
1133}
1134
1135status_t IinfBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
1136 if (type != FOURCC('i', 'n', 'f', 'e')) {
1137 return OK;
1138 }
1139
1140 InfeBox infeBox(source());
1141 ItemInfo itemInfo;
1142 status_t err = infeBox.parse(offset, size, &itemInfo);
1143 if (err != OK) {
1144 return err;
1145 }
1146 mItemInfos->push_back(itemInfo);
1147 mHasGrids |= (itemInfo.itemType == FOURCC('g', 'r', 'i', 'd'));
1148 return OK;
1149}
1150
1151//////////////////////////////////////////////////////////////////
1152
1153ItemTable::ItemTable(const sp<DataSource> &source)
1154 : mDataSource(source),
1155 mPrimaryItemId(0),
1156 mIdatOffset(0),
1157 mIdatSize(0),
1158 mImageItemsValid(false),
1159 mCurrentImageIndex(0) {
1160 mRequiredBoxes.insert('iprp');
1161 mRequiredBoxes.insert('iloc');
1162 mRequiredBoxes.insert('pitm');
1163 mRequiredBoxes.insert('iinf');
1164}
1165
1166ItemTable::~ItemTable() {}
1167
1168status_t ItemTable::parse(uint32_t type, off64_t data_offset, size_t chunk_data_size) {
1169 switch(type) {
1170 case FOURCC('i', 'l', 'o', 'c'):
1171 {
1172 return parseIlocBox(data_offset, chunk_data_size);
1173 }
1174 case FOURCC('i', 'i', 'n', 'f'):
1175 {
1176 return parseIinfBox(data_offset, chunk_data_size);
1177 }
1178 case FOURCC('i', 'p', 'r', 'p'):
1179 {
1180 return parseIprpBox(data_offset, chunk_data_size);
1181 }
1182 case FOURCC('p', 'i', 't', 'm'):
1183 {
1184 return parsePitmBox(data_offset, chunk_data_size);
1185 }
1186 case FOURCC('i', 'd', 'a', 't'):
1187 {
1188 return parseIdatBox(data_offset, chunk_data_size);
1189 }
1190 case FOURCC('i', 'r', 'e', 'f'):
1191 {
1192 return parseIrefBox(data_offset, chunk_data_size);
1193 }
1194 case FOURCC('i', 'p', 'r', 'o'):
1195 {
1196 ALOGW("ipro box not supported!");
1197 break;
1198 }
1199 default:
1200 {
1201 ALOGW("unrecognized box type: 0x%x", type);
1202 break;
1203 }
1204 }
1205 return ERROR_UNSUPPORTED;
1206}
1207
1208status_t ItemTable::parseIlocBox(off64_t offset, size_t size) {
1209 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1210
1211 IlocBox ilocBox(mDataSource, &mItemLocs);
1212 status_t err = ilocBox.parse(offset, size);
1213 if (err != OK) {
1214 return err;
1215 }
1216
1217 if (ilocBox.hasConstructMethod1()) {
1218 mRequiredBoxes.insert('idat');
1219 }
1220
1221 return buildImageItemsIfPossible('iloc');
1222}
1223
1224status_t ItemTable::parseIinfBox(off64_t offset, size_t size) {
1225 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1226
1227 IinfBox iinfBox(mDataSource, &mItemInfos);
1228 status_t err = iinfBox.parse(offset, size);
1229 if (err != OK) {
1230 return err;
1231 }
1232
1233 if (iinfBox.hasGrids()) {
1234 mRequiredBoxes.insert('iref');
1235 }
1236
1237 return buildImageItemsIfPossible('iinf');
1238}
1239
1240status_t ItemTable::parsePitmBox(off64_t offset, size_t size) {
1241 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1242
1243 PitmBox pitmBox(mDataSource);
1244 status_t err = pitmBox.parse(offset, size, &mPrimaryItemId);
1245 if (err != OK) {
1246 return err;
1247 }
1248
1249 return buildImageItemsIfPossible('pitm');
1250}
1251
1252status_t ItemTable::parseIprpBox(off64_t offset, size_t size) {
1253 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1254
1255 IprpBox iprpBox(mDataSource, &mItemProperties, &mAssociations);
1256 status_t err = iprpBox.parse(offset, size);
1257 if (err != OK) {
1258 return err;
1259 }
1260
1261 return buildImageItemsIfPossible('iprp');
1262}
1263
1264status_t ItemTable::parseIdatBox(off64_t offset, size_t size) {
1265 ALOGV("%s: idat offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1266
1267 // only remember the offset and size of idat box for later use
1268 mIdatOffset = offset;
1269 mIdatSize = size;
1270
1271 return buildImageItemsIfPossible('idat');
1272}
1273
1274status_t ItemTable::parseIrefBox(off64_t offset, size_t size) {
1275 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1276
1277 IrefBox irefBox(mDataSource, &mItemReferences);
1278 status_t err = irefBox.parse(offset, size);
1279 if (err != OK) {
1280 return err;
1281 }
1282
1283 return buildImageItemsIfPossible('iref');
1284}
1285
1286status_t ItemTable::buildImageItemsIfPossible(uint32_t type) {
1287 if (mImageItemsValid) {
1288 return OK;
1289 }
1290
1291 mBoxesSeen.insert(type);
1292
1293 // need at least 'iprp', 'iloc', 'pitm', 'iinf';
1294 // need 'idat' if any items used construction_method of 2;
1295 // need 'iref' if there are grids.
1296 if (!std::includes(
1297 mBoxesSeen.begin(), mBoxesSeen.end(),
1298 mRequiredBoxes.begin(), mRequiredBoxes.end())) {
1299 return OK;
1300 }
1301
1302 ALOGV("building image table...");
1303
1304 for (size_t i = 0; i < mItemInfos.size(); i++) {
1305 const ItemInfo &info = mItemInfos[i];
1306
1307
1308 // ignore non-image items
1309 if (info.itemType != FOURCC('g', 'r', 'i', 'd') &&
1310 info.itemType != FOURCC('h', 'v', 'c', '1')) {
1311 continue;
1312 }
1313
1314 ssize_t imageIndex = mItemIdToImageMap.indexOfKey(info.itemId);
1315 if (imageIndex >= 0) {
1316 ALOGW("ignoring duplicate image item id %d", info.itemId);
1317 continue;
1318 }
1319
1320 ssize_t ilocIndex = mItemLocs.indexOfKey(info.itemId);
1321 if (ilocIndex < 0) {
1322 ALOGE("iloc missing for image item id %d", info.itemId);
1323 continue;
1324 }
1325 const ItemLoc &iloc = mItemLocs[ilocIndex];
1326
1327 off64_t offset;
1328 size_t size;
1329 if (iloc.getLoc(&offset, &size, mIdatOffset, mIdatSize) != OK) {
1330 return ERROR_MALFORMED;
1331 }
1332
1333 ImageItem image(info.itemType);
1334
1335 ALOGV("adding %s: itemId %d", image.isGrid() ? "grid" : "image", info.itemId);
1336
1337 if (image.isGrid()) {
1338 if (size > 12) {
1339 return ERROR_MALFORMED;
1340 }
1341 uint8_t buf[12];
1342 if (!mDataSource->readAt(offset, buf, size)) {
1343 return ERROR_IO;
1344 }
1345
1346 image.rows = buf[2] + 1;
1347 image.columns = buf[3] + 1;
1348
1349 ALOGV("rows %d, columans %d", image.rows, image.columns);
1350 } else {
1351 image.offset = offset;
1352 image.size = size;
1353 }
1354 mItemIdToImageMap.add(info.itemId, image);
1355 }
1356
1357 for (size_t i = 0; i < mAssociations.size(); i++) {
1358 attachProperty(mAssociations[i]);
1359 }
1360
1361 for (size_t i = 0; i < mItemReferences.size(); i++) {
1362 mItemReferences[i]->apply(mItemIdToImageMap);
1363 }
1364
1365 mImageItemsValid = true;
1366 return OK;
1367}
1368
1369void ItemTable::attachProperty(const AssociationEntry &association) {
1370 ssize_t imageIndex = mItemIdToImageMap.indexOfKey(association.itemId);
1371
1372 // ignore non-image items
1373 if (imageIndex < 0) {
1374 return;
1375 }
1376
1377 uint16_t propertyIndex = association.index;
1378 if (propertyIndex >= mItemProperties.size()) {
1379 ALOGW("Ignoring invalid property index %d", propertyIndex);
1380 return;
1381 }
1382
1383 ALOGV("attach property %d to item id %d)",
1384 propertyIndex, association.itemId);
1385
1386 mItemProperties[propertyIndex]->attachTo(
1387 mItemIdToImageMap.editValueAt(imageIndex));
1388}
1389
1390sp<MetaData> ItemTable::getImageMeta() {
1391 if (!mImageItemsValid) {
1392 return NULL;
1393 }
1394
1395 ssize_t imageIndex = mItemIdToImageMap.indexOfKey(mPrimaryItemId);
1396 if (imageIndex < 0) {
1397 ALOGE("Primary item id %d not found!", mPrimaryItemId);
1398 return NULL;
1399 }
1400
1401 ALOGV("primary image index %zu", imageIndex);
1402
1403 const ImageItem *image = &mItemIdToImageMap[imageIndex];
1404
1405 sp<MetaData> meta = new MetaData;
1406 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_HEVC);
1407
1408 ALOGV("setting image size %dx%d", image->width, image->height);
1409 meta->setInt32(kKeyWidth, image->width);
1410 meta->setInt32(kKeyHeight, image->height);
1411 if (image->rotation != 0) {
1412 meta->setInt32(kKeyRotation, image->rotation);
1413 }
1414 meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5);
1415
1416 if (!image->thumbnails.empty()) {
1417 ssize_t thumbnailIndex = mItemIdToImageMap.indexOfKey(image->thumbnails[0]);
1418 if (thumbnailIndex >= 0) {
1419 const ImageItem &thumbnail = mItemIdToImageMap[thumbnailIndex];
1420
1421 meta->setInt32(kKeyThumbnailWidth, thumbnail.width);
1422 meta->setInt32(kKeyThumbnailHeight, thumbnail.height);
1423 meta->setData(kKeyThumbnailHVCC, kTypeHVCC,
1424 thumbnail.hvcc->data(), thumbnail.hvcc->size());
1425 ALOGV("thumbnail meta: %dx%d, index %zd",
1426 thumbnail.width, thumbnail.height, thumbnailIndex);
1427 } else {
1428 ALOGW("Referenced thumbnail does not exist!");
1429 }
1430 }
1431
1432 if (image->isGrid()) {
1433 ssize_t tileIndex = mItemIdToImageMap.indexOfKey(image->dimgRefs[0]);
1434 if (tileIndex < 0) {
1435 return NULL;
1436 }
Chong Zhangee079fe2017-08-23 13:51:17 -07001437 // when there are tiles, (kKeyWidth, kKeyHeight) is the full tiled area,
1438 // and (kKeyDisplayWidth, kKeyDisplayHeight) may be smaller than that.
1439 meta->setInt32(kKeyDisplayWidth, image->width);
1440 meta->setInt32(kKeyDisplayHeight, image->height);
1441 int32_t gridRows = image->rows, gridCols = image->columns;
Chong Zhangb51ca282017-07-26 16:25:28 -07001442
Chong Zhangee079fe2017-08-23 13:51:17 -07001443 // point image to the first tile for grid size and HVCC
Chong Zhangb51ca282017-07-26 16:25:28 -07001444 image = &mItemIdToImageMap.editValueAt(tileIndex);
Chong Zhangee079fe2017-08-23 13:51:17 -07001445 meta->setInt32(kKeyWidth, image->width * gridCols);
1446 meta->setInt32(kKeyHeight, image->height * gridRows);
1447 meta->setInt32(kKeyGridWidth, image->width);
1448 meta->setInt32(kKeyGridHeight, image->height);
1449 meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5);
Chong Zhangb51ca282017-07-26 16:25:28 -07001450 }
1451
1452 if (image->hvcc == NULL) {
1453 ALOGE("hvcc is missing!");
1454 return NULL;
1455 }
1456 meta->setData(kKeyHVCC, kTypeHVCC, image->hvcc->data(), image->hvcc->size());
1457
1458 if (image->icc != NULL) {
1459 meta->setData(kKeyIccProfile, 0, image->icc->data(), image->icc->size());
1460 }
1461 return meta;
1462}
1463
1464uint32_t ItemTable::countImages() const {
1465 return mImageItemsValid ? mItemIdToImageMap.size() : 0;
1466}
1467
1468status_t ItemTable::findPrimaryImage(uint32_t *imageIndex) {
1469 if (!mImageItemsValid) {
1470 return INVALID_OPERATION;
1471 }
1472
1473 ssize_t index = mItemIdToImageMap.indexOfKey(mPrimaryItemId);
1474 if (index < 0) {
1475 return ERROR_MALFORMED;
1476 }
1477
1478 *imageIndex = index;
1479 return OK;
1480}
1481
1482status_t ItemTable::findThumbnail(uint32_t *imageIndex) {
1483 if (!mImageItemsValid) {
1484 return INVALID_OPERATION;
1485 }
1486
1487 ssize_t primaryIndex = mItemIdToImageMap.indexOfKey(mPrimaryItemId);
1488 if (primaryIndex < 0) {
1489 ALOGE("Primary item id %d not found!", mPrimaryItemId);
1490 return ERROR_MALFORMED;
1491 }
1492
1493 const ImageItem &primaryImage = mItemIdToImageMap[primaryIndex];
1494 if (primaryImage.thumbnails.empty()) {
1495 ALOGW("Using primary in place of thumbnail.");
1496 *imageIndex = primaryIndex;
1497 return OK;
1498 }
1499
1500 ssize_t thumbnailIndex = mItemIdToImageMap.indexOfKey(
1501 primaryImage.thumbnails[0]);
1502 if (thumbnailIndex < 0) {
1503 ALOGE("Thumbnail item id %d not found!", primaryImage.thumbnails[0]);
1504 return ERROR_MALFORMED;
1505 }
1506
1507 *imageIndex = thumbnailIndex;
1508 return OK;
1509}
1510
1511status_t ItemTable::getImageOffsetAndSize(
1512 uint32_t *imageIndex, off64_t *offset, size_t *size) {
1513 if (!mImageItemsValid) {
1514 return INVALID_OPERATION;
1515 }
1516
1517 if (imageIndex != NULL) {
1518 if (*imageIndex >= mItemIdToImageMap.size()) {
1519 ALOGE("Bad image index!");
1520 return BAD_VALUE;
1521 }
1522 mCurrentImageIndex = *imageIndex;
1523 }
1524
1525 ImageItem &image = mItemIdToImageMap.editValueAt(mCurrentImageIndex);
1526 if (image.isGrid()) {
1527 uint32_t tileItemId;
1528 status_t err = image.getNextTileItemId(&tileItemId, imageIndex != NULL);
1529 if (err != OK) {
1530 return err;
1531 }
1532 ssize_t tileImageIndex = mItemIdToImageMap.indexOfKey(tileItemId);
1533 if (tileImageIndex < 0) {
1534 return ERROR_END_OF_STREAM;
1535 }
1536 *offset = mItemIdToImageMap[tileImageIndex].offset;
1537 *size = mItemIdToImageMap[tileImageIndex].size;
1538 } else {
1539 if (imageIndex == NULL) {
1540 // For single images, we only allow it to be read once, after that
1541 // it's EOS. New image index must be requested each time.
1542 return ERROR_END_OF_STREAM;
1543 }
1544 *offset = mItemIdToImageMap[mCurrentImageIndex].offset;
1545 *size = mItemIdToImageMap[mCurrentImageIndex].size;
1546 }
1547
1548 return OK;
1549}
1550
1551} // namespace heif
1552
1553} // namespace android