• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "ItemTable"
19 
20 #include <unordered_set>
21 
22 #include <ItemTable.h>
23 #include <media/MediaExtractorPluginApi.h>
24 #include <media/MediaExtractorPluginHelper.h>
25 #include <media/stagefright/MetaData.h>
26 #include <media/stagefright/MediaErrors.h>
27 #include <media/stagefright/foundation/ABuffer.h>
28 #include <media/stagefright/foundation/ByteUtils.h>
29 #include <media/stagefright/foundation/hexdump.h>
30 #include <media/stagefright/foundation/MediaDefs.h>
31 #include <utils/Log.h>
32 
33 namespace android {
34 
35 namespace heif {
36 
37 /////////////////////////////////////////////////////////////////////
38 //
39 //  struct to keep track of one image item
40 //
41 
42 struct ImageItem {
43     friend struct ItemReference;
44     friend struct ItemProperty;
45 
ImageItemandroid::heif::ImageItem46     ImageItem() : ImageItem(0, 0, false) {}
ImageItemandroid::heif::ImageItem47     ImageItem(uint32_t _type, uint32_t _id, bool _hidden) :
48             type(_type), itemId(_id), hidden(_hidden),
49             rows(0), columns(0), width(0), height(0), rotation(0),
50             offset(0), size(0), nextTileIndex(0) {}
51 
isGridandroid::heif::ImageItem52     bool isGrid() const {
53         return type == FOURCC("grid");
54     }
55 
getNextTileItemIdandroid::heif::ImageItem56     status_t getNextTileItemId(uint32_t *nextTileItemId, bool reset) {
57         if (reset) {
58             nextTileIndex = 0;
59         }
60         if (nextTileIndex >= dimgRefs.size()) {
61             return ERROR_END_OF_STREAM;
62         }
63         *nextTileItemId = dimgRefs[nextTileIndex++];
64         return OK;
65     }
66 
67     uint32_t type;
68     uint32_t itemId;
69     bool hidden;
70     int32_t rows;
71     int32_t columns;
72     int32_t width;
73     int32_t height;
74     int32_t rotation;
75     off64_t offset;
76     size_t size;
77     sp<ABuffer> hvcc;
78     sp<ABuffer> icc;
79     sp<ABuffer> av1c;
80 
81     Vector<uint32_t> thumbnails;
82     Vector<uint32_t> dimgRefs;
83     Vector<uint32_t> exifRefs;
84     Vector<uint32_t> xmpRefs;
85     size_t nextTileIndex;
86 };
87 
88 struct ExternalMetaItem {
89     off64_t offset;
90     size_t size;
91     bool isExif;
92 };
93 
94 /////////////////////////////////////////////////////////////////////
95 //
96 //  ISO boxes
97 //
98 
99 struct Box {
100 protected:
Boxandroid::heif::Box101     Box(DataSourceHelper *source, uint32_t type) :
102         mDataSource(source), mType(type) {}
103 
~Boxandroid::heif::Box104     virtual ~Box() {}
105 
onChunkDataandroid::heif::Box106     virtual status_t onChunkData(
107             uint32_t /*type*/, off64_t /*offset*/, size_t /*size*/) {
108         return OK;
109     }
110 
typeandroid::heif::Box111     inline uint32_t type() const { return mType; }
112 
sourceandroid::heif::Box113     inline DataSourceHelper *source() const { return mDataSource; }
114 
115     status_t parseChunk(off64_t *offset);
116 
117     status_t parseChunks(off64_t offset, size_t size);
118 
119 private:
120     DataSourceHelper *mDataSource;
121     uint32_t mType;
122 };
123 
parseChunk(off64_t * offset)124 status_t Box::parseChunk(off64_t *offset) {
125     if (*offset < 0) {
126         ALOGE("b/23540914");
127         return ERROR_MALFORMED;
128     }
129     uint32_t hdr[2];
130     if (mDataSource->readAt(*offset, hdr, 8) < 8) {
131         return ERROR_IO;
132     }
133     uint64_t chunk_size = ntohl(hdr[0]);
134     int32_t chunk_type = ntohl(hdr[1]);
135     off64_t data_offset = *offset + 8;
136 
137     if (chunk_size == 1) {
138         if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) {
139             return ERROR_IO;
140         }
141         chunk_size = ntoh64(chunk_size);
142         data_offset += 8;
143 
144         if (chunk_size < 16) {
145             // The smallest valid chunk is 16 bytes long in this case.
146             return ERROR_MALFORMED;
147         }
148     } else if (chunk_size == 0) {
149         // This shouldn't happen since we should never be top level
150         ALOGE("invalid chunk size 0 for non-top level box");
151         return ERROR_MALFORMED;
152     } else if (chunk_size < 8) {
153         // The smallest valid chunk is 8 bytes long.
154         ALOGE("invalid chunk size: %lld", (long long)chunk_size);
155         return ERROR_MALFORMED;
156     }
157 
158     char chunk[5];
159     MakeFourCCString(chunk_type, chunk);
160     ALOGV("chunk: %s @ %lld", chunk, (long long)*offset);
161 
162     off64_t chunk_data_size = chunk_size - (data_offset - *offset);
163     if (chunk_data_size < 0) {
164         ALOGE("b/23540914");
165         return ERROR_MALFORMED;
166     }
167 
168     status_t err = onChunkData(chunk_type, data_offset, chunk_data_size);
169 
170     if (err != OK) {
171         return err;
172     }
173     *offset += chunk_size;
174     return OK;
175 }
176 
parseChunks(off64_t offset,size_t size)177 status_t Box::parseChunks(off64_t offset, size_t size) {
178     off64_t stopOffset = offset + size;
179     while (offset < stopOffset) {
180         status_t err = parseChunk(&offset);
181         if (err != OK) {
182             return err;
183         }
184     }
185     if (offset != stopOffset) {
186         return ERROR_MALFORMED;
187     }
188     return OK;
189 }
190 
191 ///////////////////////////////////////////////////////////////////////
192 
193 struct FullBox : public Box {
194 protected:
FullBoxandroid::heif::FullBox195     FullBox(DataSourceHelper *source, uint32_t type) :
196         Box(source, type), mVersion(0), mFlags(0) {}
197 
versionandroid::heif::FullBox198     inline uint8_t version() const { return mVersion; }
199 
flagsandroid::heif::FullBox200     inline uint32_t flags() const { return mFlags; }
201 
202     status_t parseFullBoxHeader(off64_t *offset, size_t *size);
203 
204 private:
205     uint8_t mVersion;
206     uint32_t mFlags;
207 };
208 
parseFullBoxHeader(off64_t * offset,size_t * size)209 status_t FullBox::parseFullBoxHeader(off64_t *offset, size_t *size) {
210     if (*size < 4) {
211         return ERROR_MALFORMED;
212     }
213     if (!source()->readAt(*offset, &mVersion, 1)) {
214         return ERROR_IO;
215     }
216     if (!source()->getUInt24(*offset + 1, &mFlags)) {
217         return ERROR_IO;
218     }
219     *offset += 4;
220     *size -= 4;
221     return OK;
222 }
223 
224 /////////////////////////////////////////////////////////////////////
225 //
226 //  PrimaryImage box
227 //
228 
229 struct PitmBox : public FullBox {
PitmBoxandroid::heif::PitmBox230     PitmBox(DataSourceHelper *source) :
231         FullBox(source, FOURCC("pitm")) {}
232 
233     status_t parse(off64_t offset, size_t size, uint32_t *primaryItemId);
234 };
235 
parse(off64_t offset,size_t size,uint32_t * primaryItemId)236 status_t PitmBox::parse(off64_t offset, size_t size, uint32_t *primaryItemId) {
237     status_t err = parseFullBoxHeader(&offset, &size);
238     if (err != OK) {
239         return err;
240     }
241 
242     size_t itemIdSize = (version() == 0) ? 2 : 4;
243     if (size < itemIdSize) {
244         return ERROR_MALFORMED;
245     }
246     uint32_t itemId;
247     if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) {
248         return ERROR_IO;
249     }
250 
251     ALOGV("primary id %d", itemId);
252     *primaryItemId = itemId;
253 
254     return OK;
255 }
256 
257 /////////////////////////////////////////////////////////////////////
258 //
259 //  ItemLocation related boxes
260 //
261 
262 struct ExtentEntry {
263     uint64_t extentIndex;
264     uint64_t extentOffset;
265     uint64_t extentLength;
266 };
267 
268 struct ItemLoc {
ItemLocandroid::heif::ItemLoc269     ItemLoc() : ItemLoc(0, 0, 0, 0) {}
ItemLocandroid::heif::ItemLoc270     ItemLoc(uint32_t item_id, uint16_t construction_method,
271             uint16_t data_reference_index, uint64_t base_offset) :
272         itemId(item_id),
273         constructionMethod(construction_method),
274         dataReferenceIndex(data_reference_index),
275         baseOffset(base_offset) {}
276 
addExtentandroid::heif::ItemLoc277     void addExtent(const ExtentEntry& extent) {
278         extents.push_back(extent);
279     }
280 
getLocandroid::heif::ItemLoc281     status_t getLoc(off64_t *offset, size_t *size,
282             off64_t idatOffset, size_t idatSize) const {
283         // TODO: fix extent handling, fix constructionMethod = 2
284         CHECK(extents.size() == 1);
285         if (constructionMethod == 0) {
286             *offset = baseOffset + extents[0].extentOffset;
287             *size = extents[0].extentLength;
288             return OK;
289         } else if (constructionMethod == 1) {
290             if (baseOffset + extents[0].extentOffset + extents[0].extentLength
291                     > idatSize) {
292                 return ERROR_MALFORMED;
293             }
294             *offset = baseOffset + extents[0].extentOffset + idatOffset;
295             *size = extents[0].extentLength;
296             return OK;
297         }
298         return ERROR_UNSUPPORTED;
299     }
300 
301     // parsed info
302     uint32_t itemId;
303     uint16_t constructionMethod;
304     uint16_t dataReferenceIndex;
305     off64_t baseOffset;
306     Vector<ExtentEntry> extents;
307 };
308 
309 struct IlocBox : public FullBox {
IlocBoxandroid::heif::IlocBox310     IlocBox(DataSourceHelper *source, KeyedVector<uint32_t, ItemLoc> *itemLocs) :
311         FullBox(source, FOURCC("iloc")),
312         mItemLocs(itemLocs), mHasConstructMethod1(false) {}
313 
314     status_t parse(off64_t offset, size_t size);
315 
hasConstructMethod1android::heif::IlocBox316     bool hasConstructMethod1() { return mHasConstructMethod1; }
317 
318 private:
isSizeFieldValidandroid::heif::IlocBox319     static bool isSizeFieldValid(uint32_t offset_size) {
320         return offset_size == 0 || offset_size == 4 || offset_size == 8;
321     }
322     KeyedVector<uint32_t, ItemLoc> *mItemLocs;
323     bool mHasConstructMethod1;
324 };
325 
parse(off64_t offset,size_t size)326 status_t IlocBox::parse(off64_t offset, size_t size) {
327     status_t err = parseFullBoxHeader(&offset, &size);
328     if (err != OK) {
329         return err;
330     }
331     if (version() > 2) {
332         ALOGE("%s: invalid version %d", __FUNCTION__, version());
333         return ERROR_MALFORMED;
334     }
335 
336     if (size < 2) {
337         return ERROR_MALFORMED;
338     }
339     uint8_t offset_size;
340     if (!source()->readAt(offset++, &offset_size, 1)) {
341         return ERROR_IO;
342     }
343     uint8_t length_size = (offset_size & 0xF);
344     offset_size >>= 4;
345 
346     uint8_t base_offset_size;
347     if (!source()->readAt(offset++, &base_offset_size, 1)) {
348         return ERROR_IO;
349     }
350     uint8_t index_size = 0;
351     if (version() == 1 || version() == 2) {
352         index_size = (base_offset_size & 0xF);
353     }
354     base_offset_size >>= 4;
355     size -= 2;
356 
357     if (!isSizeFieldValid(offset_size)
358             || !isSizeFieldValid(length_size)
359             || !isSizeFieldValid(base_offset_size)
360             || !isSizeFieldValid((index_size))) {
361         ALOGE("%s: offset size not valid: %d, %d, %d, %d", __FUNCTION__,
362                 offset_size, length_size, base_offset_size, index_size);
363         return ERROR_MALFORMED;
364     }
365 
366     uint32_t item_count;
367     size_t itemFieldSize = version() < 2 ? 2 : 4;
368     if (size < itemFieldSize) {
369         return ERROR_MALFORMED;
370     }
371     if (!source()->getUInt32Var(offset, &item_count, itemFieldSize)) {
372         return ERROR_IO;
373     }
374 
375     ALOGV("item_count %lld", (long long) item_count);
376     offset += itemFieldSize;
377     size -= itemFieldSize;
378 
379     for (size_t i = 0; i < item_count; i++) {
380         uint32_t item_id;
381         if (!source()->getUInt32Var(offset, &item_id, itemFieldSize)) {
382             return ERROR_IO;
383         }
384         ALOGV("item[%zu]: id %lld", i, (long long)item_id);
385         offset += itemFieldSize;
386 
387         uint8_t construction_method = 0;
388         if (version() == 1 || version() == 2) {
389             uint8_t buf[2];
390             if (!source()->readAt(offset, buf, 2)) {
391                 return ERROR_IO;
392             }
393             construction_method = (buf[1] & 0xF);
394             ALOGV("construction_method %d", construction_method);
395             if (construction_method == 1) {
396                 mHasConstructMethod1 = true;
397             }
398 
399             offset += 2;
400         }
401 
402         uint16_t data_reference_index;
403         if (!source()->getUInt16(offset, &data_reference_index)) {
404             return ERROR_IO;
405         }
406         ALOGV("data_reference_index %d", data_reference_index);
407         if (data_reference_index != 0) {
408             // we don't support reference to other files
409             return ERROR_UNSUPPORTED;
410         }
411         offset += 2;
412 
413         uint64_t base_offset = 0;
414         if (base_offset_size != 0) {
415             if (!source()->getUInt64Var(offset, &base_offset, base_offset_size)) {
416                 return ERROR_IO;
417             }
418             offset += base_offset_size;
419         }
420         ALOGV("base_offset %lld", (long long) base_offset);
421 
422         ssize_t index = mItemLocs->add(item_id, ItemLoc(
423                 item_id, construction_method, data_reference_index, base_offset));
424         ItemLoc &item = mItemLocs->editValueAt(index);
425 
426         uint16_t extent_count;
427         if (!source()->getUInt16(offset, &extent_count)) {
428             return ERROR_IO;
429         }
430         ALOGV("extent_count %d", extent_count);
431 
432         if (extent_count > 1) {
433             return ERROR_UNSUPPORTED;
434         }
435         offset += 2;
436 
437         for (size_t j = 0; j < extent_count; j++) {
438             uint64_t extent_index = 1; // default=1
439             if ((version() == 1 || version() == 2) && (index_size > 0)) {
440                 if (!source()->getUInt64Var(offset, &extent_index, index_size)) {
441                     return ERROR_IO;
442                 }
443                 // TODO: add support for this mode
444                 offset += index_size;
445                 ALOGV("extent_index %lld", (long long)extent_index);
446             }
447 
448             uint64_t extent_offset = 0; // default=0
449             if (offset_size > 0) {
450                 if (!source()->getUInt64Var(offset, &extent_offset, offset_size)) {
451                     return ERROR_IO;
452                 }
453                 offset += offset_size;
454             }
455             ALOGV("extent_offset %lld", (long long)extent_offset);
456 
457             uint64_t extent_length = 0; // this indicates full length of file
458             if (length_size > 0) {
459                 if (!source()->getUInt64Var(offset, &extent_length, length_size)) {
460                     return ERROR_IO;
461                 }
462                 offset += length_size;
463             }
464             ALOGV("extent_length %lld", (long long)extent_length);
465 
466             item.addExtent({ extent_index, extent_offset, extent_length });
467         }
468     }
469     return OK;
470 }
471 
472 /////////////////////////////////////////////////////////////////////
473 //
474 //  ItemReference related boxes
475 //
476 
477 struct ItemReference : public Box, public RefBase {
ItemReferenceandroid::heif::ItemReference478     ItemReference(DataSourceHelper *source, uint32_t type, uint32_t itemIdSize) :
479         Box(source, type), mItemId(0), mRefIdSize(itemIdSize) {}
480 
481     status_t parse(off64_t offset, size_t size);
482 
itemIdandroid::heif::ItemReference483     uint32_t itemId() { return mItemId; }
484 
485     void apply(
486             KeyedVector<uint32_t, ImageItem> &itemIdToItemMap,
487             KeyedVector<uint32_t, ExternalMetaItem> &itemIdToMetaMap) const;
488 
489 private:
490     uint32_t mItemId;
491     uint32_t mRefIdSize;
492     Vector<uint32_t> mRefs;
493 
494     DISALLOW_EVIL_CONSTRUCTORS(ItemReference);
495 };
496 
apply(KeyedVector<uint32_t,ImageItem> & itemIdToItemMap,KeyedVector<uint32_t,ExternalMetaItem> & itemIdToMetaMap) const497 void ItemReference::apply(
498         KeyedVector<uint32_t, ImageItem> &itemIdToItemMap,
499         KeyedVector<uint32_t, ExternalMetaItem> &itemIdToMetaMap) const {
500     ALOGV("attach reference type 0x%x to item id %d)", type(), mItemId);
501 
502     switch(type()) {
503     case FOURCC("dimg"): {
504         ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
505 
506         // ignore non-image items
507         if (itemIndex < 0) {
508             return;
509         }
510 
511         ImageItem &derivedImage = itemIdToItemMap.editValueAt(itemIndex);
512         if (!derivedImage.dimgRefs.empty()) {
513             ALOGW("dimgRefs not clean!");
514         }
515         derivedImage.dimgRefs.appendVector(mRefs);
516 
517         for (size_t i = 0; i < mRefs.size(); i++) {
518             itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]);
519 
520             // ignore non-image items
521             if (itemIndex < 0) {
522                 continue;
523             }
524             ImageItem &sourceImage = itemIdToItemMap.editValueAt(itemIndex);
525 
526             // mark the source image of the derivation as hidden
527             sourceImage.hidden = true;
528         }
529         break;
530     }
531     case FOURCC("thmb"): {
532         ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
533 
534         // ignore non-image items
535         if (itemIndex < 0) {
536             return;
537         }
538 
539         // mark thumbnail image as hidden, these can be retrieved if the client
540         // request thumbnail explicitly, but won't be exposed as displayables.
541         ImageItem &thumbImage = itemIdToItemMap.editValueAt(itemIndex);
542         thumbImage.hidden = true;
543 
544         for (size_t i = 0; i < mRefs.size(); i++) {
545             itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]);
546 
547             // ignore non-image items
548             if (itemIndex < 0) {
549                 continue;
550             }
551             ALOGV("Image item id %d uses thumbnail item id %d", mRefs[i], mItemId);
552             ImageItem &imageItem = itemIdToItemMap.editValueAt(itemIndex);
553             if (!imageItem.thumbnails.empty()) {
554                 ALOGW("already has thumbnails!");
555             }
556             imageItem.thumbnails.push_back(mItemId);
557         }
558         break;
559     }
560     case FOURCC("cdsc"): {
561         ssize_t metaIndex = itemIdToMetaMap.indexOfKey(mItemId);
562 
563         // ignore non-meta items
564         if (metaIndex < 0) {
565             return;
566         }
567 
568         for (size_t i = 0; i < mRefs.size(); i++) {
569             ssize_t itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]);
570 
571             // ignore non-image items
572             if (itemIndex < 0) {
573                 continue;
574             }
575             ALOGV("Image item id %d uses metadata item id %d", mRefs[i], mItemId);
576             ImageItem &image = itemIdToItemMap.editValueAt(itemIndex);
577             if (itemIdToMetaMap[metaIndex].isExif) {
578                 image.exifRefs.push_back(mItemId);
579             } else {
580                 image.xmpRefs.push_back(mItemId);
581             }
582         }
583         break;
584     }
585     case FOURCC("auxl"): {
586         ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
587 
588         // ignore non-image items
589         if (itemIndex < 0) {
590             return;
591         }
592 
593         // mark auxiliary image as hidden
594         ImageItem &auxImage = itemIdToItemMap.editValueAt(itemIndex);
595         auxImage.hidden = true;
596         break;
597     }
598     default:
599         ALOGW("ignoring unsupported ref type 0x%x", type());
600     }
601 }
602 
parse(off64_t offset,size_t size)603 status_t ItemReference::parse(off64_t offset, size_t size) {
604     if (size < mRefIdSize + 2) {
605         return ERROR_MALFORMED;
606     }
607     if (!source()->getUInt32Var(offset, &mItemId, mRefIdSize)) {
608         return ERROR_IO;
609     }
610     offset += mRefIdSize;
611 
612     uint16_t count;
613     if (!source()->getUInt16(offset, &count)) {
614         return ERROR_IO;
615     }
616     offset += 2;
617     size -= (mRefIdSize + 2);
618 
619     if (size < count * mRefIdSize) {
620         return ERROR_MALFORMED;
621     }
622 
623     for (size_t i = 0; i < count; i++) {
624         uint32_t refItemId;
625         if (!source()->getUInt32Var(offset, &refItemId, mRefIdSize)) {
626             return ERROR_IO;
627         }
628         offset += mRefIdSize;
629         mRefs.push_back(refItemId);
630         ALOGV("item id %d: referencing item id %d", mItemId, refItemId);
631     }
632 
633     return OK;
634 }
635 
636 struct IrefBox : public FullBox {
IrefBoxandroid::heif::IrefBox637     IrefBox(DataSourceHelper *source, Vector<sp<ItemReference> > *itemRefs) :
638         FullBox(source, FOURCC("iref")), mRefIdSize(0), mItemRefs(itemRefs) {}
639 
640     status_t parse(off64_t offset, size_t size);
641 
642 protected:
643     status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
644 
645 private:
646     uint32_t mRefIdSize;
647     Vector<sp<ItemReference> > *mItemRefs;
648 };
649 
parse(off64_t offset,size_t size)650 status_t IrefBox::parse(off64_t offset, size_t size) {
651     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
652     status_t err = parseFullBoxHeader(&offset, &size);
653     if (err != OK) {
654         return err;
655     }
656 
657     mRefIdSize = (version() == 0) ? 2 : 4;
658     return parseChunks(offset, size);
659 }
660 
onChunkData(uint32_t type,off64_t offset,size_t size)661 status_t IrefBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
662     sp<ItemReference> itemRef = new ItemReference(source(), type, mRefIdSize);
663 
664     status_t err = itemRef->parse(offset, size);
665     if (err != OK) {
666         return err;
667     }
668     mItemRefs->push_back(itemRef);
669     return OK;
670 }
671 
672 /////////////////////////////////////////////////////////////////////
673 //
674 //  ItemProperty related boxes
675 //
676 
677 struct AssociationEntry {
678     uint32_t itemId;
679     bool essential;
680     uint16_t index;
681 };
682 
683 struct ItemProperty : public RefBase {
ItemPropertyandroid::heif::ItemProperty684     ItemProperty() {}
685 
attachToandroid::heif::ItemProperty686     virtual void attachTo(ImageItem &/*image*/) const {
687         ALOGW("Unrecognized property");
688     }
parseandroid::heif::ItemProperty689     virtual status_t parse(off64_t /*offset*/, size_t /*size*/) {
690         ALOGW("Unrecognized property");
691         return OK;
692     }
693 
694 private:
695     DISALLOW_EVIL_CONSTRUCTORS(ItemProperty);
696 };
697 
698 struct IspeBox : public FullBox, public ItemProperty {
IspeBoxandroid::heif::IspeBox699     IspeBox(DataSourceHelper *source) :
700         FullBox(source, FOURCC("ispe")), mWidth(0), mHeight(0) {}
701 
702     status_t parse(off64_t offset, size_t size) override;
703 
attachToandroid::heif::IspeBox704     void attachTo(ImageItem &image) const override {
705         image.width = mWidth;
706         image.height = mHeight;
707     }
708 
709 private:
710     int32_t mWidth;
711     int32_t mHeight;
712 };
713 
parse(off64_t offset,size_t size)714 status_t IspeBox::parse(off64_t offset, size_t size) {
715     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
716 
717     status_t err = parseFullBoxHeader(&offset, &size);
718     if (err != OK) {
719         return err;
720     }
721 
722     if (size < 8) {
723         return ERROR_MALFORMED;
724     }
725     if (!source()->getUInt32(offset, (uint32_t *)&mWidth)
726             || !source()->getUInt32(offset + 4, (uint32_t *)&mHeight)) {
727         return ERROR_IO;
728     }
729 
730     // Validate that the dimension doesn't cause overflow on calculated max input size.
731     // Max input size is width*height*1.5, restrict width*height to 1<<29 so that
732     // we don't need to cast to int64_t when doing mults.
733     if (mWidth <= 0 || mHeight <= 0 || mWidth > (1 << 29) / mHeight) {
734         return ERROR_MALFORMED;
735     }
736 
737     ALOGV("property ispe: %dx%d", mWidth, mHeight);
738     return OK;
739 }
740 
741 struct HvccBox : public Box, public ItemProperty {
HvccBoxandroid::heif::HvccBox742     HvccBox(DataSourceHelper *source) :
743         Box(source, FOURCC("hvcC")) {}
744 
745     status_t parse(off64_t offset, size_t size) override;
746 
attachToandroid::heif::HvccBox747     void attachTo(ImageItem &image) const override {
748         image.hvcc = mHVCC;
749     }
750 
751 private:
752     sp<ABuffer> mHVCC;
753 };
754 
parse(off64_t offset,size_t size)755 status_t HvccBox::parse(off64_t offset, size_t size) {
756     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
757 
758     mHVCC = new ABuffer(size);
759 
760     if (mHVCC->data() == NULL) {
761         ALOGE("b/28471206");
762         return NO_MEMORY;
763     }
764 
765     if (source()->readAt(offset, mHVCC->data(), size) < (ssize_t)size) {
766         return ERROR_IO;
767     }
768 
769     ALOGV("property hvcC");
770 
771     return OK;
772 }
773 
774 struct Av1cBox : public Box, public ItemProperty {
Av1cBoxandroid::heif::Av1cBox775     Av1cBox(DataSourceHelper *source) :
776         Box(source, FOURCC("av1C")) {}
777 
778     status_t parse(off64_t offset, size_t size) override;
779 
attachToandroid::heif::Av1cBox780     void attachTo(ImageItem &image) const override {
781         image.av1c = mAv1c;
782     }
783 
784 private:
785     sp<ABuffer> mAv1c;
786 };
787 
parse(off64_t offset,size_t size)788 status_t Av1cBox::parse(off64_t offset, size_t size) {
789     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
790 
791     mAv1c = new ABuffer(size);
792 
793     if (mAv1c->data() == NULL) {
794         ALOGE("b/28471206");
795         return NO_MEMORY;
796     }
797 
798     if (source()->readAt(offset, mAv1c->data(), size) < (ssize_t)size) {
799         return ERROR_IO;
800     }
801 
802     ALOGV("property av1C");
803 
804     return OK;
805 }
806 
807 struct IrotBox : public Box, public ItemProperty {
IrotBoxandroid::heif::IrotBox808     IrotBox(DataSourceHelper *source) :
809         Box(source, FOURCC("irot")), mAngle(0) {}
810 
811     status_t parse(off64_t offset, size_t size) override;
812 
attachToandroid::heif::IrotBox813     void attachTo(ImageItem &image) const override {
814         image.rotation = mAngle * 90;
815     }
816 
817 private:
818     uint8_t mAngle;
819 };
820 
parse(off64_t offset,size_t size)821 status_t IrotBox::parse(off64_t offset, size_t size) {
822     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
823 
824     if (size < 1) {
825         return ERROR_MALFORMED;
826     }
827     if (source()->readAt(offset, &mAngle, 1) != 1) {
828         return ERROR_IO;
829     }
830     mAngle &= 0x3;
831     ALOGV("property irot: %d", mAngle);
832 
833     return OK;
834 }
835 
836 struct ColrBox : public Box, public ItemProperty {
ColrBoxandroid::heif::ColrBox837     ColrBox(DataSourceHelper *source) :
838         Box(source, FOURCC("colr")) {}
839 
840     status_t parse(off64_t offset, size_t size) override;
841 
attachToandroid::heif::ColrBox842     void attachTo(ImageItem &image) const override {
843         image.icc = mICCData;
844     }
845 
846 private:
847     sp<ABuffer> mICCData;
848 };
849 
parse(off64_t offset,size_t size)850 status_t ColrBox::parse(off64_t offset, size_t size) {
851     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
852 
853     if (size < 4) {
854         return ERROR_MALFORMED;
855     }
856     uint32_t colour_type;
857     if (!source()->getUInt32(offset, &colour_type)) {
858         return ERROR_IO;
859     }
860     offset += 4;
861     size -= 4;
862     if (colour_type == FOURCC("nclx")) {
863         return OK;
864     }
865     if ((colour_type != FOURCC("rICC")) &&
866         (colour_type != FOURCC("prof"))) {
867         return ERROR_MALFORMED;
868     }
869 
870     mICCData = new ABuffer(size);
871     if (mICCData->data() == NULL) {
872         ALOGE("b/28471206");
873         return NO_MEMORY;
874     }
875 
876     if (source()->readAt(offset, mICCData->data(), size) != (ssize_t)size) {
877         return ERROR_IO;
878     }
879 
880     ALOGV("property Colr: size %zd", size);
881     return OK;
882 }
883 
884 struct IpmaBox : public FullBox {
IpmaBoxandroid::heif::IpmaBox885     IpmaBox(DataSourceHelper *source, Vector<AssociationEntry> *associations) :
886         FullBox(source, FOURCC("ipma")), mAssociations(associations) {}
887 
888     status_t parse(off64_t offset, size_t size);
889 private:
890     Vector<AssociationEntry> *mAssociations;
891 };
892 
parse(off64_t offset,size_t size)893 status_t IpmaBox::parse(off64_t offset, size_t size) {
894     status_t err = parseFullBoxHeader(&offset, &size);
895     if (err != OK) {
896         return err;
897     }
898 
899     if (size < 4) {
900         return ERROR_MALFORMED;
901     }
902     uint32_t entryCount;
903     if (!source()->getUInt32(offset, &entryCount)) {
904         return ERROR_IO;
905     }
906     offset += 4;
907     size -= 4;
908 
909     for (size_t k = 0; k < entryCount; ++k) {
910         uint32_t itemId = 0;
911         size_t itemIdSize = (version() < 1) ? 2 : 4;
912 
913         if (size < itemIdSize + 1) {
914             return ERROR_MALFORMED;
915         }
916 
917         if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) {
918             return ERROR_IO;
919         }
920         offset += itemIdSize;
921         size -= itemIdSize;
922 
923         uint8_t associationCount;
924         if (!source()->readAt(offset, &associationCount, 1)) {
925             return ERROR_IO;
926         }
927         offset++;
928         size--;
929 
930         for (size_t i = 0; i < associationCount; ++i) {
931             size_t propIndexSize = (flags() & 1) ? 2 : 1;
932             if (size < propIndexSize) {
933                 return ERROR_MALFORMED;
934             }
935             uint16_t propIndex;
936             if (!source()->getUInt16Var(offset, &propIndex, propIndexSize)) {
937                 return ERROR_IO;
938             }
939             offset += propIndexSize;
940             size -= propIndexSize;
941             uint16_t bitmask = (1 << (8 * propIndexSize - 1));
942             AssociationEntry entry = {
943                     .itemId = itemId,
944                     .essential = !!(propIndex & bitmask),
945                     .index = (uint16_t) (propIndex & ~bitmask)
946             };
947 
948             ALOGV("item id %d associated to property %d (essential %d)",
949                     itemId, entry.index, entry.essential);
950 
951             mAssociations->push_back(entry);
952         }
953     }
954 
955     return OK;
956 }
957 
958 struct IpcoBox : public Box {
IpcoBoxandroid::heif::IpcoBox959     IpcoBox(DataSourceHelper *source, Vector<sp<ItemProperty> > *properties) :
960         Box(source, FOURCC("ipco")), mItemProperties(properties) {}
961 
962     status_t parse(off64_t offset, size_t size);
963 protected:
964     status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
965 
966 private:
967     Vector<sp<ItemProperty> > *mItemProperties;
968 };
969 
parse(off64_t offset,size_t size)970 status_t IpcoBox::parse(off64_t offset, size_t size) {
971     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
972     // push a placeholder as the index is 1-based
973     mItemProperties->push_back(new ItemProperty());
974     return parseChunks(offset, size);
975 }
976 
onChunkData(uint32_t type,off64_t offset,size_t size)977 status_t IpcoBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
978     sp<ItemProperty> itemProperty;
979     switch(type) {
980         case FOURCC("hvcC"):
981         {
982             itemProperty = new HvccBox(source());
983             break;
984         }
985         case FOURCC("ispe"):
986         {
987             itemProperty = new IspeBox(source());
988             break;
989         }
990         case FOURCC("irot"):
991         {
992             itemProperty = new IrotBox(source());
993             break;
994         }
995         case FOURCC("colr"):
996         {
997             itemProperty = new ColrBox(source());
998             break;
999         }
1000         case FOURCC("av1C"):
1001         {
1002             itemProperty = new Av1cBox(source());
1003             break;
1004         }
1005         default:
1006         {
1007             // push dummy to maintain correct item property index
1008             itemProperty = new ItemProperty();
1009             break;
1010         }
1011     }
1012     status_t err = itemProperty->parse(offset, size);
1013     if (err != OK) {
1014         return err;
1015     }
1016     mItemProperties->push_back(itemProperty);
1017     return OK;
1018 }
1019 
1020 struct IprpBox : public Box {
IprpBoxandroid::heif::IprpBox1021     IprpBox(DataSourceHelper *source,
1022             Vector<sp<ItemProperty> > *properties,
1023             Vector<AssociationEntry> *associations) :
1024         Box(source, FOURCC("iprp")),
1025         mProperties(properties), mAssociations(associations) {}
1026 
1027     status_t parse(off64_t offset, size_t size);
1028 protected:
1029     status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
1030 
1031 private:
1032     Vector<sp<ItemProperty> > *mProperties;
1033     Vector<AssociationEntry> *mAssociations;
1034 };
1035 
parse(off64_t offset,size_t size)1036 status_t IprpBox::parse(off64_t offset, size_t size) {
1037     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1038 
1039     status_t err = parseChunks(offset, size);
1040     if (err != OK) {
1041         return err;
1042     }
1043     return OK;
1044 }
1045 
onChunkData(uint32_t type,off64_t offset,size_t size)1046 status_t IprpBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
1047     switch(type) {
1048         case FOURCC("ipco"):
1049         {
1050             IpcoBox ipcoBox(source(), mProperties);
1051             return ipcoBox.parse(offset, size);
1052         }
1053         case FOURCC("ipma"):
1054         {
1055             IpmaBox ipmaBox(source(), mAssociations);
1056             return ipmaBox.parse(offset, size);
1057         }
1058         default:
1059         {
1060             ALOGW("Unrecognized box.");
1061             break;
1062         }
1063     }
1064     return OK;
1065 }
1066 
1067 /////////////////////////////////////////////////////////////////////
1068 //
1069 //  ItemInfo related boxes
1070 //
1071 struct ItemInfo {
1072     uint32_t itemId;
1073     uint32_t itemType;
1074     String8 contentType;
1075     bool hidden;
1076 
isXmpandroid::heif::ItemInfo1077     bool isXmp() const {
1078         return itemType == FOURCC("mime") && contentType == String8("application/rdf+xml");
1079     }
isExifandroid::heif::ItemInfo1080     bool isExif() const {
1081         return itemType == FOURCC("Exif");
1082     }
isGridandroid::heif::ItemInfo1083     bool isGrid() const {
1084         return itemType == FOURCC("grid");
1085     }
isSampleandroid::heif::ItemInfo1086     bool isSample() const {
1087         return itemType == FOURCC("av01") || itemType == FOURCC("hvc1");
1088     }
1089 };
1090 
1091 struct InfeBox : public FullBox {
InfeBoxandroid::heif::InfeBox1092     InfeBox(DataSourceHelper *source) :
1093         FullBox(source, FOURCC("infe")) {}
1094 
1095     status_t parse(off64_t offset, size_t size, ItemInfo *itemInfo);
1096 
1097 private:
1098     bool parseNullTerminatedString(off64_t *offset, size_t *size, String8 *out);
1099 };
1100 
parseNullTerminatedString(off64_t * offset,size_t * size,String8 * out)1101 bool InfeBox::parseNullTerminatedString(
1102         off64_t *offset, size_t *size, String8 *out) {
1103     char tmp;
1104     Vector<char> buf;
1105     buf.setCapacity(256);
1106     off64_t newOffset = *offset;
1107     off64_t stopOffset = *offset + *size;
1108     while (newOffset < stopOffset) {
1109         if (!source()->readAt(newOffset++, &tmp, 1)) {
1110             return false;
1111         }
1112         buf.push_back(tmp);
1113         if (tmp == 0) {
1114             out->setTo(buf.array());
1115 
1116             *offset = newOffset;
1117             *size = stopOffset - newOffset;
1118 
1119             return true;
1120         }
1121     }
1122     return false;
1123 }
1124 
parse(off64_t offset,size_t size,ItemInfo * itemInfo)1125 status_t InfeBox::parse(off64_t offset, size_t size, ItemInfo *itemInfo) {
1126     status_t err = parseFullBoxHeader(&offset, &size);
1127     if (err != OK) {
1128         return err;
1129     }
1130 
1131     if (version() == 0 || version() == 1) {
1132         return ERROR_UNSUPPORTED;
1133     } else { // version >= 2
1134         uint32_t item_id;
1135         size_t itemIdSize = (version() == 2) ? 2 : 4;
1136         if (size < itemIdSize + 6) {
1137             return ERROR_MALFORMED;
1138         }
1139         if (!source()->getUInt32Var(offset, &item_id, itemIdSize)) {
1140             return ERROR_IO;
1141         }
1142         ALOGV("item_id %d", item_id);
1143         offset += itemIdSize;
1144         uint16_t item_protection_index;
1145         if (!source()->getUInt16(offset, &item_protection_index)) {
1146             return ERROR_IO;
1147         }
1148         ALOGV("item_protection_index %d", item_protection_index);
1149         offset += 2;
1150         uint32_t item_type;
1151         if (!source()->getUInt32(offset, &item_type)) {
1152             return ERROR_IO;
1153         }
1154 
1155         itemInfo->itemId = item_id;
1156         itemInfo->itemType = item_type;
1157         // According to HEIF spec, (flags & 1) indicates the image is hidden
1158         // and not supposed to be displayed.
1159         itemInfo->hidden = (flags() & 1);
1160 
1161         char itemTypeString[5];
1162         MakeFourCCString(item_type, itemTypeString);
1163         ALOGV("item_type %s", itemTypeString);
1164         offset += 4;
1165         size -= itemIdSize + 6;
1166 
1167         String8 item_name;
1168         if (!parseNullTerminatedString(&offset, &size, &item_name)) {
1169             return ERROR_MALFORMED;
1170         }
1171         ALOGV("item_name %s", item_name.c_str());
1172 
1173         if (item_type == FOURCC("mime")) {
1174             String8 content_type;
1175             if (!parseNullTerminatedString(&offset, &size, &content_type)) {
1176                 return ERROR_MALFORMED;
1177             }
1178             itemInfo->contentType = content_type;
1179 
1180             // content_encoding is optional; can be omitted if would be empty
1181             if (size > 0) {
1182                 String8 content_encoding;
1183                 if (!parseNullTerminatedString(&offset, &size, &content_encoding)) {
1184                     return ERROR_MALFORMED;
1185                 }
1186             }
1187         } else if (item_type == FOURCC("uri ")) {
1188             String8 item_uri_type;
1189             if (!parseNullTerminatedString(&offset, &size, &item_uri_type)) {
1190                 return ERROR_MALFORMED;
1191             }
1192         }
1193     }
1194     return OK;
1195 }
1196 
1197 struct IinfBox : public FullBox {
IinfBoxandroid::heif::IinfBox1198     IinfBox(DataSourceHelper *source, Vector<ItemInfo> *itemInfos) :
1199         FullBox(source, FOURCC("iinf")), mItemInfos(itemInfos), mNeedIref(false) {}
1200 
1201     status_t parse(off64_t offset, size_t size);
1202 
needIrefBoxandroid::heif::IinfBox1203     bool needIrefBox() { return mNeedIref; }
1204 
1205 protected:
1206     status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
1207 
1208 private:
1209     Vector<ItemInfo> *mItemInfos;
1210     bool mNeedIref;
1211 };
1212 
parse(off64_t offset,size_t size)1213 status_t IinfBox::parse(off64_t offset, size_t size) {
1214     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1215 
1216     status_t err = parseFullBoxHeader(&offset, &size);
1217     if (err != OK) {
1218         return err;
1219     }
1220 
1221     size_t entryCountSize = version() == 0 ? 2 : 4;
1222     if (size < entryCountSize) {
1223         return ERROR_MALFORMED;
1224     }
1225     uint32_t entry_count;
1226     if (!source()->getUInt32Var(offset, &entry_count, entryCountSize)) {
1227         return ERROR_IO;
1228     }
1229     ALOGV("entry_count %d", entry_count);
1230 
1231     off64_t stopOffset = offset + size;
1232     offset += entryCountSize;
1233     for (size_t i = 0; i < entry_count && offset < stopOffset; i++) {
1234         ALOGV("entry %zu", i);
1235         status_t err = parseChunk(&offset);
1236         if (err != OK) {
1237             return err;
1238         }
1239     }
1240     if (offset != stopOffset) {
1241         return ERROR_MALFORMED;
1242     }
1243 
1244     return OK;
1245 }
1246 
onChunkData(uint32_t type,off64_t offset,size_t size)1247 status_t IinfBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
1248     if (type != FOURCC("infe")) {
1249         return OK;
1250     }
1251 
1252     InfeBox infeBox(source());
1253     ItemInfo itemInfo;
1254     status_t err = infeBox.parse(offset, size, &itemInfo);
1255     if (err == OK) {
1256         mItemInfos->push_back(itemInfo);
1257         mNeedIref |= (itemInfo.isExif() || itemInfo.isXmp() || itemInfo.isGrid());
1258     }
1259     // InfeBox parse returns ERROR_UNSUPPORTED if the box if an unsupported
1260     // version. Ignore this error as it's not fatal.
1261     return (err == ERROR_UNSUPPORTED) ? OK : err;
1262 }
1263 
1264 //////////////////////////////////////////////////////////////////
1265 
ItemTable(DataSourceHelper * source,bool isHeif)1266 ItemTable::ItemTable(DataSourceHelper *source, bool isHeif)
1267     : mDataSource(source),
1268       mIsHeif(isHeif),
1269       mPrimaryItemId(0),
1270       mIdatOffset(0),
1271       mIdatSize(0),
1272       mImageItemsValid(false),
1273       mCurrentItemIndex(0) {
1274     mRequiredBoxes.insert('iprp');
1275     mRequiredBoxes.insert('iloc');
1276     mRequiredBoxes.insert('pitm');
1277     mRequiredBoxes.insert('iinf');
1278 }
1279 
~ItemTable()1280 ItemTable::~ItemTable() {}
1281 
parse(uint32_t type,off64_t data_offset,size_t chunk_data_size)1282 status_t ItemTable::parse(uint32_t type, off64_t data_offset, size_t chunk_data_size) {
1283     switch(type) {
1284         case FOURCC("iloc"):
1285         {
1286             return parseIlocBox(data_offset, chunk_data_size);
1287         }
1288         case FOURCC("iinf"):
1289         {
1290             return parseIinfBox(data_offset, chunk_data_size);
1291         }
1292         case FOURCC("iprp"):
1293         {
1294             return parseIprpBox(data_offset, chunk_data_size);
1295         }
1296         case FOURCC("pitm"):
1297         {
1298             return parsePitmBox(data_offset, chunk_data_size);
1299         }
1300         case FOURCC("idat"):
1301         {
1302             return parseIdatBox(data_offset, chunk_data_size);
1303         }
1304         case FOURCC("iref"):
1305         {
1306             return parseIrefBox(data_offset, chunk_data_size);
1307         }
1308         case FOURCC("ipro"):
1309         {
1310             ALOGW("ipro box not supported!");
1311             break;
1312         }
1313         default:
1314         {
1315             ALOGW("unrecognized box type: 0x%x", type);
1316             break;
1317         }
1318     }
1319     return ERROR_UNSUPPORTED;
1320 }
1321 
parseIlocBox(off64_t offset,size_t size)1322 status_t ItemTable::parseIlocBox(off64_t offset, size_t size) {
1323     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1324 
1325     IlocBox ilocBox(mDataSource, &mItemLocs);
1326     status_t err = ilocBox.parse(offset, size);
1327     if (err != OK) {
1328         return err;
1329     }
1330 
1331     if (ilocBox.hasConstructMethod1()) {
1332         mRequiredBoxes.insert('idat');
1333     }
1334 
1335     return buildImageItemsIfPossible('iloc');
1336 }
1337 
parseIinfBox(off64_t offset,size_t size)1338 status_t ItemTable::parseIinfBox(off64_t offset, size_t size) {
1339     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1340 
1341     IinfBox iinfBox(mDataSource, &mItemInfos);
1342     status_t err = iinfBox.parse(offset, size);
1343     if (err != OK) {
1344         return err;
1345     }
1346 
1347     if (iinfBox.needIrefBox()) {
1348         mRequiredBoxes.insert('iref');
1349     }
1350 
1351     return buildImageItemsIfPossible('iinf');
1352 }
1353 
parsePitmBox(off64_t offset,size_t size)1354 status_t ItemTable::parsePitmBox(off64_t offset, size_t size) {
1355     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1356 
1357     PitmBox pitmBox(mDataSource);
1358     status_t err = pitmBox.parse(offset, size, &mPrimaryItemId);
1359     if (err != OK) {
1360         return err;
1361     }
1362 
1363     return buildImageItemsIfPossible('pitm');
1364 }
1365 
parseIprpBox(off64_t offset,size_t size)1366 status_t ItemTable::parseIprpBox(off64_t offset, size_t size) {
1367     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1368 
1369     IprpBox iprpBox(mDataSource, &mItemProperties, &mAssociations);
1370     status_t err = iprpBox.parse(offset, size);
1371     if (err != OK) {
1372         return err;
1373     }
1374 
1375     return buildImageItemsIfPossible('iprp');
1376 }
1377 
parseIdatBox(off64_t offset,size_t size)1378 status_t ItemTable::parseIdatBox(off64_t offset, size_t size) {
1379     ALOGV("%s: idat offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1380 
1381     // only remember the offset and size of idat box for later use
1382     mIdatOffset = offset;
1383     mIdatSize = size;
1384 
1385     return buildImageItemsIfPossible('idat');
1386 }
1387 
parseIrefBox(off64_t offset,size_t size)1388 status_t ItemTable::parseIrefBox(off64_t offset, size_t size) {
1389     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1390 
1391     IrefBox irefBox(mDataSource, &mItemReferences);
1392     status_t err = irefBox.parse(offset, size);
1393     if (err != OK) {
1394         return err;
1395     }
1396 
1397     return buildImageItemsIfPossible('iref');
1398 }
1399 
buildImageItemsIfPossible(uint32_t type)1400 status_t ItemTable::buildImageItemsIfPossible(uint32_t type) {
1401     if (mImageItemsValid) {
1402         return OK;
1403     }
1404 
1405     mBoxesSeen.insert(type);
1406 
1407     // need at least 'iprp', 'iloc', 'pitm', 'iinf';
1408     // need 'idat' if any items used construction_method of 2;
1409     // need 'iref' if there are grids.
1410     if (!std::includes(
1411             mBoxesSeen.begin(), mBoxesSeen.end(),
1412             mRequiredBoxes.begin(), mRequiredBoxes.end())) {
1413         return OK;
1414     }
1415 
1416     ALOGV("building image table...");
1417 
1418     for (size_t i = 0; i < mItemInfos.size(); i++) {
1419         const ItemInfo &info = mItemInfos[i];
1420 
1421         // Only handle 3 types of items, all others are ignored:
1422         //   'grid': derived image from tiles
1423         //   'hvc1' or 'av01': coded image (or tile)
1424         //   'Exif' or XMP: metadata
1425         if (!info.isGrid() && !info.isSample() && !info.isExif() && !info.isXmp()) {
1426             continue;
1427         }
1428 
1429         ssize_t itemIndex = mItemIdToItemMap.indexOfKey(info.itemId);
1430         if (itemIndex >= 0) {
1431             ALOGW("ignoring duplicate image item id %d", info.itemId);
1432             continue;
1433         }
1434 
1435         ssize_t ilocIndex = mItemLocs.indexOfKey(info.itemId);
1436         if (ilocIndex < 0) {
1437             ALOGE("iloc missing for image item id %d", info.itemId);
1438             continue;
1439         }
1440         const ItemLoc &iloc = mItemLocs[ilocIndex];
1441 
1442         off64_t offset;
1443         size_t size;
1444         if (iloc.getLoc(&offset, &size, mIdatOffset, mIdatSize) != OK) {
1445             return ERROR_MALFORMED;
1446         }
1447 
1448         if (info.isExif() || info.isXmp()) {
1449             // Only add if the meta is non-empty. For Exif, the first 4 bytes contain
1450             // the offset to TIFF header, which the Exif parser doesn't use.
1451             ALOGV("adding meta to mItemIdToMetaMap: isExif %d, offset %lld, size %lld",
1452                     info.isExif(), (long long)offset, (long long)size);
1453             if ((info.isExif() && size > 4) || (info.isXmp() && size > 0)) {
1454                 ExternalMetaItem metaItem = {
1455                         .isExif = info.isExif(),
1456                         .offset = offset,
1457                         .size = size,
1458                 };
1459                 mItemIdToMetaMap.add(info.itemId, metaItem);
1460             }
1461             continue;
1462         }
1463 
1464         ImageItem image(info.itemType, info.itemId, info.hidden);
1465 
1466         ALOGV("adding %s: itemId %d", image.isGrid() ? "grid" : "image", info.itemId);
1467 
1468         if (image.isGrid()) {
1469             // ImageGrid struct is at least 8-byte, at most 12-byte (if flags&1)
1470             if (size < 8 || size > 12) {
1471                 return ERROR_MALFORMED;
1472             }
1473             uint8_t buf[12];
1474             if (!mDataSource->readAt(offset, buf, size)) {
1475                 return ERROR_IO;
1476             }
1477 
1478             image.rows = buf[2] + 1;
1479             image.columns = buf[3] + 1;
1480 
1481             ALOGV("rows %d, columans %d", image.rows, image.columns);
1482         } else {
1483             image.offset = offset;
1484             image.size = size;
1485         }
1486         mItemIdToItemMap.add(info.itemId, image);
1487     }
1488 
1489     for (size_t i = 0; i < mAssociations.size(); i++) {
1490         attachProperty(mAssociations[i]);
1491     }
1492 
1493     for (size_t i = 0; i < mItemReferences.size(); i++) {
1494         mItemReferences[i]->apply(mItemIdToItemMap, mItemIdToMetaMap);
1495     }
1496 
1497     bool foundPrimary = false;
1498     for (size_t i = 0; i < mItemIdToItemMap.size(); i++) {
1499         // add all non-hidden images, also add the primary even if it's marked
1500         // hidden, in case the primary is set to a thumbnail
1501         bool isPrimary = (mItemIdToItemMap[i].itemId == mPrimaryItemId);
1502         if (!mItemIdToItemMap[i].hidden || isPrimary) {
1503             mDisplayables.push_back(i);
1504         }
1505         foundPrimary |= isPrimary;
1506     }
1507 
1508     ALOGV("found %zu displayables", mDisplayables.size());
1509 
1510     // fail if no displayables are found
1511     if (mDisplayables.empty()) {
1512         return ERROR_MALFORMED;
1513     }
1514 
1515     // if the primary item id is invalid, set primary to the first displayable
1516     if (!foundPrimary) {
1517         mPrimaryItemId = mItemIdToItemMap[mDisplayables[0]].itemId;
1518     }
1519 
1520     mImageItemsValid = true;
1521     return OK;
1522 }
1523 
attachProperty(const AssociationEntry & association)1524 void ItemTable::attachProperty(const AssociationEntry &association) {
1525     ssize_t itemIndex = mItemIdToItemMap.indexOfKey(association.itemId);
1526 
1527     // ignore non-image items
1528     if (itemIndex < 0) {
1529         return;
1530     }
1531 
1532     uint16_t propertyIndex = association.index;
1533     if (propertyIndex >= mItemProperties.size()) {
1534         ALOGW("Ignoring invalid property index %d", propertyIndex);
1535         return;
1536     }
1537 
1538     ALOGV("attach property %d to item id %d)",
1539             propertyIndex, association.itemId);
1540 
1541     mItemProperties[propertyIndex]->attachTo(mItemIdToItemMap.editValueAt(itemIndex));
1542 }
1543 
countImages() const1544 uint32_t ItemTable::countImages() const {
1545     return mImageItemsValid ? mDisplayables.size() : 0;
1546 }
1547 
getImageMeta(const uint32_t imageIndex)1548 AMediaFormat *ItemTable::getImageMeta(const uint32_t imageIndex) {
1549     if (!mImageItemsValid) {
1550         return NULL;
1551     }
1552 
1553     if (imageIndex >= mDisplayables.size()) {
1554         ALOGE("%s: invalid image index %u", __FUNCTION__, imageIndex);
1555         return NULL;
1556     }
1557     const uint32_t itemIndex = mDisplayables[imageIndex];
1558     ALOGV("image[%u]: item index %u", imageIndex, itemIndex);
1559 
1560     const ImageItem *image = &mItemIdToItemMap[itemIndex];
1561 
1562     ssize_t tileItemIndex = -1;
1563     if (image->isGrid()) {
1564         if (image->dimgRefs.empty()) {
1565             return NULL;
1566         }
1567         tileItemIndex = mItemIdToItemMap.indexOfKey(image->dimgRefs[0]);
1568         if (tileItemIndex < 0) {
1569             return NULL;
1570         }
1571     }
1572 
1573     AMediaFormat *meta = AMediaFormat_new();
1574     AMediaFormat_setString(
1575         meta, AMEDIAFORMAT_KEY_MIME,
1576         mIsHeif ? MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC : MEDIA_MIMETYPE_IMAGE_AVIF);
1577 
1578     if (image->itemId == mPrimaryItemId) {
1579         AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_DEFAULT, 1);
1580     }
1581 
1582     ALOGV("image[%u]: size %dx%d", imageIndex, image->width, image->height);
1583 
1584     AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_WIDTH, image->width);
1585     AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_HEIGHT, image->height);
1586     if (image->rotation != 0) {
1587         // Rotation angle in HEIF is CCW, convert to CW here to be
1588         // consistent with the other media formats.
1589         switch(image->rotation) {
1590             case 90:
1591             case 180:
1592             case 270:
1593                 AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_ROTATION, 360 - image->rotation);
1594                 break;
1595             default: break; // don't set if invalid
1596         }
1597     }
1598     // we validated no overflow in IspeBox::parse()
1599     AMediaFormat_setInt32(meta,
1600             AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 3 / 2);
1601 
1602     if (!image->thumbnails.empty()) {
1603         ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(image->thumbnails[0]);
1604         if (thumbItemIndex >= 0) {
1605             const ImageItem &thumbnail = mItemIdToItemMap[thumbItemIndex];
1606             if (thumbnail.hvcc != NULL || thumbnail.av1c != NULL) {
1607                 AMediaFormat_setInt32(meta,
1608                         AMEDIAFORMAT_KEY_THUMBNAIL_WIDTH, thumbnail.width);
1609                 AMediaFormat_setInt32(meta,
1610                         AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT, thumbnail.height);
1611                 if (thumbnail.hvcc != NULL) {
1612                     AMediaFormat_setBuffer(meta,
1613                             AMEDIAFORMAT_KEY_THUMBNAIL_CSD_HEVC,
1614                             thumbnail.hvcc->data(), thumbnail.hvcc->size());
1615                 } else {
1616                     // We use a hard-coded string here instead of
1617                     // AMEDIAFORMAT_KEY_THUMBNAIL_CSD_AV1C. The key is available only from SDK 31.
1618                     // The mp4 extractor is part of mainline and builds against SDK 29 as of
1619                     // writing. This hard-coded string can be replaced with the named constant once
1620                     // the mp4 extractor is built against SDK >= 31.
1621                     AMediaFormat_setBuffer(meta,
1622                             "thumbnail-csd-av1c", thumbnail.av1c->data(), thumbnail.av1c->size());
1623                 }
1624                 ALOGV("image[%u]: thumbnail: size %dx%d, item index %zd",
1625                         imageIndex, thumbnail.width, thumbnail.height, thumbItemIndex);
1626             } else {
1627                 ALOGW("%s: thumbnail data is missing for image[%u]!", __FUNCTION__, imageIndex);
1628             }
1629         } else {
1630             ALOGW("%s: Referenced thumbnail does not exist!", __FUNCTION__);
1631         }
1632     }
1633 
1634     if (image->isGrid()) {
1635         AMediaFormat_setInt32(meta,
1636                 AMEDIAFORMAT_KEY_GRID_ROWS, image->rows);
1637         AMediaFormat_setInt32(meta,
1638                 AMEDIAFORMAT_KEY_GRID_COLUMNS, image->columns);
1639         // point image to the first tile for grid size and HVCC
1640         image = &mItemIdToItemMap.editValueAt(tileItemIndex);
1641         AMediaFormat_setInt32(meta,
1642                 AMEDIAFORMAT_KEY_TILE_WIDTH, image->width);
1643         AMediaFormat_setInt32(meta,
1644                 AMEDIAFORMAT_KEY_TILE_HEIGHT, image->height);
1645         // we validated no overflow in IspeBox::parse()
1646         AMediaFormat_setInt32(meta,
1647                 AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 3 / 2);
1648     }
1649 
1650     if (mIsHeif) {
1651         if (image->hvcc == NULL) {
1652             ALOGE("%s: hvcc is missing for image[%u]!", __FUNCTION__, imageIndex);
1653             return NULL;
1654         }
1655         AMediaFormat_setBuffer(meta,
1656                 AMEDIAFORMAT_KEY_CSD_HEVC, image->hvcc->data(), image->hvcc->size());
1657     } else {
1658         if (image->av1c == NULL) {
1659             ALOGE("%s: av1c is missing for image[%u]!", __FUNCTION__, imageIndex);
1660             return NULL;
1661         }
1662         AMediaFormat_setBuffer(meta,
1663                 AMEDIAFORMAT_KEY_CSD_0, image->av1c->data(), image->av1c->size());
1664     }
1665 
1666     if (image->icc != NULL) {
1667         AMediaFormat_setBuffer(meta,
1668                 AMEDIAFORMAT_KEY_ICC_PROFILE, image->icc->data(), image->icc->size());
1669     }
1670     return meta;
1671 }
1672 
findImageItem(const uint32_t imageIndex,uint32_t * itemIndex)1673 status_t ItemTable::findImageItem(const uint32_t imageIndex, uint32_t *itemIndex) {
1674     if (!mImageItemsValid) {
1675         return INVALID_OPERATION;
1676     }
1677 
1678     if (imageIndex >= mDisplayables.size()) {
1679         ALOGE("%s: invalid image index %d", __FUNCTION__, imageIndex);
1680         return BAD_VALUE;
1681     }
1682 
1683     *itemIndex = mDisplayables[imageIndex];
1684 
1685     ALOGV("image[%u]: item index %u", imageIndex, *itemIndex);
1686     return OK;
1687 }
1688 
findThumbnailItem(const uint32_t imageIndex,uint32_t * itemIndex)1689 status_t ItemTable::findThumbnailItem(const uint32_t imageIndex, uint32_t *itemIndex) {
1690     if (!mImageItemsValid) {
1691         return INVALID_OPERATION;
1692     }
1693 
1694     if (imageIndex >= mDisplayables.size()) {
1695         ALOGE("%s: invalid image index %d", __FUNCTION__, imageIndex);
1696         return BAD_VALUE;
1697     }
1698 
1699     uint32_t imageItemIndex = mDisplayables[imageIndex];
1700 
1701     const ImageItem &imageItem = mItemIdToItemMap[imageItemIndex];
1702     if (imageItem.thumbnails.empty()) {
1703         *itemIndex = imageItemIndex;
1704         return OK;
1705     }
1706 
1707     ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(imageItem.thumbnails[0]);
1708     if (thumbItemIndex < 0) {
1709         // Do not return the image item in this case, fail it so that the
1710         // thumbnail extraction code knows we really don't have it.
1711         return INVALID_OPERATION;
1712     }
1713 
1714     *itemIndex = thumbItemIndex;
1715     return OK;
1716 }
1717 
getImageOffsetAndSize(uint32_t * itemIndex,off64_t * offset,size_t * size)1718 status_t ItemTable::getImageOffsetAndSize(
1719         uint32_t *itemIndex, off64_t *offset, size_t *size) {
1720     if (!mImageItemsValid) {
1721         return INVALID_OPERATION;
1722     }
1723 
1724     if (itemIndex != NULL) {
1725         if (*itemIndex >= mItemIdToItemMap.size()) {
1726             ALOGE("%s: Bad item index!", __FUNCTION__);
1727             return BAD_VALUE;
1728         }
1729         mCurrentItemIndex = *itemIndex;
1730     }
1731 
1732     ImageItem &image = mItemIdToItemMap.editValueAt(mCurrentItemIndex);
1733     if (image.isGrid()) {
1734         uint32_t tileItemId;
1735         status_t err = image.getNextTileItemId(&tileItemId, itemIndex != NULL);
1736         if (err != OK) {
1737             return err;
1738         }
1739         ssize_t tileItemIndex = mItemIdToItemMap.indexOfKey(tileItemId);
1740         if (tileItemIndex < 0) {
1741             return ERROR_END_OF_STREAM;
1742         }
1743         *offset = mItemIdToItemMap[tileItemIndex].offset;
1744         *size = mItemIdToItemMap[tileItemIndex].size;
1745     } else {
1746         if (itemIndex == NULL) {
1747             // For single images, we only allow it to be read once, after that
1748             // it's EOS.  New item index must be requested each time.
1749             return ERROR_END_OF_STREAM;
1750         }
1751         *offset = mItemIdToItemMap[mCurrentItemIndex].offset;
1752         *size = mItemIdToItemMap[mCurrentItemIndex].size;
1753     }
1754 
1755     return OK;
1756 }
1757 
getExifOffsetAndSize(off64_t * offset,size_t * size)1758 status_t ItemTable::getExifOffsetAndSize(off64_t *offset, size_t *size) {
1759     if (!mImageItemsValid) {
1760         return INVALID_OPERATION;
1761     }
1762 
1763     ssize_t itemIndex = mItemIdToItemMap.indexOfKey(mPrimaryItemId);
1764 
1765     // this should not happen, something's seriously wrong.
1766     if (itemIndex < 0) {
1767         return INVALID_OPERATION;
1768     }
1769 
1770     const ImageItem &image = mItemIdToItemMap[itemIndex];
1771     if (image.exifRefs.size() == 0) {
1772         return NAME_NOT_FOUND;
1773     }
1774 
1775     ssize_t exifIndex = mItemIdToMetaMap.indexOfKey(image.exifRefs[0]);
1776     if (exifIndex < 0) {
1777         return NAME_NOT_FOUND;
1778     }
1779 
1780     // skip the first 4-byte of the offset to TIFF header
1781     uint32_t tiffOffset;
1782     if (!mDataSource->readAt(
1783             mItemIdToMetaMap[exifIndex].offset, &tiffOffset, 4)) {
1784         return ERROR_IO;
1785     }
1786 
1787     // We need 'Exif\0\0' before the tiff header
1788     tiffOffset = ntohl(tiffOffset);
1789     if (tiffOffset < 6) {
1790         return ERROR_MALFORMED;
1791     }
1792     // The first 4-byte of the item is the offset of the tiff header within the
1793     // exif data. The size of the item should be > 4 for a non-empty exif (this
1794     // was already checked when the item was added). Also check that the tiff
1795     // header offset is valid.
1796     if (mItemIdToMetaMap[exifIndex].size <= 4 ||
1797             tiffOffset > mItemIdToMetaMap[exifIndex].size - 4) {
1798         return ERROR_MALFORMED;
1799     }
1800 
1801     // Offset of 'Exif\0\0' relative to the beginning of 'Exif' item
1802     // (first 4-byte is the tiff header offset)
1803     uint32_t exifOffset = 4 + tiffOffset - 6;
1804     *offset = mItemIdToMetaMap[exifIndex].offset + exifOffset;
1805     *size = mItemIdToMetaMap[exifIndex].size - exifOffset;
1806     return OK;
1807 }
1808 
getXmpOffsetAndSize(off64_t * offset,size_t * size)1809 status_t ItemTable::getXmpOffsetAndSize(off64_t *offset, size_t *size) {
1810     if (!mImageItemsValid) {
1811         return INVALID_OPERATION;
1812     }
1813 
1814     ssize_t itemIndex = mItemIdToItemMap.indexOfKey(mPrimaryItemId);
1815 
1816     // this should not happen, something's seriously wrong.
1817     if (itemIndex < 0) {
1818         return INVALID_OPERATION;
1819     }
1820 
1821     const ImageItem &image = mItemIdToItemMap[itemIndex];
1822     if (image.xmpRefs.size() == 0) {
1823         return NAME_NOT_FOUND;
1824     }
1825 
1826     ssize_t xmpIndex = mItemIdToMetaMap.indexOfKey(image.xmpRefs[0]);
1827     if (xmpIndex < 0) {
1828         return NAME_NOT_FOUND;
1829     }
1830 
1831     *offset = mItemIdToMetaMap[xmpIndex].offset;
1832     *size = mItemIdToMetaMap[xmpIndex].size;
1833     return OK;
1834 }
1835 
1836 } // namespace heif
1837 
1838 }  // namespace android
1839