Merge "Adds support for 8-bit (unsigned) PCM wave files."
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index c87007c..f935bb9 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -244,6 +244,8 @@
                 DEVICE_OUT_WIRED_HEADPHONE | DEVICE_OUT_BLUETOOTH_SCO | DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
                 DEVICE_OUT_BLUETOOTH_SCO_CARKIT | DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
                 DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | DEVICE_OUT_AUX_DIGITAL | DEVICE_OUT_DEFAULT),
+        DEVICE_OUT_ALL_A2DP = (DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
+                DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER),
 
         // input devices
         DEVICE_IN_COMMUNICATION = 0x10000,
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 2bc2734..3b21468 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -60,6 +60,7 @@
     kKeyAuthor            = 'auth',  // cstring
     kKeyCDTrackNumber     = 'cdtr',  // cstring
     kKeyDate              = 'date',  // cstring
+    kKeyWriter            = 'writ',  // cstring
 };
 
 enum {
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 @@
       mHaveMetadata(false),
       mHasVideo(false),
       mFirstTrack(NULL),
-      mLastTrack(NULL) {
+      mLastTrack(NULL),
+      mFileMetaData(new MetaData) {
 }
 
 MPEG4Extractor::~MPEG4Extractor() {
@@ -169,20 +170,12 @@
 }
 
 sp<MetaData> MPEG4Extractor::getMetaData() {
-    sp<MetaData> meta = new MetaData;
-
     status_t err;
     if ((err = readMetaData()) != OK) {
-        return meta;
+        return new MetaData;
     }
 
-    if (mHasVideo) {
-        meta->setCString(kKeyMIMEType, "video/mp4");
-    } else {
-        meta->setCString(kKeyMIMEType, "audio/mp4");
-    }
-
-    return meta;
+    return mFileMetaData;
 }
 
 size_t MPEG4Extractor::countTracks() {
@@ -256,6 +249,12 @@
     }
 
     if (mHaveMetadata) {
+        if (mHasVideo) {
+            mFileMetaData->setCString(kKeyMIMEType, "video/mp4");
+        } else {
+            mFileMetaData->setCString(kKeyMIMEType, "audio/mp4");
+        }
+
         return OK;
     }
 
@@ -270,6 +269,41 @@
     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 @@
 
     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 @@
     }
 #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 @@
         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 @@
             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 @@
     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 @@
         { 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 @@
 
     // 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 @@
 
     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 &);