blob: 2599c2cb56860fbc20c5dd9b18a2f4e3bdb9914d [file] [log] [blame]
Chong Zhangb51ca282017-07-26 16:25:28 -07001/*
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
Chong Zhangb51ca282017-07-26 16:25:28 -070017//#define LOG_NDEBUG 0
Chong Zhangee079fe2017-08-23 13:51:17 -070018#define LOG_TAG "ItemTable"
Chong Zhangb51ca282017-07-26 16:25:28 -070019
Chong Zhang866f0f02019-02-01 14:19:37 -080020#include <unordered_set>
21
Marco Nelissen75226172016-11-16 14:10:52 -080022#include <ItemTable.h>
Marco Nelissencec44d02018-06-17 22:21:09 -070023#include <media/MediaExtractorPluginApi.h>
24#include <media/MediaExtractorPluginHelper.h>
Chong Zhangb51ca282017-07-26 16:25:28 -070025#include <media/stagefright/MetaData.h>
26#include <media/stagefright/MediaErrors.h>
Chong Zhangb51ca282017-07-26 16:25:28 -070027#include <media/stagefright/foundation/ABuffer.h>
Dongwon Kang60761282017-10-09 11:16:48 -070028#include <media/stagefright/foundation/ByteUtils.h>
Chong Zhangb51ca282017-07-26 16:25:28 -070029#include <media/stagefright/foundation/hexdump.h>
Dongwon Kange7a8a632017-10-09 14:52:58 -070030#include <media/stagefright/foundation/MediaDefs.h>
Chong Zhangb51ca282017-07-26 16:25:28 -070031#include <utils/Log.h>
32
33namespace android {
34
35namespace heif {
36
37/////////////////////////////////////////////////////////////////////
38//
39// struct to keep track of one image item
40//
41
42struct ImageItem {
43 friend struct ItemReference;
44 friend struct ItemProperty;
45
Chong Zhangd3e0d862017-10-03 13:17:13 -070046 ImageItem() : ImageItem(0, 0, false) {}
47 ImageItem(uint32_t _type, uint32_t _id, bool _hidden) :
48 type(_type), itemId(_id), hidden(_hidden),
Chong Zhangb51ca282017-07-26 16:25:28 -070049 rows(0), columns(0), width(0), height(0), rotation(0),
50 offset(0), size(0), nextTileIndex(0) {}
51
52 bool isGrid() const {
Marco Nelissen51087de2019-01-22 15:39:07 -080053 return type == FOURCC("grid");
Chong Zhangb51ca282017-07-26 16:25:28 -070054 }
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;
Chong Zhangd3e0d862017-10-03 13:17:13 -070068 uint32_t itemId;
69 bool hidden;
Chong Zhangb51ca282017-07-26 16:25:28 -070070 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;
Chong Zhang01a76012018-03-14 18:19:49 -070082 Vector<uint32_t> cdscRefs;
Chong Zhangb51ca282017-07-26 16:25:28 -070083 size_t nextTileIndex;
84};
85
Chong Zhang01a76012018-03-14 18:19:49 -070086struct ExifItem {
87 off64_t offset;
88 size_t size;
89};
Chong Zhangb51ca282017-07-26 16:25:28 -070090
91/////////////////////////////////////////////////////////////////////
92//
93// ISO boxes
94//
95
96struct Box {
97protected:
Marco Nelissencec44d02018-06-17 22:21:09 -070098 Box(DataSourceHelper *source, uint32_t type) :
Chong Zhangb51ca282017-07-26 16:25:28 -070099 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
Marco Nelissencec44d02018-06-17 22:21:09 -0700110 inline DataSourceHelper *source() const { return mDataSource; }
Chong Zhangb51ca282017-07-26 16:25:28 -0700111
112 status_t parseChunk(off64_t *offset);
113
114 status_t parseChunks(off64_t offset, size_t size);
115
116private:
Marco Nelissencec44d02018-06-17 22:21:09 -0700117 DataSourceHelper *mDataSource;
Chong Zhangb51ca282017-07-26 16:25:28 -0700118 uint32_t mType;
119};
120
121status_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
174status_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
190struct FullBox : public Box {
191protected:
Marco Nelissencec44d02018-06-17 22:21:09 -0700192 FullBox(DataSourceHelper *source, uint32_t type) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700193 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
201private:
202 uint8_t mVersion;
203 uint32_t mFlags;
204};
205
206status_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
226struct PitmBox : public FullBox {
Marco Nelissencec44d02018-06-17 22:21:09 -0700227 PitmBox(DataSourceHelper *source) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800228 FullBox(source, FOURCC("pitm")) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700229
230 status_t parse(off64_t offset, size_t size, uint32_t *primaryItemId);
231};
232
233status_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
259struct ExtentEntry {
260 uint64_t extentIndex;
261 uint64_t extentOffset;
262 uint64_t extentLength;
263};
264
265struct 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
306struct IlocBox : public FullBox {
Marco Nelissencec44d02018-06-17 22:21:09 -0700307 IlocBox(DataSourceHelper *source, KeyedVector<uint32_t, ItemLoc> *itemLocs) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800308 FullBox(source, FOURCC("iloc")),
Chong Zhangb51ca282017-07-26 16:25:28 -0700309 mItemLocs(itemLocs), mHasConstructMethod1(false) {}
310
311 status_t parse(off64_t offset, size_t size);
312
313 bool hasConstructMethod1() { return mHasConstructMethod1; }
314
315private:
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
323status_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
Dongwon Kang69b6ab32019-05-08 17:11:39 -0700429 if (extent_count > 1) {
430 return ERROR_UNSUPPORTED;
Chong Zhangb51ca282017-07-26 16:25:28 -0700431 }
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
474struct ItemReference : public Box, public RefBase {
Marco Nelissencec44d02018-06-17 22:21:09 -0700475 ItemReference(DataSourceHelper *source, uint32_t type, uint32_t itemIdSize) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700476 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
Chong Zhang01a76012018-03-14 18:19:49 -0700482 void apply(
483 KeyedVector<uint32_t, ImageItem> &itemIdToItemMap,
484 KeyedVector<uint32_t, ExifItem> &itemIdToExifMap) const;
Chong Zhangb51ca282017-07-26 16:25:28 -0700485
486private:
487 uint32_t mItemId;
488 uint32_t mRefIdSize;
489 Vector<uint32_t> mRefs;
490
491 DISALLOW_EVIL_CONSTRUCTORS(ItemReference);
492};
493
Chong Zhang01a76012018-03-14 18:19:49 -0700494void ItemReference::apply(
495 KeyedVector<uint32_t, ImageItem> &itemIdToItemMap,
496 KeyedVector<uint32_t, ExifItem> &itemIdToExifMap) const {
Chong Zhangecd08132017-10-05 16:09:29 -0700497 ALOGV("attach reference type 0x%x to item id %d)", type(), mItemId);
498
Chong Zhang01a76012018-03-14 18:19:49 -0700499 switch(type()) {
Marco Nelissen51087de2019-01-22 15:39:07 -0800500 case FOURCC("dimg"): {
Chong Zhang01a76012018-03-14 18:19:49 -0700501 ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
502
503 // ignore non-image items
504 if (itemIndex < 0) {
505 return;
506 }
507
Chong Zhangecd08132017-10-05 16:09:29 -0700508 ImageItem &derivedImage = itemIdToItemMap.editValueAt(itemIndex);
509 if (!derivedImage.dimgRefs.empty()) {
Chong Zhang99745d12018-05-15 09:50:52 -0700510 ALOGW("dimgRefs not clean!");
Chong Zhangecd08132017-10-05 16:09:29 -0700511 }
512 derivedImage.dimgRefs.appendVector(mRefs);
Chong Zhangd3e0d862017-10-03 13:17:13 -0700513
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 }
Chong Zhang01a76012018-03-14 18:19:49 -0700526 break;
527 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800528 case FOURCC("thmb"): {
Chong Zhang01a76012018-03-14 18:19:49 -0700529 ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
530
531 // ignore non-image items
532 if (itemIndex < 0) {
533 return;
534 }
535
Chong Zhangd3e0d862017-10-03 13:17:13 -0700536 // 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
Chong Zhangecd08132017-10-05 16:09:29 -0700541 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);
Chong Zhangb3ca2592020-07-27 15:31:35 -0700549 ImageItem &imageItem = itemIdToItemMap.editValueAt(itemIndex);
550 if (!imageItem.thumbnails.empty()) {
Chong Zhangecd08132017-10-05 16:09:29 -0700551 ALOGW("already has thumbnails!");
552 }
Chong Zhangb3ca2592020-07-27 15:31:35 -0700553 imageItem.thumbnails.push_back(mItemId);
Chong Zhangecd08132017-10-05 16:09:29 -0700554 }
Chong Zhang01a76012018-03-14 18:19:49 -0700555 break;
556 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800557 case FOURCC("cdsc"): {
Chong Zhang01a76012018-03-14 18:19:49 -0700558 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 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800578 case FOURCC("auxl"): {
Chong Zhang01a76012018-03-14 18:19:49 -0700579 ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
580
581 // ignore non-image items
582 if (itemIndex < 0) {
583 return;
584 }
585
Chong Zhangd3e0d862017-10-03 13:17:13 -0700586 // mark auxiliary image as hidden
587 ImageItem &auxImage = itemIdToItemMap.editValueAt(itemIndex);
588 auxImage.hidden = true;
Chong Zhang01a76012018-03-14 18:19:49 -0700589 break;
590 }
591 default:
Chong Zhangecd08132017-10-05 16:09:29 -0700592 ALOGW("ignoring unsupported ref type 0x%x", type());
593 }
594}
595
Chong Zhangb51ca282017-07-26 16:25:28 -0700596status_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
629struct IrefBox : public FullBox {
Marco Nelissencec44d02018-06-17 22:21:09 -0700630 IrefBox(DataSourceHelper *source, Vector<sp<ItemReference> > *itemRefs) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800631 FullBox(source, FOURCC("iref")), mRefIdSize(0), mItemRefs(itemRefs) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700632
633 status_t parse(off64_t offset, size_t size);
634
635protected:
636 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
637
638private:
639 uint32_t mRefIdSize;
640 Vector<sp<ItemReference> > *mItemRefs;
641};
642
643status_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
654status_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
670struct AssociationEntry {
671 uint32_t itemId;
672 bool essential;
673 uint16_t index;
674};
675
676struct 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
687private:
688 DISALLOW_EVIL_CONSTRUCTORS(ItemProperty);
689};
690
691struct IspeBox : public FullBox, public ItemProperty {
Marco Nelissencec44d02018-06-17 22:21:09 -0700692 IspeBox(DataSourceHelper *source) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800693 FullBox(source, FOURCC("ispe")), mWidth(0), mHeight(0) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700694
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
702private:
Chong Zhang242a9252019-12-09 16:38:39 -0800703 int32_t mWidth;
704 int32_t mHeight;
Chong Zhangb51ca282017-07-26 16:25:28 -0700705};
706
707status_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 }
Chong Zhang242a9252019-12-09 16:38:39 -0800718 if (!source()->getUInt32(offset, (uint32_t *)&mWidth)
719 || !source()->getUInt32(offset + 4, (uint32_t *)&mHeight)) {
Chong Zhangb51ca282017-07-26 16:25:28 -0700720 return ERROR_IO;
721 }
Chong Zhangb51ca282017-07-26 16:25:28 -0700722
Chong Zhang242a9252019-12-09 16:38:39 -0800723 // Validate that the dimension doesn't cause overflow on calculated max input size.
724 // Max input size is width*height*1.5, restrict width*height to 1<<29 so that
725 // we don't need to cast to int64_t when doing mults.
726 if (mWidth <= 0 || mHeight <= 0 || mWidth > (1 << 29) / mHeight) {
727 return ERROR_MALFORMED;
728 }
729
730 ALOGV("property ispe: %dx%d", mWidth, mHeight);
Chong Zhangb51ca282017-07-26 16:25:28 -0700731 return OK;
732}
733
734struct HvccBox : public Box, public ItemProperty {
Marco Nelissencec44d02018-06-17 22:21:09 -0700735 HvccBox(DataSourceHelper *source) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800736 Box(source, FOURCC("hvcC")) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700737
738 status_t parse(off64_t offset, size_t size) override;
739
740 void attachTo(ImageItem &image) const override {
741 image.hvcc = mHVCC;
742 }
743
744private:
745 sp<ABuffer> mHVCC;
746};
747
748status_t HvccBox::parse(off64_t offset, size_t size) {
749 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
750
751 mHVCC = new ABuffer(size);
752
753 if (mHVCC->data() == NULL) {
754 ALOGE("b/28471206");
755 return NO_MEMORY;
756 }
757
758 if (source()->readAt(offset, mHVCC->data(), size) < (ssize_t)size) {
759 return ERROR_IO;
760 }
761
762 ALOGV("property hvcC");
763
764 return OK;
765}
766
767struct IrotBox : public Box, public ItemProperty {
Marco Nelissencec44d02018-06-17 22:21:09 -0700768 IrotBox(DataSourceHelper *source) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800769 Box(source, FOURCC("irot")), mAngle(0) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700770
771 status_t parse(off64_t offset, size_t size) override;
772
773 void attachTo(ImageItem &image) const override {
774 image.rotation = mAngle * 90;
775 }
776
777private:
778 uint8_t mAngle;
779};
780
781status_t IrotBox::parse(off64_t offset, size_t size) {
782 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
783
784 if (size < 1) {
785 return ERROR_MALFORMED;
786 }
787 if (source()->readAt(offset, &mAngle, 1) != 1) {
788 return ERROR_IO;
789 }
790 mAngle &= 0x3;
791 ALOGV("property irot: %d", mAngle);
792
793 return OK;
794}
795
796struct ColrBox : public Box, public ItemProperty {
Marco Nelissencec44d02018-06-17 22:21:09 -0700797 ColrBox(DataSourceHelper *source) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800798 Box(source, FOURCC("colr")) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700799
800 status_t parse(off64_t offset, size_t size) override;
801
802 void attachTo(ImageItem &image) const override {
803 image.icc = mICCData;
804 }
805
806private:
807 sp<ABuffer> mICCData;
808};
809
810status_t ColrBox::parse(off64_t offset, size_t size) {
811 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
812
813 if (size < 4) {
814 return ERROR_MALFORMED;
815 }
816 uint32_t colour_type;
817 if (!source()->getUInt32(offset, &colour_type)) {
818 return ERROR_IO;
819 }
820 offset += 4;
821 size -= 4;
Marco Nelissen51087de2019-01-22 15:39:07 -0800822 if (colour_type == FOURCC("nclx")) {
Chong Zhangb51ca282017-07-26 16:25:28 -0700823 return OK;
824 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800825 if ((colour_type != FOURCC("rICC")) &&
826 (colour_type != FOURCC("prof"))) {
Chong Zhangb51ca282017-07-26 16:25:28 -0700827 return ERROR_MALFORMED;
828 }
829
830 mICCData = new ABuffer(size);
831 if (mICCData->data() == NULL) {
832 ALOGE("b/28471206");
833 return NO_MEMORY;
834 }
835
836 if (source()->readAt(offset, mICCData->data(), size) != (ssize_t)size) {
837 return ERROR_IO;
838 }
839
840 ALOGV("property Colr: size %zd", size);
841 return OK;
842}
843
844struct IpmaBox : public FullBox {
Marco Nelissencec44d02018-06-17 22:21:09 -0700845 IpmaBox(DataSourceHelper *source, Vector<AssociationEntry> *associations) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800846 FullBox(source, FOURCC("ipma")), mAssociations(associations) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700847
848 status_t parse(off64_t offset, size_t size);
849private:
850 Vector<AssociationEntry> *mAssociations;
851};
852
853status_t IpmaBox::parse(off64_t offset, size_t size) {
854 status_t err = parseFullBoxHeader(&offset, &size);
855 if (err != OK) {
856 return err;
857 }
858
859 if (size < 4) {
860 return ERROR_MALFORMED;
861 }
862 uint32_t entryCount;
863 if (!source()->getUInt32(offset, &entryCount)) {
864 return ERROR_IO;
865 }
866 offset += 4;
867 size -= 4;
868
869 for (size_t k = 0; k < entryCount; ++k) {
870 uint32_t itemId = 0;
871 size_t itemIdSize = (version() < 1) ? 2 : 4;
872
873 if (size < itemIdSize + 1) {
874 return ERROR_MALFORMED;
875 }
876
877 if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) {
878 return ERROR_IO;
879 }
880 offset += itemIdSize;
881 size -= itemIdSize;
882
883 uint8_t associationCount;
884 if (!source()->readAt(offset, &associationCount, 1)) {
885 return ERROR_IO;
886 }
887 offset++;
888 size--;
889
890 for (size_t i = 0; i < associationCount; ++i) {
891 size_t propIndexSize = (flags() & 1) ? 2 : 1;
892 if (size < propIndexSize) {
893 return ERROR_MALFORMED;
894 }
895 uint16_t propIndex;
896 if (!source()->getUInt16Var(offset, &propIndex, propIndexSize)) {
897 return ERROR_IO;
898 }
899 offset += propIndexSize;
900 size -= propIndexSize;
901 uint16_t bitmask = (1 << (8 * propIndexSize - 1));
902 AssociationEntry entry = {
903 .itemId = itemId,
904 .essential = !!(propIndex & bitmask),
905 .index = (uint16_t) (propIndex & ~bitmask)
906 };
907
908 ALOGV("item id %d associated to property %d (essential %d)",
909 itemId, entry.index, entry.essential);
910
911 mAssociations->push_back(entry);
912 }
913 }
914
915 return OK;
916}
917
918struct IpcoBox : public Box {
Marco Nelissencec44d02018-06-17 22:21:09 -0700919 IpcoBox(DataSourceHelper *source, Vector<sp<ItemProperty> > *properties) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800920 Box(source, FOURCC("ipco")), mItemProperties(properties) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700921
922 status_t parse(off64_t offset, size_t size);
923protected:
924 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
925
926private:
927 Vector<sp<ItemProperty> > *mItemProperties;
928};
929
930status_t IpcoBox::parse(off64_t offset, size_t size) {
931 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
Chong Zhangb3ca2592020-07-27 15:31:35 -0700932 // push a placeholder as the index is 1-based
Chong Zhangb51ca282017-07-26 16:25:28 -0700933 mItemProperties->push_back(new ItemProperty());
934 return parseChunks(offset, size);
935}
936
937status_t IpcoBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
938 sp<ItemProperty> itemProperty;
939 switch(type) {
Marco Nelissen51087de2019-01-22 15:39:07 -0800940 case FOURCC("hvcC"):
Chong Zhangb51ca282017-07-26 16:25:28 -0700941 {
942 itemProperty = new HvccBox(source());
943 break;
944 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800945 case FOURCC("ispe"):
Chong Zhangb51ca282017-07-26 16:25:28 -0700946 {
947 itemProperty = new IspeBox(source());
948 break;
949 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800950 case FOURCC("irot"):
Chong Zhangb51ca282017-07-26 16:25:28 -0700951 {
952 itemProperty = new IrotBox(source());
953 break;
954 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800955 case FOURCC("colr"):
Chong Zhangb51ca282017-07-26 16:25:28 -0700956 {
957 itemProperty = new ColrBox(source());
958 break;
959 }
960 default:
961 {
962 // push dummy to maintain correct item property index
963 itemProperty = new ItemProperty();
964 break;
965 }
966 }
967 status_t err = itemProperty->parse(offset, size);
968 if (err != OK) {
969 return err;
970 }
971 mItemProperties->push_back(itemProperty);
972 return OK;
973}
974
975struct IprpBox : public Box {
Marco Nelissencec44d02018-06-17 22:21:09 -0700976 IprpBox(DataSourceHelper *source,
Chong Zhangb51ca282017-07-26 16:25:28 -0700977 Vector<sp<ItemProperty> > *properties,
978 Vector<AssociationEntry> *associations) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800979 Box(source, FOURCC("iprp")),
Chong Zhangb51ca282017-07-26 16:25:28 -0700980 mProperties(properties), mAssociations(associations) {}
981
982 status_t parse(off64_t offset, size_t size);
983protected:
984 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
985
986private:
987 Vector<sp<ItemProperty> > *mProperties;
988 Vector<AssociationEntry> *mAssociations;
989};
990
991status_t IprpBox::parse(off64_t offset, size_t size) {
992 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
993
994 status_t err = parseChunks(offset, size);
995 if (err != OK) {
996 return err;
997 }
998 return OK;
999}
1000
1001status_t IprpBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
1002 switch(type) {
Marco Nelissen51087de2019-01-22 15:39:07 -08001003 case FOURCC("ipco"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001004 {
1005 IpcoBox ipcoBox(source(), mProperties);
1006 return ipcoBox.parse(offset, size);
1007 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001008 case FOURCC("ipma"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001009 {
1010 IpmaBox ipmaBox(source(), mAssociations);
1011 return ipmaBox.parse(offset, size);
1012 }
1013 default:
1014 {
1015 ALOGW("Unrecognized box.");
1016 break;
1017 }
1018 }
1019 return OK;
1020}
1021
1022/////////////////////////////////////////////////////////////////////
1023//
1024// ItemInfo related boxes
1025//
1026struct ItemInfo {
1027 uint32_t itemId;
1028 uint32_t itemType;
Chong Zhangd3e0d862017-10-03 13:17:13 -07001029 bool hidden;
Chong Zhangb51ca282017-07-26 16:25:28 -07001030};
1031
1032struct InfeBox : public FullBox {
Marco Nelissencec44d02018-06-17 22:21:09 -07001033 InfeBox(DataSourceHelper *source) :
Marco Nelissen51087de2019-01-22 15:39:07 -08001034 FullBox(source, FOURCC("infe")) {}
Chong Zhangb51ca282017-07-26 16:25:28 -07001035
1036 status_t parse(off64_t offset, size_t size, ItemInfo *itemInfo);
1037
1038private:
1039 bool parseNullTerminatedString(off64_t *offset, size_t *size, String8 *out);
1040};
1041
1042bool InfeBox::parseNullTerminatedString(
1043 off64_t *offset, size_t *size, String8 *out) {
Chong Zhang70601852018-03-08 15:23:43 -08001044 char tmp;
1045 Vector<char> buf;
1046 buf.setCapacity(256);
Chong Zhangb51ca282017-07-26 16:25:28 -07001047 off64_t newOffset = *offset;
1048 off64_t stopOffset = *offset + *size;
1049 while (newOffset < stopOffset) {
Chong Zhang70601852018-03-08 15:23:43 -08001050 if (!source()->readAt(newOffset++, &tmp, 1)) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001051 return false;
1052 }
Chong Zhang70601852018-03-08 15:23:43 -08001053 buf.push_back(tmp);
1054 if (tmp == 0) {
1055 out->setTo(buf.array());
Chong Zhangb51ca282017-07-26 16:25:28 -07001056
1057 *offset = newOffset;
1058 *size = stopOffset - newOffset;
1059
1060 return true;
1061 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001062 }
1063 return false;
1064}
1065
1066status_t InfeBox::parse(off64_t offset, size_t size, ItemInfo *itemInfo) {
1067 status_t err = parseFullBoxHeader(&offset, &size);
1068 if (err != OK) {
1069 return err;
1070 }
1071
1072 if (version() == 0 || version() == 1) {
Chong Zhangecd08132017-10-05 16:09:29 -07001073 return ERROR_UNSUPPORTED;
Chong Zhangb51ca282017-07-26 16:25:28 -07001074 } else { // version >= 2
1075 uint32_t item_id;
1076 size_t itemIdSize = (version() == 2) ? 2 : 4;
1077 if (size < itemIdSize + 6) {
1078 return ERROR_MALFORMED;
1079 }
1080 if (!source()->getUInt32Var(offset, &item_id, itemIdSize)) {
1081 return ERROR_IO;
1082 }
1083 ALOGV("item_id %d", item_id);
1084 offset += itemIdSize;
1085 uint16_t item_protection_index;
1086 if (!source()->getUInt16(offset, &item_protection_index)) {
1087 return ERROR_IO;
1088 }
1089 ALOGV("item_protection_index %d", item_protection_index);
1090 offset += 2;
1091 uint32_t item_type;
1092 if (!source()->getUInt32(offset, &item_type)) {
1093 return ERROR_IO;
1094 }
1095
1096 itemInfo->itemId = item_id;
1097 itemInfo->itemType = item_type;
Chong Zhangd3e0d862017-10-03 13:17:13 -07001098 // According to HEIF spec, (flags & 1) indicates the image is hidden
1099 // and not supposed to be displayed.
1100 itemInfo->hidden = (flags() & 1);
Chong Zhangb51ca282017-07-26 16:25:28 -07001101
1102 char itemTypeString[5];
1103 MakeFourCCString(item_type, itemTypeString);
1104 ALOGV("item_type %s", itemTypeString);
1105 offset += 4;
1106 size -= itemIdSize + 6;
1107
1108 String8 item_name;
1109 if (!parseNullTerminatedString(&offset, &size, &item_name)) {
1110 return ERROR_MALFORMED;
1111 }
1112 ALOGV("item_name %s", item_name.c_str());
1113
Marco Nelissen51087de2019-01-22 15:39:07 -08001114 if (item_type == FOURCC("mime")) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001115 String8 content_type;
1116 if (!parseNullTerminatedString(&offset, &size, &content_type)) {
1117 return ERROR_MALFORMED;
1118 }
1119
Ray Essick46c24cd2018-03-08 13:54:19 -08001120 // content_encoding is optional; can be omitted if would be empty
1121 if (size > 0) {
1122 String8 content_encoding;
1123 if (!parseNullTerminatedString(&offset, &size, &content_encoding)) {
1124 return ERROR_MALFORMED;
1125 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001126 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001127 } else if (item_type == FOURCC("uri ")) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001128 String8 item_uri_type;
1129 if (!parseNullTerminatedString(&offset, &size, &item_uri_type)) {
1130 return ERROR_MALFORMED;
1131 }
1132 }
1133 }
1134 return OK;
1135}
1136
1137struct IinfBox : public FullBox {
Marco Nelissencec44d02018-06-17 22:21:09 -07001138 IinfBox(DataSourceHelper *source, Vector<ItemInfo> *itemInfos) :
Chong Zhang866f0f02019-02-01 14:19:37 -08001139 FullBox(source, FOURCC("iinf")), mItemInfos(itemInfos) {}
Chong Zhangb51ca282017-07-26 16:25:28 -07001140
1141 status_t parse(off64_t offset, size_t size);
1142
Chong Zhang866f0f02019-02-01 14:19:37 -08001143 bool hasFourCC(uint32_t type) { return mFourCCSeen.count(type) > 0; }
Chong Zhangb51ca282017-07-26 16:25:28 -07001144
1145protected:
1146 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
1147
1148private:
1149 Vector<ItemInfo> *mItemInfos;
Chong Zhang866f0f02019-02-01 14:19:37 -08001150 std::unordered_set<uint32_t> mFourCCSeen;
Chong Zhangb51ca282017-07-26 16:25:28 -07001151};
1152
1153status_t IinfBox::parse(off64_t offset, size_t size) {
1154 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1155
1156 status_t err = parseFullBoxHeader(&offset, &size);
1157 if (err != OK) {
1158 return err;
1159 }
1160
1161 size_t entryCountSize = version() == 0 ? 2 : 4;
1162 if (size < entryCountSize) {
1163 return ERROR_MALFORMED;
1164 }
1165 uint32_t entry_count;
1166 if (!source()->getUInt32Var(offset, &entry_count, entryCountSize)) {
1167 return ERROR_IO;
1168 }
1169 ALOGV("entry_count %d", entry_count);
1170
1171 off64_t stopOffset = offset + size;
1172 offset += entryCountSize;
1173 for (size_t i = 0; i < entry_count && offset < stopOffset; i++) {
1174 ALOGV("entry %zu", i);
1175 status_t err = parseChunk(&offset);
1176 if (err != OK) {
1177 return err;
1178 }
1179 }
1180 if (offset != stopOffset) {
1181 return ERROR_MALFORMED;
1182 }
1183
1184 return OK;
1185}
1186
1187status_t IinfBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
Marco Nelissen51087de2019-01-22 15:39:07 -08001188 if (type != FOURCC("infe")) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001189 return OK;
1190 }
1191
1192 InfeBox infeBox(source());
1193 ItemInfo itemInfo;
1194 status_t err = infeBox.parse(offset, size, &itemInfo);
Chong Zhangecd08132017-10-05 16:09:29 -07001195 if (err == OK) {
1196 mItemInfos->push_back(itemInfo);
Chong Zhang866f0f02019-02-01 14:19:37 -08001197 mFourCCSeen.insert(itemInfo.itemType);
Chong Zhangb51ca282017-07-26 16:25:28 -07001198 }
Chong Zhang5518a2c2017-10-13 18:31:25 -07001199 // InfeBox parse returns ERROR_UNSUPPORTED if the box if an unsupported
1200 // version. Ignore this error as it's not fatal.
1201 return (err == ERROR_UNSUPPORTED) ? OK : err;
Chong Zhangb51ca282017-07-26 16:25:28 -07001202}
1203
1204//////////////////////////////////////////////////////////////////
1205
Marco Nelissencec44d02018-06-17 22:21:09 -07001206ItemTable::ItemTable(DataSourceHelper *source)
Chong Zhangb51ca282017-07-26 16:25:28 -07001207 : mDataSource(source),
1208 mPrimaryItemId(0),
1209 mIdatOffset(0),
1210 mIdatSize(0),
1211 mImageItemsValid(false),
Chong Zhangecd08132017-10-05 16:09:29 -07001212 mCurrentItemIndex(0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001213 mRequiredBoxes.insert('iprp');
1214 mRequiredBoxes.insert('iloc');
1215 mRequiredBoxes.insert('pitm');
1216 mRequiredBoxes.insert('iinf');
1217}
1218
1219ItemTable::~ItemTable() {}
1220
1221status_t ItemTable::parse(uint32_t type, off64_t data_offset, size_t chunk_data_size) {
1222 switch(type) {
Marco Nelissen51087de2019-01-22 15:39:07 -08001223 case FOURCC("iloc"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001224 {
1225 return parseIlocBox(data_offset, chunk_data_size);
1226 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001227 case FOURCC("iinf"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001228 {
1229 return parseIinfBox(data_offset, chunk_data_size);
1230 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001231 case FOURCC("iprp"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001232 {
1233 return parseIprpBox(data_offset, chunk_data_size);
1234 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001235 case FOURCC("pitm"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001236 {
1237 return parsePitmBox(data_offset, chunk_data_size);
1238 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001239 case FOURCC("idat"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001240 {
1241 return parseIdatBox(data_offset, chunk_data_size);
1242 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001243 case FOURCC("iref"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001244 {
1245 return parseIrefBox(data_offset, chunk_data_size);
1246 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001247 case FOURCC("ipro"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001248 {
1249 ALOGW("ipro box not supported!");
1250 break;
1251 }
1252 default:
1253 {
1254 ALOGW("unrecognized box type: 0x%x", type);
1255 break;
1256 }
1257 }
1258 return ERROR_UNSUPPORTED;
1259}
1260
1261status_t ItemTable::parseIlocBox(off64_t offset, size_t size) {
1262 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1263
1264 IlocBox ilocBox(mDataSource, &mItemLocs);
1265 status_t err = ilocBox.parse(offset, size);
1266 if (err != OK) {
1267 return err;
1268 }
1269
1270 if (ilocBox.hasConstructMethod1()) {
1271 mRequiredBoxes.insert('idat');
1272 }
1273
1274 return buildImageItemsIfPossible('iloc');
1275}
1276
1277status_t ItemTable::parseIinfBox(off64_t offset, size_t size) {
1278 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1279
1280 IinfBox iinfBox(mDataSource, &mItemInfos);
1281 status_t err = iinfBox.parse(offset, size);
1282 if (err != OK) {
1283 return err;
1284 }
1285
Chong Zhang866f0f02019-02-01 14:19:37 -08001286 if (iinfBox.hasFourCC(FOURCC("grid")) || iinfBox.hasFourCC(FOURCC("Exif"))) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001287 mRequiredBoxes.insert('iref');
1288 }
1289
1290 return buildImageItemsIfPossible('iinf');
1291}
1292
1293status_t ItemTable::parsePitmBox(off64_t offset, size_t size) {
1294 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1295
1296 PitmBox pitmBox(mDataSource);
1297 status_t err = pitmBox.parse(offset, size, &mPrimaryItemId);
1298 if (err != OK) {
1299 return err;
1300 }
1301
1302 return buildImageItemsIfPossible('pitm');
1303}
1304
1305status_t ItemTable::parseIprpBox(off64_t offset, size_t size) {
1306 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1307
1308 IprpBox iprpBox(mDataSource, &mItemProperties, &mAssociations);
1309 status_t err = iprpBox.parse(offset, size);
1310 if (err != OK) {
1311 return err;
1312 }
1313
1314 return buildImageItemsIfPossible('iprp');
1315}
1316
1317status_t ItemTable::parseIdatBox(off64_t offset, size_t size) {
1318 ALOGV("%s: idat offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1319
1320 // only remember the offset and size of idat box for later use
1321 mIdatOffset = offset;
1322 mIdatSize = size;
1323
1324 return buildImageItemsIfPossible('idat');
1325}
1326
1327status_t ItemTable::parseIrefBox(off64_t offset, size_t size) {
1328 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1329
1330 IrefBox irefBox(mDataSource, &mItemReferences);
1331 status_t err = irefBox.parse(offset, size);
1332 if (err != OK) {
1333 return err;
1334 }
1335
1336 return buildImageItemsIfPossible('iref');
1337}
1338
1339status_t ItemTable::buildImageItemsIfPossible(uint32_t type) {
1340 if (mImageItemsValid) {
1341 return OK;
1342 }
1343
1344 mBoxesSeen.insert(type);
1345
1346 // need at least 'iprp', 'iloc', 'pitm', 'iinf';
1347 // need 'idat' if any items used construction_method of 2;
1348 // need 'iref' if there are grids.
1349 if (!std::includes(
1350 mBoxesSeen.begin(), mBoxesSeen.end(),
1351 mRequiredBoxes.begin(), mRequiredBoxes.end())) {
1352 return OK;
1353 }
1354
1355 ALOGV("building image table...");
1356
1357 for (size_t i = 0; i < mItemInfos.size(); i++) {
1358 const ItemInfo &info = mItemInfos[i];
1359
Chong Zhang01a76012018-03-14 18:19:49 -07001360 // Only handle 3 types of items, all others are ignored:
1361 // 'grid': derived image from tiles
1362 // 'hvc1': coded image (or tile)
1363 // 'Exif': EXIF metadata
Marco Nelissen51087de2019-01-22 15:39:07 -08001364 if (info.itemType != FOURCC("grid") &&
1365 info.itemType != FOURCC("hvc1") &&
1366 info.itemType != FOURCC("Exif")) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001367 continue;
1368 }
1369
Chong Zhangecd08132017-10-05 16:09:29 -07001370 ssize_t itemIndex = mItemIdToItemMap.indexOfKey(info.itemId);
1371 if (itemIndex >= 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001372 ALOGW("ignoring duplicate image item id %d", info.itemId);
1373 continue;
1374 }
1375
1376 ssize_t ilocIndex = mItemLocs.indexOfKey(info.itemId);
1377 if (ilocIndex < 0) {
1378 ALOGE("iloc missing for image item id %d", info.itemId);
1379 continue;
1380 }
1381 const ItemLoc &iloc = mItemLocs[ilocIndex];
1382
1383 off64_t offset;
1384 size_t size;
1385 if (iloc.getLoc(&offset, &size, mIdatOffset, mIdatSize) != OK) {
1386 return ERROR_MALFORMED;
1387 }
1388
Marco Nelissen51087de2019-01-22 15:39:07 -08001389 if (info.itemType == FOURCC("Exif")) {
Chong Zhang01a76012018-03-14 18:19:49 -07001390 // Only add if the Exif data is non-empty. The first 4 bytes contain
1391 // the offset to TIFF header, which the Exif parser doesn't use.
1392 if (size > 4) {
1393 ExifItem exifItem = {
1394 .offset = offset,
1395 .size = size,
1396 };
1397 mItemIdToExifMap.add(info.itemId, exifItem);
1398 }
1399 continue;
1400 }
1401
Chong Zhangd3e0d862017-10-03 13:17:13 -07001402 ImageItem image(info.itemType, info.itemId, info.hidden);
Chong Zhangb51ca282017-07-26 16:25:28 -07001403
1404 ALOGV("adding %s: itemId %d", image.isGrid() ? "grid" : "image", info.itemId);
1405
1406 if (image.isGrid()) {
Chong Zhanga9879ef2018-05-10 12:32:50 -07001407 // ImageGrid struct is at least 8-byte, at most 12-byte (if flags&1)
1408 if (size < 8 || size > 12) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001409 return ERROR_MALFORMED;
1410 }
1411 uint8_t buf[12];
1412 if (!mDataSource->readAt(offset, buf, size)) {
1413 return ERROR_IO;
1414 }
1415
1416 image.rows = buf[2] + 1;
1417 image.columns = buf[3] + 1;
1418
1419 ALOGV("rows %d, columans %d", image.rows, image.columns);
1420 } else {
1421 image.offset = offset;
1422 image.size = size;
1423 }
Chong Zhangecd08132017-10-05 16:09:29 -07001424 mItemIdToItemMap.add(info.itemId, image);
Chong Zhangb51ca282017-07-26 16:25:28 -07001425 }
1426
1427 for (size_t i = 0; i < mAssociations.size(); i++) {
1428 attachProperty(mAssociations[i]);
1429 }
1430
1431 for (size_t i = 0; i < mItemReferences.size(); i++) {
Chong Zhang01a76012018-03-14 18:19:49 -07001432 mItemReferences[i]->apply(mItemIdToItemMap, mItemIdToExifMap);
Chong Zhangb51ca282017-07-26 16:25:28 -07001433 }
1434
Chong Zhangd3e0d862017-10-03 13:17:13 -07001435 bool foundPrimary = false;
1436 for (size_t i = 0; i < mItemIdToItemMap.size(); i++) {
1437 // add all non-hidden images, also add the primary even if it's marked
1438 // hidden, in case the primary is set to a thumbnail
1439 bool isPrimary = (mItemIdToItemMap[i].itemId == mPrimaryItemId);
1440 if (!mItemIdToItemMap[i].hidden || isPrimary) {
1441 mDisplayables.push_back(i);
1442 }
1443 foundPrimary |= isPrimary;
1444 }
1445
1446 ALOGV("found %zu displayables", mDisplayables.size());
1447
1448 // fail if no displayables are found
1449 if (mDisplayables.empty()) {
1450 return ERROR_MALFORMED;
1451 }
1452
1453 // if the primary item id is invalid, set primary to the first displayable
1454 if (!foundPrimary) {
1455 mPrimaryItemId = mItemIdToItemMap[mDisplayables[0]].itemId;
1456 }
1457
Chong Zhangb51ca282017-07-26 16:25:28 -07001458 mImageItemsValid = true;
1459 return OK;
1460}
1461
1462void ItemTable::attachProperty(const AssociationEntry &association) {
Chong Zhangecd08132017-10-05 16:09:29 -07001463 ssize_t itemIndex = mItemIdToItemMap.indexOfKey(association.itemId);
Chong Zhangb51ca282017-07-26 16:25:28 -07001464
1465 // ignore non-image items
Chong Zhangecd08132017-10-05 16:09:29 -07001466 if (itemIndex < 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001467 return;
1468 }
1469
1470 uint16_t propertyIndex = association.index;
1471 if (propertyIndex >= mItemProperties.size()) {
1472 ALOGW("Ignoring invalid property index %d", propertyIndex);
1473 return;
1474 }
1475
1476 ALOGV("attach property %d to item id %d)",
1477 propertyIndex, association.itemId);
1478
Chong Zhangd3e0d862017-10-03 13:17:13 -07001479 mItemProperties[propertyIndex]->attachTo(mItemIdToItemMap.editValueAt(itemIndex));
Chong Zhangb51ca282017-07-26 16:25:28 -07001480}
1481
Chong Zhangd3e0d862017-10-03 13:17:13 -07001482uint32_t ItemTable::countImages() const {
1483 return mImageItemsValid ? mDisplayables.size() : 0;
1484}
1485
Marco Nelissendba610b2018-11-01 16:07:03 -07001486AMediaFormat *ItemTable::getImageMeta(const uint32_t imageIndex) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001487 if (!mImageItemsValid) {
1488 return NULL;
1489 }
1490
Chong Zhangd3e0d862017-10-03 13:17:13 -07001491 if (imageIndex >= mDisplayables.size()) {
1492 ALOGE("%s: invalid image index %u", __FUNCTION__, imageIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001493 return NULL;
1494 }
Chong Zhangd3e0d862017-10-03 13:17:13 -07001495 const uint32_t itemIndex = mDisplayables[imageIndex];
1496 ALOGV("image[%u]: item index %u", imageIndex, itemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001497
Chong Zhangecd08132017-10-05 16:09:29 -07001498 const ImageItem *image = &mItemIdToItemMap[itemIndex];
Chong Zhangb51ca282017-07-26 16:25:28 -07001499
Chong Zhang99745d12018-05-15 09:50:52 -07001500 ssize_t tileItemIndex = -1;
1501 if (image->isGrid()) {
1502 if (image->dimgRefs.empty()) {
1503 return NULL;
1504 }
1505 tileItemIndex = mItemIdToItemMap.indexOfKey(image->dimgRefs[0]);
1506 if (tileItemIndex < 0) {
1507 return NULL;
1508 }
1509 }
1510
Marco Nelissendba610b2018-11-01 16:07:03 -07001511 AMediaFormat *meta = AMediaFormat_new();
1512 AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
Chong Zhangb51ca282017-07-26 16:25:28 -07001513
Chong Zhangd3e0d862017-10-03 13:17:13 -07001514 if (image->itemId == mPrimaryItemId) {
Marco Nelissendba610b2018-11-01 16:07:03 -07001515 AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_DEFAULT, 1);
Chong Zhangd3e0d862017-10-03 13:17:13 -07001516 }
1517
1518 ALOGV("image[%u]: size %dx%d", imageIndex, image->width, image->height);
1519
Marco Nelissendba610b2018-11-01 16:07:03 -07001520 AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_WIDTH, image->width);
1521 AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_HEIGHT, image->height);
Chong Zhangb51ca282017-07-26 16:25:28 -07001522 if (image->rotation != 0) {
Chong Zhang686c23b2017-10-05 15:16:59 -07001523 // Rotation angle in HEIF is CCW, convert to CW here to be
1524 // consistent with the other media formats.
1525 switch(image->rotation) {
Marco Nelissendba610b2018-11-01 16:07:03 -07001526 case 90:
1527 case 180:
1528 case 270:
1529 AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_ROTATION, 360 - image->rotation);
1530 break;
Chong Zhang686c23b2017-10-05 15:16:59 -07001531 default: break; // don't set if invalid
1532 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001533 }
Chong Zhang242a9252019-12-09 16:38:39 -08001534 // we validated no overflow in IspeBox::parse()
Marco Nelissendba610b2018-11-01 16:07:03 -07001535 AMediaFormat_setInt32(meta,
Chong Zhang242a9252019-12-09 16:38:39 -08001536 AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 3 / 2);
Chong Zhangb51ca282017-07-26 16:25:28 -07001537
1538 if (!image->thumbnails.empty()) {
Chong Zhangecd08132017-10-05 16:09:29 -07001539 ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(image->thumbnails[0]);
1540 if (thumbItemIndex >= 0) {
1541 const ImageItem &thumbnail = mItemIdToItemMap[thumbItemIndex];
Chong Zhangb51ca282017-07-26 16:25:28 -07001542
Dongwon Kangc0fa2712018-06-20 16:39:58 -07001543 if (thumbnail.hvcc != NULL) {
Marco Nelissendba610b2018-11-01 16:07:03 -07001544 AMediaFormat_setInt32(meta,
1545 AMEDIAFORMAT_KEY_THUMBNAIL_WIDTH, thumbnail.width);
1546 AMediaFormat_setInt32(meta,
1547 AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT, thumbnail.height);
1548 AMediaFormat_setBuffer(meta,
Chong Zhang4c1f9582019-03-13 15:15:53 +09001549 AMEDIAFORMAT_KEY_THUMBNAIL_CSD_HEVC,
1550 thumbnail.hvcc->data(), thumbnail.hvcc->size());
Dongwon Kangc0fa2712018-06-20 16:39:58 -07001551 ALOGV("image[%u]: thumbnail: size %dx%d, item index %zd",
1552 imageIndex, thumbnail.width, thumbnail.height, thumbItemIndex);
1553 } else {
1554 ALOGW("%s: thumbnail data is missing for image[%u]!", __FUNCTION__, imageIndex);
1555 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001556 } else {
Chong Zhangecd08132017-10-05 16:09:29 -07001557 ALOGW("%s: Referenced thumbnail does not exist!", __FUNCTION__);
Chong Zhangb51ca282017-07-26 16:25:28 -07001558 }
1559 }
1560
1561 if (image->isGrid()) {
Marco Nelissendba610b2018-11-01 16:07:03 -07001562 AMediaFormat_setInt32(meta,
1563 AMEDIAFORMAT_KEY_GRID_ROWS, image->rows);
1564 AMediaFormat_setInt32(meta,
1565 AMEDIAFORMAT_KEY_GRID_COLUMNS, image->columns);
Chong Zhangee079fe2017-08-23 13:51:17 -07001566 // point image to the first tile for grid size and HVCC
Chong Zhangecd08132017-10-05 16:09:29 -07001567 image = &mItemIdToItemMap.editValueAt(tileItemIndex);
Marco Nelissendba610b2018-11-01 16:07:03 -07001568 AMediaFormat_setInt32(meta,
1569 AMEDIAFORMAT_KEY_TILE_WIDTH, image->width);
1570 AMediaFormat_setInt32(meta,
1571 AMEDIAFORMAT_KEY_TILE_HEIGHT, image->height);
Chong Zhang242a9252019-12-09 16:38:39 -08001572 // we validated no overflow in IspeBox::parse()
Marco Nelissendba610b2018-11-01 16:07:03 -07001573 AMediaFormat_setInt32(meta,
Chong Zhang242a9252019-12-09 16:38:39 -08001574 AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 3 / 2);
Chong Zhangb51ca282017-07-26 16:25:28 -07001575 }
1576
1577 if (image->hvcc == NULL) {
Chong Zhangd3e0d862017-10-03 13:17:13 -07001578 ALOGE("%s: hvcc is missing for image[%u]!", __FUNCTION__, imageIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001579 return NULL;
1580 }
Marco Nelissendba610b2018-11-01 16:07:03 -07001581 AMediaFormat_setBuffer(meta,
1582 AMEDIAFORMAT_KEY_CSD_HEVC, image->hvcc->data(), image->hvcc->size());
Chong Zhangb51ca282017-07-26 16:25:28 -07001583
1584 if (image->icc != NULL) {
Marco Nelissendba610b2018-11-01 16:07:03 -07001585 AMediaFormat_setBuffer(meta,
1586 AMEDIAFORMAT_KEY_ICC_PROFILE, image->icc->data(), image->icc->size());
Chong Zhangb51ca282017-07-26 16:25:28 -07001587 }
1588 return meta;
1589}
1590
Chong Zhangd3e0d862017-10-03 13:17:13 -07001591status_t ItemTable::findImageItem(const uint32_t imageIndex, uint32_t *itemIndex) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001592 if (!mImageItemsValid) {
1593 return INVALID_OPERATION;
1594 }
1595
Chong Zhangd3e0d862017-10-03 13:17:13 -07001596 if (imageIndex >= mDisplayables.size()) {
1597 ALOGE("%s: invalid image index %d", __FUNCTION__, imageIndex);
1598 return BAD_VALUE;
Chong Zhangb51ca282017-07-26 16:25:28 -07001599 }
1600
Chong Zhangd3e0d862017-10-03 13:17:13 -07001601 *itemIndex = mDisplayables[imageIndex];
1602
1603 ALOGV("image[%u]: item index %u", imageIndex, *itemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001604 return OK;
1605}
1606
Chong Zhangd3e0d862017-10-03 13:17:13 -07001607status_t ItemTable::findThumbnailItem(const uint32_t imageIndex, uint32_t *itemIndex) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001608 if (!mImageItemsValid) {
1609 return INVALID_OPERATION;
1610 }
1611
Chong Zhangd3e0d862017-10-03 13:17:13 -07001612 if (imageIndex >= mDisplayables.size()) {
1613 ALOGE("%s: invalid image index %d", __FUNCTION__, imageIndex);
1614 return BAD_VALUE;
Chong Zhangb51ca282017-07-26 16:25:28 -07001615 }
1616
Chong Zhangb3ca2592020-07-27 15:31:35 -07001617 uint32_t imageItemIndex = mDisplayables[imageIndex];
Chong Zhangd3e0d862017-10-03 13:17:13 -07001618
Chong Zhangb3ca2592020-07-27 15:31:35 -07001619 const ImageItem &imageItem = mItemIdToItemMap[imageItemIndex];
1620 if (imageItem.thumbnails.empty()) {
1621 *itemIndex = imageItemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001622 return OK;
1623 }
1624
Chong Zhangb3ca2592020-07-27 15:31:35 -07001625 ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(imageItem.thumbnails[0]);
Chong Zhangecd08132017-10-05 16:09:29 -07001626 if (thumbItemIndex < 0) {
Chong Zhangb3ca2592020-07-27 15:31:35 -07001627 // Do not return the image item in this case, fail it so that the
Chong Zhangd5fa3572018-04-09 19:03:10 -07001628 // thumbnail extraction code knows we really don't have it.
1629 return INVALID_OPERATION;
Chong Zhangb51ca282017-07-26 16:25:28 -07001630 }
1631
Chong Zhangecd08132017-10-05 16:09:29 -07001632 *itemIndex = thumbItemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001633 return OK;
1634}
1635
1636status_t ItemTable::getImageOffsetAndSize(
Chong Zhangecd08132017-10-05 16:09:29 -07001637 uint32_t *itemIndex, off64_t *offset, size_t *size) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001638 if (!mImageItemsValid) {
1639 return INVALID_OPERATION;
1640 }
1641
Chong Zhangecd08132017-10-05 16:09:29 -07001642 if (itemIndex != NULL) {
1643 if (*itemIndex >= mItemIdToItemMap.size()) {
1644 ALOGE("%s: Bad item index!", __FUNCTION__);
Chong Zhangb51ca282017-07-26 16:25:28 -07001645 return BAD_VALUE;
1646 }
Chong Zhangecd08132017-10-05 16:09:29 -07001647 mCurrentItemIndex = *itemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001648 }
1649
Chong Zhangecd08132017-10-05 16:09:29 -07001650 ImageItem &image = mItemIdToItemMap.editValueAt(mCurrentItemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001651 if (image.isGrid()) {
1652 uint32_t tileItemId;
Chong Zhangecd08132017-10-05 16:09:29 -07001653 status_t err = image.getNextTileItemId(&tileItemId, itemIndex != NULL);
Chong Zhangb51ca282017-07-26 16:25:28 -07001654 if (err != OK) {
1655 return err;
1656 }
Chong Zhangecd08132017-10-05 16:09:29 -07001657 ssize_t tileItemIndex = mItemIdToItemMap.indexOfKey(tileItemId);
1658 if (tileItemIndex < 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001659 return ERROR_END_OF_STREAM;
1660 }
Chong Zhangecd08132017-10-05 16:09:29 -07001661 *offset = mItemIdToItemMap[tileItemIndex].offset;
1662 *size = mItemIdToItemMap[tileItemIndex].size;
Chong Zhangb51ca282017-07-26 16:25:28 -07001663 } else {
Chong Zhangecd08132017-10-05 16:09:29 -07001664 if (itemIndex == NULL) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001665 // For single images, we only allow it to be read once, after that
Chong Zhangecd08132017-10-05 16:09:29 -07001666 // it's EOS. New item index must be requested each time.
Chong Zhangb51ca282017-07-26 16:25:28 -07001667 return ERROR_END_OF_STREAM;
1668 }
Chong Zhangecd08132017-10-05 16:09:29 -07001669 *offset = mItemIdToItemMap[mCurrentItemIndex].offset;
1670 *size = mItemIdToItemMap[mCurrentItemIndex].size;
Chong Zhangb51ca282017-07-26 16:25:28 -07001671 }
1672
1673 return OK;
1674}
1675
Chong Zhang01a76012018-03-14 18:19:49 -07001676status_t ItemTable::getExifOffsetAndSize(off64_t *offset, size_t *size) {
1677 if (!mImageItemsValid) {
1678 return INVALID_OPERATION;
1679 }
1680
1681 ssize_t itemIndex = mItemIdToItemMap.indexOfKey(mPrimaryItemId);
1682
1683 // this should not happen, something's seriously wrong.
1684 if (itemIndex < 0) {
1685 return INVALID_OPERATION;
1686 }
1687
1688 const ImageItem &image = mItemIdToItemMap[itemIndex];
1689 if (image.cdscRefs.size() == 0) {
1690 return NAME_NOT_FOUND;
1691 }
1692
1693 ssize_t exifIndex = mItemIdToExifMap.indexOfKey(image.cdscRefs[0]);
1694 if (exifIndex < 0) {
1695 return NAME_NOT_FOUND;
1696 }
1697
1698 // skip the first 4-byte of the offset to TIFF header
Chong Zhang72500a12019-01-11 18:05:12 -08001699 uint32_t tiffOffset;
1700 if (!mDataSource->readAt(
1701 mItemIdToExifMap[exifIndex].offset, &tiffOffset, 4)) {
1702 return ERROR_IO;
1703 }
1704
1705 // We need 'Exif\0\0' before the tiff header
1706 tiffOffset = ntohl(tiffOffset);
1707 if (tiffOffset < 6) {
1708 return ERROR_MALFORMED;
1709 }
1710 // The first 4-byte of the item is the offset of the tiff header within the
1711 // exif data. The size of the item should be > 4 for a non-empty exif (this
1712 // was already checked when the item was added). Also check that the tiff
1713 // header offset is valid.
1714 if (mItemIdToExifMap[exifIndex].size <= 4 ||
1715 tiffOffset > mItemIdToExifMap[exifIndex].size - 4) {
1716 return ERROR_MALFORMED;
1717 }
1718
1719 // Offset of 'Exif\0\0' relative to the beginning of 'Exif' item
1720 // (first 4-byte is the tiff header offset)
1721 uint32_t exifOffset = 4 + tiffOffset - 6;
1722 *offset = mItemIdToExifMap[exifIndex].offset + exifOffset;
1723 *size = mItemIdToExifMap[exifIndex].size - exifOffset;
Chong Zhang01a76012018-03-14 18:19:49 -07001724 return OK;
1725}
1726
Chong Zhangb51ca282017-07-26 16:25:28 -07001727} // namespace heif
1728
1729} // namespace android