1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 //#define LOG_NDEBUG 0 18 #define LOG_TAG "ItemTable" 19 20 #include <include/ItemTable.h> 21 #include <media/MediaDefs.h> 22 #include <media/stagefright/DataSource.h> 23 #include <media/stagefright/MetaData.h> 24 #include <media/stagefright/MediaErrors.h> 25 #include <media/stagefright/Utils.h> 26 #include <media/stagefright/foundation/ABuffer.h> 27 #include <media/stagefright/foundation/hexdump.h> 28 #include <utils/Log.h> 29 30 namespace android { 31 32 namespace heif { 33 34 ///////////////////////////////////////////////////////////////////// 35 // 36 // struct to keep track of one image item 37 // 38 39 struct ImageItem { 40 friend struct ItemReference; 41 friend struct ItemProperty; 42 43 ImageItem() : ImageItem(0) {} 44 ImageItem(uint32_t _type) : type(_type), 45 rows(0), columns(0), width(0), height(0), rotation(0), 46 offset(0), size(0), nextTileIndex(0) {} 47 48 bool isGrid() const { 49 return type == FOURCC('g', 'r', 'i', 'd'); 50 } 51 52 status_t getNextTileItemId(uint32_t *nextTileItemId, bool reset) { 53 if (reset) { 54 nextTileIndex = 0; 55 } 56 if (nextTileIndex >= dimgRefs.size()) { 57 return ERROR_END_OF_STREAM; 58 } 59 *nextTileItemId = dimgRefs[nextTileIndex++]; 60 return OK; 61 } 62 63 uint32_t type; 64 int32_t rows; 65 int32_t columns; 66 int32_t width; 67 int32_t height; 68 int32_t rotation; 69 off64_t offset; 70 size_t size; 71 sp<ABuffer> hvcc; 72 sp<ABuffer> icc; 73 74 Vector<uint32_t> thumbnails; 75 Vector<uint32_t> dimgRefs; 76 size_t nextTileIndex; 77 }; 78 79 80 ///////////////////////////////////////////////////////////////////// 81 // 82 // ISO boxes 83 // 84 85 struct Box { 86 protected: 87 Box(const sp<DataSource> source, uint32_t type) : 88 mDataSource(source), mType(type) {} 89 90 virtual ~Box() {} 91 92 virtual status_t onChunkData( 93 uint32_t /*type*/, off64_t /*offset*/, size_t /*size*/) { 94 return OK; 95 } 96 97 inline uint32_t type() const { return mType; } 98 99 inline sp<DataSource> source() const { return mDataSource; } 100 101 status_t parseChunk(off64_t *offset); 102 103 status_t parseChunks(off64_t offset, size_t size); 104 105 private: 106 sp<DataSource> mDataSource; 107 uint32_t mType; 108 }; 109 110 status_t Box::parseChunk(off64_t *offset) { 111 if (*offset < 0) { 112 ALOGE("b/23540914"); 113 return ERROR_MALFORMED; 114 } 115 uint32_t hdr[2]; 116 if (mDataSource->readAt(*offset, hdr, 8) < 8) { 117 return ERROR_IO; 118 } 119 uint64_t chunk_size = ntohl(hdr[0]); 120 int32_t chunk_type = ntohl(hdr[1]); 121 off64_t data_offset = *offset + 8; 122 123 if (chunk_size == 1) { 124 if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) { 125 return ERROR_IO; 126 } 127 chunk_size = ntoh64(chunk_size); 128 data_offset += 8; 129 130 if (chunk_size < 16) { 131 // The smallest valid chunk is 16 bytes long in this case. 132 return ERROR_MALFORMED; 133 } 134 } else if (chunk_size == 0) { 135 // This shouldn't happen since we should never be top level 136 ALOGE("invalid chunk size 0 for non-top level box"); 137 return ERROR_MALFORMED; 138 } else if (chunk_size < 8) { 139 // The smallest valid chunk is 8 bytes long. 140 ALOGE("invalid chunk size: %lld", (long long)chunk_size); 141 return ERROR_MALFORMED; 142 } 143 144 char chunk[5]; 145 MakeFourCCString(chunk_type, chunk); 146 ALOGV("chunk: %s @ %lld", chunk, (long long)*offset); 147 148 off64_t chunk_data_size = chunk_size - (data_offset - *offset); 149 if (chunk_data_size < 0) { 150 ALOGE("b/23540914"); 151 return ERROR_MALFORMED; 152 } 153 154 status_t err = onChunkData(chunk_type, data_offset, chunk_data_size); 155 156 if (err != OK) { 157 return err; 158 } 159 *offset += chunk_size; 160 return OK; 161 } 162 163 status_t Box::parseChunks(off64_t offset, size_t size) { 164 off64_t stopOffset = offset + size; 165 while (offset < stopOffset) { 166 status_t err = parseChunk(&offset); 167 if (err != OK) { 168 return err; 169 } 170 } 171 if (offset != stopOffset) { 172 return ERROR_MALFORMED; 173 } 174 return OK; 175 } 176 177 /////////////////////////////////////////////////////////////////////// 178 179 struct FullBox : public Box { 180 protected: 181 FullBox(const sp<DataSource> source, uint32_t type) : 182 Box(source, type), mVersion(0), mFlags(0) {} 183 184 inline uint8_t version() const { return mVersion; } 185 186 inline uint32_t flags() const { return mFlags; } 187 188 status_t parseFullBoxHeader(off64_t *offset, size_t *size); 189 190 private: 191 uint8_t mVersion; 192 uint32_t mFlags; 193 }; 194 195 status_t FullBox::parseFullBoxHeader(off64_t *offset, size_t *size) { 196 if (*size < 4) { 197 return ERROR_MALFORMED; 198 } 199 if (!source()->readAt(*offset, &mVersion, 1)) { 200 return ERROR_IO; 201 } 202 if (!source()->getUInt24(*offset + 1, &mFlags)) { 203 return ERROR_IO; 204 } 205 *offset += 4; 206 *size -= 4; 207 return OK; 208 } 209 210 ///////////////////////////////////////////////////////////////////// 211 // 212 // PrimaryImage box 213 // 214 215 struct PitmBox : public FullBox { 216 PitmBox(const sp<DataSource> source) : 217 FullBox(source, FOURCC('p', 'i', 't', 'm')) {} 218 219 status_t parse(off64_t offset, size_t size, uint32_t *primaryItemId); 220 }; 221 222 status_t PitmBox::parse(off64_t offset, size_t size, uint32_t *primaryItemId) { 223 status_t err = parseFullBoxHeader(&offset, &size); 224 if (err != OK) { 225 return err; 226 } 227 228 size_t itemIdSize = (version() == 0) ? 2 : 4; 229 if (size < itemIdSize) { 230 return ERROR_MALFORMED; 231 } 232 uint32_t itemId; 233 if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) { 234 return ERROR_IO; 235 } 236 237 ALOGV("primary id %d", itemId); 238 *primaryItemId = itemId; 239 240 return OK; 241 } 242 243 ///////////////////////////////////////////////////////////////////// 244 // 245 // ItemLocation related boxes 246 // 247 248 struct ExtentEntry { 249 uint64_t extentIndex; 250 uint64_t extentOffset; 251 uint64_t extentLength; 252 }; 253 254 struct ItemLoc { 255 ItemLoc() : ItemLoc(0, 0, 0, 0) {} 256 ItemLoc(uint32_t item_id, uint16_t construction_method, 257 uint16_t data_reference_index, uint64_t base_offset) : 258 itemId(item_id), 259 constructionMethod(construction_method), 260 dataReferenceIndex(data_reference_index), 261 baseOffset(base_offset) {} 262 263 void addExtent(const ExtentEntry& extent) { 264 extents.push_back(extent); 265 } 266 267 status_t getLoc(off64_t *offset, size_t *size, 268 off64_t idatOffset, size_t idatSize) const { 269 // TODO: fix extent handling, fix constructionMethod = 2 270 CHECK(extents.size() == 1); 271 if (constructionMethod == 0) { 272 *offset = baseOffset + extents[0].extentOffset; 273 *size = extents[0].extentLength; 274 return OK; 275 } else if (constructionMethod == 1) { 276 if (baseOffset + extents[0].extentOffset + extents[0].extentLength 277 > idatSize) { 278 return ERROR_MALFORMED; 279 } 280 *offset = baseOffset + extents[0].extentOffset + idatOffset; 281 *size = extents[0].extentLength; 282 return OK; 283 } 284 return ERROR_UNSUPPORTED; 285 } 286 287 // parsed info 288 uint32_t itemId; 289 uint16_t constructionMethod; 290 uint16_t dataReferenceIndex; 291 off64_t baseOffset; 292 Vector<ExtentEntry> extents; 293 }; 294 295 struct IlocBox : public FullBox { 296 IlocBox(const sp<DataSource> source, KeyedVector<uint32_t, ItemLoc> *itemLocs) : 297 FullBox(source, FOURCC('i', 'l', 'o', 'c')), 298 mItemLocs(itemLocs), mHasConstructMethod1(false) {} 299 300 status_t parse(off64_t offset, size_t size); 301 302 bool hasConstructMethod1() { return mHasConstructMethod1; } 303 304 private: 305 static bool isSizeFieldValid(uint32_t offset_size) { 306 return offset_size == 0 || offset_size == 4 || offset_size == 8; 307 } 308 KeyedVector<uint32_t, ItemLoc> *mItemLocs; 309 bool mHasConstructMethod1; 310 }; 311 312 status_t IlocBox::parse(off64_t offset, size_t size) { 313 status_t err = parseFullBoxHeader(&offset, &size); 314 if (err != OK) { 315 return err; 316 } 317 if (version() > 2) { 318 ALOGE("%s: invalid version %d", __FUNCTION__, version()); 319 return ERROR_MALFORMED; 320 } 321 322 if (size < 2) { 323 return ERROR_MALFORMED; 324 } 325 uint8_t offset_size; 326 if (!source()->readAt(offset++, &offset_size, 1)) { 327 return ERROR_IO; 328 } 329 uint8_t length_size = (offset_size & 0xF); 330 offset_size >>= 4; 331 332 uint8_t base_offset_size; 333 if (!source()->readAt(offset++, &base_offset_size, 1)) { 334 return ERROR_IO; 335 } 336 uint8_t index_size = 0; 337 if (version() == 1 || version() == 2) { 338 index_size = (base_offset_size & 0xF); 339 } 340 base_offset_size >>= 4; 341 size -= 2; 342 343 if (!isSizeFieldValid(offset_size) 344 || !isSizeFieldValid(length_size) 345 || !isSizeFieldValid(base_offset_size) 346 || !isSizeFieldValid((index_size))) { 347 ALOGE("%s: offset size not valid: %d, %d, %d, %d", __FUNCTION__, 348 offset_size, length_size, base_offset_size, index_size); 349 return ERROR_MALFORMED; 350 } 351 352 uint32_t item_count; 353 size_t itemFieldSize = version() < 2 ? 2 : 4; 354 if (size < itemFieldSize) { 355 return ERROR_MALFORMED; 356 } 357 if (!source()->getUInt32Var(offset, &item_count, itemFieldSize)) { 358 return ERROR_IO; 359 } 360 361 ALOGV("item_count %lld", (long long) item_count); 362 offset += itemFieldSize; 363 size -= itemFieldSize; 364 365 for (size_t i = 0; i < item_count; i++) { 366 uint32_t item_id; 367 if (!source()->getUInt32Var(offset, &item_id, itemFieldSize)) { 368 return ERROR_IO; 369 } 370 ALOGV("item[%zu]: id %lld", i, (long long)item_id); 371 offset += itemFieldSize; 372 373 uint8_t construction_method = 0; 374 if (version() == 1 || version() == 2) { 375 uint8_t buf[2]; 376 if (!source()->readAt(offset, buf, 2)) { 377 return ERROR_IO; 378 } 379 construction_method = (buf[1] & 0xF); 380 ALOGV("construction_method %d", construction_method); 381 if (construction_method == 1) { 382 mHasConstructMethod1 = true; 383 } 384 385 offset += 2; 386 } 387 388 uint16_t data_reference_index; 389 if (!source()->getUInt16(offset, &data_reference_index)) { 390 return ERROR_IO; 391 } 392 ALOGV("data_reference_index %d", data_reference_index); 393 if (data_reference_index != 0) { 394 // we don't support reference to other files 395 return ERROR_UNSUPPORTED; 396 } 397 offset += 2; 398 399 uint64_t base_offset = 0; 400 if (base_offset_size != 0) { 401 if (!source()->getUInt64Var(offset, &base_offset, base_offset_size)) { 402 return ERROR_IO; 403 } 404 offset += base_offset_size; 405 } 406 ALOGV("base_offset %lld", (long long) base_offset); 407 408 ssize_t index = mItemLocs->add(item_id, ItemLoc( 409 item_id, construction_method, data_reference_index, base_offset)); 410 ItemLoc &item = mItemLocs->editValueAt(index); 411 412 uint16_t extent_count; 413 if (!source()->getUInt16(offset, &extent_count)) { 414 return ERROR_IO; 415 } 416 ALOGV("extent_count %d", extent_count); 417 418 if (extent_count > 1 && (offset_size == 0 || length_size == 0)) { 419 // if the item is dividec into more than one extents, offset and 420 // length must be present. 421 return ERROR_MALFORMED; 422 } 423 offset += 2; 424 425 for (size_t j = 0; j < extent_count; j++) { 426 uint64_t extent_index = 1; // default=1 427 if ((version() == 1 || version() == 2) && (index_size > 0)) { 428 if (!source()->getUInt64Var(offset, &extent_index, index_size)) { 429 return ERROR_IO; 430 } 431 // TODO: add support for this mode 432 offset += index_size; 433 ALOGV("extent_index %lld", (long long)extent_index); 434 } 435 436 uint64_t extent_offset = 0; // default=0 437 if (offset_size > 0) { 438 if (!source()->getUInt64Var(offset, &extent_offset, offset_size)) { 439 return ERROR_IO; 440 } 441 offset += offset_size; 442 } 443 ALOGV("extent_offset %lld", (long long)extent_offset); 444 445 uint64_t extent_length = 0; // this indicates full length of file 446 if (length_size > 0) { 447 if (!source()->getUInt64Var(offset, &extent_length, length_size)) { 448 return ERROR_IO; 449 } 450 offset += length_size; 451 } 452 ALOGV("extent_length %lld", (long long)extent_length); 453 454 item.addExtent({ extent_index, extent_offset, extent_length }); 455 } 456 } 457 return OK; 458 } 459 460 ///////////////////////////////////////////////////////////////////// 461 // 462 // ItemReference related boxes 463 // 464 465 struct ItemReference : public Box, public RefBase { 466 ItemReference(const sp<DataSource> source, uint32_t type, uint32_t itemIdSize) : 467 Box(source, type), mItemId(0), mRefIdSize(itemIdSize) {} 468 469 status_t parse(off64_t offset, size_t size); 470 471 uint32_t itemId() { return mItemId; } 472 473 void apply(KeyedVector<uint32_t, ImageItem> &itemIdToImageMap) const { 474 ssize_t imageIndex = itemIdToImageMap.indexOfKey(mItemId); 475 476 // ignore non-image items 477 if (imageIndex < 0) { 478 return; 479 } 480 481 ALOGV("attach reference type 0x%x to item id %d)", type(), mItemId); 482 483 if (type() == FOURCC('d', 'i', 'm', 'g')) { 484 ImageItem &image = itemIdToImageMap.editValueAt(imageIndex); 485 if (!image.dimgRefs.empty()) { 486 ALOGW("dimgRefs if not clean!"); 487 } 488 image.dimgRefs.appendVector(mRefs); 489 } else if (type() == FOURCC('t', 'h', 'm', 'b')) { 490 for (size_t i = 0; i < mRefs.size(); i++) { 491 imageIndex = itemIdToImageMap.indexOfKey(mRefs[i]); 492 493 // ignore non-image items 494 if (imageIndex < 0) { 495 continue; 496 } 497 ALOGV("Image item id %d uses thumbnail item id %d", mRefs[i], mItemId); 498 ImageItem &image = itemIdToImageMap.editValueAt(imageIndex); 499 if (!image.thumbnails.empty()) { 500 ALOGW("already has thumbnails!"); 501 } 502 image.thumbnails.push_back(mItemId); 503 } 504 } else { 505 ALOGW("ignoring unsupported ref type 0x%x", type()); 506 } 507 } 508 509 private: 510 uint32_t mItemId; 511 uint32_t mRefIdSize; 512 Vector<uint32_t> mRefs; 513 514 DISALLOW_EVIL_CONSTRUCTORS(ItemReference); 515 }; 516 517 status_t ItemReference::parse(off64_t offset, size_t size) { 518 if (size < mRefIdSize + 2) { 519 return ERROR_MALFORMED; 520 } 521 if (!source()->getUInt32Var(offset, &mItemId, mRefIdSize)) { 522 return ERROR_IO; 523 } 524 offset += mRefIdSize; 525 526 uint16_t count; 527 if (!source()->getUInt16(offset, &count)) { 528 return ERROR_IO; 529 } 530 offset += 2; 531 size -= (mRefIdSize + 2); 532 533 if (size < count * mRefIdSize) { 534 return ERROR_MALFORMED; 535 } 536 537 for (size_t i = 0; i < count; i++) { 538 uint32_t refItemId; 539 if (!source()->getUInt32Var(offset, &refItemId, mRefIdSize)) { 540 return ERROR_IO; 541 } 542 offset += mRefIdSize; 543 mRefs.push_back(refItemId); 544 ALOGV("item id %d: referencing item id %d", mItemId, refItemId); 545 } 546 547 return OK; 548 } 549 550 struct IrefBox : public FullBox { 551 IrefBox(const sp<DataSource> source, Vector<sp<ItemReference> > *itemRefs) : 552 FullBox(source, FOURCC('i', 'r', 'e', 'f')), mRefIdSize(0), mItemRefs(itemRefs) {} 553 554 status_t parse(off64_t offset, size_t size); 555 556 protected: 557 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override; 558 559 private: 560 uint32_t mRefIdSize; 561 Vector<sp<ItemReference> > *mItemRefs; 562 }; 563 564 status_t IrefBox::parse(off64_t offset, size_t size) { 565 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size); 566 status_t err = parseFullBoxHeader(&offset, &size); 567 if (err != OK) { 568 return err; 569 } 570 571 mRefIdSize = (version() == 0) ? 2 : 4; 572 return parseChunks(offset, size); 573 } 574 575 status_t IrefBox::onChunkData(uint32_t type, off64_t offset, size_t size) { 576 sp<ItemReference> itemRef = new ItemReference(source(), type, mRefIdSize); 577 578 status_t err = itemRef->parse(offset, size); 579 if (err != OK) { 580 return err; 581 } 582 mItemRefs->push_back(itemRef); 583 return OK; 584 } 585 586 ///////////////////////////////////////////////////////////////////// 587 // 588 // ItemProperty related boxes 589 // 590 591 struct AssociationEntry { 592 uint32_t itemId; 593 bool essential; 594 uint16_t index; 595 }; 596 597 struct ItemProperty : public RefBase { 598 ItemProperty() {} 599 600 virtual void attachTo(ImageItem &/*image*/) const { 601 ALOGW("Unrecognized property"); 602 } 603 virtual status_t parse(off64_t /*offset*/, size_t /*size*/) { 604 ALOGW("Unrecognized property"); 605 return OK; 606 } 607 608 private: 609 DISALLOW_EVIL_CONSTRUCTORS(ItemProperty); 610 }; 611 612 struct IspeBox : public FullBox, public ItemProperty { 613 IspeBox(const sp<DataSource> source) : 614 FullBox(source, FOURCC('i', 's', 'p', 'e')), mWidth(0), mHeight(0) {} 615 616 status_t parse(off64_t offset, size_t size) override; 617 618 void attachTo(ImageItem &image) const override { 619 image.width = mWidth; 620 image.height = mHeight; 621 } 622 623 private: 624 uint32_t mWidth; 625 uint32_t mHeight; 626 }; 627 628 status_t IspeBox::parse(off64_t offset, size_t size) { 629 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size); 630 631 status_t err = parseFullBoxHeader(&offset, &size); 632 if (err != OK) { 633 return err; 634 } 635 636 if (size < 8) { 637 return ERROR_MALFORMED; 638 } 639 if (!source()->getUInt32(offset, &mWidth) 640 || !source()->getUInt32(offset + 4, &mHeight)) { 641 return ERROR_IO; 642 } 643 ALOGV("property ispe: %dx%d", mWidth, mHeight); 644 645 return OK; 646 } 647 648 struct HvccBox : public Box, public ItemProperty { 649 HvccBox(const sp<DataSource> source) : 650 Box(source, FOURCC('h', 'v', 'c', 'C')) {} 651 652 status_t parse(off64_t offset, size_t size) override; 653 654 void attachTo(ImageItem &image) const override { 655 image.hvcc = mHVCC; 656 } 657 658 private: 659 sp<ABuffer> mHVCC; 660 }; 661 662 status_t HvccBox::parse(off64_t offset, size_t size) { 663 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size); 664 665 mHVCC = new ABuffer(size); 666 667 if (mHVCC->data() == NULL) { 668 ALOGE("b/28471206"); 669 return NO_MEMORY; 670 } 671 672 if (source()->readAt(offset, mHVCC->data(), size) < (ssize_t)size) { 673 return ERROR_IO; 674 } 675 676 ALOGV("property hvcC"); 677 678 return OK; 679 } 680 681 struct IrotBox : public Box, public ItemProperty { 682 IrotBox(const sp<DataSource> source) : 683 Box(source, FOURCC('i', 'r', 'o', 't')), mAngle(0) {} 684 685 status_t parse(off64_t offset, size_t size) override; 686 687 void attachTo(ImageItem &image) const override { 688 image.rotation = mAngle * 90; 689 } 690 691 private: 692 uint8_t mAngle; 693 }; 694 695 status_t IrotBox::parse(off64_t offset, size_t size) { 696 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size); 697 698 if (size < 1) { 699 return ERROR_MALFORMED; 700 } 701 if (source()->readAt(offset, &mAngle, 1) != 1) { 702 return ERROR_IO; 703 } 704 mAngle &= 0x3; 705 ALOGV("property irot: %d", mAngle); 706 707 return OK; 708 } 709 710 struct ColrBox : public Box, public ItemProperty { 711 ColrBox(const sp<DataSource> source) : 712 Box(source, FOURCC('c', 'o', 'l', 'r')) {} 713 714 status_t parse(off64_t offset, size_t size) override; 715 716 void attachTo(ImageItem &image) const override { 717 image.icc = mICCData; 718 } 719 720 private: 721 sp<ABuffer> mICCData; 722 }; 723 724 status_t ColrBox::parse(off64_t offset, size_t size) { 725 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size); 726 727 if (size < 4) { 728 return ERROR_MALFORMED; 729 } 730 uint32_t colour_type; 731 if (!source()->getUInt32(offset, &colour_type)) { 732 return ERROR_IO; 733 } 734 offset += 4; 735 size -= 4; 736 if (colour_type == FOURCC('n', 'c', 'l', 'x')) { 737 return OK; 738 } 739 if ((colour_type != FOURCC('r', 'I', 'C', 'C')) && 740 (colour_type != FOURCC('p', 'r', 'o', 'f'))) { 741 return ERROR_MALFORMED; 742 } 743 744 mICCData = new ABuffer(size); 745 if (mICCData->data() == NULL) { 746 ALOGE("b/28471206"); 747 return NO_MEMORY; 748 } 749 750 if (source()->readAt(offset, mICCData->data(), size) != (ssize_t)size) { 751 return ERROR_IO; 752 } 753 754 ALOGV("property Colr: size %zd", size); 755 return OK; 756 } 757 758 struct IpmaBox : public FullBox { 759 IpmaBox(const sp<DataSource> source, Vector<AssociationEntry> *associations) : 760 FullBox(source, FOURCC('i', 'p', 'm', 'a')), mAssociations(associations) {} 761 762 status_t parse(off64_t offset, size_t size); 763 private: 764 Vector<AssociationEntry> *mAssociations; 765 }; 766 767 status_t IpmaBox::parse(off64_t offset, size_t size) { 768 status_t err = parseFullBoxHeader(&offset, &size); 769 if (err != OK) { 770 return err; 771 } 772 773 if (size < 4) { 774 return ERROR_MALFORMED; 775 } 776 uint32_t entryCount; 777 if (!source()->getUInt32(offset, &entryCount)) { 778 return ERROR_IO; 779 } 780 offset += 4; 781 size -= 4; 782 783 for (size_t k = 0; k < entryCount; ++k) { 784 uint32_t itemId = 0; 785 size_t itemIdSize = (version() < 1) ? 2 : 4; 786 787 if (size < itemIdSize + 1) { 788 return ERROR_MALFORMED; 789 } 790 791 if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) { 792 return ERROR_IO; 793 } 794 offset += itemIdSize; 795 size -= itemIdSize; 796 797 uint8_t associationCount; 798 if (!source()->readAt(offset, &associationCount, 1)) { 799 return ERROR_IO; 800 } 801 offset++; 802 size--; 803 804 for (size_t i = 0; i < associationCount; ++i) { 805 size_t propIndexSize = (flags() & 1) ? 2 : 1; 806 if (size < propIndexSize) { 807 return ERROR_MALFORMED; 808 } 809 uint16_t propIndex; 810 if (!source()->getUInt16Var(offset, &propIndex, propIndexSize)) { 811 return ERROR_IO; 812 } 813 offset += propIndexSize; 814 size -= propIndexSize; 815 uint16_t bitmask = (1 << (8 * propIndexSize - 1)); 816 AssociationEntry entry = { 817 .itemId = itemId, 818 .essential = !!(propIndex & bitmask), 819 .index = (uint16_t) (propIndex & ~bitmask) 820 }; 821 822 ALOGV("item id %d associated to property %d (essential %d)", 823 itemId, entry.index, entry.essential); 824 825 mAssociations->push_back(entry); 826 } 827 } 828 829 return OK; 830 } 831 832 struct IpcoBox : public Box { 833 IpcoBox(const sp<DataSource> source, Vector<sp<ItemProperty> > *properties) : 834 Box(source, FOURCC('i', 'p', 'c', 'o')), mItemProperties(properties) {} 835 836 status_t parse(off64_t offset, size_t size); 837 protected: 838 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override; 839 840 private: 841 Vector<sp<ItemProperty> > *mItemProperties; 842 }; 843 844 status_t IpcoBox::parse(off64_t offset, size_t size) { 845 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size); 846 // push dummy as the index is 1-based 847 mItemProperties->push_back(new ItemProperty()); 848 return parseChunks(offset, size); 849 } 850 851 status_t IpcoBox::onChunkData(uint32_t type, off64_t offset, size_t size) { 852 sp<ItemProperty> itemProperty; 853 switch(type) { 854 case FOURCC('h', 'v', 'c', 'C'): 855 { 856 itemProperty = new HvccBox(source()); 857 break; 858 } 859 case FOURCC('i', 's', 'p', 'e'): 860 { 861 itemProperty = new IspeBox(source()); 862 break; 863 } 864 case FOURCC('i', 'r', 'o', 't'): 865 { 866 itemProperty = new IrotBox(source()); 867 break; 868 } 869 case FOURCC('c', 'o', 'l', 'r'): 870 { 871 itemProperty = new ColrBox(source()); 872 break; 873 } 874 default: 875 { 876 // push dummy to maintain correct item property index 877 itemProperty = new ItemProperty(); 878 break; 879 } 880 } 881 status_t err = itemProperty->parse(offset, size); 882 if (err != OK) { 883 return err; 884 } 885 mItemProperties->push_back(itemProperty); 886 return OK; 887 } 888 889 struct IprpBox : public Box { 890 IprpBox(const sp<DataSource> source, 891 Vector<sp<ItemProperty> > *properties, 892 Vector<AssociationEntry> *associations) : 893 Box(source, FOURCC('i', 'p', 'r', 'p')), 894 mProperties(properties), mAssociations(associations) {} 895 896 status_t parse(off64_t offset, size_t size); 897 protected: 898 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override; 899 900 private: 901 Vector<sp<ItemProperty> > *mProperties; 902 Vector<AssociationEntry> *mAssociations; 903 }; 904 905 status_t IprpBox::parse(off64_t offset, size_t size) { 906 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size); 907 908 status_t err = parseChunks(offset, size); 909 if (err != OK) { 910 return err; 911 } 912 return OK; 913 } 914 915 status_t IprpBox::onChunkData(uint32_t type, off64_t offset, size_t size) { 916 switch(type) { 917 case FOURCC('i', 'p', 'c', 'o'): 918 { 919 IpcoBox ipcoBox(source(), mProperties); 920 return ipcoBox.parse(offset, size); 921 } 922 case FOURCC('i', 'p', 'm', 'a'): 923 { 924 IpmaBox ipmaBox(source(), mAssociations); 925 return ipmaBox.parse(offset, size); 926 } 927 default: 928 { 929 ALOGW("Unrecognized box."); 930 break; 931 } 932 } 933 return OK; 934 } 935 936 ///////////////////////////////////////////////////////////////////// 937 // 938 // ItemInfo related boxes 939 // 940 struct ItemInfo { 941 uint32_t itemId; 942 uint32_t itemType; 943 }; 944 945 struct InfeBox : public FullBox { 946 InfeBox(const sp<DataSource> source) : 947 FullBox(source, FOURCC('i', 'n', 'f', 'e')) {} 948 949 status_t parse(off64_t offset, size_t size, ItemInfo *itemInfo); 950 951 private: 952 bool parseNullTerminatedString(off64_t *offset, size_t *size, String8 *out); 953 }; 954 955 bool InfeBox::parseNullTerminatedString( 956 off64_t *offset, size_t *size, String8 *out) { 957 char tmp[256]; 958 size_t len = 0; 959 off64_t newOffset = *offset; 960 off64_t stopOffset = *offset + *size; 961 while (newOffset < stopOffset) { 962 if (!source()->readAt(newOffset++, &tmp[len], 1)) { 963 return false; 964 } 965 if (tmp[len] == 0) { 966 out->append(tmp, len); 967 968 *offset = newOffset; 969 *size = stopOffset - newOffset; 970 971 return true; 972 } 973 if (++len >= sizeof(tmp)) { 974 out->append(tmp, len); 975 len = 0; 976 } 977 } 978 return false; 979 } 980 981 status_t InfeBox::parse(off64_t offset, size_t size, ItemInfo *itemInfo) { 982 status_t err = parseFullBoxHeader(&offset, &size); 983 if (err != OK) { 984 return err; 985 } 986 987 if (version() == 0 || version() == 1) { 988 if (size < 4) { 989 return ERROR_MALFORMED; 990 } 991 uint16_t item_id; 992 if (!source()->getUInt16(offset, &item_id)) { 993 return ERROR_IO; 994 } 995 ALOGV("item_id %d", item_id); 996 uint16_t item_protection_index; 997 if (!source()->getUInt16(offset + 2, &item_protection_index)) { 998 return ERROR_IO; 999 } 1000 offset += 4; 1001 size -= 4; 1002 1003 String8 item_name; 1004 if (!parseNullTerminatedString(&offset, &size, &item_name)) { 1005 return ERROR_MALFORMED; 1006 } 1007 1008 String8 content_type; 1009 if (!parseNullTerminatedString(&offset, &size, &content_type)) { 1010 return ERROR_MALFORMED; 1011 } 1012 1013 String8 content_encoding; 1014 if (!parseNullTerminatedString(&offset, &size, &content_encoding)) { 1015 return ERROR_MALFORMED; 1016 } 1017 1018 if (version() == 1) { 1019 uint32_t extension_type; 1020 if (!source()->getUInt32(offset, &extension_type)) { 1021 return ERROR_IO; 1022 } 1023 offset++; 1024 size--; 1025 // TODO: handle this case 1026 } 1027 } else { // version >= 2 1028 uint32_t item_id; 1029 size_t itemIdSize = (version() == 2) ? 2 : 4; 1030 if (size < itemIdSize + 6) { 1031 return ERROR_MALFORMED; 1032 } 1033 if (!source()->getUInt32Var(offset, &item_id, itemIdSize)) { 1034 return ERROR_IO; 1035 } 1036 ALOGV("item_id %d", item_id); 1037 offset += itemIdSize; 1038 uint16_t item_protection_index; 1039 if (!source()->getUInt16(offset, &item_protection_index)) { 1040 return ERROR_IO; 1041 } 1042 ALOGV("item_protection_index %d", item_protection_index); 1043 offset += 2; 1044 uint32_t item_type; 1045 if (!source()->getUInt32(offset, &item_type)) { 1046 return ERROR_IO; 1047 } 1048 1049 itemInfo->itemId = item_id; 1050 itemInfo->itemType = item_type; 1051 1052 char itemTypeString[5]; 1053 MakeFourCCString(item_type, itemTypeString); 1054 ALOGV("item_type %s", itemTypeString); 1055 offset += 4; 1056 size -= itemIdSize + 6; 1057 1058 String8 item_name; 1059 if (!parseNullTerminatedString(&offset, &size, &item_name)) { 1060 return ERROR_MALFORMED; 1061 } 1062 ALOGV("item_name %s", item_name.c_str()); 1063 1064 if (item_type == FOURCC('m', 'i', 'm', 'e')) { 1065 String8 content_type; 1066 if (!parseNullTerminatedString(&offset, &size, &content_type)) { 1067 return ERROR_MALFORMED; 1068 } 1069 1070 String8 content_encoding; 1071 if (!parseNullTerminatedString(&offset, &size, &content_encoding)) { 1072 return ERROR_MALFORMED; 1073 } 1074 } else if (item_type == FOURCC('u', 'r', 'i', ' ')) { 1075 String8 item_uri_type; 1076 if (!parseNullTerminatedString(&offset, &size, &item_uri_type)) { 1077 return ERROR_MALFORMED; 1078 } 1079 } 1080 } 1081 return OK; 1082 } 1083 1084 struct IinfBox : public FullBox { 1085 IinfBox(const sp<DataSource> source, Vector<ItemInfo> *itemInfos) : 1086 FullBox(source, FOURCC('i', 'i', 'n', 'f')), 1087 mItemInfos(itemInfos), mHasGrids(false) {} 1088 1089 status_t parse(off64_t offset, size_t size); 1090 1091 bool hasGrids() { return mHasGrids; } 1092 1093 protected: 1094 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override; 1095 1096 private: 1097 Vector<ItemInfo> *mItemInfos; 1098 bool mHasGrids; 1099 }; 1100 1101 status_t IinfBox::parse(off64_t offset, size_t size) { 1102 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size); 1103 1104 status_t err = parseFullBoxHeader(&offset, &size); 1105 if (err != OK) { 1106 return err; 1107 } 1108 1109 size_t entryCountSize = version() == 0 ? 2 : 4; 1110 if (size < entryCountSize) { 1111 return ERROR_MALFORMED; 1112 } 1113 uint32_t entry_count; 1114 if (!source()->getUInt32Var(offset, &entry_count, entryCountSize)) { 1115 return ERROR_IO; 1116 } 1117 ALOGV("entry_count %d", entry_count); 1118 1119 off64_t stopOffset = offset + size; 1120 offset += entryCountSize; 1121 for (size_t i = 0; i < entry_count && offset < stopOffset; i++) { 1122 ALOGV("entry %zu", i); 1123 status_t err = parseChunk(&offset); 1124 if (err != OK) { 1125 return err; 1126 } 1127 } 1128 if (offset != stopOffset) { 1129 return ERROR_MALFORMED; 1130 } 1131 1132 return OK; 1133 } 1134 1135 status_t IinfBox::onChunkData(uint32_t type, off64_t offset, size_t size) { 1136 if (type != FOURCC('i', 'n', 'f', 'e')) { 1137 return OK; 1138 } 1139 1140 InfeBox infeBox(source()); 1141 ItemInfo itemInfo; 1142 status_t err = infeBox.parse(offset, size, &itemInfo); 1143 if (err != OK) { 1144 return err; 1145 } 1146 mItemInfos->push_back(itemInfo); 1147 mHasGrids |= (itemInfo.itemType == FOURCC('g', 'r', 'i', 'd')); 1148 return OK; 1149 } 1150 1151 ////////////////////////////////////////////////////////////////// 1152 1153 ItemTable::ItemTable(const sp<DataSource> &source) 1154 : mDataSource(source), 1155 mPrimaryItemId(0), 1156 mIdatOffset(0), 1157 mIdatSize(0), 1158 mImageItemsValid(false), 1159 mCurrentImageIndex(0) { 1160 mRequiredBoxes.insert('iprp'); 1161 mRequiredBoxes.insert('iloc'); 1162 mRequiredBoxes.insert('pitm'); 1163 mRequiredBoxes.insert('iinf'); 1164 } 1165 1166 ItemTable::~ItemTable() {} 1167 1168 status_t ItemTable::parse(uint32_t type, off64_t data_offset, size_t chunk_data_size) { 1169 switch(type) { 1170 case FOURCC('i', 'l', 'o', 'c'): 1171 { 1172 return parseIlocBox(data_offset, chunk_data_size); 1173 } 1174 case FOURCC('i', 'i', 'n', 'f'): 1175 { 1176 return parseIinfBox(data_offset, chunk_data_size); 1177 } 1178 case FOURCC('i', 'p', 'r', 'p'): 1179 { 1180 return parseIprpBox(data_offset, chunk_data_size); 1181 } 1182 case FOURCC('p', 'i', 't', 'm'): 1183 { 1184 return parsePitmBox(data_offset, chunk_data_size); 1185 } 1186 case FOURCC('i', 'd', 'a', 't'): 1187 { 1188 return parseIdatBox(data_offset, chunk_data_size); 1189 } 1190 case FOURCC('i', 'r', 'e', 'f'): 1191 { 1192 return parseIrefBox(data_offset, chunk_data_size); 1193 } 1194 case FOURCC('i', 'p', 'r', 'o'): 1195 { 1196 ALOGW("ipro box not supported!"); 1197 break; 1198 } 1199 default: 1200 { 1201 ALOGW("unrecognized box type: 0x%x", type); 1202 break; 1203 } 1204 } 1205 return ERROR_UNSUPPORTED; 1206 } 1207 1208 status_t ItemTable::parseIlocBox(off64_t offset, size_t size) { 1209 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size); 1210 1211 IlocBox ilocBox(mDataSource, &mItemLocs); 1212 status_t err = ilocBox.parse(offset, size); 1213 if (err != OK) { 1214 return err; 1215 } 1216 1217 if (ilocBox.hasConstructMethod1()) { 1218 mRequiredBoxes.insert('idat'); 1219 } 1220 1221 return buildImageItemsIfPossible('iloc'); 1222 } 1223 1224 status_t ItemTable::parseIinfBox(off64_t offset, size_t size) { 1225 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size); 1226 1227 IinfBox iinfBox(mDataSource, &mItemInfos); 1228 status_t err = iinfBox.parse(offset, size); 1229 if (err != OK) { 1230 return err; 1231 } 1232 1233 if (iinfBox.hasGrids()) { 1234 mRequiredBoxes.insert('iref'); 1235 } 1236 1237 return buildImageItemsIfPossible('iinf'); 1238 } 1239 1240 status_t ItemTable::parsePitmBox(off64_t offset, size_t size) { 1241 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size); 1242 1243 PitmBox pitmBox(mDataSource); 1244 status_t err = pitmBox.parse(offset, size, &mPrimaryItemId); 1245 if (err != OK) { 1246 return err; 1247 } 1248 1249 return buildImageItemsIfPossible('pitm'); 1250 } 1251 1252 status_t ItemTable::parseIprpBox(off64_t offset, size_t size) { 1253 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size); 1254 1255 IprpBox iprpBox(mDataSource, &mItemProperties, &mAssociations); 1256 status_t err = iprpBox.parse(offset, size); 1257 if (err != OK) { 1258 return err; 1259 } 1260 1261 return buildImageItemsIfPossible('iprp'); 1262 } 1263 1264 status_t ItemTable::parseIdatBox(off64_t offset, size_t size) { 1265 ALOGV("%s: idat offset %lld, size %zu", __FUNCTION__, (long long)offset, size); 1266 1267 // only remember the offset and size of idat box for later use 1268 mIdatOffset = offset; 1269 mIdatSize = size; 1270 1271 return buildImageItemsIfPossible('idat'); 1272 } 1273 1274 status_t ItemTable::parseIrefBox(off64_t offset, size_t size) { 1275 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size); 1276 1277 IrefBox irefBox(mDataSource, &mItemReferences); 1278 status_t err = irefBox.parse(offset, size); 1279 if (err != OK) { 1280 return err; 1281 } 1282 1283 return buildImageItemsIfPossible('iref'); 1284 } 1285 1286 status_t ItemTable::buildImageItemsIfPossible(uint32_t type) { 1287 if (mImageItemsValid) { 1288 return OK; 1289 } 1290 1291 mBoxesSeen.insert(type); 1292 1293 // need at least 'iprp', 'iloc', 'pitm', 'iinf'; 1294 // need 'idat' if any items used construction_method of 2; 1295 // need 'iref' if there are grids. 1296 if (!std::includes( 1297 mBoxesSeen.begin(), mBoxesSeen.end(), 1298 mRequiredBoxes.begin(), mRequiredBoxes.end())) { 1299 return OK; 1300 } 1301 1302 ALOGV("building image table..."); 1303 1304 for (size_t i = 0; i < mItemInfos.size(); i++) { 1305 const ItemInfo &info = mItemInfos[i]; 1306 1307 1308 // ignore non-image items 1309 if (info.itemType != FOURCC('g', 'r', 'i', 'd') && 1310 info.itemType != FOURCC('h', 'v', 'c', '1')) { 1311 continue; 1312 } 1313 1314 ssize_t imageIndex = mItemIdToImageMap.indexOfKey(info.itemId); 1315 if (imageIndex >= 0) { 1316 ALOGW("ignoring duplicate image item id %d", info.itemId); 1317 continue; 1318 } 1319 1320 ssize_t ilocIndex = mItemLocs.indexOfKey(info.itemId); 1321 if (ilocIndex < 0) { 1322 ALOGE("iloc missing for image item id %d", info.itemId); 1323 continue; 1324 } 1325 const ItemLoc &iloc = mItemLocs[ilocIndex]; 1326 1327 off64_t offset; 1328 size_t size; 1329 if (iloc.getLoc(&offset, &size, mIdatOffset, mIdatSize) != OK) { 1330 return ERROR_MALFORMED; 1331 } 1332 1333 ImageItem image(info.itemType); 1334 1335 ALOGV("adding %s: itemId %d", image.isGrid() ? "grid" : "image", info.itemId); 1336 1337 if (image.isGrid()) { 1338 if (size > 12) { 1339 return ERROR_MALFORMED; 1340 } 1341 uint8_t buf[12]; 1342 if (!mDataSource->readAt(offset, buf, size)) { 1343 return ERROR_IO; 1344 } 1345 1346 image.rows = buf[2] + 1; 1347 image.columns = buf[3] + 1; 1348 1349 ALOGV("rows %d, columans %d", image.rows, image.columns); 1350 } else { 1351 image.offset = offset; 1352 image.size = size; 1353 } 1354 mItemIdToImageMap.add(info.itemId, image); 1355 } 1356 1357 for (size_t i = 0; i < mAssociations.size(); i++) { 1358 attachProperty(mAssociations[i]); 1359 } 1360 1361 for (size_t i = 0; i < mItemReferences.size(); i++) { 1362 mItemReferences[i]->apply(mItemIdToImageMap); 1363 } 1364 1365 mImageItemsValid = true; 1366 return OK; 1367 } 1368 1369 void ItemTable::attachProperty(const AssociationEntry &association) { 1370 ssize_t imageIndex = mItemIdToImageMap.indexOfKey(association.itemId); 1371 1372 // ignore non-image items 1373 if (imageIndex < 0) { 1374 return; 1375 } 1376 1377 uint16_t propertyIndex = association.index; 1378 if (propertyIndex >= mItemProperties.size()) { 1379 ALOGW("Ignoring invalid property index %d", propertyIndex); 1380 return; 1381 } 1382 1383 ALOGV("attach property %d to item id %d)", 1384 propertyIndex, association.itemId); 1385 1386 mItemProperties[propertyIndex]->attachTo( 1387 mItemIdToImageMap.editValueAt(imageIndex)); 1388 } 1389 1390 sp<MetaData> ItemTable::getImageMeta() { 1391 if (!mImageItemsValid) { 1392 return NULL; 1393 } 1394 1395 ssize_t imageIndex = mItemIdToImageMap.indexOfKey(mPrimaryItemId); 1396 if (imageIndex < 0) { 1397 ALOGE("Primary item id %d not found!", mPrimaryItemId); 1398 return NULL; 1399 } 1400 1401 ALOGV("primary image index %zu", imageIndex); 1402 1403 const ImageItem *image = &mItemIdToImageMap[imageIndex]; 1404 1405 sp<MetaData> meta = new MetaData; 1406 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_HEVC); 1407 1408 ALOGV("setting image size %dx%d", image->width, image->height); 1409 meta->setInt32(kKeyWidth, image->width); 1410 meta->setInt32(kKeyHeight, image->height); 1411 if (image->rotation != 0) { 1412 // Rotation angle in HEIF is CCW, convert to CW here to be 1413 // consistent with the other media formats. 1414 switch(image->rotation) { 1415 case 90: meta->setInt32(kKeyRotation, 270); break; 1416 case 180: meta->setInt32(kKeyRotation, 180); break; 1417 case 270: meta->setInt32(kKeyRotation, 90); break; 1418 default: break; // don't set if invalid 1419 } 1420 } 1421 meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5); 1422 1423 if (!image->thumbnails.empty()) { 1424 ssize_t thumbnailIndex = mItemIdToImageMap.indexOfKey(image->thumbnails[0]); 1425 if (thumbnailIndex >= 0) { 1426 const ImageItem &thumbnail = mItemIdToImageMap[thumbnailIndex]; 1427 1428 meta->setInt32(kKeyThumbnailWidth, thumbnail.width); 1429 meta->setInt32(kKeyThumbnailHeight, thumbnail.height); 1430 meta->setData(kKeyThumbnailHVCC, kTypeHVCC, 1431 thumbnail.hvcc->data(), thumbnail.hvcc->size()); 1432 ALOGV("thumbnail meta: %dx%d, index %zd", 1433 thumbnail.width, thumbnail.height, thumbnailIndex); 1434 } else { 1435 ALOGW("Referenced thumbnail does not exist!"); 1436 } 1437 } 1438 1439 if (image->isGrid()) { 1440 ssize_t tileIndex = mItemIdToImageMap.indexOfKey(image->dimgRefs[0]); 1441 if (tileIndex < 0) { 1442 return NULL; 1443 } 1444 // when there are tiles, (kKeyWidth, kKeyHeight) is the full tiled area, 1445 // and (kKeyDisplayWidth, kKeyDisplayHeight) may be smaller than that. 1446 meta->setInt32(kKeyDisplayWidth, image->width); 1447 meta->setInt32(kKeyDisplayHeight, image->height); 1448 int32_t gridRows = image->rows, gridCols = image->columns; 1449 1450 // point image to the first tile for grid size and HVCC 1451 image = &mItemIdToImageMap.editValueAt(tileIndex); 1452 meta->setInt32(kKeyWidth, image->width * gridCols); 1453 meta->setInt32(kKeyHeight, image->height * gridRows); 1454 meta->setInt32(kKeyGridWidth, image->width); 1455 meta->setInt32(kKeyGridHeight, image->height); 1456 meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5); 1457 } 1458 1459 if (image->hvcc == NULL) { 1460 ALOGE("hvcc is missing!"); 1461 return NULL; 1462 } 1463 meta->setData(kKeyHVCC, kTypeHVCC, image->hvcc->data(), image->hvcc->size()); 1464 1465 if (image->icc != NULL) { 1466 meta->setData(kKeyIccProfile, 0, image->icc->data(), image->icc->size()); 1467 } 1468 return meta; 1469 } 1470 1471 uint32_t ItemTable::countImages() const { 1472 return mImageItemsValid ? mItemIdToImageMap.size() : 0; 1473 } 1474 1475 status_t ItemTable::findPrimaryImage(uint32_t *imageIndex) { 1476 if (!mImageItemsValid) { 1477 return INVALID_OPERATION; 1478 } 1479 1480 ssize_t index = mItemIdToImageMap.indexOfKey(mPrimaryItemId); 1481 if (index < 0) { 1482 return ERROR_MALFORMED; 1483 } 1484 1485 *imageIndex = index; 1486 return OK; 1487 } 1488 1489 status_t ItemTable::findThumbnail(uint32_t *imageIndex) { 1490 if (!mImageItemsValid) { 1491 return INVALID_OPERATION; 1492 } 1493 1494 ssize_t primaryIndex = mItemIdToImageMap.indexOfKey(mPrimaryItemId); 1495 if (primaryIndex < 0) { 1496 ALOGE("Primary item id %d not found!", mPrimaryItemId); 1497 return ERROR_MALFORMED; 1498 } 1499 1500 const ImageItem &primaryImage = mItemIdToImageMap[primaryIndex]; 1501 if (primaryImage.thumbnails.empty()) { 1502 ALOGW("Using primary in place of thumbnail."); 1503 *imageIndex = primaryIndex; 1504 return OK; 1505 } 1506 1507 ssize_t thumbnailIndex = mItemIdToImageMap.indexOfKey( 1508 primaryImage.thumbnails[0]); 1509 if (thumbnailIndex < 0) { 1510 ALOGE("Thumbnail item id %d not found!", primaryImage.thumbnails[0]); 1511 return ERROR_MALFORMED; 1512 } 1513 1514 *imageIndex = thumbnailIndex; 1515 return OK; 1516 } 1517 1518 status_t ItemTable::getImageOffsetAndSize( 1519 uint32_t *imageIndex, off64_t *offset, size_t *size) { 1520 if (!mImageItemsValid) { 1521 return INVALID_OPERATION; 1522 } 1523 1524 if (imageIndex != NULL) { 1525 if (*imageIndex >= mItemIdToImageMap.size()) { 1526 ALOGE("Bad image index!"); 1527 return BAD_VALUE; 1528 } 1529 mCurrentImageIndex = *imageIndex; 1530 } 1531 1532 ImageItem &image = mItemIdToImageMap.editValueAt(mCurrentImageIndex); 1533 if (image.isGrid()) { 1534 uint32_t tileItemId; 1535 status_t err = image.getNextTileItemId(&tileItemId, imageIndex != NULL); 1536 if (err != OK) { 1537 return err; 1538 } 1539 ssize_t tileImageIndex = mItemIdToImageMap.indexOfKey(tileItemId); 1540 if (tileImageIndex < 0) { 1541 return ERROR_END_OF_STREAM; 1542 } 1543 *offset = mItemIdToImageMap[tileImageIndex].offset; 1544 *size = mItemIdToImageMap[tileImageIndex].size; 1545 } else { 1546 if (imageIndex == NULL) { 1547 // For single images, we only allow it to be read once, after that 1548 // it's EOS. New image index must be requested each time. 1549 return ERROR_END_OF_STREAM; 1550 } 1551 *offset = mItemIdToImageMap[mCurrentImageIndex].offset; 1552 *size = mItemIdToImageMap[mCurrentImageIndex].size; 1553 } 1554 1555 return OK; 1556 } 1557 1558 } // namespace heif 1559 1560 } // namespace android 1561