Home | History | Annotate | Download | only in libstagefright
      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 <include/ItemTable.h>
     21 #include <media/MediaDefs.h>
     22 #include <media/stagefright/DataSource.h>
     23 #include <media/stagefright/MetaData.h>
     24 #include <media/stagefright/MediaErrors.h>
     25 #include <media/stagefright/Utils.h>
     26 #include <media/stagefright/foundation/ABuffer.h>
     27 #include <media/stagefright/foundation/hexdump.h>
     28 #include <utils/Log.h>
     29 
     30 namespace android {
     31 
     32 namespace heif {
     33 
     34 /////////////////////////////////////////////////////////////////////
     35 //
     36 //  struct to keep track of one image item
     37 //
     38 
     39 struct 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 
     85 struct Box {
     86 protected:
     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 
    105 private:
    106     sp<DataSource> mDataSource;
    107     uint32_t mType;
    108 };
    109 
    110 status_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 
    163 status_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 
    179 struct FullBox : public Box {
    180 protected:
    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 
    190 private:
    191     uint8_t mVersion;
    192     uint32_t mFlags;
    193 };
    194 
    195 status_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 
    215 struct 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 
    222 status_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 
    248 struct ExtentEntry {
    249     uint64_t extentIndex;
    250     uint64_t extentOffset;
    251     uint64_t extentLength;
    252 };
    253 
    254 struct 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 
    295 struct 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 
    304 private:
    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 
    312 status_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 
    465 struct 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 
    509 private:
    510     uint32_t mItemId;
    511     uint32_t mRefIdSize;
    512     Vector<uint32_t> mRefs;
    513 
    514     DISALLOW_EVIL_CONSTRUCTORS(ItemReference);
    515 };
    516 
    517 status_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 
    550 struct 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 
    556 protected:
    557     status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
    558 
    559 private:
    560     uint32_t mRefIdSize;
    561     Vector<sp<ItemReference> > *mItemRefs;
    562 };
    563 
    564 status_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 
    575 status_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 
    591 struct AssociationEntry {
    592     uint32_t itemId;
    593     bool essential;
    594     uint16_t index;
    595 };
    596 
    597 struct 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 
    608 private:
    609     DISALLOW_EVIL_CONSTRUCTORS(ItemProperty);
    610 };
    611 
    612 struct 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 
    623 private:
    624     uint32_t mWidth;
    625     uint32_t mHeight;
    626 };
    627 
    628 status_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 
    648 struct 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 
    658 private:
    659     sp<ABuffer> mHVCC;
    660 };
    661 
    662 status_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 
    681 struct 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 
    691 private:
    692     uint8_t mAngle;
    693 };
    694 
    695 status_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 
    710 struct 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 
    720 private:
    721     sp<ABuffer> mICCData;
    722 };
    723 
    724 status_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 
    758 struct 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);
    763 private:
    764     Vector<AssociationEntry> *mAssociations;
    765 };
    766 
    767 status_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 
    832 struct 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);
    837 protected:
    838     status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
    839 
    840 private:
    841     Vector<sp<ItemProperty> > *mItemProperties;
    842 };
    843 
    844 status_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 
    851 status_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 
    889 struct 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);
    897 protected:
    898     status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
    899 
    900 private:
    901     Vector<sp<ItemProperty> > *mProperties;
    902     Vector<AssociationEntry> *mAssociations;
    903 };
    904 
    905 status_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 
    915 status_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 //
    940 struct ItemInfo {
    941     uint32_t itemId;
    942     uint32_t itemType;
    943 };
    944 
    945 struct 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 
    951 private:
    952     bool parseNullTerminatedString(off64_t *offset, size_t *size, String8 *out);
    953 };
    954 
    955 bool 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 
    981 status_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 
   1084 struct 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 
   1093 protected:
   1094     status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
   1095 
   1096 private:
   1097     Vector<ItemInfo> *mItemInfos;
   1098     bool mHasGrids;
   1099 };
   1100 
   1101 status_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 
   1135 status_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 
   1153 ItemTable::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 
   1166 ItemTable::~ItemTable() {}
   1167 
   1168 status_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 
   1208 status_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 
   1224 status_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 
   1240 status_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 
   1252 status_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 
   1264 status_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 
   1274 status_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 
   1286 status_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 
   1369 void 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 
   1390 sp<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         // Rotation angle in HEIF is CCW, convert to CW here to be
   1413         // consistent with the other media formats.
   1414         switch(image->rotation) {
   1415             case 90: meta->setInt32(kKeyRotation, 270); break;
   1416             case 180: meta->setInt32(kKeyRotation, 180); break;
   1417             case 270: meta->setInt32(kKeyRotation, 90); break;
   1418             default: break; // don't set if invalid
   1419         }
   1420     }
   1421     meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5);
   1422 
   1423     if (!image->thumbnails.empty()) {
   1424         ssize_t thumbnailIndex = mItemIdToImageMap.indexOfKey(image->thumbnails[0]);
   1425         if (thumbnailIndex >= 0) {
   1426             const ImageItem &thumbnail = mItemIdToImageMap[thumbnailIndex];
   1427 
   1428             meta->setInt32(kKeyThumbnailWidth, thumbnail.width);
   1429             meta->setInt32(kKeyThumbnailHeight, thumbnail.height);
   1430             meta->setData(kKeyThumbnailHVCC, kTypeHVCC,
   1431                     thumbnail.hvcc->data(), thumbnail.hvcc->size());
   1432             ALOGV("thumbnail meta: %dx%d, index %zd",
   1433                     thumbnail.width, thumbnail.height, thumbnailIndex);
   1434         } else {
   1435             ALOGW("Referenced thumbnail does not exist!");
   1436         }
   1437     }
   1438 
   1439     if (image->isGrid()) {
   1440         ssize_t tileIndex = mItemIdToImageMap.indexOfKey(image->dimgRefs[0]);
   1441         if (tileIndex < 0) {
   1442             return NULL;
   1443         }
   1444         // when there are tiles, (kKeyWidth, kKeyHeight) is the full tiled area,
   1445         // and (kKeyDisplayWidth, kKeyDisplayHeight) may be smaller than that.
   1446         meta->setInt32(kKeyDisplayWidth, image->width);
   1447         meta->setInt32(kKeyDisplayHeight, image->height);
   1448         int32_t gridRows = image->rows, gridCols = image->columns;
   1449 
   1450         // point image to the first tile for grid size and HVCC
   1451         image = &mItemIdToImageMap.editValueAt(tileIndex);
   1452         meta->setInt32(kKeyWidth, image->width * gridCols);
   1453         meta->setInt32(kKeyHeight, image->height * gridRows);
   1454         meta->setInt32(kKeyGridWidth, image->width);
   1455         meta->setInt32(kKeyGridHeight, image->height);
   1456         meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5);
   1457     }
   1458 
   1459     if (image->hvcc == NULL) {
   1460         ALOGE("hvcc is missing!");
   1461         return NULL;
   1462     }
   1463     meta->setData(kKeyHVCC, kTypeHVCC, image->hvcc->data(), image->hvcc->size());
   1464 
   1465     if (image->icc != NULL) {
   1466         meta->setData(kKeyIccProfile, 0, image->icc->data(), image->icc->size());
   1467     }
   1468     return meta;
   1469 }
   1470 
   1471 uint32_t ItemTable::countImages() const {
   1472     return mImageItemsValid ? mItemIdToImageMap.size() : 0;
   1473 }
   1474 
   1475 status_t ItemTable::findPrimaryImage(uint32_t *imageIndex) {
   1476     if (!mImageItemsValid) {
   1477         return INVALID_OPERATION;
   1478     }
   1479 
   1480     ssize_t index = mItemIdToImageMap.indexOfKey(mPrimaryItemId);
   1481     if (index < 0) {
   1482         return ERROR_MALFORMED;
   1483     }
   1484 
   1485     *imageIndex = index;
   1486     return OK;
   1487 }
   1488 
   1489 status_t ItemTable::findThumbnail(uint32_t *imageIndex) {
   1490     if (!mImageItemsValid) {
   1491         return INVALID_OPERATION;
   1492     }
   1493 
   1494     ssize_t primaryIndex = mItemIdToImageMap.indexOfKey(mPrimaryItemId);
   1495     if (primaryIndex < 0) {
   1496         ALOGE("Primary item id %d not found!", mPrimaryItemId);
   1497         return ERROR_MALFORMED;
   1498     }
   1499 
   1500     const ImageItem &primaryImage = mItemIdToImageMap[primaryIndex];
   1501     if (primaryImage.thumbnails.empty()) {
   1502         ALOGW("Using primary in place of thumbnail.");
   1503         *imageIndex = primaryIndex;
   1504         return OK;
   1505     }
   1506 
   1507     ssize_t thumbnailIndex = mItemIdToImageMap.indexOfKey(
   1508             primaryImage.thumbnails[0]);
   1509     if (thumbnailIndex < 0) {
   1510         ALOGE("Thumbnail item id %d not found!", primaryImage.thumbnails[0]);
   1511         return ERROR_MALFORMED;
   1512     }
   1513 
   1514     *imageIndex = thumbnailIndex;
   1515     return OK;
   1516 }
   1517 
   1518 status_t ItemTable::getImageOffsetAndSize(
   1519         uint32_t *imageIndex, off64_t *offset, size_t *size) {
   1520     if (!mImageItemsValid) {
   1521         return INVALID_OPERATION;
   1522     }
   1523 
   1524     if (imageIndex != NULL) {
   1525         if (*imageIndex >= mItemIdToImageMap.size()) {
   1526             ALOGE("Bad image index!");
   1527             return BAD_VALUE;
   1528         }
   1529         mCurrentImageIndex = *imageIndex;
   1530     }
   1531 
   1532     ImageItem &image = mItemIdToImageMap.editValueAt(mCurrentImageIndex);
   1533     if (image.isGrid()) {
   1534         uint32_t tileItemId;
   1535         status_t err = image.getNextTileItemId(&tileItemId, imageIndex != NULL);
   1536         if (err != OK) {
   1537             return err;
   1538         }
   1539         ssize_t tileImageIndex = mItemIdToImageMap.indexOfKey(tileItemId);
   1540         if (tileImageIndex < 0) {
   1541             return ERROR_END_OF_STREAM;
   1542         }
   1543         *offset = mItemIdToImageMap[tileImageIndex].offset;
   1544         *size = mItemIdToImageMap[tileImageIndex].size;
   1545     } else {
   1546         if (imageIndex == NULL) {
   1547             // For single images, we only allow it to be read once, after that
   1548             // it's EOS.  New image index must be requested each time.
   1549             return ERROR_END_OF_STREAM;
   1550         }
   1551         *offset = mItemIdToImageMap[mCurrentImageIndex].offset;
   1552         *size = mItemIdToImageMap[mCurrentImageIndex].size;
   1553     }
   1554 
   1555     return OK;
   1556 }
   1557 
   1558 } // namespace heif
   1559 
   1560 }  // namespace android
   1561