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