summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorAndreas Huber <andih@google.com>2010-01-19 16:43:53 -0800
committerAndreas Huber <andih@google.com>2010-01-20 09:50:32 -0800
commit72b8c5ae6a5f97a4fcfc6d23d33159d6ae50179c (patch)
tree8606dbc6c30039a5ca9f54d2d1c3d36e83c50c0b /media
parent08e7eb983fcb9860a574e964ff905f75aab88d2f (diff)
downloadframeworks_av-72b8c5ae6a5f97a4fcfc6d23d33159d6ae50179c.zip
frameworks_av-72b8c5ae6a5f97a4fcfc6d23d33159d6ae50179c.tar.gz
frameworks_av-72b8c5ae6a5f97a4fcfc6d23d33159d6ae50179c.tar.bz2
Support for 'iTunes-style' metadata in .mp4 and .3gp files.
related-to-bug: 2368967
Diffstat (limited to 'media')
-rw-r--r--media/libstagefright/MPEG4Extractor.cpp244
-rw-r--r--media/libstagefright/StagefrightMetadataRetriever.cpp2
-rw-r--r--media/libstagefright/include/MPEG4Extractor.h5
3 files changed, 238 insertions, 13 deletions
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 0e9900b..ed12b6d 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -154,7 +154,8 @@ MPEG4Extractor::MPEG4Extractor(const sp<DataSource> &source)
mHaveMetadata(false),
mHasVideo(false),
mFirstTrack(NULL),
- mLastTrack(NULL) {
+ mLastTrack(NULL),
+ mFileMetaData(new MetaData) {
}
MPEG4Extractor::~MPEG4Extractor() {
@@ -169,20 +170,12 @@ MPEG4Extractor::~MPEG4Extractor() {
}
sp<MetaData> MPEG4Extractor::getMetaData() {
- sp<MetaData> meta = new MetaData;
-
status_t err;
if ((err = readMetaData()) != OK) {
- return meta;
- }
-
- if (mHasVideo) {
- meta->setCString(kKeyMIMEType, "video/mp4");
- } else {
- meta->setCString(kKeyMIMEType, "audio/mp4");
+ return new MetaData;
}
- return meta;
+ return mFileMetaData;
}
size_t MPEG4Extractor::countTracks() {
@@ -256,6 +249,12 @@ status_t MPEG4Extractor::readMetaData() {
}
if (mHaveMetadata) {
+ if (mHasVideo) {
+ mFileMetaData->setCString(kKeyMIMEType, "video/mp4");
+ } else {
+ mFileMetaData->setCString(kKeyMIMEType, "audio/mp4");
+ }
+
return OK;
}
@@ -270,6 +269,41 @@ static void MakeFourCCString(uint32_t x, char *s) {
s[4] = '\0';
}
+struct PathAdder {
+ PathAdder(Vector<uint32_t> *path, uint32_t chunkType)
+ : mPath(path) {
+ mPath->push(chunkType);
+ }
+
+ ~PathAdder() {
+ mPath->pop();
+ }
+
+private:
+ Vector<uint32_t> *mPath;
+
+ PathAdder(const PathAdder &);
+ PathAdder &operator=(const PathAdder &);
+};
+
+static bool underMetaDataPath(const Vector<uint32_t> &path) {
+ return path.size() >= 5
+ && path[0] == FOURCC('m', 'o', 'o', 'v')
+ && path[1] == FOURCC('u', 'd', 't', 'a')
+ && path[2] == FOURCC('m', 'e', 't', 'a')
+ && path[3] == FOURCC('i', 'l', 's', 't');
+}
+
+// Given a time in seconds since Jan 1 1904, produce a human-readable string.
+static void convertTimeToDate(int64_t time_1904, String8 *s) {
+ time_t time_1970 = time_1904 - (((66 * 365 + 17) * 24) * 3600);
+
+ char tmp[32];
+ strftime(tmp, sizeof(tmp), "%Y%m%dT%H%M%S.000Z", gmtime(&time_1970));
+
+ s->setTo(tmp);
+}
+
status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
uint32_t hdr[2];
if (mDataSource->readAt(*offset, hdr, 8) < 8) {
@@ -297,7 +331,8 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
char buffer[256];
if (chunk_size <= sizeof(buffer)) {
- if (mDataSource->readAt(*offset, buffer, chunk_size) < chunk_size) {
+ if (mDataSource->readAt(*offset, buffer, chunk_size)
+ < (ssize_t)chunk_size) {
return ERROR_IO;
}
@@ -305,8 +340,25 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
}
#endif
+ PathAdder autoAdder(&mPath, chunk_type);
+
off_t chunk_data_size = *offset + chunk_size - data_offset;
+ if (chunk_type != FOURCC('c', 'p', 'r', 't')
+ && mPath.size() == 5 && underMetaDataPath(mPath)) {
+ off_t stop_offset = *offset + chunk_size;
+ *offset = data_offset;
+ while (*offset < stop_offset) {
+ status_t err = parseChunk(offset, depth + 1);
+ if (err != OK) {
+ return err;
+ }
+ }
+ CHECK_EQ(*offset, stop_offset);
+
+ return OK;
+ }
+
switch(chunk_type) {
case FOURCC('m', 'o', 'o', 'v'):
case FOURCC('t', 'r', 'a', 'k'):
@@ -319,6 +371,8 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
case FOURCC('t', 'r', 'a', 'f'):
case FOURCC('m', 'f', 'r', 'a'):
case FOURCC('s', 'k', 'i' ,'p'):
+ case FOURCC('u', 'd', 't', 'a'):
+ case FOURCC('i', 'l', 's', 't'):
{
off_t stop_offset = *offset + chunk_size;
*offset = data_offset;
@@ -748,6 +802,76 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
break;
}
+ case FOURCC('m', 'e', 't', 'a'):
+ {
+ uint8_t buffer[4];
+ CHECK(chunk_data_size >= (off_t)sizeof(buffer));
+ if (mDataSource->readAt(
+ data_offset, buffer, 4) < 4) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(buffer) != 0) {
+ // Should be version 0, flags 0.
+ return ERROR_MALFORMED;
+ }
+
+ off_t stop_offset = *offset + chunk_size;
+ *offset = data_offset + sizeof(buffer);
+ while (*offset < stop_offset) {
+ status_t err = parseChunk(offset, depth + 1);
+ if (err != OK) {
+ return err;
+ }
+ }
+ CHECK_EQ(*offset, stop_offset);
+ break;
+ }
+
+ case FOURCC('d', 'a', 't', 'a'):
+ {
+ if (mPath.size() == 6 && underMetaDataPath(mPath)) {
+ status_t err = parseMetaData(data_offset, chunk_data_size);
+
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('m', 'v', 'h', 'd'):
+ {
+ if (chunk_data_size < 12) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t header[12];
+ if (mDataSource->readAt(
+ data_offset, header, sizeof(header))
+ < (ssize_t)sizeof(header)) {
+ return ERROR_IO;
+ }
+
+ int64_t creationTime;
+ if (header[0] == 1) {
+ creationTime = U64_AT(&header[4]);
+ } else {
+ CHECK_EQ(header[0], 0);
+ creationTime = U32_AT(&header[4]);
+ }
+
+ String8 s;
+ convertTimeToDate(creationTime, &s);
+
+ mFileMetaData->setCString(kKeyDate, s.string());
+
+ *offset += chunk_size;
+ break;
+ }
+
default:
{
*offset += chunk_size;
@@ -758,6 +882,102 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
return OK;
}
+status_t MPEG4Extractor::parseMetaData(off_t offset, size_t size) {
+ if (size < 4) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t *buffer = new uint8_t[size + 1];
+ if (mDataSource->readAt(
+ offset, buffer, size) != (ssize_t)size) {
+ delete[] buffer;
+ buffer = NULL;
+
+ return ERROR_IO;
+ }
+
+ uint32_t flags = U32_AT(buffer);
+
+ uint32_t metadataKey = 0;
+ switch (mPath[4]) {
+ case FOURCC(0xa9, 'a', 'l', 'b'):
+ {
+ metadataKey = kKeyAlbum;
+ break;
+ }
+ case FOURCC(0xa9, 'A', 'R', 'T'):
+ {
+ metadataKey = kKeyArtist;
+ break;
+ }
+ case FOURCC(0xa9, 'd', 'a', 'y'):
+ {
+ metadataKey = kKeyYear;
+ break;
+ }
+ case FOURCC(0xa9, 'n', 'a', 'm'):
+ {
+ metadataKey = kKeyTitle;
+ break;
+ }
+ case FOURCC(0xa9, 'w', 'r', 't'):
+ {
+ metadataKey = kKeyWriter;
+ break;
+ }
+ case FOURCC('c', 'o', 'v', 'r'):
+ {
+ metadataKey = kKeyAlbumArt;
+ break;
+ }
+ case FOURCC('g', 'n', 'r', 'e'):
+ {
+ metadataKey = kKeyGenre;
+ break;
+ }
+ case FOURCC('t', 'r', 'k', 'n'):
+ {
+ if (size == 16 && flags == 0) {
+ char tmp[16];
+ sprintf(tmp, "%d/%d",
+ (int)buffer[size - 5], (int)buffer[size - 3]);
+
+ printf("track: %s\n", tmp);
+ mFileMetaData->setCString(kKeyCDTrackNumber, tmp);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (size >= 8 && metadataKey) {
+ if (metadataKey == kKeyAlbumArt) {
+ mFileMetaData->setData(
+ kKeyAlbumArt, MetaData::TYPE_NONE,
+ buffer + 8, size - 8);
+ } else if (metadataKey == kKeyGenre) {
+ if (flags == 0) {
+ // uint8_t
+ char genre[10];
+ sprintf(genre, "%d", (int)buffer[size - 1]);
+
+ mFileMetaData->setCString(metadataKey, genre);
+ }
+ } else {
+ buffer[size] = '\0';
+
+ mFileMetaData->setCString(
+ metadataKey, (const char *)buffer + 8);
+ }
+ }
+
+ delete[] buffer;
+ buffer = NULL;
+
+ return OK;
+}
+
sp<MediaSource> MPEG4Extractor::getTrack(size_t index) {
status_t err;
if ((err = readMetaData()) != OK) {
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 020887c..313a9ed 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -315,6 +315,7 @@ void StagefrightMetadataRetriever::parseMetaData() {
{ kKeyGenre, METADATA_KEY_GENRE },
{ kKeyTitle, METADATA_KEY_TITLE },
{ kKeyYear, METADATA_KEY_YEAR },
+ { kKeyWriter, METADATA_KEY_WRITER },
};
static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);
@@ -357,7 +358,6 @@ void StagefrightMetadataRetriever::parseMetaData() {
// The duration value is a string representing the duration in ms.
sprintf(tmp, "%lld", (maxDurationUs + 500) / 1000);
-
mMetaData.add(METADATA_KEY_DURATION, String8(tmp));
}
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index 0e360e8..1a13446 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -19,6 +19,7 @@
#define MPEG4_EXTRACTOR_H_
#include <media/stagefright/MediaExtractor.h>
+#include <utils/Vector.h>
namespace android {
@@ -55,10 +56,14 @@ private:
Track *mFirstTrack, *mLastTrack;
+ sp<MetaData> mFileMetaData;
+
uint32_t mHandlerType;
+ Vector<uint32_t> mPath;
status_t readMetaData();
status_t parseChunk(off_t *offset, int depth);
+ status_t parseMetaData(off_t offset, size_t size);
MPEG4Extractor(const MPEG4Extractor &);
MPEG4Extractor &operator=(const MPEG4Extractor &);