blob: 78164733687c2e49d33f38a66d1fe98eb9827523 [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
Marco Nelissen75226172016-11-16 14:10:52 -080020#include <ItemTable.h>
Marco Nelissencec44d02018-06-17 22:21:09 -070021#include <media/MediaExtractorPluginApi.h>
22#include <media/MediaExtractorPluginHelper.h>
Chong Zhangb51ca282017-07-26 16:25:28 -070023#include <media/stagefright/MetaData.h>
24#include <media/stagefright/MediaErrors.h>
Chong Zhangb51ca282017-07-26 16:25:28 -070025#include <media/stagefright/foundation/ABuffer.h>
Dongwon Kang60761282017-10-09 11:16:48 -070026#include <media/stagefright/foundation/ByteUtils.h>
Chong Zhangb51ca282017-07-26 16:25:28 -070027#include <media/stagefright/foundation/hexdump.h>
Dongwon Kange7a8a632017-10-09 14:52:58 -070028#include <media/stagefright/foundation/MediaDefs.h>
Chong Zhangb51ca282017-07-26 16:25:28 -070029#include <utils/Log.h>
30
31namespace android {
32
33namespace heif {
34
35/////////////////////////////////////////////////////////////////////
36//
37// struct to keep track of one image item
38//
39
40struct ImageItem {
41 friend struct ItemReference;
42 friend struct ItemProperty;
43
Chong Zhangd3e0d862017-10-03 13:17:13 -070044 ImageItem() : ImageItem(0, 0, false) {}
45 ImageItem(uint32_t _type, uint32_t _id, bool _hidden) :
46 type(_type), itemId(_id), hidden(_hidden),
Chong Zhangb51ca282017-07-26 16:25:28 -070047 rows(0), columns(0), width(0), height(0), rotation(0),
48 offset(0), size(0), nextTileIndex(0) {}
49
50 bool isGrid() const {
Marco Nelissen51087de2019-01-22 15:39:07 -080051 return type == FOURCC("grid");
Chong Zhangb51ca282017-07-26 16:25:28 -070052 }
53
54 status_t getNextTileItemId(uint32_t *nextTileItemId, bool reset) {
55 if (reset) {
56 nextTileIndex = 0;
57 }
58 if (nextTileIndex >= dimgRefs.size()) {
59 return ERROR_END_OF_STREAM;
60 }
61 *nextTileItemId = dimgRefs[nextTileIndex++];
62 return OK;
63 }
64
65 uint32_t type;
Chong Zhangd3e0d862017-10-03 13:17:13 -070066 uint32_t itemId;
67 bool hidden;
Chong Zhangb51ca282017-07-26 16:25:28 -070068 int32_t rows;
69 int32_t columns;
70 int32_t width;
71 int32_t height;
72 int32_t rotation;
73 off64_t offset;
74 size_t size;
75 sp<ABuffer> hvcc;
76 sp<ABuffer> icc;
77
78 Vector<uint32_t> thumbnails;
79 Vector<uint32_t> dimgRefs;
Chong Zhang01a76012018-03-14 18:19:49 -070080 Vector<uint32_t> cdscRefs;
Chong Zhangb51ca282017-07-26 16:25:28 -070081 size_t nextTileIndex;
82};
83
Chong Zhang01a76012018-03-14 18:19:49 -070084struct ExifItem {
85 off64_t offset;
86 size_t size;
87};
Chong Zhangb51ca282017-07-26 16:25:28 -070088
89/////////////////////////////////////////////////////////////////////
90//
91// ISO boxes
92//
93
94struct Box {
95protected:
Marco Nelissencec44d02018-06-17 22:21:09 -070096 Box(DataSourceHelper *source, uint32_t type) :
Chong Zhangb51ca282017-07-26 16:25:28 -070097 mDataSource(source), mType(type) {}
98
99 virtual ~Box() {}
100
101 virtual status_t onChunkData(
102 uint32_t /*type*/, off64_t /*offset*/, size_t /*size*/) {
103 return OK;
104 }
105
106 inline uint32_t type() const { return mType; }
107
Marco Nelissencec44d02018-06-17 22:21:09 -0700108 inline DataSourceHelper *source() const { return mDataSource; }
Chong Zhangb51ca282017-07-26 16:25:28 -0700109
110 status_t parseChunk(off64_t *offset);
111
112 status_t parseChunks(off64_t offset, size_t size);
113
114private:
Marco Nelissencec44d02018-06-17 22:21:09 -0700115 DataSourceHelper *mDataSource;
Chong Zhangb51ca282017-07-26 16:25:28 -0700116 uint32_t mType;
117};
118
119status_t Box::parseChunk(off64_t *offset) {
120 if (*offset < 0) {
121 ALOGE("b/23540914");
122 return ERROR_MALFORMED;
123 }
124 uint32_t hdr[2];
125 if (mDataSource->readAt(*offset, hdr, 8) < 8) {
126 return ERROR_IO;
127 }
128 uint64_t chunk_size = ntohl(hdr[0]);
129 int32_t chunk_type = ntohl(hdr[1]);
130 off64_t data_offset = *offset + 8;
131
132 if (chunk_size == 1) {
133 if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) {
134 return ERROR_IO;
135 }
136 chunk_size = ntoh64(chunk_size);
137 data_offset += 8;
138
139 if (chunk_size < 16) {
140 // The smallest valid chunk is 16 bytes long in this case.
141 return ERROR_MALFORMED;
142 }
143 } else if (chunk_size == 0) {
144 // This shouldn't happen since we should never be top level
145 ALOGE("invalid chunk size 0 for non-top level box");
146 return ERROR_MALFORMED;
147 } else if (chunk_size < 8) {
148 // The smallest valid chunk is 8 bytes long.
149 ALOGE("invalid chunk size: %lld", (long long)chunk_size);
150 return ERROR_MALFORMED;
151 }
152
153 char chunk[5];
154 MakeFourCCString(chunk_type, chunk);
155 ALOGV("chunk: %s @ %lld", chunk, (long long)*offset);
156
157 off64_t chunk_data_size = chunk_size - (data_offset - *offset);
158 if (chunk_data_size < 0) {
159 ALOGE("b/23540914");
160 return ERROR_MALFORMED;
161 }
162
163 status_t err = onChunkData(chunk_type, data_offset, chunk_data_size);
164
165 if (err != OK) {
166 return err;
167 }
168 *offset += chunk_size;
169 return OK;
170}
171
172status_t Box::parseChunks(off64_t offset, size_t size) {
173 off64_t stopOffset = offset + size;
174 while (offset < stopOffset) {
175 status_t err = parseChunk(&offset);
176 if (err != OK) {
177 return err;
178 }
179 }
180 if (offset != stopOffset) {
181 return ERROR_MALFORMED;
182 }
183 return OK;
184}
185
186///////////////////////////////////////////////////////////////////////
187
188struct FullBox : public Box {
189protected:
Marco Nelissencec44d02018-06-17 22:21:09 -0700190 FullBox(DataSourceHelper *source, uint32_t type) :
Chong Zhangb51ca282017-07-26 16:25:28 -0700191 Box(source, type), mVersion(0), mFlags(0) {}
192
193 inline uint8_t version() const { return mVersion; }
194
195 inline uint32_t flags() const { return mFlags; }
196
197 status_t parseFullBoxHeader(off64_t *offset, size_t *size);
198
199private:
200 uint8_t mVersion;
201 uint32_t mFlags;
202};
203
204status_t FullBox::parseFullBoxHeader(off64_t *offset, size_t *size) {
205 if (*size < 4) {
206 return ERROR_MALFORMED;
207 }
208 if (!source()->readAt(*offset, &mVersion, 1)) {
209 return ERROR_IO;
210 }
211 if (!source()->getUInt24(*offset + 1, &mFlags)) {
212 return ERROR_IO;
213 }
214 *offset += 4;
215 *size -= 4;
216 return OK;
217}
218
219/////////////////////////////////////////////////////////////////////
220//
221// PrimaryImage box
222//
223
224struct PitmBox : public FullBox {
Marco Nelissencec44d02018-06-17 22:21:09 -0700225 PitmBox(DataSourceHelper *source) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800226 FullBox(source, FOURCC("pitm")) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700227
228 status_t parse(off64_t offset, size_t size, uint32_t *primaryItemId);
229};
230
231status_t PitmBox::parse(off64_t offset, size_t size, uint32_t *primaryItemId) {
232 status_t err = parseFullBoxHeader(&offset, &size);
233 if (err != OK) {
234 return err;
235 }
236
237 size_t itemIdSize = (version() == 0) ? 2 : 4;
238 if (size < itemIdSize) {
239 return ERROR_MALFORMED;
240 }
241 uint32_t itemId;
242 if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) {
243 return ERROR_IO;
244 }
245
246 ALOGV("primary id %d", itemId);
247 *primaryItemId = itemId;
248
249 return OK;
250}
251
252/////////////////////////////////////////////////////////////////////
253//
254// ItemLocation related boxes
255//
256
257struct ExtentEntry {
258 uint64_t extentIndex;
259 uint64_t extentOffset;
260 uint64_t extentLength;
261};
262
263struct ItemLoc {
264 ItemLoc() : ItemLoc(0, 0, 0, 0) {}
265 ItemLoc(uint32_t item_id, uint16_t construction_method,
266 uint16_t data_reference_index, uint64_t base_offset) :
267 itemId(item_id),
268 constructionMethod(construction_method),
269 dataReferenceIndex(data_reference_index),
270 baseOffset(base_offset) {}
271
272 void addExtent(const ExtentEntry& extent) {
273 extents.push_back(extent);
274 }
275
276 status_t getLoc(off64_t *offset, size_t *size,
277 off64_t idatOffset, size_t idatSize) const {
278 // TODO: fix extent handling, fix constructionMethod = 2
279 CHECK(extents.size() == 1);
280 if (constructionMethod == 0) {
281 *offset = baseOffset + extents[0].extentOffset;
282 *size = extents[0].extentLength;
283 return OK;
284 } else if (constructionMethod == 1) {
285 if (baseOffset + extents[0].extentOffset + extents[0].extentLength
286 > idatSize) {
287 return ERROR_MALFORMED;
288 }
289 *offset = baseOffset + extents[0].extentOffset + idatOffset;
290 *size = extents[0].extentLength;
291 return OK;
292 }
293 return ERROR_UNSUPPORTED;
294 }
295
296 // parsed info
297 uint32_t itemId;
298 uint16_t constructionMethod;
299 uint16_t dataReferenceIndex;
300 off64_t baseOffset;
301 Vector<ExtentEntry> extents;
302};
303
304struct IlocBox : public FullBox {
Marco Nelissencec44d02018-06-17 22:21:09 -0700305 IlocBox(DataSourceHelper *source, KeyedVector<uint32_t, ItemLoc> *itemLocs) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800306 FullBox(source, FOURCC("iloc")),
Chong Zhangb51ca282017-07-26 16:25:28 -0700307 mItemLocs(itemLocs), mHasConstructMethod1(false) {}
308
309 status_t parse(off64_t offset, size_t size);
310
311 bool hasConstructMethod1() { return mHasConstructMethod1; }
312
313private:
314 static bool isSizeFieldValid(uint32_t offset_size) {
315 return offset_size == 0 || offset_size == 4 || offset_size == 8;
316 }
317 KeyedVector<uint32_t, ItemLoc> *mItemLocs;
318 bool mHasConstructMethod1;
319};
320
321status_t IlocBox::parse(off64_t offset, size_t size) {
322 status_t err = parseFullBoxHeader(&offset, &size);
323 if (err != OK) {
324 return err;
325 }
326 if (version() > 2) {
327 ALOGE("%s: invalid version %d", __FUNCTION__, version());
328 return ERROR_MALFORMED;
329 }
330
331 if (size < 2) {
332 return ERROR_MALFORMED;
333 }
334 uint8_t offset_size;
335 if (!source()->readAt(offset++, &offset_size, 1)) {
336 return ERROR_IO;
337 }
338 uint8_t length_size = (offset_size & 0xF);
339 offset_size >>= 4;
340
341 uint8_t base_offset_size;
342 if (!source()->readAt(offset++, &base_offset_size, 1)) {
343 return ERROR_IO;
344 }
345 uint8_t index_size = 0;
346 if (version() == 1 || version() == 2) {
347 index_size = (base_offset_size & 0xF);
348 }
349 base_offset_size >>= 4;
350 size -= 2;
351
352 if (!isSizeFieldValid(offset_size)
353 || !isSizeFieldValid(length_size)
354 || !isSizeFieldValid(base_offset_size)
355 || !isSizeFieldValid((index_size))) {
356 ALOGE("%s: offset size not valid: %d, %d, %d, %d", __FUNCTION__,
357 offset_size, length_size, base_offset_size, index_size);
358 return ERROR_MALFORMED;
359 }
360
361 uint32_t item_count;
362 size_t itemFieldSize = version() < 2 ? 2 : 4;
363 if (size < itemFieldSize) {
364 return ERROR_MALFORMED;
365 }
366 if (!source()->getUInt32Var(offset, &item_count, itemFieldSize)) {
367 return ERROR_IO;
368 }
369
370 ALOGV("item_count %lld", (long long) item_count);
371 offset += itemFieldSize;
372 size -= itemFieldSize;
373
374 for (size_t i = 0; i < item_count; i++) {
375 uint32_t item_id;
376 if (!source()->getUInt32Var(offset, &item_id, itemFieldSize)) {
377 return ERROR_IO;
378 }
379 ALOGV("item[%zu]: id %lld", i, (long long)item_id);
380 offset += itemFieldSize;
381
382 uint8_t construction_method = 0;
383 if (version() == 1 || version() == 2) {
384 uint8_t buf[2];
385 if (!source()->readAt(offset, buf, 2)) {
386 return ERROR_IO;
387 }
388 construction_method = (buf[1] & 0xF);
389 ALOGV("construction_method %d", construction_method);
390 if (construction_method == 1) {
391 mHasConstructMethod1 = true;
392 }
393
394 offset += 2;
395 }
396
397 uint16_t data_reference_index;
398 if (!source()->getUInt16(offset, &data_reference_index)) {
399 return ERROR_IO;
400 }
401 ALOGV("data_reference_index %d", data_reference_index);
402 if (data_reference_index != 0) {
403 // we don't support reference to other files
404 return ERROR_UNSUPPORTED;
405 }
406 offset += 2;
407
408 uint64_t base_offset = 0;
409 if (base_offset_size != 0) {
410 if (!source()->getUInt64Var(offset, &base_offset, base_offset_size)) {
411 return ERROR_IO;
412 }
413 offset += base_offset_size;
414 }
415 ALOGV("base_offset %lld", (long long) base_offset);
416
417 ssize_t index = mItemLocs->add(item_id, ItemLoc(
418 item_id, construction_method, data_reference_index, base_offset));
419 ItemLoc &item = mItemLocs->editValueAt(index);
420
421 uint16_t extent_count;
422 if (!source()->getUInt16(offset, &extent_count)) {
423 return ERROR_IO;
424 }
425 ALOGV("extent_count %d", extent_count);
426
427 if (extent_count > 1 && (offset_size == 0 || length_size == 0)) {
428 // if the item is dividec into more than one extents, offset and
429 // length must be present.
430 return ERROR_MALFORMED;
431 }
432 offset += 2;
433
434 for (size_t j = 0; j < extent_count; j++) {
435 uint64_t extent_index = 1; // default=1
436 if ((version() == 1 || version() == 2) && (index_size > 0)) {
437 if (!source()->getUInt64Var(offset, &extent_index, index_size)) {
438 return ERROR_IO;
439 }
440 // TODO: add support for this mode
441 offset += index_size;
442 ALOGV("extent_index %lld", (long long)extent_index);
443 }
444
445 uint64_t extent_offset = 0; // default=0
446 if (offset_size > 0) {
447 if (!source()->getUInt64Var(offset, &extent_offset, offset_size)) {
448 return ERROR_IO;
449 }
450 offset += offset_size;
451 }
452 ALOGV("extent_offset %lld", (long long)extent_offset);
453
454 uint64_t extent_length = 0; // this indicates full length of file
455 if (length_size > 0) {
456 if (!source()->getUInt64Var(offset, &extent_length, length_size)) {
457 return ERROR_IO;
458 }
459 offset += length_size;
460 }
461 ALOGV("extent_length %lld", (long long)extent_length);
462
463 item.addExtent({ extent_index, extent_offset, extent_length });
464 }
465 }
466 return OK;
467}
468
469/////////////////////////////////////////////////////////////////////
470//
471// ItemReference related boxes
472//
473
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);
549 ImageItem &masterImage = itemIdToItemMap.editValueAt(itemIndex);
550 if (!masterImage.thumbnails.empty()) {
551 ALOGW("already has thumbnails!");
552 }
553 masterImage.thumbnails.push_back(mItemId);
554 }
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:
703 uint32_t mWidth;
704 uint32_t mHeight;
705};
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 }
718 if (!source()->getUInt32(offset, &mWidth)
719 || !source()->getUInt32(offset + 4, &mHeight)) {
720 return ERROR_IO;
721 }
722 ALOGV("property ispe: %dx%d", mWidth, mHeight);
723
724 return OK;
725}
726
727struct HvccBox : public Box, public ItemProperty {
Marco Nelissencec44d02018-06-17 22:21:09 -0700728 HvccBox(DataSourceHelper *source) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800729 Box(source, FOURCC("hvcC")) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700730
731 status_t parse(off64_t offset, size_t size) override;
732
733 void attachTo(ImageItem &image) const override {
734 image.hvcc = mHVCC;
735 }
736
737private:
738 sp<ABuffer> mHVCC;
739};
740
741status_t HvccBox::parse(off64_t offset, size_t size) {
742 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
743
744 mHVCC = new ABuffer(size);
745
746 if (mHVCC->data() == NULL) {
747 ALOGE("b/28471206");
748 return NO_MEMORY;
749 }
750
751 if (source()->readAt(offset, mHVCC->data(), size) < (ssize_t)size) {
752 return ERROR_IO;
753 }
754
755 ALOGV("property hvcC");
756
757 return OK;
758}
759
760struct IrotBox : public Box, public ItemProperty {
Marco Nelissencec44d02018-06-17 22:21:09 -0700761 IrotBox(DataSourceHelper *source) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800762 Box(source, FOURCC("irot")), mAngle(0) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700763
764 status_t parse(off64_t offset, size_t size) override;
765
766 void attachTo(ImageItem &image) const override {
767 image.rotation = mAngle * 90;
768 }
769
770private:
771 uint8_t mAngle;
772};
773
774status_t IrotBox::parse(off64_t offset, size_t size) {
775 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
776
777 if (size < 1) {
778 return ERROR_MALFORMED;
779 }
780 if (source()->readAt(offset, &mAngle, 1) != 1) {
781 return ERROR_IO;
782 }
783 mAngle &= 0x3;
784 ALOGV("property irot: %d", mAngle);
785
786 return OK;
787}
788
789struct ColrBox : public Box, public ItemProperty {
Marco Nelissencec44d02018-06-17 22:21:09 -0700790 ColrBox(DataSourceHelper *source) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800791 Box(source, FOURCC("colr")) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700792
793 status_t parse(off64_t offset, size_t size) override;
794
795 void attachTo(ImageItem &image) const override {
796 image.icc = mICCData;
797 }
798
799private:
800 sp<ABuffer> mICCData;
801};
802
803status_t ColrBox::parse(off64_t offset, size_t size) {
804 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
805
806 if (size < 4) {
807 return ERROR_MALFORMED;
808 }
809 uint32_t colour_type;
810 if (!source()->getUInt32(offset, &colour_type)) {
811 return ERROR_IO;
812 }
813 offset += 4;
814 size -= 4;
Marco Nelissen51087de2019-01-22 15:39:07 -0800815 if (colour_type == FOURCC("nclx")) {
Chong Zhangb51ca282017-07-26 16:25:28 -0700816 return OK;
817 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800818 if ((colour_type != FOURCC("rICC")) &&
819 (colour_type != FOURCC("prof"))) {
Chong Zhangb51ca282017-07-26 16:25:28 -0700820 return ERROR_MALFORMED;
821 }
822
823 mICCData = new ABuffer(size);
824 if (mICCData->data() == NULL) {
825 ALOGE("b/28471206");
826 return NO_MEMORY;
827 }
828
829 if (source()->readAt(offset, mICCData->data(), size) != (ssize_t)size) {
830 return ERROR_IO;
831 }
832
833 ALOGV("property Colr: size %zd", size);
834 return OK;
835}
836
837struct IpmaBox : public FullBox {
Marco Nelissencec44d02018-06-17 22:21:09 -0700838 IpmaBox(DataSourceHelper *source, Vector<AssociationEntry> *associations) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800839 FullBox(source, FOURCC("ipma")), mAssociations(associations) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700840
841 status_t parse(off64_t offset, size_t size);
842private:
843 Vector<AssociationEntry> *mAssociations;
844};
845
846status_t IpmaBox::parse(off64_t offset, size_t size) {
847 status_t err = parseFullBoxHeader(&offset, &size);
848 if (err != OK) {
849 return err;
850 }
851
852 if (size < 4) {
853 return ERROR_MALFORMED;
854 }
855 uint32_t entryCount;
856 if (!source()->getUInt32(offset, &entryCount)) {
857 return ERROR_IO;
858 }
859 offset += 4;
860 size -= 4;
861
862 for (size_t k = 0; k < entryCount; ++k) {
863 uint32_t itemId = 0;
864 size_t itemIdSize = (version() < 1) ? 2 : 4;
865
866 if (size < itemIdSize + 1) {
867 return ERROR_MALFORMED;
868 }
869
870 if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) {
871 return ERROR_IO;
872 }
873 offset += itemIdSize;
874 size -= itemIdSize;
875
876 uint8_t associationCount;
877 if (!source()->readAt(offset, &associationCount, 1)) {
878 return ERROR_IO;
879 }
880 offset++;
881 size--;
882
883 for (size_t i = 0; i < associationCount; ++i) {
884 size_t propIndexSize = (flags() & 1) ? 2 : 1;
885 if (size < propIndexSize) {
886 return ERROR_MALFORMED;
887 }
888 uint16_t propIndex;
889 if (!source()->getUInt16Var(offset, &propIndex, propIndexSize)) {
890 return ERROR_IO;
891 }
892 offset += propIndexSize;
893 size -= propIndexSize;
894 uint16_t bitmask = (1 << (8 * propIndexSize - 1));
895 AssociationEntry entry = {
896 .itemId = itemId,
897 .essential = !!(propIndex & bitmask),
898 .index = (uint16_t) (propIndex & ~bitmask)
899 };
900
901 ALOGV("item id %d associated to property %d (essential %d)",
902 itemId, entry.index, entry.essential);
903
904 mAssociations->push_back(entry);
905 }
906 }
907
908 return OK;
909}
910
911struct IpcoBox : public Box {
Marco Nelissencec44d02018-06-17 22:21:09 -0700912 IpcoBox(DataSourceHelper *source, Vector<sp<ItemProperty> > *properties) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800913 Box(source, FOURCC("ipco")), mItemProperties(properties) {}
Chong Zhangb51ca282017-07-26 16:25:28 -0700914
915 status_t parse(off64_t offset, size_t size);
916protected:
917 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
918
919private:
920 Vector<sp<ItemProperty> > *mItemProperties;
921};
922
923status_t IpcoBox::parse(off64_t offset, size_t size) {
924 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
925 // push dummy as the index is 1-based
926 mItemProperties->push_back(new ItemProperty());
927 return parseChunks(offset, size);
928}
929
930status_t IpcoBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
931 sp<ItemProperty> itemProperty;
932 switch(type) {
Marco Nelissen51087de2019-01-22 15:39:07 -0800933 case FOURCC("hvcC"):
Chong Zhangb51ca282017-07-26 16:25:28 -0700934 {
935 itemProperty = new HvccBox(source());
936 break;
937 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800938 case FOURCC("ispe"):
Chong Zhangb51ca282017-07-26 16:25:28 -0700939 {
940 itemProperty = new IspeBox(source());
941 break;
942 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800943 case FOURCC("irot"):
Chong Zhangb51ca282017-07-26 16:25:28 -0700944 {
945 itemProperty = new IrotBox(source());
946 break;
947 }
Marco Nelissen51087de2019-01-22 15:39:07 -0800948 case FOURCC("colr"):
Chong Zhangb51ca282017-07-26 16:25:28 -0700949 {
950 itemProperty = new ColrBox(source());
951 break;
952 }
953 default:
954 {
955 // push dummy to maintain correct item property index
956 itemProperty = new ItemProperty();
957 break;
958 }
959 }
960 status_t err = itemProperty->parse(offset, size);
961 if (err != OK) {
962 return err;
963 }
964 mItemProperties->push_back(itemProperty);
965 return OK;
966}
967
968struct IprpBox : public Box {
Marco Nelissencec44d02018-06-17 22:21:09 -0700969 IprpBox(DataSourceHelper *source,
Chong Zhangb51ca282017-07-26 16:25:28 -0700970 Vector<sp<ItemProperty> > *properties,
971 Vector<AssociationEntry> *associations) :
Marco Nelissen51087de2019-01-22 15:39:07 -0800972 Box(source, FOURCC("iprp")),
Chong Zhangb51ca282017-07-26 16:25:28 -0700973 mProperties(properties), mAssociations(associations) {}
974
975 status_t parse(off64_t offset, size_t size);
976protected:
977 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
978
979private:
980 Vector<sp<ItemProperty> > *mProperties;
981 Vector<AssociationEntry> *mAssociations;
982};
983
984status_t IprpBox::parse(off64_t offset, size_t size) {
985 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
986
987 status_t err = parseChunks(offset, size);
988 if (err != OK) {
989 return err;
990 }
991 return OK;
992}
993
994status_t IprpBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
995 switch(type) {
Marco Nelissen51087de2019-01-22 15:39:07 -0800996 case FOURCC("ipco"):
Chong Zhangb51ca282017-07-26 16:25:28 -0700997 {
998 IpcoBox ipcoBox(source(), mProperties);
999 return ipcoBox.parse(offset, size);
1000 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001001 case FOURCC("ipma"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001002 {
1003 IpmaBox ipmaBox(source(), mAssociations);
1004 return ipmaBox.parse(offset, size);
1005 }
1006 default:
1007 {
1008 ALOGW("Unrecognized box.");
1009 break;
1010 }
1011 }
1012 return OK;
1013}
1014
1015/////////////////////////////////////////////////////////////////////
1016//
1017// ItemInfo related boxes
1018//
1019struct ItemInfo {
1020 uint32_t itemId;
1021 uint32_t itemType;
Chong Zhangd3e0d862017-10-03 13:17:13 -07001022 bool hidden;
Chong Zhangb51ca282017-07-26 16:25:28 -07001023};
1024
1025struct InfeBox : public FullBox {
Marco Nelissencec44d02018-06-17 22:21:09 -07001026 InfeBox(DataSourceHelper *source) :
Marco Nelissen51087de2019-01-22 15:39:07 -08001027 FullBox(source, FOURCC("infe")) {}
Chong Zhangb51ca282017-07-26 16:25:28 -07001028
1029 status_t parse(off64_t offset, size_t size, ItemInfo *itemInfo);
1030
1031private:
1032 bool parseNullTerminatedString(off64_t *offset, size_t *size, String8 *out);
1033};
1034
1035bool InfeBox::parseNullTerminatedString(
1036 off64_t *offset, size_t *size, String8 *out) {
Chong Zhang70601852018-03-08 15:23:43 -08001037 char tmp;
1038 Vector<char> buf;
1039 buf.setCapacity(256);
Chong Zhangb51ca282017-07-26 16:25:28 -07001040 off64_t newOffset = *offset;
1041 off64_t stopOffset = *offset + *size;
1042 while (newOffset < stopOffset) {
Chong Zhang70601852018-03-08 15:23:43 -08001043 if (!source()->readAt(newOffset++, &tmp, 1)) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001044 return false;
1045 }
Chong Zhang70601852018-03-08 15:23:43 -08001046 buf.push_back(tmp);
1047 if (tmp == 0) {
1048 out->setTo(buf.array());
Chong Zhangb51ca282017-07-26 16:25:28 -07001049
1050 *offset = newOffset;
1051 *size = stopOffset - newOffset;
1052
1053 return true;
1054 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001055 }
1056 return false;
1057}
1058
1059status_t InfeBox::parse(off64_t offset, size_t size, ItemInfo *itemInfo) {
1060 status_t err = parseFullBoxHeader(&offset, &size);
1061 if (err != OK) {
1062 return err;
1063 }
1064
1065 if (version() == 0 || version() == 1) {
Chong Zhangecd08132017-10-05 16:09:29 -07001066 return ERROR_UNSUPPORTED;
Chong Zhangb51ca282017-07-26 16:25:28 -07001067 } else { // version >= 2
1068 uint32_t item_id;
1069 size_t itemIdSize = (version() == 2) ? 2 : 4;
1070 if (size < itemIdSize + 6) {
1071 return ERROR_MALFORMED;
1072 }
1073 if (!source()->getUInt32Var(offset, &item_id, itemIdSize)) {
1074 return ERROR_IO;
1075 }
1076 ALOGV("item_id %d", item_id);
1077 offset += itemIdSize;
1078 uint16_t item_protection_index;
1079 if (!source()->getUInt16(offset, &item_protection_index)) {
1080 return ERROR_IO;
1081 }
1082 ALOGV("item_protection_index %d", item_protection_index);
1083 offset += 2;
1084 uint32_t item_type;
1085 if (!source()->getUInt32(offset, &item_type)) {
1086 return ERROR_IO;
1087 }
1088
1089 itemInfo->itemId = item_id;
1090 itemInfo->itemType = item_type;
Chong Zhangd3e0d862017-10-03 13:17:13 -07001091 // According to HEIF spec, (flags & 1) indicates the image is hidden
1092 // and not supposed to be displayed.
1093 itemInfo->hidden = (flags() & 1);
Chong Zhangb51ca282017-07-26 16:25:28 -07001094
1095 char itemTypeString[5];
1096 MakeFourCCString(item_type, itemTypeString);
1097 ALOGV("item_type %s", itemTypeString);
1098 offset += 4;
1099 size -= itemIdSize + 6;
1100
1101 String8 item_name;
1102 if (!parseNullTerminatedString(&offset, &size, &item_name)) {
1103 return ERROR_MALFORMED;
1104 }
1105 ALOGV("item_name %s", item_name.c_str());
1106
Marco Nelissen51087de2019-01-22 15:39:07 -08001107 if (item_type == FOURCC("mime")) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001108 String8 content_type;
1109 if (!parseNullTerminatedString(&offset, &size, &content_type)) {
1110 return ERROR_MALFORMED;
1111 }
1112
Ray Essick46c24cd2018-03-08 13:54:19 -08001113 // content_encoding is optional; can be omitted if would be empty
1114 if (size > 0) {
1115 String8 content_encoding;
1116 if (!parseNullTerminatedString(&offset, &size, &content_encoding)) {
1117 return ERROR_MALFORMED;
1118 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001119 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001120 } else if (item_type == FOURCC("uri ")) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001121 String8 item_uri_type;
1122 if (!parseNullTerminatedString(&offset, &size, &item_uri_type)) {
1123 return ERROR_MALFORMED;
1124 }
1125 }
1126 }
1127 return OK;
1128}
1129
1130struct IinfBox : public FullBox {
Marco Nelissencec44d02018-06-17 22:21:09 -07001131 IinfBox(DataSourceHelper *source, Vector<ItemInfo> *itemInfos) :
Marco Nelissen51087de2019-01-22 15:39:07 -08001132 FullBox(source, FOURCC("iinf")),
Chong Zhangb51ca282017-07-26 16:25:28 -07001133 mItemInfos(itemInfos), mHasGrids(false) {}
1134
1135 status_t parse(off64_t offset, size_t size);
1136
1137 bool hasGrids() { return mHasGrids; }
1138
1139protected:
1140 status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
1141
1142private:
1143 Vector<ItemInfo> *mItemInfos;
1144 bool mHasGrids;
1145};
1146
1147status_t IinfBox::parse(off64_t offset, size_t size) {
1148 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1149
1150 status_t err = parseFullBoxHeader(&offset, &size);
1151 if (err != OK) {
1152 return err;
1153 }
1154
1155 size_t entryCountSize = version() == 0 ? 2 : 4;
1156 if (size < entryCountSize) {
1157 return ERROR_MALFORMED;
1158 }
1159 uint32_t entry_count;
1160 if (!source()->getUInt32Var(offset, &entry_count, entryCountSize)) {
1161 return ERROR_IO;
1162 }
1163 ALOGV("entry_count %d", entry_count);
1164
1165 off64_t stopOffset = offset + size;
1166 offset += entryCountSize;
1167 for (size_t i = 0; i < entry_count && offset < stopOffset; i++) {
1168 ALOGV("entry %zu", i);
1169 status_t err = parseChunk(&offset);
1170 if (err != OK) {
1171 return err;
1172 }
1173 }
1174 if (offset != stopOffset) {
1175 return ERROR_MALFORMED;
1176 }
1177
1178 return OK;
1179}
1180
1181status_t IinfBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
Marco Nelissen51087de2019-01-22 15:39:07 -08001182 if (type != FOURCC("infe")) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001183 return OK;
1184 }
1185
1186 InfeBox infeBox(source());
1187 ItemInfo itemInfo;
1188 status_t err = infeBox.parse(offset, size, &itemInfo);
Chong Zhangecd08132017-10-05 16:09:29 -07001189 if (err == OK) {
1190 mItemInfos->push_back(itemInfo);
Marco Nelissen51087de2019-01-22 15:39:07 -08001191 mHasGrids |= (itemInfo.itemType == FOURCC("grid"));
Chong Zhangb51ca282017-07-26 16:25:28 -07001192 }
Chong Zhang5518a2c2017-10-13 18:31:25 -07001193 // InfeBox parse returns ERROR_UNSUPPORTED if the box if an unsupported
1194 // version. Ignore this error as it's not fatal.
1195 return (err == ERROR_UNSUPPORTED) ? OK : err;
Chong Zhangb51ca282017-07-26 16:25:28 -07001196}
1197
1198//////////////////////////////////////////////////////////////////
1199
Marco Nelissencec44d02018-06-17 22:21:09 -07001200ItemTable::ItemTable(DataSourceHelper *source)
Chong Zhangb51ca282017-07-26 16:25:28 -07001201 : mDataSource(source),
1202 mPrimaryItemId(0),
1203 mIdatOffset(0),
1204 mIdatSize(0),
1205 mImageItemsValid(false),
Chong Zhangecd08132017-10-05 16:09:29 -07001206 mCurrentItemIndex(0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001207 mRequiredBoxes.insert('iprp');
1208 mRequiredBoxes.insert('iloc');
1209 mRequiredBoxes.insert('pitm');
1210 mRequiredBoxes.insert('iinf');
1211}
1212
1213ItemTable::~ItemTable() {}
1214
1215status_t ItemTable::parse(uint32_t type, off64_t data_offset, size_t chunk_data_size) {
1216 switch(type) {
Marco Nelissen51087de2019-01-22 15:39:07 -08001217 case FOURCC("iloc"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001218 {
1219 return parseIlocBox(data_offset, chunk_data_size);
1220 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001221 case FOURCC("iinf"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001222 {
1223 return parseIinfBox(data_offset, chunk_data_size);
1224 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001225 case FOURCC("iprp"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001226 {
1227 return parseIprpBox(data_offset, chunk_data_size);
1228 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001229 case FOURCC("pitm"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001230 {
1231 return parsePitmBox(data_offset, chunk_data_size);
1232 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001233 case FOURCC("idat"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001234 {
1235 return parseIdatBox(data_offset, chunk_data_size);
1236 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001237 case FOURCC("iref"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001238 {
1239 return parseIrefBox(data_offset, chunk_data_size);
1240 }
Marco Nelissen51087de2019-01-22 15:39:07 -08001241 case FOURCC("ipro"):
Chong Zhangb51ca282017-07-26 16:25:28 -07001242 {
1243 ALOGW("ipro box not supported!");
1244 break;
1245 }
1246 default:
1247 {
1248 ALOGW("unrecognized box type: 0x%x", type);
1249 break;
1250 }
1251 }
1252 return ERROR_UNSUPPORTED;
1253}
1254
1255status_t ItemTable::parseIlocBox(off64_t offset, size_t size) {
1256 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1257
1258 IlocBox ilocBox(mDataSource, &mItemLocs);
1259 status_t err = ilocBox.parse(offset, size);
1260 if (err != OK) {
1261 return err;
1262 }
1263
1264 if (ilocBox.hasConstructMethod1()) {
1265 mRequiredBoxes.insert('idat');
1266 }
1267
1268 return buildImageItemsIfPossible('iloc');
1269}
1270
1271status_t ItemTable::parseIinfBox(off64_t offset, size_t size) {
1272 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1273
1274 IinfBox iinfBox(mDataSource, &mItemInfos);
1275 status_t err = iinfBox.parse(offset, size);
1276 if (err != OK) {
1277 return err;
1278 }
1279
1280 if (iinfBox.hasGrids()) {
1281 mRequiredBoxes.insert('iref');
1282 }
1283
1284 return buildImageItemsIfPossible('iinf');
1285}
1286
1287status_t ItemTable::parsePitmBox(off64_t offset, size_t size) {
1288 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1289
1290 PitmBox pitmBox(mDataSource);
1291 status_t err = pitmBox.parse(offset, size, &mPrimaryItemId);
1292 if (err != OK) {
1293 return err;
1294 }
1295
1296 return buildImageItemsIfPossible('pitm');
1297}
1298
1299status_t ItemTable::parseIprpBox(off64_t offset, size_t size) {
1300 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1301
1302 IprpBox iprpBox(mDataSource, &mItemProperties, &mAssociations);
1303 status_t err = iprpBox.parse(offset, size);
1304 if (err != OK) {
1305 return err;
1306 }
1307
1308 return buildImageItemsIfPossible('iprp');
1309}
1310
1311status_t ItemTable::parseIdatBox(off64_t offset, size_t size) {
1312 ALOGV("%s: idat offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1313
1314 // only remember the offset and size of idat box for later use
1315 mIdatOffset = offset;
1316 mIdatSize = size;
1317
1318 return buildImageItemsIfPossible('idat');
1319}
1320
1321status_t ItemTable::parseIrefBox(off64_t offset, size_t size) {
1322 ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1323
1324 IrefBox irefBox(mDataSource, &mItemReferences);
1325 status_t err = irefBox.parse(offset, size);
1326 if (err != OK) {
1327 return err;
1328 }
1329
1330 return buildImageItemsIfPossible('iref');
1331}
1332
1333status_t ItemTable::buildImageItemsIfPossible(uint32_t type) {
1334 if (mImageItemsValid) {
1335 return OK;
1336 }
1337
1338 mBoxesSeen.insert(type);
1339
1340 // need at least 'iprp', 'iloc', 'pitm', 'iinf';
1341 // need 'idat' if any items used construction_method of 2;
1342 // need 'iref' if there are grids.
1343 if (!std::includes(
1344 mBoxesSeen.begin(), mBoxesSeen.end(),
1345 mRequiredBoxes.begin(), mRequiredBoxes.end())) {
1346 return OK;
1347 }
1348
1349 ALOGV("building image table...");
1350
1351 for (size_t i = 0; i < mItemInfos.size(); i++) {
1352 const ItemInfo &info = mItemInfos[i];
1353
Chong Zhang01a76012018-03-14 18:19:49 -07001354 // Only handle 3 types of items, all others are ignored:
1355 // 'grid': derived image from tiles
1356 // 'hvc1': coded image (or tile)
1357 // 'Exif': EXIF metadata
Marco Nelissen51087de2019-01-22 15:39:07 -08001358 if (info.itemType != FOURCC("grid") &&
1359 info.itemType != FOURCC("hvc1") &&
1360 info.itemType != FOURCC("Exif")) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001361 continue;
1362 }
1363
Chong Zhangecd08132017-10-05 16:09:29 -07001364 ssize_t itemIndex = mItemIdToItemMap.indexOfKey(info.itemId);
1365 if (itemIndex >= 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001366 ALOGW("ignoring duplicate image item id %d", info.itemId);
1367 continue;
1368 }
1369
1370 ssize_t ilocIndex = mItemLocs.indexOfKey(info.itemId);
1371 if (ilocIndex < 0) {
1372 ALOGE("iloc missing for image item id %d", info.itemId);
1373 continue;
1374 }
1375 const ItemLoc &iloc = mItemLocs[ilocIndex];
1376
1377 off64_t offset;
1378 size_t size;
1379 if (iloc.getLoc(&offset, &size, mIdatOffset, mIdatSize) != OK) {
1380 return ERROR_MALFORMED;
1381 }
1382
Marco Nelissen51087de2019-01-22 15:39:07 -08001383 if (info.itemType == FOURCC("Exif")) {
Chong Zhang01a76012018-03-14 18:19:49 -07001384 // Only add if the Exif data is non-empty. The first 4 bytes contain
1385 // the offset to TIFF header, which the Exif parser doesn't use.
1386 if (size > 4) {
1387 ExifItem exifItem = {
1388 .offset = offset,
1389 .size = size,
1390 };
1391 mItemIdToExifMap.add(info.itemId, exifItem);
1392 }
1393 continue;
1394 }
1395
Chong Zhangd3e0d862017-10-03 13:17:13 -07001396 ImageItem image(info.itemType, info.itemId, info.hidden);
Chong Zhangb51ca282017-07-26 16:25:28 -07001397
1398 ALOGV("adding %s: itemId %d", image.isGrid() ? "grid" : "image", info.itemId);
1399
1400 if (image.isGrid()) {
Chong Zhanga9879ef2018-05-10 12:32:50 -07001401 // ImageGrid struct is at least 8-byte, at most 12-byte (if flags&1)
1402 if (size < 8 || size > 12) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001403 return ERROR_MALFORMED;
1404 }
1405 uint8_t buf[12];
1406 if (!mDataSource->readAt(offset, buf, size)) {
1407 return ERROR_IO;
1408 }
1409
1410 image.rows = buf[2] + 1;
1411 image.columns = buf[3] + 1;
1412
1413 ALOGV("rows %d, columans %d", image.rows, image.columns);
1414 } else {
1415 image.offset = offset;
1416 image.size = size;
1417 }
Chong Zhangecd08132017-10-05 16:09:29 -07001418 mItemIdToItemMap.add(info.itemId, image);
Chong Zhangb51ca282017-07-26 16:25:28 -07001419 }
1420
1421 for (size_t i = 0; i < mAssociations.size(); i++) {
1422 attachProperty(mAssociations[i]);
1423 }
1424
1425 for (size_t i = 0; i < mItemReferences.size(); i++) {
Chong Zhang01a76012018-03-14 18:19:49 -07001426 mItemReferences[i]->apply(mItemIdToItemMap, mItemIdToExifMap);
Chong Zhangb51ca282017-07-26 16:25:28 -07001427 }
1428
Chong Zhangd3e0d862017-10-03 13:17:13 -07001429 bool foundPrimary = false;
1430 for (size_t i = 0; i < mItemIdToItemMap.size(); i++) {
1431 // add all non-hidden images, also add the primary even if it's marked
1432 // hidden, in case the primary is set to a thumbnail
1433 bool isPrimary = (mItemIdToItemMap[i].itemId == mPrimaryItemId);
1434 if (!mItemIdToItemMap[i].hidden || isPrimary) {
1435 mDisplayables.push_back(i);
1436 }
1437 foundPrimary |= isPrimary;
1438 }
1439
1440 ALOGV("found %zu displayables", mDisplayables.size());
1441
1442 // fail if no displayables are found
1443 if (mDisplayables.empty()) {
1444 return ERROR_MALFORMED;
1445 }
1446
1447 // if the primary item id is invalid, set primary to the first displayable
1448 if (!foundPrimary) {
1449 mPrimaryItemId = mItemIdToItemMap[mDisplayables[0]].itemId;
1450 }
1451
Chong Zhangb51ca282017-07-26 16:25:28 -07001452 mImageItemsValid = true;
1453 return OK;
1454}
1455
1456void ItemTable::attachProperty(const AssociationEntry &association) {
Chong Zhangecd08132017-10-05 16:09:29 -07001457 ssize_t itemIndex = mItemIdToItemMap.indexOfKey(association.itemId);
Chong Zhangb51ca282017-07-26 16:25:28 -07001458
1459 // ignore non-image items
Chong Zhangecd08132017-10-05 16:09:29 -07001460 if (itemIndex < 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001461 return;
1462 }
1463
1464 uint16_t propertyIndex = association.index;
1465 if (propertyIndex >= mItemProperties.size()) {
1466 ALOGW("Ignoring invalid property index %d", propertyIndex);
1467 return;
1468 }
1469
1470 ALOGV("attach property %d to item id %d)",
1471 propertyIndex, association.itemId);
1472
Chong Zhangd3e0d862017-10-03 13:17:13 -07001473 mItemProperties[propertyIndex]->attachTo(mItemIdToItemMap.editValueAt(itemIndex));
Chong Zhangb51ca282017-07-26 16:25:28 -07001474}
1475
Chong Zhangd3e0d862017-10-03 13:17:13 -07001476uint32_t ItemTable::countImages() const {
1477 return mImageItemsValid ? mDisplayables.size() : 0;
1478}
1479
Marco Nelissendba610b2018-11-01 16:07:03 -07001480AMediaFormat *ItemTable::getImageMeta(const uint32_t imageIndex) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001481 if (!mImageItemsValid) {
1482 return NULL;
1483 }
1484
Chong Zhangd3e0d862017-10-03 13:17:13 -07001485 if (imageIndex >= mDisplayables.size()) {
1486 ALOGE("%s: invalid image index %u", __FUNCTION__, imageIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001487 return NULL;
1488 }
Chong Zhangd3e0d862017-10-03 13:17:13 -07001489 const uint32_t itemIndex = mDisplayables[imageIndex];
1490 ALOGV("image[%u]: item index %u", imageIndex, itemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001491
Chong Zhangecd08132017-10-05 16:09:29 -07001492 const ImageItem *image = &mItemIdToItemMap[itemIndex];
Chong Zhangb51ca282017-07-26 16:25:28 -07001493
Chong Zhang99745d12018-05-15 09:50:52 -07001494 ssize_t tileItemIndex = -1;
1495 if (image->isGrid()) {
1496 if (image->dimgRefs.empty()) {
1497 return NULL;
1498 }
1499 tileItemIndex = mItemIdToItemMap.indexOfKey(image->dimgRefs[0]);
1500 if (tileItemIndex < 0) {
1501 return NULL;
1502 }
1503 }
1504
Marco Nelissendba610b2018-11-01 16:07:03 -07001505 AMediaFormat *meta = AMediaFormat_new();
1506 AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
Chong Zhangb51ca282017-07-26 16:25:28 -07001507
Chong Zhangd3e0d862017-10-03 13:17:13 -07001508 if (image->itemId == mPrimaryItemId) {
Marco Nelissendba610b2018-11-01 16:07:03 -07001509 AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_DEFAULT, 1);
Chong Zhangd3e0d862017-10-03 13:17:13 -07001510 }
1511
1512 ALOGV("image[%u]: size %dx%d", imageIndex, image->width, image->height);
1513
Marco Nelissendba610b2018-11-01 16:07:03 -07001514 AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_WIDTH, image->width);
1515 AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_HEIGHT, image->height);
Chong Zhangb51ca282017-07-26 16:25:28 -07001516 if (image->rotation != 0) {
Chong Zhang686c23b2017-10-05 15:16:59 -07001517 // Rotation angle in HEIF is CCW, convert to CW here to be
1518 // consistent with the other media formats.
1519 switch(image->rotation) {
Marco Nelissendba610b2018-11-01 16:07:03 -07001520 case 90:
1521 case 180:
1522 case 270:
1523 AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_ROTATION, 360 - image->rotation);
1524 break;
Chong Zhang686c23b2017-10-05 15:16:59 -07001525 default: break; // don't set if invalid
1526 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001527 }
Marco Nelissendba610b2018-11-01 16:07:03 -07001528 AMediaFormat_setInt32(meta,
1529 AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 1.5);
Chong Zhangb51ca282017-07-26 16:25:28 -07001530
1531 if (!image->thumbnails.empty()) {
Chong Zhangecd08132017-10-05 16:09:29 -07001532 ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(image->thumbnails[0]);
1533 if (thumbItemIndex >= 0) {
1534 const ImageItem &thumbnail = mItemIdToItemMap[thumbItemIndex];
Chong Zhangb51ca282017-07-26 16:25:28 -07001535
Dongwon Kangc0fa2712018-06-20 16:39:58 -07001536 if (thumbnail.hvcc != NULL) {
Marco Nelissendba610b2018-11-01 16:07:03 -07001537 AMediaFormat_setInt32(meta,
1538 AMEDIAFORMAT_KEY_THUMBNAIL_WIDTH, thumbnail.width);
1539 AMediaFormat_setInt32(meta,
1540 AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT, thumbnail.height);
1541 AMediaFormat_setBuffer(meta,
1542 AMEDIAFORMAT_KEY_CSD_HEVC, thumbnail.hvcc->data(), thumbnail.hvcc->size());
Dongwon Kangc0fa2712018-06-20 16:39:58 -07001543 ALOGV("image[%u]: thumbnail: size %dx%d, item index %zd",
1544 imageIndex, thumbnail.width, thumbnail.height, thumbItemIndex);
1545 } else {
1546 ALOGW("%s: thumbnail data is missing for image[%u]!", __FUNCTION__, imageIndex);
1547 }
Chong Zhangb51ca282017-07-26 16:25:28 -07001548 } else {
Chong Zhangecd08132017-10-05 16:09:29 -07001549 ALOGW("%s: Referenced thumbnail does not exist!", __FUNCTION__);
Chong Zhangb51ca282017-07-26 16:25:28 -07001550 }
1551 }
1552
1553 if (image->isGrid()) {
Marco Nelissendba610b2018-11-01 16:07:03 -07001554 AMediaFormat_setInt32(meta,
1555 AMEDIAFORMAT_KEY_GRID_ROWS, image->rows);
1556 AMediaFormat_setInt32(meta,
1557 AMEDIAFORMAT_KEY_GRID_COLUMNS, image->columns);
Chong Zhangee079fe2017-08-23 13:51:17 -07001558 // point image to the first tile for grid size and HVCC
Chong Zhangecd08132017-10-05 16:09:29 -07001559 image = &mItemIdToItemMap.editValueAt(tileItemIndex);
Marco Nelissendba610b2018-11-01 16:07:03 -07001560 AMediaFormat_setInt32(meta,
1561 AMEDIAFORMAT_KEY_TILE_WIDTH, image->width);
1562 AMediaFormat_setInt32(meta,
1563 AMEDIAFORMAT_KEY_TILE_HEIGHT, image->height);
1564 AMediaFormat_setInt32(meta,
1565 AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 1.5);
Chong Zhangb51ca282017-07-26 16:25:28 -07001566 }
1567
1568 if (image->hvcc == NULL) {
Chong Zhangd3e0d862017-10-03 13:17:13 -07001569 ALOGE("%s: hvcc is missing for image[%u]!", __FUNCTION__, imageIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001570 return NULL;
1571 }
Marco Nelissendba610b2018-11-01 16:07:03 -07001572 AMediaFormat_setBuffer(meta,
1573 AMEDIAFORMAT_KEY_CSD_HEVC, image->hvcc->data(), image->hvcc->size());
Chong Zhangb51ca282017-07-26 16:25:28 -07001574
1575 if (image->icc != NULL) {
Marco Nelissendba610b2018-11-01 16:07:03 -07001576 AMediaFormat_setBuffer(meta,
1577 AMEDIAFORMAT_KEY_ICC_PROFILE, image->icc->data(), image->icc->size());
Chong Zhangb51ca282017-07-26 16:25:28 -07001578 }
1579 return meta;
1580}
1581
Chong Zhangd3e0d862017-10-03 13:17:13 -07001582status_t ItemTable::findImageItem(const uint32_t imageIndex, uint32_t *itemIndex) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001583 if (!mImageItemsValid) {
1584 return INVALID_OPERATION;
1585 }
1586
Chong Zhangd3e0d862017-10-03 13:17:13 -07001587 if (imageIndex >= mDisplayables.size()) {
1588 ALOGE("%s: invalid image index %d", __FUNCTION__, imageIndex);
1589 return BAD_VALUE;
Chong Zhangb51ca282017-07-26 16:25:28 -07001590 }
1591
Chong Zhangd3e0d862017-10-03 13:17:13 -07001592 *itemIndex = mDisplayables[imageIndex];
1593
1594 ALOGV("image[%u]: item index %u", imageIndex, *itemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001595 return OK;
1596}
1597
Chong Zhangd3e0d862017-10-03 13:17:13 -07001598status_t ItemTable::findThumbnailItem(const uint32_t imageIndex, uint32_t *itemIndex) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001599 if (!mImageItemsValid) {
1600 return INVALID_OPERATION;
1601 }
1602
Chong Zhangd3e0d862017-10-03 13:17:13 -07001603 if (imageIndex >= mDisplayables.size()) {
1604 ALOGE("%s: invalid image index %d", __FUNCTION__, imageIndex);
1605 return BAD_VALUE;
Chong Zhangb51ca282017-07-26 16:25:28 -07001606 }
1607
Chong Zhangd3e0d862017-10-03 13:17:13 -07001608 uint32_t masterItemIndex = mDisplayables[imageIndex];
1609
1610 const ImageItem &masterImage = mItemIdToItemMap[masterItemIndex];
1611 if (masterImage.thumbnails.empty()) {
1612 *itemIndex = masterItemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001613 return OK;
1614 }
1615
Chong Zhangd3e0d862017-10-03 13:17:13 -07001616 ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(masterImage.thumbnails[0]);
Chong Zhangecd08132017-10-05 16:09:29 -07001617 if (thumbItemIndex < 0) {
Chong Zhangd5fa3572018-04-09 19:03:10 -07001618 // Do not return the master image in this case, fail it so that the
1619 // thumbnail extraction code knows we really don't have it.
1620 return INVALID_OPERATION;
Chong Zhangb51ca282017-07-26 16:25:28 -07001621 }
1622
Chong Zhangecd08132017-10-05 16:09:29 -07001623 *itemIndex = thumbItemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001624 return OK;
1625}
1626
1627status_t ItemTable::getImageOffsetAndSize(
Chong Zhangecd08132017-10-05 16:09:29 -07001628 uint32_t *itemIndex, off64_t *offset, size_t *size) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001629 if (!mImageItemsValid) {
1630 return INVALID_OPERATION;
1631 }
1632
Chong Zhangecd08132017-10-05 16:09:29 -07001633 if (itemIndex != NULL) {
1634 if (*itemIndex >= mItemIdToItemMap.size()) {
1635 ALOGE("%s: Bad item index!", __FUNCTION__);
Chong Zhangb51ca282017-07-26 16:25:28 -07001636 return BAD_VALUE;
1637 }
Chong Zhangecd08132017-10-05 16:09:29 -07001638 mCurrentItemIndex = *itemIndex;
Chong Zhangb51ca282017-07-26 16:25:28 -07001639 }
1640
Chong Zhangecd08132017-10-05 16:09:29 -07001641 ImageItem &image = mItemIdToItemMap.editValueAt(mCurrentItemIndex);
Chong Zhangb51ca282017-07-26 16:25:28 -07001642 if (image.isGrid()) {
1643 uint32_t tileItemId;
Chong Zhangecd08132017-10-05 16:09:29 -07001644 status_t err = image.getNextTileItemId(&tileItemId, itemIndex != NULL);
Chong Zhangb51ca282017-07-26 16:25:28 -07001645 if (err != OK) {
1646 return err;
1647 }
Chong Zhangecd08132017-10-05 16:09:29 -07001648 ssize_t tileItemIndex = mItemIdToItemMap.indexOfKey(tileItemId);
1649 if (tileItemIndex < 0) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001650 return ERROR_END_OF_STREAM;
1651 }
Chong Zhangecd08132017-10-05 16:09:29 -07001652 *offset = mItemIdToItemMap[tileItemIndex].offset;
1653 *size = mItemIdToItemMap[tileItemIndex].size;
Chong Zhangb51ca282017-07-26 16:25:28 -07001654 } else {
Chong Zhangecd08132017-10-05 16:09:29 -07001655 if (itemIndex == NULL) {
Chong Zhangb51ca282017-07-26 16:25:28 -07001656 // For single images, we only allow it to be read once, after that
Chong Zhangecd08132017-10-05 16:09:29 -07001657 // it's EOS. New item index must be requested each time.
Chong Zhangb51ca282017-07-26 16:25:28 -07001658 return ERROR_END_OF_STREAM;
1659 }
Chong Zhangecd08132017-10-05 16:09:29 -07001660 *offset = mItemIdToItemMap[mCurrentItemIndex].offset;
1661 *size = mItemIdToItemMap[mCurrentItemIndex].size;
Chong Zhangb51ca282017-07-26 16:25:28 -07001662 }
1663
1664 return OK;
1665}
1666
Chong Zhang01a76012018-03-14 18:19:49 -07001667status_t ItemTable::getExifOffsetAndSize(off64_t *offset, size_t *size) {
1668 if (!mImageItemsValid) {
1669 return INVALID_OPERATION;
1670 }
1671
1672 ssize_t itemIndex = mItemIdToItemMap.indexOfKey(mPrimaryItemId);
1673
1674 // this should not happen, something's seriously wrong.
1675 if (itemIndex < 0) {
1676 return INVALID_OPERATION;
1677 }
1678
1679 const ImageItem &image = mItemIdToItemMap[itemIndex];
1680 if (image.cdscRefs.size() == 0) {
1681 return NAME_NOT_FOUND;
1682 }
1683
1684 ssize_t exifIndex = mItemIdToExifMap.indexOfKey(image.cdscRefs[0]);
1685 if (exifIndex < 0) {
1686 return NAME_NOT_FOUND;
1687 }
1688
1689 // skip the first 4-byte of the offset to TIFF header
1690 *offset = mItemIdToExifMap[exifIndex].offset + 4;
1691 *size = mItemIdToExifMap[exifIndex].size - 4;
1692 return OK;
1693}
1694
Chong Zhangb51ca282017-07-26 16:25:28 -07001695} // namespace heif
1696
1697} // namespace android