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