Merge "Parse XMP offset/size for HEIF"
diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp
index ded3d1a..444664c 100644
--- a/media/extractors/mp4/ItemTable.cpp
+++ b/media/extractors/mp4/ItemTable.cpp
@@ -80,13 +80,15 @@
Vector<uint32_t> thumbnails;
Vector<uint32_t> dimgRefs;
- Vector<uint32_t> cdscRefs;
+ Vector<uint32_t> exifRefs;
+ Vector<uint32_t> xmpRefs;
size_t nextTileIndex;
};
-struct ExifItem {
+struct ExternalMetaItem {
off64_t offset;
size_t size;
+ bool isExif;
};
/////////////////////////////////////////////////////////////////////
@@ -482,7 +484,7 @@
void apply(
KeyedVector<uint32_t, ImageItem> &itemIdToItemMap,
- KeyedVector<uint32_t, ExifItem> &itemIdToExifMap) const;
+ KeyedVector<uint32_t, ExternalMetaItem> &itemIdToMetaMap) const;
private:
uint32_t mItemId;
@@ -494,7 +496,7 @@
void ItemReference::apply(
KeyedVector<uint32_t, ImageItem> &itemIdToItemMap,
- KeyedVector<uint32_t, ExifItem> &itemIdToExifMap) const {
+ KeyedVector<uint32_t, ExternalMetaItem> &itemIdToMetaMap) const {
ALOGV("attach reference type 0x%x to item id %d)", type(), mItemId);
switch(type()) {
@@ -556,15 +558,15 @@
break;
}
case FOURCC("cdsc"): {
- ssize_t itemIndex = itemIdToExifMap.indexOfKey(mItemId);
+ ssize_t metaIndex = itemIdToMetaMap.indexOfKey(mItemId);
- // ignore non-exif block items
- if (itemIndex < 0) {
+ // ignore non-meta items
+ if (metaIndex < 0) {
return;
}
for (size_t i = 0; i < mRefs.size(); i++) {
- itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]);
+ ssize_t itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]);
// ignore non-image items
if (itemIndex < 0) {
@@ -572,7 +574,11 @@
}
ALOGV("Image item id %d uses metadata item id %d", mRefs[i], mItemId);
ImageItem &image = itemIdToItemMap.editValueAt(itemIndex);
- image.cdscRefs.push_back(mItemId);
+ if (itemIdToMetaMap[metaIndex].isExif) {
+ image.exifRefs.push_back(mItemId);
+ } else {
+ image.xmpRefs.push_back(mItemId);
+ }
}
break;
}
@@ -1065,7 +1071,21 @@
struct ItemInfo {
uint32_t itemId;
uint32_t itemType;
+ String8 contentType;
bool hidden;
+
+ bool isXmp() const {
+ return itemType == FOURCC("mime") && contentType == String8("application/rdf+xml");
+ }
+ bool isExif() const {
+ return itemType == FOURCC("Exif");
+ }
+ bool isGrid() const {
+ return itemType == FOURCC("grid");
+ }
+ bool isSample() const {
+ return itemType == FOURCC("av01") || itemType == FOURCC("hvc1");
+ }
};
struct InfeBox : public FullBox {
@@ -1155,6 +1175,7 @@
if (!parseNullTerminatedString(&offset, &size, &content_type)) {
return ERROR_MALFORMED;
}
+ itemInfo->contentType = content_type;
// content_encoding is optional; can be omitted if would be empty
if (size > 0) {
@@ -1175,18 +1196,18 @@
struct IinfBox : public FullBox {
IinfBox(DataSourceHelper *source, Vector<ItemInfo> *itemInfos) :
- FullBox(source, FOURCC("iinf")), mItemInfos(itemInfos) {}
+ FullBox(source, FOURCC("iinf")), mItemInfos(itemInfos), mNeedIref(false) {}
status_t parse(off64_t offset, size_t size);
- bool hasFourCC(uint32_t type) { return mFourCCSeen.count(type) > 0; }
+ bool needIrefBox() { return mNeedIref; }
protected:
status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
private:
Vector<ItemInfo> *mItemInfos;
- std::unordered_set<uint32_t> mFourCCSeen;
+ bool mNeedIref;
};
status_t IinfBox::parse(off64_t offset, size_t size) {
@@ -1233,7 +1254,7 @@
status_t err = infeBox.parse(offset, size, &itemInfo);
if (err == OK) {
mItemInfos->push_back(itemInfo);
- mFourCCSeen.insert(itemInfo.itemType);
+ mNeedIref |= (itemInfo.isExif() || itemInfo.isXmp() || itemInfo.isGrid());
}
// InfeBox parse returns ERROR_UNSUPPORTED if the box if an unsupported
// version. Ignore this error as it's not fatal.
@@ -1323,7 +1344,7 @@
return err;
}
- if (iinfBox.hasFourCC(FOURCC("grid")) || iinfBox.hasFourCC(FOURCC("Exif"))) {
+ if (iinfBox.needIrefBox()) {
mRequiredBoxes.insert('iref');
}
@@ -1399,12 +1420,9 @@
// Only handle 3 types of items, all others are ignored:
// 'grid': derived image from tiles
- // 'hvc1': coded image (or tile)
- // 'Exif': EXIF metadata
- if (info.itemType != FOURCC("grid") &&
- info.itemType != FOURCC("hvc1") &&
- info.itemType != FOURCC("Exif") &&
- info.itemType != FOURCC("av01")) {
+ // 'hvc1' or 'av01': coded image (or tile)
+ // 'Exif' or XMP: metadata
+ if (!info.isGrid() && !info.isSample() && !info.isExif() && !info.isXmp()) {
continue;
}
@@ -1427,15 +1445,18 @@
return ERROR_MALFORMED;
}
- if (info.itemType == FOURCC("Exif")) {
- // Only add if the Exif data is non-empty. The first 4 bytes contain
+ if (info.isExif() || info.isXmp()) {
+ // Only add if the meta is non-empty. For Exif, the first 4 bytes contain
// the offset to TIFF header, which the Exif parser doesn't use.
- if (size > 4) {
- ExifItem exifItem = {
+ ALOGV("adding meta to mItemIdToMetaMap: isExif %d, offset %lld, size %lld",
+ info.isExif(), (long long)offset, (long long)size);
+ if ((info.isExif() && size > 4) || (info.isXmp() && size > 0)) {
+ ExternalMetaItem metaItem = {
+ .isExif = info.isExif(),
.offset = offset,
.size = size,
};
- mItemIdToExifMap.add(info.itemId, exifItem);
+ mItemIdToMetaMap.add(info.itemId, metaItem);
}
continue;
}
@@ -1470,7 +1491,7 @@
}
for (size_t i = 0; i < mItemReferences.size(); i++) {
- mItemReferences[i]->apply(mItemIdToItemMap, mItemIdToExifMap);
+ mItemReferences[i]->apply(mItemIdToItemMap, mItemIdToMetaMap);
}
bool foundPrimary = false;
@@ -1747,11 +1768,11 @@
}
const ImageItem &image = mItemIdToItemMap[itemIndex];
- if (image.cdscRefs.size() == 0) {
+ if (image.exifRefs.size() == 0) {
return NAME_NOT_FOUND;
}
- ssize_t exifIndex = mItemIdToExifMap.indexOfKey(image.cdscRefs[0]);
+ ssize_t exifIndex = mItemIdToMetaMap.indexOfKey(image.exifRefs[0]);
if (exifIndex < 0) {
return NAME_NOT_FOUND;
}
@@ -1759,7 +1780,7 @@
// skip the first 4-byte of the offset to TIFF header
uint32_t tiffOffset;
if (!mDataSource->readAt(
- mItemIdToExifMap[exifIndex].offset, &tiffOffset, 4)) {
+ mItemIdToMetaMap[exifIndex].offset, &tiffOffset, 4)) {
return ERROR_IO;
}
@@ -1772,16 +1793,43 @@
// exif data. The size of the item should be > 4 for a non-empty exif (this
// was already checked when the item was added). Also check that the tiff
// header offset is valid.
- if (mItemIdToExifMap[exifIndex].size <= 4 ||
- tiffOffset > mItemIdToExifMap[exifIndex].size - 4) {
+ if (mItemIdToMetaMap[exifIndex].size <= 4 ||
+ tiffOffset > mItemIdToMetaMap[exifIndex].size - 4) {
return ERROR_MALFORMED;
}
// Offset of 'Exif\0\0' relative to the beginning of 'Exif' item
// (first 4-byte is the tiff header offset)
uint32_t exifOffset = 4 + tiffOffset - 6;
- *offset = mItemIdToExifMap[exifIndex].offset + exifOffset;
- *size = mItemIdToExifMap[exifIndex].size - exifOffset;
+ *offset = mItemIdToMetaMap[exifIndex].offset + exifOffset;
+ *size = mItemIdToMetaMap[exifIndex].size - exifOffset;
+ return OK;
+}
+
+status_t ItemTable::getXmpOffsetAndSize(off64_t *offset, size_t *size) {
+ if (!mImageItemsValid) {
+ return INVALID_OPERATION;
+ }
+
+ ssize_t itemIndex = mItemIdToItemMap.indexOfKey(mPrimaryItemId);
+
+ // this should not happen, something's seriously wrong.
+ if (itemIndex < 0) {
+ return INVALID_OPERATION;
+ }
+
+ const ImageItem &image = mItemIdToItemMap[itemIndex];
+ if (image.xmpRefs.size() == 0) {
+ return NAME_NOT_FOUND;
+ }
+
+ ssize_t xmpIndex = mItemIdToMetaMap.indexOfKey(image.xmpRefs[0]);
+ if (xmpIndex < 0) {
+ return NAME_NOT_FOUND;
+ }
+
+ *offset = mItemIdToMetaMap[xmpIndex].offset;
+ *size = mItemIdToMetaMap[xmpIndex].size;
return OK;
}
diff --git a/media/extractors/mp4/ItemTable.h b/media/extractors/mp4/ItemTable.h
index b19dc18..62826b6 100644
--- a/media/extractors/mp4/ItemTable.h
+++ b/media/extractors/mp4/ItemTable.h
@@ -34,7 +34,7 @@
struct AssociationEntry;
struct ImageItem;
-struct ExifItem;
+struct ExternalMetaItem;
struct ItemLoc;
struct ItemInfo;
struct ItemProperty;
@@ -59,6 +59,7 @@
status_t getImageOffsetAndSize(
uint32_t *itemIndex, off64_t *offset, size_t *size);
status_t getExifOffsetAndSize(off64_t *offset, size_t *size);
+ status_t getXmpOffsetAndSize(off64_t *offset, size_t *size);
protected:
~ItemTable();
@@ -84,7 +85,7 @@
bool mImageItemsValid;
uint32_t mCurrentItemIndex;
KeyedVector<uint32_t, ImageItem> mItemIdToItemMap;
- KeyedVector<uint32_t, ExifItem> mItemIdToExifMap;
+ KeyedVector<uint32_t, ExternalMetaItem> mItemIdToMetaMap;
Vector<uint32_t> mDisplayables;
status_t parseIlocBox(off64_t offset, size_t size);
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 7989d4b..221bf4f 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -681,6 +681,19 @@
AMediaFormat_setInt64(mFileMetaData,
AMEDIAFORMAT_KEY_EXIF_SIZE, (int64_t)exifSize);
}
+ off64_t xmpOffset;
+ size_t xmpSize;
+ if (mItemTable->getXmpOffsetAndSize(&xmpOffset, &xmpSize) == OK) {
+ // TODO(chz): b/175717339
+ // Use a hard-coded string here instead of named keys. The keys are available
+ // only on API 31+. The mp4 extractor is part of mainline and has min_sdk_version
+ // of 29. This hard-coded string can be replaced with the named constant once
+ // the mp4 extractor is built against API 31+.
+ AMediaFormat_setInt64(mFileMetaData,
+ "xmp-offset" /*AMEDIAFORMAT_KEY_XMP_OFFSET*/, (int64_t)xmpOffset);
+ AMediaFormat_setInt64(mFileMetaData,
+ "xmp-size" /*AMEDIAFORMAT_KEY_XMP_SIZE*/, (int64_t)xmpSize);
+ }
for (uint32_t imageIndex = 0;
imageIndex < mItemTable->countImages(); imageIndex++) {
AMediaFormat *meta = mItemTable->getImageMeta(imageIndex);
diff --git a/media/libmedia/include/media/mediametadataretriever.h b/media/libmedia/include/media/mediametadataretriever.h
index 1fe6ffc..fba1a30 100644
--- a/media/libmedia/include/media/mediametadataretriever.h
+++ b/media/libmedia/include/media/mediametadataretriever.h
@@ -74,6 +74,8 @@
METADATA_KEY_SAMPLERATE = 38,
METADATA_KEY_BITS_PER_SAMPLE = 39,
METADATA_KEY_VIDEO_CODEC_MIME_TYPE = 40,
+ METADATA_KEY_XMP_OFFSET = 41,
+ METADATA_KEY_XMP_LENGTH = 42,
// Add more here...
};
diff --git a/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp b/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp
index 02fb6bb..93e03ee 100644
--- a/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp
+++ b/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp
@@ -529,6 +529,15 @@
mMetaData.add(METADATA_KEY_EXIF_LENGTH, String8(tmp));
}
+ int64_t xmpOffset, xmpSize;
+ if (meta->findInt64(kKeyXmpOffset, &xmpOffset)
+ && meta->findInt64(kKeyXmpSize, &xmpSize)) {
+ sprintf(tmp, "%lld", (long long)xmpOffset);
+ mMetaData.add(METADATA_KEY_XMP_OFFSET, String8(tmp));
+ sprintf(tmp, "%lld", (long long)xmpSize);
+ mMetaData.add(METADATA_KEY_XMP_LENGTH, String8(tmp));
+ }
+
bool hasAudio = false;
bool hasVideo = false;
int32_t videoWidth = -1;
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index fbaa38e..f63740e 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -729,6 +729,8 @@
{
{ "exif-offset", kKeyExifOffset },
{ "exif-size", kKeyExifSize },
+ { "xmp-offset", kKeyXmpOffset },
+ { "xmp-size", kKeyXmpSize },
{ "target-time", kKeyTargetTime },
{ "thumbnail-time", kKeyThumbnailTime },
{ "timeUs", kKeyTime },
diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h
index f260510..940bd86 100644
--- a/media/libstagefright/include/media/stagefright/MetaDataBase.h
+++ b/media/libstagefright/include/media/stagefright/MetaDataBase.h
@@ -225,6 +225,8 @@
kKeyExifSize = 'exsz', // int64_t, Exif data size
kKeyExifTiffOffset = 'thdr', // int32_t, if > 0, buffer contains exif data block with
// tiff hdr at specified offset
+ kKeyXmpOffset = 'xmof', // int64_t, XMP data offset
+ kKeyXmpSize = 'xmsz', // int64_t, XMP data size
kKeyPcmBigEndian = 'pcmb', // bool (int32_t)
// Key for ALAC Magic Cookie
diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index 47214c5..8e673ca 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -384,6 +384,8 @@
EXPORT const char* AMEDIAFORMAT_KEY_TRACK_INDEX = "track-index";
EXPORT const char* AMEDIAFORMAT_KEY_VALID_SAMPLES = "valid-samples";
EXPORT const char* AMEDIAFORMAT_KEY_WIDTH = "width";
+EXPORT const char* AMEDIAFORMAT_KEY_XMP_OFFSET = "xmp-offset";
+EXPORT const char* AMEDIAFORMAT_KEY_XMP_SIZE = "xmp-size";
EXPORT const char* AMEDIAFORMAT_KEY_YEAR = "year";
} // extern "C"
diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h
index 8f39929..0b9024f 100644
--- a/media/ndk/include/media/NdkMediaFormat.h
+++ b/media/ndk/include/media/NdkMediaFormat.h
@@ -325,6 +325,8 @@
#if __ANDROID_API__ >= 31
extern const char* AMEDIAFORMAT_KEY_SLOW_MOTION_MARKERS __INTRODUCED_IN(31);
extern const char* AMEDIAFORMAT_KEY_THUMBNAIL_CSD_AV1C __INTRODUCED_IN(31);
+extern const char* AMEDIAFORMAT_KEY_XMP_OFFSET __INTRODUCED_IN(31);
+extern const char* AMEDIAFORMAT_KEY_XMP_SIZE __INTRODUCED_IN(31);
#endif /* __ANDROID_API__ >= 31 */
__END_DECLS
diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt
index 96f1710..237b66e 100644
--- a/media/ndk/libmediandk.map.txt
+++ b/media/ndk/libmediandk.map.txt
@@ -151,6 +151,8 @@
AMEDIAFORMAT_KEY_TRACK_ID; # var introduced=28
AMEDIAFORMAT_KEY_VALID_SAMPLES; # var introduced=29
AMEDIAFORMAT_KEY_WIDTH; # var introduced=21
+ AMEDIAFORMAT_KEY_XMP_OFFSET; # var introduced=31
+ AMEDIAFORMAT_KEY_XMP_SIZE; # var introduced=31
AMEDIAFORMAT_KEY_YEAR; # var introduced=29
AMediaCodecActionCode_isRecoverable; # introduced=28
AMediaCodecActionCode_isTransient; # introduced=28