Bug 908503: Import libstagefright code from AOSP; r=cpearce
authorAnthony Jones <ajones@mozilla.com>
Mon, 12 May 2014 09:46:09 +1200
changeset 182522 05fe24d0499a55e15dc521075dd9cf20e92c2347
parent 182521 ca32138df109d5189cf75ae1469a2c9c33ab4282
child 182523 1917418786902a900ff00ff637f80efee5638301
push id26764
push usercbook@mozilla.com
push dateMon, 12 May 2014 11:35:17 +0000
treeherdermozilla-central@a64ed5aba131 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs908503
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 908503: Import libstagefright code from AOSP; r=cpearce
media/libstagefright/frameworks/av/include/media/stagefright/DataSource.h
media/libstagefright/frameworks/av/include/media/stagefright/MediaBuffer.h
media/libstagefright/frameworks/av/include/media/stagefright/MediaBufferGroup.h
media/libstagefright/frameworks/av/include/media/stagefright/MediaDefs.h
media/libstagefright/frameworks/av/include/media/stagefright/MediaErrors.h
media/libstagefright/frameworks/av/include/media/stagefright/MediaExtractor.h
media/libstagefright/frameworks/av/include/media/stagefright/MediaSource.h
media/libstagefright/frameworks/av/include/media/stagefright/MetaData.h
media/libstagefright/frameworks/av/include/media/stagefright/Utils.h
media/libstagefright/frameworks/av/include/media/stagefright/foundation/AAtomizer.h
media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABase.h
media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABitReader.h
media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABuffer.h
media/libstagefright/frameworks/av/include/media/stagefright/foundation/ADebug.h
media/libstagefright/frameworks/av/include/media/stagefright/foundation/AHandler.h
media/libstagefright/frameworks/av/include/media/stagefright/foundation/AString.h
media/libstagefright/frameworks/av/include/media/stagefright/foundation/hexdump.h
media/libstagefright/frameworks/av/media/libstagefright/DataSource.cpp
media/libstagefright/frameworks/av/media/libstagefright/ESDS.cpp
media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
media/libstagefright/frameworks/av/media/libstagefright/MediaBuffer.cpp
media/libstagefright/frameworks/av/media/libstagefright/MediaBufferGroup.cpp
media/libstagefright/frameworks/av/media/libstagefright/MediaDefs.cpp
media/libstagefright/frameworks/av/media/libstagefright/MediaSource.cpp
media/libstagefright/frameworks/av/media/libstagefright/MetaData.cpp
media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp
media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp
media/libstagefright/frameworks/av/media/libstagefright/Utils.cpp
media/libstagefright/frameworks/av/media/libstagefright/foundation/AAtomizer.cpp
media/libstagefright/frameworks/av/media/libstagefright/foundation/ABitReader.cpp
media/libstagefright/frameworks/av/media/libstagefright/foundation/ABuffer.cpp
media/libstagefright/frameworks/av/media/libstagefright/foundation/AString.cpp
media/libstagefright/frameworks/av/media/libstagefright/foundation/hexdump.cpp
media/libstagefright/frameworks/av/media/libstagefright/id3/ID3.cpp
media/libstagefright/frameworks/av/media/libstagefright/include/AMRExtractor.h
media/libstagefright/frameworks/av/media/libstagefright/include/ESDS.h
media/libstagefright/frameworks/av/media/libstagefright/include/ID3.h
media/libstagefright/frameworks/av/media/libstagefright/include/MPEG4Extractor.h
media/libstagefright/frameworks/av/media/libstagefright/include/SampleIterator.h
media/libstagefright/frameworks/av/media/libstagefright/include/SampleTable.h
media/libstagefright/system/core/debuggerd/backtrace.h
media/libstagefright/system/core/include/android/log.h
media/libstagefright/system/core/include/corkscrew/backtrace.h
media/libstagefright/system/core/include/corkscrew/map_info.h
media/libstagefright/system/core/include/corkscrew/ptrace.h
media/libstagefright/system/core/include/corkscrew/symbol_table.h
media/libstagefright/system/core/include/cutils/jstring.h
media/libstagefright/system/core/include/cutils/log.h
media/libstagefright/system/core/include/cutils/properties.h
media/libstagefright/system/core/include/cutils/sched_policy.h
media/libstagefright/system/core/include/log/event_tag_map.h
media/libstagefright/system/core/include/log/log.h
media/libstagefright/system/core/include/log/logd.h
media/libstagefright/system/core/include/log/logger.h
media/libstagefright/system/core/include/log/logprint.h
media/libstagefright/system/core/include/log/uio.h
media/libstagefright/system/core/include/system/graphics.h
media/libstagefright/system/core/include/sysutils/List.h
media/libstagefright/system/core/include/utils/Atomic.h
media/libstagefright/system/core/include/utils/CallStack.h
media/libstagefright/system/core/include/utils/Condition.h
media/libstagefright/system/core/include/utils/Debug.h
media/libstagefright/system/core/include/utils/Errors.h
media/libstagefright/system/core/include/utils/KeyedVector.h
media/libstagefright/system/core/include/utils/List.h
media/libstagefright/system/core/include/utils/Log.h
media/libstagefright/system/core/include/utils/Mutex.h
media/libstagefright/system/core/include/utils/RWLock.h
media/libstagefright/system/core/include/utils/RefBase.h
media/libstagefright/system/core/include/utils/SharedBuffer.h
media/libstagefright/system/core/include/utils/SortedVector.h
media/libstagefright/system/core/include/utils/String16.h
media/libstagefright/system/core/include/utils/String8.h
media/libstagefright/system/core/include/utils/StrongPointer.h
media/libstagefright/system/core/include/utils/Timers.h
media/libstagefright/system/core/include/utils/TypeHelpers.h
media/libstagefright/system/core/include/utils/Unicode.h
media/libstagefright/system/core/include/utils/Vector.h
media/libstagefright/system/core/include/utils/VectorImpl.h
media/libstagefright/system/core/libcutils/strdup16to8.c
media/libstagefright/system/core/liblog/fake_log_device.c
media/libstagefright/system/core/liblog/logd_write.c
media/libstagefright/system/core/liblog/logprint.c
media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/Errors.h
media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/KeyedVector.h
media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/SortedVector.h
media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/Vector.h
media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/VectorImpl.h
media/libstagefright/system/core/libutils/RefBase.cpp
media/libstagefright/system/core/libutils/SharedBuffer.cpp
media/libstagefright/system/core/libutils/Static.cpp
media/libstagefright/system/core/libutils/String16.cpp
media/libstagefright/system/core/libutils/String8.cpp
media/libstagefright/system/core/libutils/Unicode.cpp
media/libstagefright/system/core/libutils/VectorImpl.cpp
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/DataSource.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DATA_SOURCE_H_
+
+#define DATA_SOURCE_H_
+
+#include <sys/types.h>
+
+#include <media/stagefright/MediaErrors.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/List.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+#include <drm/DrmManagerClient.h>
+
+namespace android {
+
+struct AMessage;
+class String8;
+
+class DataSource : public RefBase {
+public:
+    enum Flags {
+        kWantsPrefetching      = 1,
+        kStreamedFromLocalHost = 2,
+        kIsCachingDataSource   = 4,
+        kIsHTTPBasedSource     = 8,
+    };
+
+    static sp<DataSource> CreateFromURI(
+            const char *uri,
+            const KeyedVector<String8, String8> *headers = NULL);
+
+    DataSource() {}
+
+    virtual status_t initCheck() const = 0;
+
+    virtual ssize_t readAt(off64_t offset, void *data, size_t size) = 0;
+
+    // Convenience methods:
+    bool getUInt16(off64_t offset, uint16_t *x);
+    bool getUInt24(off64_t offset, uint32_t *x); // 3 byte int, returned as a 32-bit int
+    bool getUInt32(off64_t offset, uint32_t *x);
+    bool getUInt64(off64_t offset, uint64_t *x);
+
+    // May return ERROR_UNSUPPORTED.
+    virtual status_t getSize(off64_t *size);
+
+    virtual uint32_t flags() {
+        return 0;
+    }
+
+    virtual status_t reconnectAtOffset(off64_t offset) {
+        return ERROR_UNSUPPORTED;
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    bool sniff(String8 *mimeType, float *confidence, sp<AMessage> *meta);
+
+    // The sniffer can optionally fill in "meta" with an AMessage containing
+    // a dictionary of values that helps the corresponding extractor initialize
+    // its state without duplicating effort already exerted by the sniffer.
+    typedef bool (*SnifferFunc)(
+            const sp<DataSource> &source, String8 *mimeType,
+            float *confidence, sp<AMessage> *meta);
+
+    static void RegisterSniffer(SnifferFunc func);
+    static void RegisterDefaultSniffers();
+
+    // for DRM
+    virtual sp<DecryptHandle> DrmInitialization(const char *mime = NULL) {
+        return NULL;
+    }
+    virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client) {};
+
+    virtual String8 getUri() {
+        return String8();
+    }
+
+    virtual String8 getMIMEType() const;
+
+protected:
+    virtual ~DataSource() {}
+
+private:
+    static Mutex gSnifferMutex;
+    static List<SnifferFunc> gSniffers;
+
+    DataSource(const DataSource &);
+    DataSource &operator=(const DataSource &);
+};
+
+}  // namespace android
+
+#endif  // DATA_SOURCE_H_
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/MediaBuffer.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_BUFFER_H_
+
+#define MEDIA_BUFFER_H_
+
+#include <pthread.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ABuffer;
+class GraphicBuffer;
+class MediaBuffer;
+class MediaBufferObserver;
+class MetaData;
+
+class MediaBufferObserver {
+public:
+    MediaBufferObserver() {}
+    virtual ~MediaBufferObserver() {}
+
+    virtual void signalBufferReturned(MediaBuffer *buffer) = 0;
+
+private:
+    MediaBufferObserver(const MediaBufferObserver &);
+    MediaBufferObserver &operator=(const MediaBufferObserver &);
+};
+
+class MediaBuffer {
+public:
+    // The underlying data remains the responsibility of the caller!
+    MediaBuffer(void *data, size_t size);
+
+    MediaBuffer(size_t size);
+
+    MediaBuffer(const sp<GraphicBuffer>& graphicBuffer);
+
+    MediaBuffer(const sp<ABuffer> &buffer);
+
+    // Decrements the reference count and returns the buffer to its
+    // associated MediaBufferGroup if the reference count drops to 0.
+    void release();
+
+    // Increments the reference count.
+    void add_ref();
+
+    void *data() const;
+    size_t size() const;
+
+    size_t range_offset() const;
+    size_t range_length() const;
+
+    void set_range(size_t offset, size_t length);
+
+    sp<GraphicBuffer> graphicBuffer() const;
+
+    sp<MetaData> meta_data();
+
+    // Clears meta data and resets the range to the full extent.
+    void reset();
+
+    void setObserver(MediaBufferObserver *group);
+
+    // Returns a clone of this MediaBuffer increasing its reference count.
+    // The clone references the same data but has its own range and
+    // MetaData.
+    MediaBuffer *clone();
+
+    int refcount() const;
+
+protected:
+    virtual ~MediaBuffer();
+
+private:
+    friend class MediaBufferGroup;
+    friend class OMXDecoder;
+
+    // For use by OMXDecoder, reference count must be 1, drop reference
+    // count to 0 without signalling the observer.
+    void claim();
+
+    MediaBufferObserver *mObserver;
+    MediaBuffer *mNextBuffer;
+    int mRefCount;
+
+    void *mData;
+    size_t mSize, mRangeOffset, mRangeLength;
+    sp<GraphicBuffer> mGraphicBuffer;
+    sp<ABuffer> mBuffer;
+
+    bool mOwnsData;
+
+    sp<MetaData> mMetaData;
+
+    MediaBuffer *mOriginal;
+
+    void setNextBuffer(MediaBuffer *buffer);
+    MediaBuffer *nextBuffer();
+
+    MediaBuffer(const MediaBuffer &);
+    MediaBuffer &operator=(const MediaBuffer &);
+};
+
+}  // namespace android
+
+#endif  // MEDIA_BUFFER_H_
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/MediaBufferGroup.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_BUFFER_GROUP_H_
+
+#define MEDIA_BUFFER_GROUP_H_
+
+#include <media/stagefright/MediaBuffer.h>
+#include <utils/Errors.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class MediaBuffer;
+class MetaData;
+
+class MediaBufferGroup : public MediaBufferObserver {
+public:
+    MediaBufferGroup();
+    ~MediaBufferGroup();
+
+    void add_buffer(MediaBuffer *buffer);
+
+    // Blocks until a buffer is available and returns it to the caller,
+    // the returned buffer will have a reference count of 1.
+    status_t acquire_buffer(MediaBuffer **buffer);
+
+protected:
+    virtual void signalBufferReturned(MediaBuffer *buffer);
+
+private:
+    friend class MediaBuffer;
+
+    Mutex mLock;
+    Condition mCondition;
+
+    MediaBuffer *mFirstBuffer, *mLastBuffer;
+
+    MediaBufferGroup(const MediaBufferGroup &);
+    MediaBufferGroup &operator=(const MediaBufferGroup &);
+};
+
+}  // namespace android
+
+#endif  // MEDIA_BUFFER_GROUP_H_
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/MediaDefs.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_DEFS_H_
+
+#define MEDIA_DEFS_H_
+
+namespace android {
+
+extern const char *MEDIA_MIMETYPE_IMAGE_JPEG;
+
+extern const char *MEDIA_MIMETYPE_VIDEO_VP8;
+extern const char *MEDIA_MIMETYPE_VIDEO_VP9;
+extern const char *MEDIA_MIMETYPE_VIDEO_AVC;
+extern const char *MEDIA_MIMETYPE_VIDEO_MPEG4;
+extern const char *MEDIA_MIMETYPE_VIDEO_H263;
+extern const char *MEDIA_MIMETYPE_VIDEO_MPEG2;
+extern const char *MEDIA_MIMETYPE_VIDEO_RAW;
+
+extern const char *MEDIA_MIMETYPE_AUDIO_AMR_NB;
+extern const char *MEDIA_MIMETYPE_AUDIO_AMR_WB;
+extern const char *MEDIA_MIMETYPE_AUDIO_MPEG;           // layer III
+extern const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I;
+extern const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II;
+extern const char *MEDIA_MIMETYPE_AUDIO_AAC;
+extern const char *MEDIA_MIMETYPE_AUDIO_QCELP;
+extern const char *MEDIA_MIMETYPE_AUDIO_VORBIS;
+extern const char *MEDIA_MIMETYPE_AUDIO_G711_ALAW;
+extern const char *MEDIA_MIMETYPE_AUDIO_G711_MLAW;
+extern const char *MEDIA_MIMETYPE_AUDIO_RAW;
+extern const char *MEDIA_MIMETYPE_AUDIO_FLAC;
+extern const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS;
+extern const char *MEDIA_MIMETYPE_AUDIO_MSGSM;
+
+extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4;
+extern const char *MEDIA_MIMETYPE_CONTAINER_WAV;
+extern const char *MEDIA_MIMETYPE_CONTAINER_OGG;
+extern const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA;
+extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG2TS;
+extern const char *MEDIA_MIMETYPE_CONTAINER_AVI;
+extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG2PS;
+
+extern const char *MEDIA_MIMETYPE_CONTAINER_WVM;
+
+extern const char *MEDIA_MIMETYPE_TEXT_3GPP;
+extern const char *MEDIA_MIMETYPE_TEXT_SUBRIP;
+
+}  // namespace android
+
+#endif  // MEDIA_DEFS_H_
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/MediaErrors.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_ERRORS_H_
+
+#define MEDIA_ERRORS_H_
+
+#include <utils/Errors.h>
+
+namespace android {
+
+enum {
+    MEDIA_ERROR_BASE        = -1000,
+
+    ERROR_ALREADY_CONNECTED = MEDIA_ERROR_BASE,
+    ERROR_NOT_CONNECTED     = MEDIA_ERROR_BASE - 1,
+    ERROR_UNKNOWN_HOST      = MEDIA_ERROR_BASE - 2,
+    ERROR_CANNOT_CONNECT    = MEDIA_ERROR_BASE - 3,
+    ERROR_IO                = MEDIA_ERROR_BASE - 4,
+    ERROR_CONNECTION_LOST   = MEDIA_ERROR_BASE - 5,
+    ERROR_MALFORMED         = MEDIA_ERROR_BASE - 7,
+    ERROR_OUT_OF_RANGE      = MEDIA_ERROR_BASE - 8,
+    ERROR_BUFFER_TOO_SMALL  = MEDIA_ERROR_BASE - 9,
+    ERROR_UNSUPPORTED       = MEDIA_ERROR_BASE - 10,
+    ERROR_END_OF_STREAM     = MEDIA_ERROR_BASE - 11,
+
+    // Not technically an error.
+    INFO_FORMAT_CHANGED    = MEDIA_ERROR_BASE - 12,
+    INFO_DISCONTINUITY     = MEDIA_ERROR_BASE - 13,
+    INFO_OUTPUT_BUFFERS_CHANGED = MEDIA_ERROR_BASE - 14,
+
+    // The following constant values should be in sync with
+    // drm/drm_framework_common.h
+    DRM_ERROR_BASE = -2000,
+
+    ERROR_DRM_UNKNOWN                       = DRM_ERROR_BASE,
+    ERROR_DRM_NO_LICENSE                    = DRM_ERROR_BASE - 1,
+    ERROR_DRM_LICENSE_EXPIRED               = DRM_ERROR_BASE - 2,
+    ERROR_DRM_SESSION_NOT_OPENED            = DRM_ERROR_BASE - 3,
+    ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED  = DRM_ERROR_BASE - 4,
+    ERROR_DRM_DECRYPT                       = DRM_ERROR_BASE - 5,
+    ERROR_DRM_CANNOT_HANDLE                 = DRM_ERROR_BASE - 6,
+    ERROR_DRM_TAMPER_DETECTED               = DRM_ERROR_BASE - 7,
+    ERROR_DRM_NOT_PROVISIONED               = DRM_ERROR_BASE - 8,
+    ERROR_DRM_DEVICE_REVOKED                = DRM_ERROR_BASE - 9,
+    ERROR_DRM_RESOURCE_BUSY                 = DRM_ERROR_BASE - 10,
+
+    ERROR_DRM_VENDOR_MAX                    = DRM_ERROR_BASE - 500,
+    ERROR_DRM_VENDOR_MIN                    = DRM_ERROR_BASE - 999,
+
+    // Heartbeat Error Codes
+    HEARTBEAT_ERROR_BASE = -3000,
+    ERROR_HEARTBEAT_TERMINATE_REQUESTED                     = HEARTBEAT_ERROR_BASE,
+};
+
+}  // namespace android
+
+#endif  // MEDIA_ERRORS_H_
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/MediaExtractor.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_EXTRACTOR_H_
+
+#define MEDIA_EXTRACTOR_H_
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+class DataSource;
+class MediaSource;
+class MetaData;
+
+class MediaExtractor : public RefBase {
+public:
+    static sp<MediaExtractor> Create(
+            const sp<DataSource> &source, const char *mime = NULL);
+
+    virtual size_t countTracks() = 0;
+    virtual sp<MediaSource> getTrack(size_t index) = 0;
+
+    enum GetTrackMetaDataFlags {
+        kIncludeExtensiveMetaData = 1
+    };
+    virtual sp<MetaData> getTrackMetaData(
+            size_t index, uint32_t flags = 0) = 0;
+
+    // Return container specific meta-data. The default implementation
+    // returns an empty metadata object.
+    virtual sp<MetaData> getMetaData();
+
+    enum Flags {
+        CAN_SEEK_BACKWARD  = 1,  // the "seek 10secs back button"
+        CAN_SEEK_FORWARD   = 2,  // the "seek 10secs forward button"
+        CAN_PAUSE          = 4,
+        CAN_SEEK           = 8,  // the "seek bar"
+    };
+
+    // If subclasses do _not_ override this, the default is
+    // CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE
+    virtual uint32_t flags() const;
+
+    // for DRM
+    void setDrmFlag(bool flag) {
+        mIsDrm = flag;
+    };
+    bool getDrmFlag() {
+        return mIsDrm;
+    }
+    virtual char* getDrmTrackInfo(size_t trackID, int *len) {
+        return NULL;
+    }
+
+protected:
+    MediaExtractor() : mIsDrm(false) {}
+    virtual ~MediaExtractor() {}
+
+private:
+    bool mIsDrm;
+
+    MediaExtractor(const MediaExtractor &);
+    MediaExtractor &operator=(const MediaExtractor &);
+};
+
+}  // namespace android
+
+#endif  // MEDIA_EXTRACTOR_H_
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/MediaSource.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_SOURCE_H_
+
+#define MEDIA_SOURCE_H_
+
+#include <sys/types.h>
+
+#include <media/stagefright/MediaErrors.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class MediaBuffer;
+class MetaData;
+
+struct MediaSource : public virtual RefBase {
+    MediaSource();
+
+    // To be called before any other methods on this object, except
+    // getFormat().
+    virtual status_t start(MetaData *params = NULL) = 0;
+
+    // Any blocking read call returns immediately with a result of NO_INIT.
+    // It is an error to call any methods other than start after this call
+    // returns. Any buffers the object may be holding onto at the time of
+    // the stop() call are released.
+    // Also, it is imperative that any buffers output by this object and
+    // held onto by callers be released before a call to stop() !!!
+    virtual status_t stop() = 0;
+
+    // Returns the format of the data output by this media source.
+    virtual sp<MetaData> getFormat() = 0;
+
+    struct ReadOptions;
+
+    // Returns a new buffer of data. Call blocks until a
+    // buffer is available, an error is encountered of the end of the stream
+    // is reached.
+    // End of stream is signalled by a result of ERROR_END_OF_STREAM.
+    // A result of INFO_FORMAT_CHANGED indicates that the format of this
+    // MediaSource has changed mid-stream, the client can continue reading
+    // but should be prepared for buffers of the new configuration.
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options = NULL) = 0;
+
+    // Options that modify read() behaviour. The default is to
+    // a) not request a seek
+    // b) not be late, i.e. lateness_us = 0
+    struct ReadOptions {
+        enum SeekMode {
+            SEEK_PREVIOUS_SYNC,
+            SEEK_NEXT_SYNC,
+            SEEK_CLOSEST_SYNC,
+            SEEK_CLOSEST,
+        };
+
+        ReadOptions();
+
+        // Reset everything back to defaults.
+        void reset();
+
+        void setSeekTo(int64_t time_us, SeekMode mode = SEEK_CLOSEST_SYNC);
+        void clearSeekTo();
+        bool getSeekTo(int64_t *time_us, SeekMode *mode) const;
+
+        void setLateBy(int64_t lateness_us);
+        int64_t getLateBy() const;
+
+    private:
+        enum Options {
+            kSeekTo_Option      = 1,
+        };
+
+        uint32_t mOptions;
+        int64_t mSeekTimeUs;
+        SeekMode mSeekMode;
+        int64_t mLatenessUs;
+    };
+
+    // Causes this source to suspend pulling data from its upstream source
+    // until a subsequent read-with-seek. Currently only supported by
+    // OMXCodec.
+    virtual status_t pause() {
+        return ERROR_UNSUPPORTED;
+    }
+
+    // The consumer of this media source requests that the given buffers
+    // are to be returned exclusively in response to read calls.
+    // This will be called after a successful start() and before the
+    // first read() call.
+    // Callee assumes ownership of the buffers if no error is returned.
+    virtual status_t setBuffers(const Vector<MediaBuffer *> &buffers) {
+        return ERROR_UNSUPPORTED;
+    }
+
+protected:
+    virtual ~MediaSource();
+
+private:
+    MediaSource(const MediaSource &);
+    MediaSource &operator=(const MediaSource &);
+};
+
+}  // namespace android
+
+#endif  // MEDIA_SOURCE_H_
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/MetaData.h
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef META_DATA_H_
+
+#define META_DATA_H_
+
+#include <sys/types.h>
+
+#include <stdint.h>
+
+#include <utils/RefBase.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+
+namespace android {
+
+// The following keys map to int32_t data unless indicated otherwise.
+enum {
+    kKeyMIMEType          = 'mime',  // cstring
+    kKeyWidth             = 'widt',  // int32_t, image pixel
+    kKeyHeight            = 'heig',  // int32_t, image pixel
+    kKeyDisplayWidth      = 'dWid',  // int32_t, display/presentation
+    kKeyDisplayHeight     = 'dHgt',  // int32_t, display/presentation
+    kKeySARWidth          = 'sarW',  // int32_t, sampleAspectRatio width
+    kKeySARHeight         = 'sarH',  // int32_t, sampleAspectRatio height
+
+    // a rectangle, if absent assumed to be (0, 0, width - 1, height - 1)
+    kKeyCropRect          = 'crop',
+
+    kKeyRotation          = 'rotA',  // int32_t (angle in degrees)
+    kKeyIFramesInterval   = 'ifiv',  // int32_t
+    kKeyStride            = 'strd',  // int32_t
+    kKeySliceHeight       = 'slht',  // int32_t
+    kKeyChannelCount      = '#chn',  // int32_t
+    kKeyChannelMask       = 'chnm',  // int32_t
+    kKeySampleRate        = 'srte',  // int32_t (audio sampling rate Hz)
+    kKeyFrameRate         = 'frmR',  // int32_t (video frame rate fps)
+    kKeyBitRate           = 'brte',  // int32_t (bps)
+    kKeyESDS              = 'esds',  // raw data
+    kKeyAACProfile        = 'aacp',  // int32_t
+    kKeyAVCC              = 'avcc',  // raw data
+    kKeyD263              = 'd263',  // raw data
+    kKeyVorbisInfo        = 'vinf',  // raw data
+    kKeyVorbisBooks       = 'vboo',  // raw data
+    kKeyWantsNALFragments = 'NALf',
+    kKeyIsSyncFrame       = 'sync',  // int32_t (bool)
+    kKeyIsCodecConfig     = 'conf',  // int32_t (bool)
+    kKeyTime              = 'time',  // int64_t (usecs)
+    kKeyDecodingTime      = 'decT',  // int64_t (decoding timestamp in usecs)
+    kKeyNTPTime           = 'ntpT',  // uint64_t (ntp-timestamp)
+    kKeyTargetTime        = 'tarT',  // int64_t (usecs)
+    kKeyDriftTime         = 'dftT',  // int64_t (usecs)
+    kKeyAnchorTime        = 'ancT',  // int64_t (usecs)
+    kKeyDuration          = 'dura',  // int64_t (usecs)
+    kKeyColorFormat       = 'colf',
+    kKeyPlatformPrivate   = 'priv',  // pointer
+    kKeyDecoderComponent  = 'decC',  // cstring
+    kKeyBufferID          = 'bfID',
+    kKeyMaxInputSize      = 'inpS',
+    kKeyThumbnailTime     = 'thbT',  // int64_t (usecs)
+    kKeyTrackID           = 'trID',
+    kKeyIsDRM             = 'idrm',  // int32_t (bool)
+    kKeyEncoderDelay      = 'encd',  // int32_t (frames)
+    kKeyEncoderPadding    = 'encp',  // int32_t (frames)
+
+    kKeyAlbum             = 'albu',  // cstring
+    kKeyArtist            = 'arti',  // cstring
+    kKeyAlbumArtist       = 'aart',  // cstring
+    kKeyComposer          = 'comp',  // cstring
+    kKeyGenre             = 'genr',  // cstring
+    kKeyTitle             = 'titl',  // cstring
+    kKeyYear              = 'year',  // cstring
+    kKeyAlbumArt          = 'albA',  // compressed image data
+    kKeyAlbumArtMIME      = 'alAM',  // cstring
+    kKeyAuthor            = 'auth',  // cstring
+    kKeyCDTrackNumber     = 'cdtr',  // cstring
+    kKeyDiscNumber        = 'dnum',  // cstring
+    kKeyDate              = 'date',  // cstring
+    kKeyWriter            = 'writ',  // cstring
+    kKeyCompilation       = 'cpil',  // cstring
+    kKeyLocation          = 'loc ',  // cstring
+    kKeyTimeScale         = 'tmsl',  // int32_t
+
+    // video profile and level
+    kKeyVideoProfile      = 'vprf',  // int32_t
+    kKeyVideoLevel        = 'vlev',  // int32_t
+
+    // Set this key to enable authoring files in 64-bit offset
+    kKey64BitFileOffset   = 'fobt',  // int32_t (bool)
+    kKey2ByteNalLength    = '2NAL',  // int32_t (bool)
+
+    // Identify the file output format for authoring
+    // Please see <media/mediarecorder.h> for the supported
+    // file output formats.
+    kKeyFileType          = 'ftyp',  // int32_t
+
+    // Track authoring progress status
+    // kKeyTrackTimeStatus is used to track progress in elapsed time
+    kKeyTrackTimeStatus   = 'tktm',  // int64_t
+
+    kKeyRealTimeRecording = 'rtrc',  // bool (int32_t)
+    kKeyNumBuffers        = 'nbbf',  // int32_t
+
+    // Ogg files can be tagged to be automatically looping...
+    kKeyAutoLoop          = 'autL',  // bool (int32_t)
+
+    kKeyValidSamples      = 'valD',  // int32_t
+
+    kKeyIsUnreadable      = 'unre',  // bool (int32_t)
+
+    // An indication that a video buffer has been rendered.
+    kKeyRendered          = 'rend',  // bool (int32_t)
+
+    // The language code for this media
+    kKeyMediaLanguage     = 'lang',  // cstring
+
+    // To store the timed text format data
+    kKeyTextFormatData    = 'text',  // raw data
+
+    kKeyRequiresSecureBuffers = 'secu',  // bool (int32_t)
+
+    kKeyIsADTS            = 'adts',  // bool (int32_t)
+
+    // If a MediaBuffer's data represents (at least partially) encrypted
+    // data, the following fields aid in decryption.
+    // The data can be thought of as pairs of plain and encrypted data
+    // fragments, i.e. plain and encrypted data alternate.
+    // The first fragment is by convention plain data (if that's not the
+    // case, simply specify plain fragment size of 0).
+    // kKeyEncryptedSizes and kKeyPlainSizes each map to an array of
+    // size_t values. The sum total of all size_t values of both arrays
+    // must equal the amount of data (i.e. MediaBuffer's range_length()).
+    // If both arrays are present, they must be of the same size.
+    // If only encrypted sizes are present it is assumed that all
+    // plain sizes are 0, i.e. all fragments are encrypted.
+    // To programmatically set these array, use the MetaData::setData API, i.e.
+    // const size_t encSizes[];
+    // meta->setData(
+    //  kKeyEncryptedSizes, 0 /* type */, encSizes, sizeof(encSizes));
+    // A plain sizes array by itself makes no sense.
+    kKeyEncryptedSizes    = 'encr',  // size_t[]
+    kKeyPlainSizes        = 'plai',  // size_t[]
+    kKeyCryptoKey         = 'cryK',  // uint8_t[16]
+    kKeyCryptoIV          = 'cryI',  // uint8_t[16]
+    kKeyCryptoMode        = 'cryM',  // int32_t
+
+    kKeyCryptoDefaultIVSize = 'cryS',  // int32_t
+
+    kKeyPssh              = 'pssh',  // raw data
+};
+
+enum {
+    kTypeESDS        = 'esds',
+    kTypeAVCC        = 'avcc',
+    kTypeD263        = 'd263',
+};
+
+class MetaData : public RefBase {
+public:
+    MetaData();
+    MetaData(const MetaData &from);
+
+    enum Type {
+        TYPE_NONE     = 'none',
+        TYPE_C_STRING = 'cstr',
+        TYPE_INT32    = 'in32',
+        TYPE_INT64    = 'in64',
+        TYPE_FLOAT    = 'floa',
+        TYPE_POINTER  = 'ptr ',
+        TYPE_RECT     = 'rect',
+    };
+
+    void clear();
+    bool remove(uint32_t key);
+
+    bool setCString(uint32_t key, const char *value);
+    bool setInt32(uint32_t key, int32_t value);
+    bool setInt64(uint32_t key, int64_t value);
+    bool setFloat(uint32_t key, float value);
+    bool setPointer(uint32_t key, void *value);
+
+    bool setRect(
+            uint32_t key,
+            int32_t left, int32_t top,
+            int32_t right, int32_t bottom);
+
+    bool findCString(uint32_t key, const char **value);
+    bool findInt32(uint32_t key, int32_t *value);
+    bool findInt64(uint32_t key, int64_t *value);
+    bool findFloat(uint32_t key, float *value);
+    bool findPointer(uint32_t key, void **value);
+
+    bool findRect(
+            uint32_t key,
+            int32_t *left, int32_t *top,
+            int32_t *right, int32_t *bottom);
+
+    bool setData(uint32_t key, uint32_t type, const void *data, size_t size);
+
+    bool findData(uint32_t key, uint32_t *type,
+                  const void **data, size_t *size) const;
+
+    void dumpToLog() const;
+
+protected:
+    virtual ~MetaData();
+
+private:
+    struct typed_data {
+        typed_data();
+        ~typed_data();
+
+        typed_data(const MetaData::typed_data &);
+        typed_data &operator=(const MetaData::typed_data &);
+
+        void clear();
+        void setData(uint32_t type, const void *data, size_t size);
+        void getData(uint32_t *type, const void **data, size_t *size) const;
+        String8 asString() const;
+
+    private:
+        uint32_t mType;
+        size_t mSize;
+
+        union {
+            void *ext_data;
+            float reservoir;
+        } u;
+
+        bool usesReservoir() const {
+            return mSize <= sizeof(u.reservoir);
+        }
+
+        void allocateStorage(size_t size);
+        void freeStorage();
+
+        void *storage() {
+            return usesReservoir() ? &u.reservoir : u.ext_data;
+        }
+
+        const void *storage() const {
+            return usesReservoir() ? &u.reservoir : u.ext_data;
+        }
+    };
+
+    struct Rect {
+        int32_t mLeft, mTop, mRight, mBottom;
+    };
+
+    KeyedVector<uint32_t, typed_data> mItems;
+
+    // MetaData &operator=(const MetaData &);
+};
+
+}  // namespace android
+
+#endif  // META_DATA_H_
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/Utils.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UTILS_H_
+
+#define UTILS_H_
+
+#include <media/stagefright/foundation/AString.h>
+#include <stdint.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <system/audio.h>
+#include <media/MediaPlayerInterface.h>
+
+namespace android {
+
+#define FOURCC(c1, c2, c3, c4) \
+    (c1 << 24 | c2 << 16 | c3 << 8 | c4)
+
+uint16_t U16_AT(const uint8_t *ptr);
+uint32_t U32_AT(const uint8_t *ptr);
+uint64_t U64_AT(const uint8_t *ptr);
+
+uint16_t U16LE_AT(const uint8_t *ptr);
+uint32_t U32LE_AT(const uint8_t *ptr);
+uint64_t U64LE_AT(const uint8_t *ptr);
+
+uint64_t ntoh64(uint64_t x);
+uint64_t hton64(uint64_t x);
+
+struct MetaData;
+struct AMessage;
+status_t convertMetaDataToMessage(
+        const sp<MetaData> &meta, sp<AMessage> *format);
+void convertMessageToMetaData(
+        const sp<AMessage> &format, sp<MetaData> &meta);
+
+AString MakeUserAgent();
+
+// Convert a MIME type to a AudioSystem::audio_format
+status_t mapMimeToAudioFormat(audio_format_t& format, const char* mime);
+
+// Send information from MetaData to the HAL via AudioSink
+status_t sendMetaDataToHal(sp<MediaPlayerBase::AudioSink>& sink, const sp<MetaData>& meta);
+
+// Check whether the stream defined by meta can be offloaded to hardware
+bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo, bool isStreaming);
+
+}  // namespace android
+
+#endif  // UTILS_H_
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/AAtomizer.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef A_ATOMIZER_H_
+
+#define A_ATOMIZER_H_
+
+#include <stdint.h>
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AString.h>
+#include <utils/List.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+
+namespace android {
+
+struct AAtomizer {
+    static const char *Atomize(const char *name);
+
+private:
+    static AAtomizer gAtomizer;
+
+    Mutex mLock;
+    Vector<List<AString> > mAtoms;
+
+    AAtomizer();
+
+    const char *atomize(const char *name);
+
+    static uint32_t Hash(const char *s);
+
+    DISALLOW_EVIL_CONSTRUCTORS(AAtomizer);
+};
+
+}  // namespace android
+
+#endif  // A_ATOMIZER_H_
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABase.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef A_BASE_H_
+
+#define A_BASE_H_
+
+#define DISALLOW_EVIL_CONSTRUCTORS(name) \
+    name(const name &); \
+    name &operator=(const name &)
+
+#endif  // A_BASE_H_
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABitReader.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef A_BIT_READER_H_
+
+#define A_BIT_READER_H_
+
+#include <media/stagefright/foundation/ABase.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+
+namespace android {
+
+struct ABitReader {
+    ABitReader(const uint8_t *data, size_t size);
+
+    uint32_t getBits(size_t n);
+    void skipBits(size_t n);
+
+    void putBits(uint32_t x, size_t n);
+
+    size_t numBitsLeft() const;
+
+    const uint8_t *data() const;
+
+private:
+    const uint8_t *mData;
+    size_t mSize;
+
+    uint32_t mReservoir;  // left-aligned bits
+    size_t mNumBitsLeft;
+
+    void fillReservoir();
+
+    DISALLOW_EVIL_CONSTRUCTORS(ABitReader);
+};
+
+}  // namespace android
+
+#endif  // A_BIT_READER_H_
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABuffer.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef A_BUFFER_H_
+
+#define A_BUFFER_H_
+
+#include <sys/types.h>
+#include <stdint.h>
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct AMessage;
+
+struct ABuffer : public RefBase {
+    ABuffer(size_t capacity);
+    ABuffer(void *data, size_t capacity);
+
+    void setFarewellMessage(const sp<AMessage> msg);
+
+    uint8_t *base() { return (uint8_t *)mData; }
+    uint8_t *data() { return (uint8_t *)mData + mRangeOffset; }
+    size_t capacity() const { return mCapacity; }
+    size_t size() const { return mRangeLength; }
+    size_t offset() const { return mRangeOffset; }
+
+    void setRange(size_t offset, size_t size);
+
+    void setInt32Data(int32_t data) { mInt32Data = data; }
+    int32_t int32Data() const { return mInt32Data; }
+
+    sp<AMessage> meta();
+
+protected:
+    virtual ~ABuffer();
+
+private:
+    sp<AMessage> mFarewell;
+    sp<AMessage> mMeta;
+
+    void *mData;
+    size_t mCapacity;
+    size_t mRangeOffset;
+    size_t mRangeLength;
+
+    int32_t mInt32Data;
+
+    bool mOwnsData;
+
+    DISALLOW_EVIL_CONSTRUCTORS(ABuffer);
+};
+
+}  // namespace android
+
+#endif  // A_BUFFER_H_
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ADebug.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef A_DEBUG_H_
+
+#define A_DEBUG_H_
+
+#include <string.h>
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AString.h>
+#include <utils/Log.h>
+
+namespace android {
+
+#define LITERAL_TO_STRING_INTERNAL(x)    #x
+#define LITERAL_TO_STRING(x) LITERAL_TO_STRING_INTERNAL(x)
+
+#define CHECK(condition)                                \
+    LOG_ALWAYS_FATAL_IF(                                \
+            !(condition),                               \
+            "%s",                                       \
+            __FILE__ ":" LITERAL_TO_STRING(__LINE__)    \
+            " CHECK(" #condition ") failed.")
+
+#define MAKE_COMPARATOR(suffix,op)                          \
+    template<class A, class B>                              \
+    AString Compare_##suffix(const A &a, const B &b) {      \
+        AString res;                                        \
+        if (!(a op b)) {                                    \
+            res.append(a);                                  \
+            res.append(" vs. ");                            \
+            res.append(b);                                  \
+        }                                                   \
+        return res;                                         \
+    }
+
+MAKE_COMPARATOR(EQ,==)
+MAKE_COMPARATOR(NE,!=)
+MAKE_COMPARATOR(LE,<=)
+MAKE_COMPARATOR(GE,>=)
+MAKE_COMPARATOR(LT,<)
+MAKE_COMPARATOR(GT,>)
+
+#define CHECK_OP(x,y,suffix,op)                                         \
+    do {                                                                \
+        AString ___res = Compare_##suffix(x, y);                        \
+        if (!___res.empty()) {                                          \
+            AString ___full =                                           \
+                __FILE__ ":" LITERAL_TO_STRING(__LINE__)                \
+                    " CHECK_" #suffix "( " #x "," #y ") failed: ";      \
+            ___full.append(___res);                                     \
+                                                                        \
+            LOG_ALWAYS_FATAL("%s", ___full.c_str());                    \
+        }                                                               \
+    } while (false)
+
+#define CHECK_EQ(x,y)   CHECK_OP(x,y,EQ,==)
+#define CHECK_NE(x,y)   CHECK_OP(x,y,NE,!=)
+#define CHECK_LE(x,y)   CHECK_OP(x,y,LE,<=)
+#define CHECK_LT(x,y)   CHECK_OP(x,y,LT,<)
+#define CHECK_GE(x,y)   CHECK_OP(x,y,GE,>=)
+#define CHECK_GT(x,y)   CHECK_OP(x,y,GT,>)
+
+#define TRESPASS() \
+        LOG_ALWAYS_FATAL(                                       \
+            __FILE__ ":" LITERAL_TO_STRING(__LINE__)            \
+                " Should not be here.");
+
+}  // namespace android
+
+#endif  // A_DEBUG_H_
+
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/AHandler.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef A_HANDLER_H_
+
+#define A_HANDLER_H_
+
+#include <media/stagefright/foundation/ALooper.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct AMessage;
+
+struct AHandler : public RefBase {
+    AHandler()
+        : mID(0) {
+    }
+
+    ALooper::handler_id id() const {
+        return mID;
+    }
+
+    sp<ALooper> looper();
+
+protected:
+    virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
+
+private:
+    friend struct ALooperRoster;
+
+    ALooper::handler_id mID;
+
+    void setID(ALooper::handler_id id) {
+        mID = id;
+    }
+
+    DISALLOW_EVIL_CONSTRUCTORS(AHandler);
+};
+
+}  // namespace android
+
+#endif  // A_HANDLER_H_
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/AString.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef A_STRING_H_
+
+#define A_STRING_H_
+
+#include <sys/types.h>
+
+namespace android {
+
+struct AString {
+    AString();
+    AString(const char *s);
+    AString(const char *s, size_t size);
+    AString(const AString &from);
+    AString(const AString &from, size_t offset, size_t n);
+    ~AString();
+
+    AString &operator=(const AString &from);
+    void setTo(const char *s);
+    void setTo(const char *s, size_t size);
+    void setTo(const AString &from, size_t offset, size_t n);
+
+    size_t size() const;
+    const char *c_str() const;
+
+    bool empty() const;
+
+    void clear();
+    void trim();
+    void erase(size_t start, size_t n);
+
+    void append(char c) { append(&c, 1); }
+    void append(const char *s);
+    void append(const char *s, size_t size);
+    void append(const AString &from);
+    void append(const AString &from, size_t offset, size_t n);
+    void append(int x);
+    void append(unsigned x);
+    void append(long x);
+    void append(unsigned long x);
+    void append(long long x);
+    void append(unsigned long long x);
+    void append(float x);
+    void append(double x);
+    void append(void *x);
+
+    void insert(const AString &from, size_t insertionPos);
+    void insert(const char *from, size_t size, size_t insertionPos);
+
+    ssize_t find(const char *substring, size_t start = 0) const;
+
+    size_t hash() const;
+
+    bool operator==(const AString &other) const;
+    bool operator<(const AString &other) const;
+    bool operator>(const AString &other) const;
+
+    int compare(const AString &other) const;
+
+    bool startsWith(const char *prefix) const;
+    bool endsWith(const char *suffix) const;
+
+    void tolower();
+
+private:
+    static const char *kEmptyString;
+
+    char *mData;
+    size_t mSize;
+    size_t mAllocSize;
+
+    void makeMutable();
+};
+
+AString StringPrintf(const char *format, ...);
+
+}  // namespace android
+
+#endif  // A_STRING_H_
+
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/hexdump.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HEXDUMP_H_
+
+#define HEXDUMP_H_
+
+#include <sys/types.h>
+
+namespace android {
+
+struct AString;
+
+void hexdump(
+        const void *_data, size_t size,
+        size_t indent = 0, AString *appendTo = NULL);
+
+}  // namespace android
+
+#endif  // HEXDUMP_H_
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/DataSource.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "include/AMRExtractor.h"
+
+#if CHROMIUM_AVAILABLE
+#include "include/chromium_http_stub.h"
+#endif
+
+#include "include/AACExtractor.h"
+#include "include/DRMExtractor.h"
+#include "include/FLACExtractor.h"
+#include "include/HTTPBase.h"
+#include "include/MP3Extractor.h"
+#include "include/MPEG2PSExtractor.h"
+#include "include/MPEG2TSExtractor.h"
+#include "include/MPEG4Extractor.h"
+#include "include/NuCachedSource2.h"
+#include "include/OggExtractor.h"
+#include "include/WAVExtractor.h"
+#include "include/WVMExtractor.h"
+
+#include "matroska/MatroskaExtractor.h"
+
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/FileSource.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/String8.h>
+
+#include <cutils/properties.h>
+
+namespace android {
+
+bool DataSource::getUInt16(off64_t offset, uint16_t *x) {
+    *x = 0;
+
+    uint8_t byte[2];
+    if (readAt(offset, byte, 2) != 2) {
+        return false;
+    }
+
+    *x = (byte[0] << 8) | byte[1];
+
+    return true;
+}
+
+bool DataSource::getUInt24(off64_t offset, uint32_t *x) {
+    *x = 0;
+
+    uint8_t byte[3];
+    if (readAt(offset, byte, 3) != 3) {
+        return false;
+    }
+
+    *x = (byte[0] << 16) | (byte[1] << 8) | byte[2];
+
+    return true;
+}
+
+bool DataSource::getUInt32(off64_t offset, uint32_t *x) {
+    *x = 0;
+
+    uint32_t tmp;
+    if (readAt(offset, &tmp, 4) != 4) {
+        return false;
+    }
+
+    *x = ntohl(tmp);
+
+    return true;
+}
+
+bool DataSource::getUInt64(off64_t offset, uint64_t *x) {
+    *x = 0;
+
+    uint64_t tmp;
+    if (readAt(offset, &tmp, 8) != 8) {
+        return false;
+    }
+
+    *x = ntoh64(tmp);
+
+    return true;
+}
+
+status_t DataSource::getSize(off64_t *size) {
+    *size = 0;
+
+    return ERROR_UNSUPPORTED;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+Mutex DataSource::gSnifferMutex;
+List<DataSource::SnifferFunc> DataSource::gSniffers;
+
+bool DataSource::sniff(
+        String8 *mimeType, float *confidence, sp<AMessage> *meta) {
+    *mimeType = "";
+    *confidence = 0.0f;
+    meta->clear();
+
+    Mutex::Autolock autoLock(gSnifferMutex);
+    for (List<SnifferFunc>::iterator it = gSniffers.begin();
+         it != gSniffers.end(); ++it) {
+        String8 newMimeType;
+        float newConfidence;
+        sp<AMessage> newMeta;
+        if ((*it)(this, &newMimeType, &newConfidence, &newMeta)) {
+            if (newConfidence > *confidence) {
+                *mimeType = newMimeType;
+                *confidence = newConfidence;
+                *meta = newMeta;
+            }
+        }
+    }
+
+    return *confidence > 0.0;
+}
+
+// static
+void DataSource::RegisterSniffer(SnifferFunc func) {
+    Mutex::Autolock autoLock(gSnifferMutex);
+
+    for (List<SnifferFunc>::iterator it = gSniffers.begin();
+         it != gSniffers.end(); ++it) {
+        if (*it == func) {
+            return;
+        }
+    }
+
+    gSniffers.push_back(func);
+}
+
+// static
+void DataSource::RegisterDefaultSniffers() {
+    RegisterSniffer(SniffMPEG4);
+    RegisterSniffer(SniffMatroska);
+    RegisterSniffer(SniffOgg);
+    RegisterSniffer(SniffWAV);
+    RegisterSniffer(SniffFLAC);
+    RegisterSniffer(SniffAMR);
+    RegisterSniffer(SniffMPEG2TS);
+    RegisterSniffer(SniffMP3);
+    RegisterSniffer(SniffAAC);
+    RegisterSniffer(SniffMPEG2PS);
+    RegisterSniffer(SniffWVM);
+
+    char value[PROPERTY_VALUE_MAX];
+    if (property_get("drm.service.enabled", value, NULL)
+            && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
+        RegisterSniffer(SniffDRM);
+    }
+}
+
+// static
+sp<DataSource> DataSource::CreateFromURI(
+        const char *uri, const KeyedVector<String8, String8> *headers) {
+    bool isWidevine = !strncasecmp("widevine://", uri, 11);
+
+    sp<DataSource> source;
+    if (!strncasecmp("file://", uri, 7)) {
+        source = new FileSource(uri + 7);
+    } else if (!strncasecmp("http://", uri, 7)
+            || !strncasecmp("https://", uri, 8)
+            || isWidevine) {
+        sp<HTTPBase> httpSource = HTTPBase::Create();
+
+        String8 tmp;
+        if (isWidevine) {
+            tmp = String8("http://");
+            tmp.append(uri + 11);
+
+            uri = tmp.string();
+        }
+
+        if (httpSource->connect(uri, headers) != OK) {
+            return NULL;
+        }
+
+        if (!isWidevine) {
+            String8 cacheConfig;
+            bool disconnectAtHighwatermark;
+            if (headers != NULL) {
+                KeyedVector<String8, String8> copy = *headers;
+                NuCachedSource2::RemoveCacheSpecificHeaders(
+                        &copy, &cacheConfig, &disconnectAtHighwatermark);
+            }
+
+            source = new NuCachedSource2(
+                    httpSource,
+                    cacheConfig.isEmpty() ? NULL : cacheConfig.string());
+        } else {
+            // We do not want that prefetching, caching, datasource wrapper
+            // in the widevine:// case.
+            source = httpSource;
+        }
+
+# if CHROMIUM_AVAILABLE
+    } else if (!strncasecmp("data:", uri, 5)) {
+        source = createDataUriSource(uri);
+#endif
+    } else {
+        // Assume it's a filename.
+        source = new FileSource(uri);
+    }
+
+    if (source == NULL || source->initCheck() != OK) {
+        return NULL;
+    }
+
+    return source;
+}
+
+String8 DataSource::getMIMEType() const {
+    return String8("application/octet-stream");
+}
+
+}  // namespace android
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/ESDS.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ESDS"
+#include <utils/Log.h>
+
+#include "include/ESDS.h"
+
+#include <string.h>
+
+namespace android {
+
+ESDS::ESDS(const void *data, size_t size)
+    : mData(new uint8_t[size]),
+      mSize(size),
+      mInitCheck(NO_INIT),
+      mDecoderSpecificOffset(0),
+      mDecoderSpecificLength(0),
+      mObjectTypeIndication(0) {
+    memcpy(mData, data, size);
+
+    mInitCheck = parse();
+}
+
+ESDS::~ESDS() {
+    delete[] mData;
+    mData = NULL;
+}
+
+status_t ESDS::InitCheck() const {
+    return mInitCheck;
+}
+
+status_t ESDS::getObjectTypeIndication(uint8_t *objectTypeIndication) const {
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    *objectTypeIndication = mObjectTypeIndication;
+
+    return OK;
+}
+
+status_t ESDS::getCodecSpecificInfo(const void **data, size_t *size) const {
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    *data = &mData[mDecoderSpecificOffset];
+    *size = mDecoderSpecificLength;
+
+    return OK;
+}
+
+status_t ESDS::skipDescriptorHeader(
+        size_t offset, size_t size,
+        uint8_t *tag, size_t *data_offset, size_t *data_size) const {
+    if (size == 0) {
+        return ERROR_MALFORMED;
+    }
+
+    *tag = mData[offset++];
+    --size;
+
+    *data_size = 0;
+    bool more;
+    do {
+        if (size == 0) {
+            return ERROR_MALFORMED;
+        }
+
+        uint8_t x = mData[offset++];
+        --size;
+
+        *data_size = (*data_size << 7) | (x & 0x7f);
+        more = (x & 0x80) != 0;
+    }
+    while (more);
+
+    ALOGV("tag=0x%02x data_size=%d", *tag, *data_size);
+
+    if (*data_size > size) {
+        return ERROR_MALFORMED;
+    }
+
+    *data_offset = offset;
+
+    return OK;
+}
+
+status_t ESDS::parse() {
+    uint8_t tag;
+    size_t data_offset;
+    size_t data_size;
+    status_t err =
+        skipDescriptorHeader(0, mSize, &tag, &data_offset, &data_size);
+
+    if (err != OK) {
+        return err;
+    }
+
+    if (tag != kTag_ESDescriptor) {
+        return ERROR_MALFORMED;
+    }
+
+    return parseESDescriptor(data_offset, data_size);
+}
+
+status_t ESDS::parseESDescriptor(size_t offset, size_t size) {
+    if (size < 3) {
+        return ERROR_MALFORMED;
+    }
+
+    offset += 2;  // skip ES_ID
+    size -= 2;
+
+    unsigned streamDependenceFlag = mData[offset] & 0x80;
+    unsigned URL_Flag = mData[offset] & 0x40;
+    unsigned OCRstreamFlag = mData[offset] & 0x20;
+
+    ++offset;
+    --size;
+
+    if (streamDependenceFlag) {
+        offset += 2;
+        size -= 2;
+    }
+
+    if (URL_Flag) {
+        if (offset >= size) {
+            return ERROR_MALFORMED;
+        }
+        unsigned URLlength = mData[offset];
+        offset += URLlength + 1;
+        size -= URLlength + 1;
+    }
+
+    if (OCRstreamFlag) {
+        offset += 2;
+        size -= 2;
+
+        if ((offset >= size || mData[offset] != kTag_DecoderConfigDescriptor)
+                && offset - 2 < size
+                && mData[offset - 2] == kTag_DecoderConfigDescriptor) {
+            // Content found "in the wild" had OCRstreamFlag set but was
+            // missing OCR_ES_Id, the decoder config descriptor immediately
+            // followed instead.
+            offset -= 2;
+            size += 2;
+
+            ALOGW("Found malformed 'esds' atom, ignoring missing OCR_ES_Id.");
+        }
+    }
+
+    if (offset >= size) {
+        return ERROR_MALFORMED;
+    }
+
+    uint8_t tag;
+    size_t sub_offset, sub_size;
+    status_t err = skipDescriptorHeader(
+            offset, size, &tag, &sub_offset, &sub_size);
+
+    if (err != OK) {
+        return err;
+    }
+
+    if (tag != kTag_DecoderConfigDescriptor) {
+        return ERROR_MALFORMED;
+    }
+
+    err = parseDecoderConfigDescriptor(sub_offset, sub_size);
+
+    return err;
+}
+
+status_t ESDS::parseDecoderConfigDescriptor(size_t offset, size_t size) {
+    if (size < 13) {
+        return ERROR_MALFORMED;
+    }
+
+    mObjectTypeIndication = mData[offset];
+
+    offset += 13;
+    size -= 13;
+
+    if (size == 0) {
+        mDecoderSpecificOffset = 0;
+        mDecoderSpecificLength = 0;
+        return OK;
+    }
+
+    uint8_t tag;
+    size_t sub_offset, sub_size;
+    status_t err = skipDescriptorHeader(
+            offset, size, &tag, &sub_offset, &sub_size);
+
+    if (err != OK) {
+        return err;
+    }
+
+    if (tag != kTag_DecoderSpecificInfo) {
+        return ERROR_MALFORMED;
+    }
+
+    mDecoderSpecificOffset = sub_offset;
+    mDecoderSpecificLength = sub_size;
+
+    return OK;
+}
+
+}  // namespace android
+
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
@@ -0,0 +1,3760 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MPEG4Extractor"
+#include <utils/Log.h>
+
+#include "include/MPEG4Extractor.h"
+#include "include/SampleTable.h"
+#include "include/ESDS.h"
+
+#include <ctype.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <media/stagefright/foundation/ABitReader.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <utils/String8.h>
+
+namespace android {
+
+class MPEG4Source : public MediaSource {
+public:
+    // Caller retains ownership of both "dataSource" and "sampleTable".
+    MPEG4Source(const sp<MetaData> &format,
+                const sp<DataSource> &dataSource,
+                int32_t timeScale,
+                const sp<SampleTable> &sampleTable,
+                Vector<SidxEntry> &sidx,
+                off64_t firstMoofOffset);
+
+    virtual status_t start(MetaData *params = NULL);
+    virtual status_t stop();
+
+    virtual sp<MetaData> getFormat();
+
+    virtual status_t read(MediaBuffer **buffer, const ReadOptions *options = NULL);
+    virtual status_t fragmentedRead(MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+protected:
+    virtual ~MPEG4Source();
+
+private:
+    Mutex mLock;
+
+    sp<MetaData> mFormat;
+    sp<DataSource> mDataSource;
+    int32_t mTimescale;
+    sp<SampleTable> mSampleTable;
+    uint32_t mCurrentSampleIndex;
+    uint32_t mCurrentFragmentIndex;
+    Vector<SidxEntry> &mSegments;
+    off64_t mFirstMoofOffset;
+    off64_t mCurrentMoofOffset;
+    off64_t mNextMoofOffset;
+    uint32_t mCurrentTime;
+    int32_t mLastParsedTrackId;
+    int32_t mTrackId;
+
+    int32_t mCryptoMode;    // passed in from extractor
+    int32_t mDefaultIVSize; // passed in from extractor
+    uint8_t mCryptoKey[16]; // passed in from extractor
+    uint32_t mCurrentAuxInfoType;
+    uint32_t mCurrentAuxInfoTypeParameter;
+    int32_t mCurrentDefaultSampleInfoSize;
+    uint32_t mCurrentSampleInfoCount;
+    uint32_t mCurrentSampleInfoAllocSize;
+    uint8_t* mCurrentSampleInfoSizes;
+    uint32_t mCurrentSampleInfoOffsetCount;
+    uint32_t mCurrentSampleInfoOffsetsAllocSize;
+    uint64_t* mCurrentSampleInfoOffsets;
+
+    bool mIsAVC;
+    size_t mNALLengthSize;
+
+    bool mStarted;
+
+    MediaBufferGroup *mGroup;
+
+    MediaBuffer *mBuffer;
+
+    bool mWantsNALFragments;
+
+    uint8_t *mSrcBuffer;
+
+    size_t parseNALSize(const uint8_t *data) const;
+    status_t parseChunk(off64_t *offset);
+    status_t parseTrackFragmentHeader(off64_t offset, off64_t size);
+    status_t parseTrackFragmentRun(off64_t offset, off64_t size);
+    status_t parseSampleAuxiliaryInformationSizes(off64_t offset, off64_t size);
+    status_t parseSampleAuxiliaryInformationOffsets(off64_t offset, off64_t size);
+
+    struct TrackFragmentHeaderInfo {
+        enum Flags {
+            kBaseDataOffsetPresent         = 0x01,
+            kSampleDescriptionIndexPresent = 0x02,
+            kDefaultSampleDurationPresent  = 0x08,
+            kDefaultSampleSizePresent      = 0x10,
+            kDefaultSampleFlagsPresent     = 0x20,
+            kDurationIsEmpty               = 0x10000,
+        };
+
+        uint32_t mTrackID;
+        uint32_t mFlags;
+        uint64_t mBaseDataOffset;
+        uint32_t mSampleDescriptionIndex;
+        uint32_t mDefaultSampleDuration;
+        uint32_t mDefaultSampleSize;
+        uint32_t mDefaultSampleFlags;
+
+        uint64_t mDataOffset;
+    };
+    TrackFragmentHeaderInfo mTrackFragmentHeaderInfo;
+
+    struct Sample {
+        off64_t offset;
+        size_t size;
+        uint32_t duration;
+        uint8_t iv[16];
+        Vector<size_t> clearsizes;
+        Vector<size_t> encryptedsizes;
+    };
+    Vector<Sample> mCurrentSamples;
+
+    MPEG4Source(const MPEG4Source &);
+    MPEG4Source &operator=(const MPEG4Source &);
+};
+
+// This custom data source wraps an existing one and satisfies requests
+// falling entirely within a cached range from the cache while forwarding
+// all remaining requests to the wrapped datasource.
+// This is used to cache the full sampletable metadata for a single track,
+// possibly wrapping multiple times to cover all tracks, i.e.
+// Each MPEG4DataSource caches the sampletable metadata for a single track.
+
+struct MPEG4DataSource : public DataSource {
+    MPEG4DataSource(const sp<DataSource> &source);
+
+    virtual status_t initCheck() const;
+    virtual ssize_t readAt(off64_t offset, void *data, size_t size);
+    virtual status_t getSize(off64_t *size);
+    virtual uint32_t flags();
+
+    status_t setCachedRange(off64_t offset, size_t size);
+
+protected:
+    virtual ~MPEG4DataSource();
+
+private:
+    Mutex mLock;
+
+    sp<DataSource> mSource;
+    off64_t mCachedOffset;
+    size_t mCachedSize;
+    uint8_t *mCache;
+
+    void clearCache();
+
+    MPEG4DataSource(const MPEG4DataSource &);
+    MPEG4DataSource &operator=(const MPEG4DataSource &);
+};
+
+MPEG4DataSource::MPEG4DataSource(const sp<DataSource> &source)
+    : mSource(source),
+      mCachedOffset(0),
+      mCachedSize(0),
+      mCache(NULL) {
+}
+
+MPEG4DataSource::~MPEG4DataSource() {
+    clearCache();
+}
+
+void MPEG4DataSource::clearCache() {
+    if (mCache) {
+        free(mCache);
+        mCache = NULL;
+    }
+
+    mCachedOffset = 0;
+    mCachedSize = 0;
+}
+
+status_t MPEG4DataSource::initCheck() const {
+    return mSource->initCheck();
+}
+
+ssize_t MPEG4DataSource::readAt(off64_t offset, void *data, size_t size) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (offset >= mCachedOffset
+            && offset + size <= mCachedOffset + mCachedSize) {
+        memcpy(data, &mCache[offset - mCachedOffset], size);
+        return size;
+    }
+
+    return mSource->readAt(offset, data, size);
+}
+
+status_t MPEG4DataSource::getSize(off64_t *size) {
+    return mSource->getSize(size);
+}
+
+uint32_t MPEG4DataSource::flags() {
+    return mSource->flags();
+}
+
+status_t MPEG4DataSource::setCachedRange(off64_t offset, size_t size) {
+    Mutex::Autolock autoLock(mLock);
+
+    clearCache();
+
+    mCache = (uint8_t *)malloc(size);
+
+    if (mCache == NULL) {
+        return -ENOMEM;
+    }
+
+    mCachedOffset = offset;
+    mCachedSize = size;
+
+    ssize_t err = mSource->readAt(mCachedOffset, mCache, mCachedSize);
+
+    if (err < (ssize_t)size) {
+        clearCache();
+
+        return ERROR_IO;
+    }
+
+    return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+static void hexdump(const void *_data, size_t size) {
+    const uint8_t *data = (const uint8_t *)_data;
+    size_t offset = 0;
+    while (offset < size) {
+        printf("0x%04x  ", offset);
+
+        size_t n = size - offset;
+        if (n > 16) {
+            n = 16;
+        }
+
+        for (size_t i = 0; i < 16; ++i) {
+            if (i == 8) {
+                printf(" ");
+            }
+
+            if (offset + i < size) {
+                printf("%02x ", data[offset + i]);
+            } else {
+                printf("   ");
+            }
+        }
+
+        printf(" ");
+
+        for (size_t i = 0; i < n; ++i) {
+            if (isprint(data[offset + i])) {
+                printf("%c", data[offset + i]);
+            } else {
+                printf(".");
+            }
+        }
+
+        printf("\n");
+
+        offset += 16;
+    }
+}
+
+static const char *FourCC2MIME(uint32_t fourcc) {
+    switch (fourcc) {
+        case FOURCC('m', 'p', '4', 'a'):
+            return MEDIA_MIMETYPE_AUDIO_AAC;
+
+        case FOURCC('s', 'a', 'm', 'r'):
+            return MEDIA_MIMETYPE_AUDIO_AMR_NB;
+
+        case FOURCC('s', 'a', 'w', 'b'):
+            return MEDIA_MIMETYPE_AUDIO_AMR_WB;
+
+        case FOURCC('m', 'p', '4', 'v'):
+            return MEDIA_MIMETYPE_VIDEO_MPEG4;
+
+        case FOURCC('s', '2', '6', '3'):
+        case FOURCC('h', '2', '6', '3'):
+        case FOURCC('H', '2', '6', '3'):
+            return MEDIA_MIMETYPE_VIDEO_H263;
+
+        case FOURCC('a', 'v', 'c', '1'):
+            return MEDIA_MIMETYPE_VIDEO_AVC;
+
+        default:
+            CHECK(!"should not be here.");
+            return NULL;
+    }
+}
+
+static bool AdjustChannelsAndRate(uint32_t fourcc, uint32_t *channels, uint32_t *rate) {
+    if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, FourCC2MIME(fourcc))) {
+        // AMR NB audio is always mono, 8kHz
+        *channels = 1;
+        *rate = 8000;
+        return true;
+    } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, FourCC2MIME(fourcc))) {
+        // AMR WB audio is always mono, 16kHz
+        *channels = 1;
+        *rate = 16000;
+        return true;
+    }
+    return false;
+}
+
+MPEG4Extractor::MPEG4Extractor(const sp<DataSource> &source)
+    : mSidxDuration(0),
+      mMoofOffset(0),
+      mDataSource(source),
+      mInitCheck(NO_INIT),
+      mHasVideo(false),
+      mHeaderTimescale(0),
+      mFirstTrack(NULL),
+      mLastTrack(NULL),
+      mFileMetaData(new MetaData),
+      mFirstSINF(NULL),
+      mIsDrm(false) {
+}
+
+MPEG4Extractor::~MPEG4Extractor() {
+    Track *track = mFirstTrack;
+    while (track) {
+        Track *next = track->next;
+
+        delete track;
+        track = next;
+    }
+    mFirstTrack = mLastTrack = NULL;
+
+    SINF *sinf = mFirstSINF;
+    while (sinf) {
+        SINF *next = sinf->next;
+        delete sinf->IPMPData;
+        delete sinf;
+        sinf = next;
+    }
+    mFirstSINF = NULL;
+
+    for (size_t i = 0; i < mPssh.size(); i++) {
+        delete [] mPssh[i].data;
+    }
+}
+
+uint32_t MPEG4Extractor::flags() const {
+    return CAN_PAUSE |
+            ((mMoofOffset == 0 || mSidxEntries.size() != 0) ?
+                    (CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK) : 0);
+}
+
+sp<MetaData> MPEG4Extractor::getMetaData() {
+    status_t err;
+    if ((err = readMetaData()) != OK) {
+        return new MetaData;
+    }
+
+    return mFileMetaData;
+}
+
+size_t MPEG4Extractor::countTracks() {
+    status_t err;
+    if ((err = readMetaData()) != OK) {
+        ALOGV("MPEG4Extractor::countTracks: no tracks");
+        return 0;
+    }
+
+    size_t n = 0;
+    Track *track = mFirstTrack;
+    while (track) {
+        ++n;
+        track = track->next;
+    }
+
+    ALOGV("MPEG4Extractor::countTracks: %d tracks", n);
+    return n;
+}
+
+sp<MetaData> MPEG4Extractor::getTrackMetaData(
+        size_t index, uint32_t flags) {
+    status_t err;
+    if ((err = readMetaData()) != OK) {
+        return NULL;
+    }
+
+    Track *track = mFirstTrack;
+    while (index > 0) {
+        if (track == NULL) {
+            return NULL;
+        }
+
+        track = track->next;
+        --index;
+    }
+
+    if (track == NULL) {
+        return NULL;
+    }
+
+    if ((flags & kIncludeExtensiveMetaData)
+            && !track->includes_expensive_metadata) {
+        track->includes_expensive_metadata = true;
+
+        const char *mime;
+        CHECK(track->meta->findCString(kKeyMIMEType, &mime));
+        if (!strncasecmp("video/", mime, 6)) {
+            if (mMoofOffset > 0) {
+                int64_t duration;
+                if (track->meta->findInt64(kKeyDuration, &duration)) {
+                    // nothing fancy, just pick a frame near 1/4th of the duration
+                    track->meta->setInt64(
+                            kKeyThumbnailTime, duration / 4);
+                }
+            } else {
+                uint32_t sampleIndex;
+                uint32_t sampleTime;
+                if (track->sampleTable->findThumbnailSample(&sampleIndex) == OK
+                        && track->sampleTable->getMetaDataForSample(
+                            sampleIndex, NULL /* offset */, NULL /* size */,
+                            &sampleTime) == OK) {
+                    track->meta->setInt64(
+                            kKeyThumbnailTime,
+                            ((int64_t)sampleTime * 1000000) / track->timescale);
+                }
+            }
+        }
+    }
+
+    return track->meta;
+}
+
+static void MakeFourCCString(uint32_t x, char *s) {
+    s[0] = x >> 24;
+    s[1] = (x >> 16) & 0xff;
+    s[2] = (x >> 8) & 0xff;
+    s[3] = x & 0xff;
+    s[4] = '\0';
+}
+
+status_t MPEG4Extractor::readMetaData() {
+    if (mInitCheck != NO_INIT) {
+        return mInitCheck;
+    }
+
+    off64_t offset = 0;
+    status_t err;
+    while (true) {
+        err = parseChunk(&offset, 0);
+        if (err == OK) {
+            continue;
+        }
+
+        uint32_t hdr[2];
+        if (mDataSource->readAt(offset, hdr, 8) < 8) {
+            break;
+        }
+        uint32_t chunk_type = ntohl(hdr[1]);
+        if (chunk_type == FOURCC('s', 'i', 'd', 'x')) {
+            // parse the sidx box too
+            continue;
+        } else if (chunk_type == FOURCC('m', 'o', 'o', 'f')) {
+            // store the offset of the first segment
+            mMoofOffset = offset;
+        }
+        break;
+    }
+
+    if (mInitCheck == OK) {
+        if (mHasVideo) {
+            mFileMetaData->setCString(
+                    kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG4);
+        } else {
+            mFileMetaData->setCString(kKeyMIMEType, "audio/mp4");
+        }
+
+        mInitCheck = OK;
+    } else {
+        mInitCheck = err;
+    }
+
+    CHECK_NE(err, (status_t)NO_INIT);
+
+    // copy pssh data into file metadata
+    int psshsize = 0;
+    for (size_t i = 0; i < mPssh.size(); i++) {
+        psshsize += 20 + mPssh[i].datalen;
+    }
+    if (psshsize) {
+        char *buf = (char*)malloc(psshsize);
+        char *ptr = buf;
+        for (size_t i = 0; i < mPssh.size(); i++) {
+            memcpy(ptr, mPssh[i].uuid, 20); // uuid + length
+            memcpy(ptr + 20, mPssh[i].data, mPssh[i].datalen);
+            ptr += (20 + mPssh[i].datalen);
+        }
+        mFileMetaData->setData(kKeyPssh, 'pssh', buf, psshsize);
+        free(buf);
+    }
+    return mInitCheck;
+}
+
+char* MPEG4Extractor::getDrmTrackInfo(size_t trackID, int *len) {
+    if (mFirstSINF == NULL) {
+        return NULL;
+    }
+
+    SINF *sinf = mFirstSINF;
+    while (sinf && (trackID != sinf->trackID)) {
+        sinf = sinf->next;
+    }
+
+    if (sinf == NULL) {
+        return NULL;
+    }
+
+    *len = sinf->len;
+    return sinf->IPMPData;
+}
+
+// Reads an encoded integer 7 bits at a time until it encounters the high bit clear.
+static int32_t readSize(off64_t offset,
+        const sp<DataSource> DataSource, uint8_t *numOfBytes) {
+    uint32_t size = 0;
+    uint8_t data;
+    bool moreData = true;
+    *numOfBytes = 0;
+
+    while (moreData) {
+        if (DataSource->readAt(offset, &data, 1) < 1) {
+            return -1;
+        }
+        offset ++;
+        moreData = (data >= 128) ? true : false;
+        size = (size << 7) | (data & 0x7f); // Take last 7 bits
+        (*numOfBytes) ++;
+    }
+
+    return size;
+}
+
+status_t MPEG4Extractor::parseDrmSINF(off64_t *offset, off64_t data_offset) {
+    uint8_t updateIdTag;
+    if (mDataSource->readAt(data_offset, &updateIdTag, 1) < 1) {
+        return ERROR_IO;
+    }
+    data_offset ++;
+
+    if (0x01/*OBJECT_DESCRIPTOR_UPDATE_ID_TAG*/ != updateIdTag) {
+        return ERROR_MALFORMED;
+    }
+
+    uint8_t numOfBytes;
+    int32_t size = readSize(data_offset, mDataSource, &numOfBytes);
+    if (size < 0) {
+        return ERROR_IO;
+    }
+    int32_t classSize = size;
+    data_offset += numOfBytes;
+
+    while(size >= 11 ) {
+        uint8_t descriptorTag;
+        if (mDataSource->readAt(data_offset, &descriptorTag, 1) < 1) {
+            return ERROR_IO;
+        }
+        data_offset ++;
+
+        if (0x11/*OBJECT_DESCRIPTOR_ID_TAG*/ != descriptorTag) {
+            return ERROR_MALFORMED;
+        }
+
+        uint8_t buffer[8];
+        //ObjectDescriptorID and ObjectDescriptor url flag
+        if (mDataSource->readAt(data_offset, buffer, 2) < 2) {
+            return ERROR_IO;
+        }
+        data_offset += 2;
+
+        if ((buffer[1] >> 5) & 0x0001) { //url flag is set
+            return ERROR_MALFORMED;
+        }
+
+        if (mDataSource->readAt(data_offset, buffer, 8) < 8) {
+            return ERROR_IO;
+        }
+        data_offset += 8;
+
+        if ((0x0F/*ES_ID_REF_TAG*/ != buffer[1])
+                || ( 0x0A/*IPMP_DESCRIPTOR_POINTER_ID_TAG*/ != buffer[5])) {
+            return ERROR_MALFORMED;
+        }
+
+        SINF *sinf = new SINF;
+        sinf->trackID = U16_AT(&buffer[3]);
+        sinf->IPMPDescriptorID = buffer[7];
+        sinf->next = mFirstSINF;
+        mFirstSINF = sinf;
+
+        size -= (8 + 2 + 1);
+    }
+
+    if (size != 0) {
+        return ERROR_MALFORMED;
+    }
+
+    if (mDataSource->readAt(data_offset, &updateIdTag, 1) < 1) {
+        return ERROR_IO;
+    }
+    data_offset ++;
+
+    if(0x05/*IPMP_DESCRIPTOR_UPDATE_ID_TAG*/ != updateIdTag) {
+        return ERROR_MALFORMED;
+    }
+
+    size = readSize(data_offset, mDataSource, &numOfBytes);
+    if (size < 0) {
+        return ERROR_IO;
+    }
+    classSize = size;
+    data_offset += numOfBytes;
+
+    while (size > 0) {
+        uint8_t tag;
+        int32_t dataLen;
+        if (mDataSource->readAt(data_offset, &tag, 1) < 1) {
+            return ERROR_IO;
+        }
+        data_offset ++;
+
+        if (0x0B/*IPMP_DESCRIPTOR_ID_TAG*/ == tag) {
+            uint8_t id;
+            dataLen = readSize(data_offset, mDataSource, &numOfBytes);
+            if (dataLen < 0) {
+                return ERROR_IO;
+            } else if (dataLen < 4) {
+                return ERROR_MALFORMED;
+            }
+            data_offset += numOfBytes;
+
+            if (mDataSource->readAt(data_offset, &id, 1) < 1) {
+                return ERROR_IO;
+            }
+            data_offset ++;
+
+            SINF *sinf = mFirstSINF;
+            while (sinf && (sinf->IPMPDescriptorID != id)) {
+                sinf = sinf->next;
+            }
+            if (sinf == NULL) {
+                return ERROR_MALFORMED;
+            }
+            sinf->len = dataLen - 3;
+            sinf->IPMPData = new char[sinf->len];
+
+            if (mDataSource->readAt(data_offset + 2, sinf->IPMPData, sinf->len) < sinf->len) {
+                return ERROR_IO;
+            }
+            data_offset += sinf->len;
+
+            size -= (dataLen + numOfBytes + 1);
+        }
+    }
+
+    if (size != 0) {
+        return ERROR_MALFORMED;
+    }
+
+    return UNKNOWN_ERROR;  // Return a dummy error.
+}
+
+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(off64_t *offset, int depth) {
+    ALOGV("entering parseChunk %lld/%d", *offset, depth);
+    uint32_t hdr[2];
+    if (mDataSource->readAt(*offset, hdr, 8) < 8) {
+        return ERROR_IO;
+    }
+    uint64_t chunk_size = ntohl(hdr[0]);
+    uint32_t chunk_type = ntohl(hdr[1]);
+    off64_t data_offset = *offset + 8;
+
+    if (chunk_size == 1) {
+        if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) {
+            return ERROR_IO;
+        }
+        chunk_size = ntoh64(chunk_size);
+        data_offset += 8;
+
+        if (chunk_size < 16) {
+            // The smallest valid chunk is 16 bytes long in this case.
+            return ERROR_MALFORMED;
+        }
+    } else if (chunk_size < 8) {
+        // The smallest valid chunk is 8 bytes long.
+        return ERROR_MALFORMED;
+    }
+
+    char chunk[5];
+    MakeFourCCString(chunk_type, chunk);
+    ALOGV("chunk: %s @ %lld, %d", chunk, *offset, depth);
+
+#if 0
+    static const char kWhitespace[] = "                                        ";
+    const char *indent = &kWhitespace[sizeof(kWhitespace) - 1 - 2 * depth];
+    printf("%sfound chunk '%s' of size %lld\n", indent, chunk, chunk_size);
+
+    char buffer[256];
+    size_t n = chunk_size;
+    if (n > sizeof(buffer)) {
+        n = sizeof(buffer);
+    }
+    if (mDataSource->readAt(*offset, buffer, n)
+            < (ssize_t)n) {
+        return ERROR_IO;
+    }
+
+    hexdump(buffer, n);
+#endif
+
+    PathAdder autoAdder(&mPath, chunk_type);
+
+    off64_t chunk_data_size = *offset + chunk_size - data_offset;
+
+    if (chunk_type != FOURCC('c', 'p', 'r', 't')
+            && chunk_type != FOURCC('c', 'o', 'v', 'r')
+            && mPath.size() == 5 && underMetaDataPath(mPath)) {
+        off64_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;
+            }
+        }
+
+        if (*offset != stop_offset) {
+            return ERROR_MALFORMED;
+        }
+
+        return OK;
+    }
+
+    switch(chunk_type) {
+        case FOURCC('m', 'o', 'o', 'v'):
+        case FOURCC('t', 'r', 'a', 'k'):
+        case FOURCC('m', 'd', 'i', 'a'):
+        case FOURCC('m', 'i', 'n', 'f'):
+        case FOURCC('d', 'i', 'n', 'f'):
+        case FOURCC('s', 't', 'b', 'l'):
+        case FOURCC('m', 'v', 'e', 'x'):
+        case FOURCC('m', 'o', 'o', 'f'):
+        case FOURCC('t', 'r', 'a', 'f'):
+        case FOURCC('m', 'f', 'r', 'a'):
+        case FOURCC('u', 'd', 't', 'a'):
+        case FOURCC('i', 'l', 's', 't'):
+        case FOURCC('s', 'i', 'n', 'f'):
+        case FOURCC('s', 'c', 'h', 'i'):
+        case FOURCC('e', 'd', 't', 's'):
+        {
+            if (chunk_type == FOURCC('s', 't', 'b', 'l')) {
+                ALOGV("sampleTable chunk is %d bytes long.", (size_t)chunk_size);
+
+                if (mDataSource->flags()
+                        & (DataSource::kWantsPrefetching
+                            | DataSource::kIsCachingDataSource)) {
+                    sp<MPEG4DataSource> cachedSource =
+                        new MPEG4DataSource(mDataSource);
+
+                    if (cachedSource->setCachedRange(*offset, chunk_size) == OK) {
+                        mDataSource = cachedSource;
+                    }
+                }
+
+                mLastTrack->sampleTable = new SampleTable(mDataSource);
+            }
+
+            bool isTrack = false;
+            if (chunk_type == FOURCC('t', 'r', 'a', 'k')) {
+                isTrack = true;
+
+                Track *track = new Track;
+                track->next = NULL;
+                if (mLastTrack) {
+                    mLastTrack->next = track;
+                } else {
+                    mFirstTrack = track;
+                }
+                mLastTrack = track;
+
+                track->meta = new MetaData;
+                track->includes_expensive_metadata = false;
+                track->skipTrack = false;
+                track->timescale = 0;
+                track->meta->setCString(kKeyMIMEType, "application/octet-stream");
+            }
+
+            off64_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;
+                }
+            }
+
+            if (*offset != stop_offset) {
+                return ERROR_MALFORMED;
+            }
+
+            if (isTrack) {
+                if (mLastTrack->skipTrack) {
+                    Track *cur = mFirstTrack;
+
+                    if (cur == mLastTrack) {
+                        delete cur;
+                        mFirstTrack = mLastTrack = NULL;
+                    } else {
+                        while (cur && cur->next != mLastTrack) {
+                            cur = cur->next;
+                        }
+                        cur->next = NULL;
+                        delete mLastTrack;
+                        mLastTrack = cur;
+                    }
+
+                    return OK;
+                }
+
+                status_t err = verifyTrack(mLastTrack);
+
+                if (err != OK) {
+                    return err;
+                }
+            } else if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
+                mInitCheck = OK;
+
+                if (!mIsDrm) {
+                    return UNKNOWN_ERROR;  // Return a dummy error.
+                } else {
+                    return OK;
+                }
+            }
+            break;
+        }
+
+        case FOURCC('e', 'l', 's', 't'):
+        {
+            // See 14496-12 8.6.6
+            uint8_t version;
+            if (mDataSource->readAt(data_offset, &version, 1) < 1) {
+                return ERROR_IO;
+            }
+
+            uint32_t entry_count;
+            if (!mDataSource->getUInt32(data_offset + 4, &entry_count)) {
+                return ERROR_IO;
+            }
+
+            if (entry_count != 1) {
+                // we only support a single entry at the moment, for gapless playback
+                ALOGW("ignoring edit list with %d entries", entry_count);
+            } else if (mHeaderTimescale == 0) {
+                ALOGW("ignoring edit list because timescale is 0");
+            } else {
+                off64_t entriesoffset = data_offset + 8;
+                uint64_t segment_duration;
+                int64_t media_time;
+
+                if (version == 1) {
+                    if (!mDataSource->getUInt64(entriesoffset, &segment_duration) ||
+                            !mDataSource->getUInt64(entriesoffset + 8, (uint64_t*)&media_time)) {
+                        return ERROR_IO;
+                    }
+                } else if (version == 0) {
+                    uint32_t sd;
+                    int32_t mt;
+                    if (!mDataSource->getUInt32(entriesoffset, &sd) ||
+                            !mDataSource->getUInt32(entriesoffset + 4, (uint32_t*)&mt)) {
+                        return ERROR_IO;
+                    }
+                    segment_duration = sd;
+                    media_time = mt;
+                } else {
+                    return ERROR_IO;
+                }
+
+                uint64_t halfscale = mHeaderTimescale / 2;
+                segment_duration = (segment_duration * 1000000 + halfscale)/ mHeaderTimescale;
+                media_time = (media_time * 1000000 + halfscale) / mHeaderTimescale;
+
+                int64_t duration;
+                int32_t samplerate;
+                if (mLastTrack->meta->findInt64(kKeyDuration, &duration) &&
+                        mLastTrack->meta->findInt32(kKeySampleRate, &samplerate)) {
+
+                    int64_t delay = (media_time  * samplerate + 500000) / 1000000;
+                    mLastTrack->meta->setInt32(kKeyEncoderDelay, delay);
+
+                    int64_t paddingus = duration - (segment_duration + media_time);
+                    int64_t paddingsamples = (paddingus * samplerate + 500000) / 1000000;
+                    mLastTrack->meta->setInt32(kKeyEncoderPadding, paddingsamples);
+                }
+            }
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('f', 'r', 'm', 'a'):
+        {
+            uint32_t original_fourcc;
+            if (mDataSource->readAt(data_offset, &original_fourcc, 4) < 4) {
+                return ERROR_IO;
+            }
+            original_fourcc = ntohl(original_fourcc);
+            ALOGV("read original format: %d", original_fourcc);
+            mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(original_fourcc));
+            uint32_t num_channels = 0;
+            uint32_t sample_rate = 0;
+            if (AdjustChannelsAndRate(original_fourcc, &num_channels, &sample_rate)) {
+                mLastTrack->meta->setInt32(kKeyChannelCount, num_channels);
+                mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);
+            }
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('t', 'e', 'n', 'c'):
+        {
+            if (chunk_size < 32) {
+                return ERROR_MALFORMED;
+            }
+
+            // tenc box contains 1 byte version, 3 byte flags, 3 byte default algorithm id, one byte
+            // default IV size, 16 bytes default KeyID
+            // (ISO 23001-7)
+            char buf[4];
+            memset(buf, 0, 4);
+            if (mDataSource->readAt(data_offset + 4, buf + 1, 3) < 3) {
+                return ERROR_IO;
+            }
+            uint32_t defaultAlgorithmId = ntohl(*((int32_t*)buf));
+            if (defaultAlgorithmId > 1) {
+                // only 0 (clear) and 1 (AES-128) are valid
+                return ERROR_MALFORMED;
+            }
+
+            memset(buf, 0, 4);
+            if (mDataSource->readAt(data_offset + 7, buf + 3, 1) < 1) {
+                return ERROR_IO;
+            }
+            uint32_t defaultIVSize = ntohl(*((int32_t*)buf));
+
+            if ((defaultAlgorithmId == 0 && defaultIVSize != 0) ||
+                    (defaultAlgorithmId != 0 && defaultIVSize == 0)) {
+                // only unencrypted data must have 0 IV size
+                return ERROR_MALFORMED;
+            } else if (defaultIVSize != 0 &&
+                    defaultIVSize != 8 &&
+                    defaultIVSize != 16) {
+                // only supported sizes are 0, 8 and 16
+                return ERROR_MALFORMED;
+            }
+
+            uint8_t defaultKeyId[16];
+
+            if (mDataSource->readAt(data_offset + 8, &defaultKeyId, 16) < 16) {
+                return ERROR_IO;
+            }
+
+            mLastTrack->meta->setInt32(kKeyCryptoMode, defaultAlgorithmId);
+            mLastTrack->meta->setInt32(kKeyCryptoDefaultIVSize, defaultIVSize);
+            mLastTrack->meta->setData(kKeyCryptoKey, 'tenc', defaultKeyId, 16);
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('t', 'k', 'h', 'd'):
+        {
+            status_t err;
+            if ((err = parseTrackHeader(data_offset, chunk_data_size)) != OK) {
+                return err;
+            }
+
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('p', 's', 's', 'h'):
+        {
+            PsshInfo pssh;
+
+            if (mDataSource->readAt(data_offset + 4, &pssh.uuid, 16) < 16) {
+                return ERROR_IO;
+            }
+
+            uint32_t psshdatalen = 0;
+            if (mDataSource->readAt(data_offset + 20, &psshdatalen, 4) < 4) {
+                return ERROR_IO;
+            }
+            pssh.datalen = ntohl(psshdatalen);
+            ALOGV("pssh data size: %d", pssh.datalen);
+            if (pssh.datalen + 20 > chunk_size) {
+                // pssh data length exceeds size of containing box
+                return ERROR_MALFORMED;
+            }
+
+            pssh.data = new uint8_t[pssh.datalen];
+            ALOGV("allocated pssh @ %p", pssh.data);
+            ssize_t requested = (ssize_t) pssh.datalen;
+            if (mDataSource->readAt(data_offset + 24, pssh.data, requested) < requested) {
+                return ERROR_IO;
+            }
+            mPssh.push_back(pssh);
+
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('m', 'd', 'h', 'd'):
+        {
+            if (chunk_data_size < 4) {
+                return ERROR_MALFORMED;
+            }
+
+            uint8_t version;
+            if (mDataSource->readAt(
+                        data_offset, &version, sizeof(version))
+                    < (ssize_t)sizeof(version)) {
+                return ERROR_IO;
+            }
+
+            off64_t timescale_offset;
+
+            if (version == 1) {
+                timescale_offset = data_offset + 4 + 16;
+            } else if (version == 0) {
+                timescale_offset = data_offset + 4 + 8;
+            } else {
+                return ERROR_IO;
+            }
+
+            uint32_t timescale;
+            if (mDataSource->readAt(
+                        timescale_offset, &timescale, sizeof(timescale))
+                    < (ssize_t)sizeof(timescale)) {
+                return ERROR_IO;
+            }
+
+            mLastTrack->timescale = ntohl(timescale);
+
+            int64_t duration = 0;
+            if (version == 1) {
+                if (mDataSource->readAt(
+                            timescale_offset + 4, &duration, sizeof(duration))
+                        < (ssize_t)sizeof(duration)) {
+                    return ERROR_IO;
+                }
+                duration = ntoh64(duration);
+            } else {
+                uint32_t duration32;
+                if (mDataSource->readAt(
+                            timescale_offset + 4, &duration32, sizeof(duration32))
+                        < (ssize_t)sizeof(duration32)) {
+                    return ERROR_IO;
+                }
+                // ffmpeg sets duration to -1, which is incorrect.
+                if (duration32 != 0xffffffff) {
+                    duration = ntohl(duration32);
+                }
+            }
+            mLastTrack->meta->setInt64(
+                    kKeyDuration, (duration * 1000000) / mLastTrack->timescale);
+
+            uint8_t lang[2];
+            off64_t lang_offset;
+            if (version == 1) {
+                lang_offset = timescale_offset + 4 + 8;
+            } else if (version == 0) {
+                lang_offset = timescale_offset + 4 + 4;
+            } else {
+                return ERROR_IO;
+            }
+
+            if (mDataSource->readAt(lang_offset, &lang, sizeof(lang))
+                    < (ssize_t)sizeof(lang)) {
+                return ERROR_IO;
+            }
+
+            // To get the ISO-639-2/T three character language code
+            // 1 bit pad followed by 3 5-bits characters. Each character
+            // is packed as the difference between its ASCII value and 0x60.
+            char lang_code[4];
+            lang_code[0] = ((lang[0] >> 2) & 0x1f) + 0x60;
+            lang_code[1] = ((lang[0] & 0x3) << 3 | (lang[1] >> 5)) + 0x60;
+            lang_code[2] = (lang[1] & 0x1f) + 0x60;
+            lang_code[3] = '\0';
+
+            mLastTrack->meta->setCString(
+                    kKeyMediaLanguage, lang_code);
+
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('s', 't', 's', 'd'):
+        {
+            if (chunk_data_size < 8) {
+                return ERROR_MALFORMED;
+            }
+
+            uint8_t buffer[8];
+            if (chunk_data_size < (off64_t)sizeof(buffer)) {
+                return ERROR_MALFORMED;
+            }
+
+            if (mDataSource->readAt(
+                        data_offset, buffer, 8) < 8) {
+                return ERROR_IO;
+            }
+
+            if (U32_AT(buffer) != 0) {
+                // Should be version 0, flags 0.
+                return ERROR_MALFORMED;
+            }
+
+            uint32_t entry_count = U32_AT(&buffer[4]);
+
+            if (entry_count > 1) {
+                // For 3GPP timed text, there could be multiple tx3g boxes contain
+                // multiple text display formats. These formats will be used to
+                // display the timed text.
+                // For encrypted files, there may also be more than one entry.
+                const char *mime;
+                CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));
+                if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) &&
+                        strcasecmp(mime, "application/octet-stream")) {
+                    // For now we only support a single type of media per track.
+                    mLastTrack->skipTrack = true;
+                    *offset += chunk_size;
+                    break;
+                }
+            }
+            off64_t stop_offset = *offset + chunk_size;
+            *offset = data_offset + 8;
+            for (uint32_t i = 0; i < entry_count; ++i) {
+                status_t err = parseChunk(offset, depth + 1);
+                if (err != OK) {
+                    return err;
+                }
+            }
+
+            if (*offset != stop_offset) {
+                return ERROR_MALFORMED;
+            }
+            break;
+        }
+
+        case FOURCC('m', 'p', '4', 'a'):
+        case FOURCC('e', 'n', 'c', 'a'):
+        case FOURCC('s', 'a', 'm', 'r'):
+        case FOURCC('s', 'a', 'w', 'b'):
+        {
+            uint8_t buffer[8 + 20];
+            if (chunk_data_size < (ssize_t)sizeof(buffer)) {
+                // Basic AudioSampleEntry size.
+                return ERROR_MALFORMED;
+            }
+
+            if (mDataSource->readAt(
+                        data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
+                return ERROR_IO;
+            }
+
+            uint16_t data_ref_index = U16_AT(&buffer[6]);
+            uint32_t num_channels = U16_AT(&buffer[16]);
+
+            uint16_t sample_size = U16_AT(&buffer[18]);
+            uint32_t sample_rate = U32_AT(&buffer[24]) >> 16;
+
+            if (chunk_type != FOURCC('e', 'n', 'c', 'a')) {
+                // if the chunk type is enca, we'll get the type from the sinf/frma box later
+                mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
+                AdjustChannelsAndRate(chunk_type, &num_channels, &sample_rate);
+            }
+            ALOGV("*** coding='%s' %d channels, size %d, rate %d\n",
+                   chunk, num_channels, sample_size, sample_rate);
+            mLastTrack->meta->setInt32(kKeyChannelCount, num_channels);
+            mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);
+
+            off64_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;
+                }
+            }
+
+            if (*offset != stop_offset) {
+                return ERROR_MALFORMED;
+            }
+            break;
+        }
+
+        case FOURCC('m', 'p', '4', 'v'):
+        case FOURCC('e', 'n', 'c', 'v'):
+        case FOURCC('s', '2', '6', '3'):
+        case FOURCC('H', '2', '6', '3'):
+        case FOURCC('h', '2', '6', '3'):
+        case FOURCC('a', 'v', 'c', '1'):
+        {
+            mHasVideo = true;
+
+            uint8_t buffer[78];
+            if (chunk_data_size < (ssize_t)sizeof(buffer)) {
+                // Basic VideoSampleEntry size.
+                return ERROR_MALFORMED;
+            }
+
+            if (mDataSource->readAt(
+                        data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
+                return ERROR_IO;
+            }
+
+            uint16_t data_ref_index = U16_AT(&buffer[6]);
+            uint16_t width = U16_AT(&buffer[6 + 18]);
+            uint16_t height = U16_AT(&buffer[6 + 20]);
+
+            // The video sample is not standard-compliant if it has invalid dimension.
+            // Use some default width and height value, and
+            // let the decoder figure out the actual width and height (and thus
+            // be prepared for INFO_FOMRAT_CHANGED event).
+            if (width == 0)  width  = 352;
+            if (height == 0) height = 288;
+
+            // printf("*** coding='%s' width=%d height=%d\n",
+            //        chunk, width, height);
+
+            if (chunk_type != FOURCC('e', 'n', 'c', 'v')) {
+                // if the chunk type is encv, we'll get the type from the sinf/frma box later
+                mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
+            }
+            mLastTrack->meta->setInt32(kKeyWidth, width);
+            mLastTrack->meta->setInt32(kKeyHeight, height);
+
+            off64_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;
+                }
+            }
+
+            if (*offset != stop_offset) {
+                return ERROR_MALFORMED;
+            }
+            break;
+        }
+
+        case FOURCC('s', 't', 'c', 'o'):
+        case FOURCC('c', 'o', '6', '4'):
+        {
+            status_t err =
+                mLastTrack->sampleTable->setChunkOffsetParams(
+                        chunk_type, data_offset, chunk_data_size);
+
+            if (err != OK) {
+                return err;
+            }
+
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('s', 't', 's', 'c'):
+        {
+            status_t err =
+                mLastTrack->sampleTable->setSampleToChunkParams(
+                        data_offset, chunk_data_size);
+
+            if (err != OK) {
+                return err;
+            }
+
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('s', 't', 's', 'z'):
+        case FOURCC('s', 't', 'z', '2'):
+        {
+            status_t err =
+                mLastTrack->sampleTable->setSampleSizeParams(
+                        chunk_type, data_offset, chunk_data_size);
+
+            if (err != OK) {
+                return err;
+            }
+
+            size_t max_size;
+            err = mLastTrack->sampleTable->getMaxSampleSize(&max_size);
+
+            if (err != OK) {
+                return err;
+            }
+
+            if (max_size != 0) {
+                // Assume that a given buffer only contains at most 10 chunks,
+                // each chunk originally prefixed with a 2 byte length will
+                // have a 4 byte header (0x00 0x00 0x00 0x01) after conversion,
+                // and thus will grow by 2 bytes per chunk.
+                mLastTrack->meta->setInt32(kKeyMaxInputSize, max_size + 10 * 2);
+            } else {
+                // No size was specified. Pick a conservatively large size.
+                int32_t width, height;
+                if (mLastTrack->meta->findInt32(kKeyWidth, &width) &&
+                        mLastTrack->meta->findInt32(kKeyHeight, &height)) {
+                    mLastTrack->meta->setInt32(kKeyMaxInputSize, width * height * 3 / 2);
+                } else {
+                    ALOGE("No width or height, assuming worst case 1080p");
+                    mLastTrack->meta->setInt32(kKeyMaxInputSize, 3110400);
+                }
+            }
+            *offset += chunk_size;
+
+            // Calculate average frame rate.
+            const char *mime;
+            CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));
+            if (!strncasecmp("video/", mime, 6)) {
+                size_t nSamples = mLastTrack->sampleTable->countSamples();
+                int64_t durationUs;
+                if (mLastTrack->meta->findInt64(kKeyDuration, &durationUs)) {
+                    if (durationUs > 0) {
+                        int32_t frameRate = (nSamples * 1000000LL +
+                                    (durationUs >> 1)) / durationUs;
+                        mLastTrack->meta->setInt32(kKeyFrameRate, frameRate);
+                    }
+                }
+            }
+
+            break;
+        }
+
+        case FOURCC('s', 't', 't', 's'):
+        {
+            status_t err =
+                mLastTrack->sampleTable->setTimeToSampleParams(
+                        data_offset, chunk_data_size);
+
+            if (err != OK) {
+                return err;
+            }
+
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('c', 't', 't', 's'):
+        {
+            status_t err =
+                mLastTrack->sampleTable->setCompositionTimeToSampleParams(
+                        data_offset, chunk_data_size);
+
+            if (err != OK) {
+                return err;
+            }
+
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('s', 't', 's', 's'):
+        {
+            status_t err =
+                mLastTrack->sampleTable->setSyncSampleParams(
+                        data_offset, chunk_data_size);
+
+            if (err != OK) {
+                return err;
+            }
+
+            *offset += chunk_size;
+            break;
+        }
+
+        // @xyz
+        case FOURCC('\xA9', 'x', 'y', 'z'):
+        {
+            // Best case the total data length inside "@xyz" box
+            // would be 8, for instance "@xyz" + "\x00\x04\x15\xc7" + "0+0/",
+            // where "\x00\x04" is the text string length with value = 4,
+            // "\0x15\xc7" is the language code = en, and "0+0" is a
+            // location (string) value with longitude = 0 and latitude = 0.
+            if (chunk_data_size < 8) {
+                return ERROR_MALFORMED;
+            }
+
+            // Worst case the location string length would be 18,
+            // for instance +90.0000-180.0000, without the trailing "/" and
+            // the string length + language code.
+            char buffer[18];
+
+            // Substracting 5 from the data size is because the text string length +
+            // language code takes 4 bytes, and the trailing slash "/" takes 1 byte.
+            off64_t location_length = chunk_data_size - 5;
+            if (location_length >= (off64_t) sizeof(buffer)) {
+                return ERROR_MALFORMED;
+            }
+
+            if (mDataSource->readAt(
+                        data_offset + 4, buffer, location_length) < location_length) {
+                return ERROR_IO;
+            }
+
+            buffer[location_length] = '\0';
+            mFileMetaData->setCString(kKeyLocation, buffer);
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('e', 's', 'd', 's'):
+        {
+            if (chunk_data_size < 4) {
+                return ERROR_MALFORMED;
+            }
+
+            uint8_t buffer[256];
+            if (chunk_data_size > (off64_t)sizeof(buffer)) {
+                return ERROR_BUFFER_TOO_SMALL;
+            }
+
+            if (mDataSource->readAt(
+                        data_offset, buffer, chunk_data_size) < chunk_data_size) {
+                return ERROR_IO;
+            }
+
+            if (U32_AT(buffer) != 0) {
+                // Should be version 0, flags 0.
+                return ERROR_MALFORMED;
+            }
+
+            mLastTrack->meta->setData(
+                    kKeyESDS, kTypeESDS, &buffer[4], chunk_data_size - 4);
+
+            if (mPath.size() >= 2
+                    && mPath[mPath.size() - 2] == FOURCC('m', 'p', '4', 'a')) {
+                // Information from the ESDS must be relied on for proper
+                // setup of sample rate and channel count for MPEG4 Audio.
+                // The generic header appears to only contain generic
+                // information...
+
+                status_t err = updateAudioTrackInfoFromESDS_MPEG4Audio(
+                        &buffer[4], chunk_data_size - 4);
+
+                if (err != OK) {
+                    return err;
+                }
+            }
+
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('a', 'v', 'c', 'C'):
+        {
+            sp<ABuffer> buffer = new ABuffer(chunk_data_size);
+
+            if (mDataSource->readAt(
+                        data_offset, buffer->data(), chunk_data_size) < chunk_data_size) {
+                return ERROR_IO;
+            }
+
+            mLastTrack->meta->setData(
+                    kKeyAVCC, kTypeAVCC, buffer->data(), chunk_data_size);
+
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('d', '2', '6', '3'):
+        {
+            /*
+             * d263 contains a fixed 7 bytes part:
+             *   vendor - 4 bytes
+             *   version - 1 byte
+             *   level - 1 byte
+             *   profile - 1 byte
+             * optionally, "d263" box itself may contain a 16-byte
+             * bit rate box (bitr)
+             *   average bit rate - 4 bytes
+             *   max bit rate - 4 bytes
+             */
+            char buffer[23];
+            if (chunk_data_size != 7 &&
+                chunk_data_size != 23) {
+                ALOGE("Incorrect D263 box size %lld", chunk_data_size);
+                return ERROR_MALFORMED;
+            }
+
+            if (mDataSource->readAt(
+                    data_offset, buffer, chunk_data_size) < chunk_data_size) {
+                return ERROR_IO;
+            }
+
+            mLastTrack->meta->setData(kKeyD263, kTypeD263, buffer, chunk_data_size);
+
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('m', 'e', 't', 'a'):
+        {
+            uint8_t buffer[4];
+            if (chunk_data_size < (off64_t)sizeof(buffer)) {
+                return ERROR_MALFORMED;
+            }
+
+            if (mDataSource->readAt(
+                        data_offset, buffer, 4) < 4) {
+                return ERROR_IO;
+            }
+
+            if (U32_AT(buffer) != 0) {
+                // Should be version 0, flags 0.
+
+                // If it's not, let's assume this is one of those
+                // apparently malformed chunks that don't have flags
+                // and completely different semantics than what's
+                // in the MPEG4 specs and skip it.
+                *offset += chunk_size;
+                return OK;
+            }
+
+            off64_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;
+                }
+            }
+
+            if (*offset != stop_offset) {
+                return ERROR_MALFORMED;
+            }
+            break;
+        }
+
+        case FOURCC('m', 'e', 'a', 'n'):
+        case FOURCC('n', 'a', 'm', 'e'):
+        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 < 24) {
+                return ERROR_MALFORMED;
+            }
+
+            uint8_t header[24];
+            if (mDataSource->readAt(
+                        data_offset, header, sizeof(header))
+                    < (ssize_t)sizeof(header)) {
+                return ERROR_IO;
+            }
+
+            uint64_t creationTime;
+            if (header[0] == 1) {
+                creationTime = U64_AT(&header[4]);
+                mHeaderTimescale = U32_AT(&header[20]);
+            } else if (header[0] != 0) {
+                return ERROR_MALFORMED;
+            } else {
+                creationTime = U32_AT(&header[4]);
+                mHeaderTimescale = U32_AT(&header[12]);
+            }
+
+            String8 s;
+            convertTimeToDate(creationTime, &s);
+
+            mFileMetaData->setCString(kKeyDate, s.string());
+
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('m', 'd', 'a', 't'):
+        {
+            ALOGV("mdat chunk, drm: %d", mIsDrm);
+            if (!mIsDrm) {
+                *offset += chunk_size;
+                break;
+            }
+
+            if (chunk_size < 8) {
+                return ERROR_MALFORMED;
+            }
+
+            return parseDrmSINF(offset, data_offset);
+        }
+
+        case FOURCC('h', 'd', 'l', 'r'):
+        {
+            uint32_t buffer;
+            if (mDataSource->readAt(
+                        data_offset + 8, &buffer, 4) < 4) {
+                return ERROR_IO;
+            }
+
+            uint32_t type = ntohl(buffer);
+            // For the 3GPP file format, the handler-type within the 'hdlr' box
+            // shall be 'text'. We also want to support 'sbtl' handler type
+            // for a practical reason as various MPEG4 containers use it.
+            if (type == FOURCC('t', 'e', 'x', 't') || type == FOURCC('s', 'b', 't', 'l')) {
+                mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_TEXT_3GPP);
+            }
+
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('t', 'x', '3', 'g'):
+        {
+            uint32_t type;
+            const void *data;
+            size_t size = 0;
+            if (!mLastTrack->meta->findData(
+                    kKeyTextFormatData, &type, &data, &size)) {
+                size = 0;
+            }
+
+            uint8_t *buffer = new uint8_t[size + chunk_size];
+
+            if (size > 0) {
+                memcpy(buffer, data, size);
+            }
+
+            if ((size_t)(mDataSource->readAt(*offset, buffer + size, chunk_size))
+                    < chunk_size) {
+                delete[] buffer;
+                buffer = NULL;
+
+                return ERROR_IO;
+            }
+
+            mLastTrack->meta->setData(
+                    kKeyTextFormatData, 0, buffer, size + chunk_size);
+
+            delete[] buffer;
+
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('c', 'o', 'v', 'r'):
+        {
+            if (mFileMetaData != NULL) {
+                ALOGV("chunk_data_size = %lld and data_offset = %lld",
+                        chunk_data_size, data_offset);
+                sp<ABuffer> buffer = new ABuffer(chunk_data_size + 1);
+                if (mDataSource->readAt(
+                    data_offset, buffer->data(), chunk_data_size) != (ssize_t)chunk_data_size) {
+                    return ERROR_IO;
+                }
+                const int kSkipBytesOfDataBox = 16;
+                mFileMetaData->setData(
+                    kKeyAlbumArt, MetaData::TYPE_NONE,
+                    buffer->data() + kSkipBytesOfDataBox, chunk_data_size - kSkipBytesOfDataBox);
+            }
+
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('-', '-', '-', '-'):
+        {
+            mLastCommentMean.clear();
+            mLastCommentName.clear();
+            mLastCommentData.clear();
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('s', 'i', 'd', 'x'):
+        {
+            parseSegmentIndex(data_offset, chunk_data_size);
+            *offset += chunk_size;
+            return UNKNOWN_ERROR; // stop parsing after sidx
+        }
+
+        default:
+        {
+            *offset += chunk_size;
+            break;
+        }
+    }
+
+    return OK;
+}
+
+status_t MPEG4Extractor::parseSegmentIndex(off64_t offset, size_t size) {
+  ALOGV("MPEG4Extractor::parseSegmentIndex");
+
+    if (size < 12) {
+      return -EINVAL;
+    }
+
+    uint32_t flags;
+    if (!mDataSource->getUInt32(offset, &flags)) {
+        return ERROR_MALFORMED;
+    }
+
+    uint32_t version = flags >> 24;
+    flags &= 0xffffff;
+
+    ALOGV("sidx version %d", version);
+
+    uint32_t referenceId;
+    if (!mDataSource->getUInt32(offset + 4, &referenceId)) {
+        return ERROR_MALFORMED;
+    }
+
+    uint32_t timeScale;
+    if (!mDataSource->getUInt32(offset + 8, &timeScale)) {
+        return ERROR_MALFORMED;
+    }
+    ALOGV("sidx refid/timescale: %d/%d", referenceId, timeScale);
+
+    uint64_t earliestPresentationTime;
+    uint64_t firstOffset;
+
+    offset += 12;
+    size -= 12;
+
+    if (version == 0) {
+        if (size < 8) {
+            return -EINVAL;
+        }
+        uint32_t tmp;
+        if (!mDataSource->getUInt32(offset, &tmp)) {
+            return ERROR_MALFORMED;
+        }
+        earliestPresentationTime = tmp;
+        if (!mDataSource->getUInt32(offset + 4, &tmp)) {
+            return ERROR_MALFORMED;
+        }
+        firstOffset = tmp;
+        offset += 8;
+        size -= 8;
+    } else {
+        if (size < 16) {
+            return -EINVAL;
+        }
+        if (!mDataSource->getUInt64(offset, &earliestPresentationTime)) {
+            return ERROR_MALFORMED;
+        }
+        if (!mDataSource->getUInt64(offset + 8, &firstOffset)) {
+            return ERROR_MALFORMED;
+        }
+        offset += 16;
+        size -= 16;
+    }
+    ALOGV("sidx pres/off: %Ld/%Ld", earliestPresentationTime, firstOffset);
+
+    if (size < 4) {
+        return -EINVAL;
+    }
+
+    uint16_t referenceCount;
+    if (!mDataSource->getUInt16(offset + 2, &referenceCount)) {
+        return ERROR_MALFORMED;
+    }
+    offset += 4;
+    size -= 4;
+    ALOGV("refcount: %d", referenceCount);
+
+    if (size < referenceCount * 12) {
+        return -EINVAL;
+    }
+
+    uint64_t total_duration = 0;
+    for (unsigned int i = 0; i < referenceCount; i++) {
+        uint32_t d1, d2, d3;
+
+        if (!mDataSource->getUInt32(offset, &d1) ||     // size
+            !mDataSource->getUInt32(offset + 4, &d2) || // duration
+            !mDataSource->getUInt32(offset + 8, &d3)) { // flags
+            return ERROR_MALFORMED;
+        }
+
+        if (d1 & 0x80000000) {
+            ALOGW("sub-sidx boxes not supported yet");
+        }
+        bool sap = d3 & 0x80000000;
+        bool saptype = d3 >> 28;
+        if (!sap || saptype > 2) {
+            ALOGW("not a stream access point, or unsupported type");
+        }
+        total_duration += d2;
+        offset += 12;
+        ALOGV(" item %d, %08x %08x %08x", i, d1, d2, d3);
+        SidxEntry se;
+        se.mSize = d1 & 0x7fffffff;
+        se.mDurationUs = 1000000LL * d2 / timeScale;
+        mSidxEntries.add(se);
+    }
+
+    mSidxDuration = total_duration * 1000000 / timeScale;
+    ALOGV("duration: %lld", mSidxDuration);
+
+    int64_t metaDuration;
+    if (!mLastTrack->meta->findInt64(kKeyDuration, &metaDuration) || metaDuration == 0) {
+        mLastTrack->meta->setInt64(kKeyDuration, mSidxDuration);
+    }
+    return OK;
+}
+
+
+
+status_t MPEG4Extractor::parseTrackHeader(
+        off64_t data_offset, off64_t data_size) {
+    if (data_size < 4) {
+        return ERROR_MALFORMED;
+    }
+
+    uint8_t version;
+    if (mDataSource->readAt(data_offset, &version, 1) < 1) {
+        return ERROR_IO;
+    }
+
+    size_t dynSize = (version == 1) ? 36 : 24;
+
+    uint8_t buffer[36 + 60];
+
+    if (data_size != (off64_t)dynSize + 60) {
+        return ERROR_MALFORMED;
+    }
+
+    if (mDataSource->readAt(
+                data_offset, buffer, data_size) < (ssize_t)data_size) {
+        return ERROR_IO;
+    }
+
+    uint64_t ctime, mtime, duration;
+    int32_t id;
+
+    if (version == 1) {
+        ctime = U64_AT(&buffer[4]);
+        mtime = U64_AT(&buffer[12]);
+        id = U32_AT(&buffer[20]);
+        duration = U64_AT(&buffer[28]);
+    } else if (version == 0) {
+        ctime = U32_AT(&buffer[4]);
+        mtime = U32_AT(&buffer[8]);
+        id = U32_AT(&buffer[12]);
+        duration = U32_AT(&buffer[20]);
+    } else {
+        return ERROR_UNSUPPORTED;
+    }
+
+    mLastTrack->meta->setInt32(kKeyTrackID, id);
+
+    size_t matrixOffset = dynSize + 16;
+    int32_t a00 = U32_AT(&buffer[matrixOffset]);
+    int32_t a01 = U32_AT(&buffer[matrixOffset + 4]);
+    int32_t dx = U32_AT(&buffer[matrixOffset + 8]);
+    int32_t a10 = U32_AT(&buffer[matrixOffset + 12]);
+    int32_t a11 = U32_AT(&buffer[matrixOffset + 16]);
+    int32_t dy = U32_AT(&buffer[matrixOffset + 20]);
+
+#if 0
+    ALOGI("x' = %.2f * x + %.2f * y + %.2f",
+         a00 / 65536.0f, a01 / 65536.0f, dx / 65536.0f);
+    ALOGI("y' = %.2f * x + %.2f * y + %.2f",
+         a10 / 65536.0f, a11 / 65536.0f, dy / 65536.0f);
+#endif
+
+    uint32_t rotationDegrees;
+
+    static const int32_t kFixedOne = 0x10000;
+    if (a00 == kFixedOne && a01 == 0 && a10 == 0 && a11 == kFixedOne) {
+        // Identity, no rotation
+        rotationDegrees = 0;
+    } else if (a00 == 0 && a01 == kFixedOne && a10 == -kFixedOne && a11 == 0) {
+        rotationDegrees = 90;
+    } else if (a00 == 0 && a01 == -kFixedOne && a10 == kFixedOne && a11 == 0) {
+        rotationDegrees = 270;
+    } else if (a00 == -kFixedOne && a01 == 0 && a10 == 0 && a11 == -kFixedOne) {
+        rotationDegrees = 180;
+    } else {
+        ALOGW("We only support 0,90,180,270 degree rotation matrices");
+        rotationDegrees = 0;
+    }
+
+    if (rotationDegrees != 0) {
+        mLastTrack->meta->setInt32(kKeyRotation, rotationDegrees);
+    }
+
+    // Handle presentation display size, which could be different
+    // from the image size indicated by kKeyWidth and kKeyHeight.
+    uint32_t width = U32_AT(&buffer[dynSize + 52]);
+    uint32_t height = U32_AT(&buffer[dynSize + 56]);
+    mLastTrack->meta->setInt32(kKeyDisplayWidth, width >> 16);
+    mLastTrack->meta->setInt32(kKeyDisplayHeight, height >> 16);
+
+    return OK;
+}
+
+status_t MPEG4Extractor::parseMetaData(off64_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;
+    char chunk[5];
+    MakeFourCCString(mPath[4], chunk);
+    ALOGV("meta: %s @ %lld", chunk, offset);
+    switch (mPath[4]) {
+        case FOURCC(0xa9, 'a', 'l', 'b'):
+        {
+            metadataKey = kKeyAlbum;
+            break;
+        }
+        case FOURCC(0xa9, 'A', 'R', 'T'):
+        {
+            metadataKey = kKeyArtist;
+            break;
+        }
+        case FOURCC('a', 'A', 'R', 'T'):
+        {
+            metadataKey = kKeyAlbumArtist;
+            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(0xa9, 'g', 'e', 'n'):
+        {
+            metadataKey = kKeyGenre;
+            break;
+        }
+        case FOURCC('c', 'p', 'i', 'l'):
+        {
+            if (size == 9 && flags == 21) {
+                char tmp[16];
+                sprintf(tmp, "%d",
+                        (int)buffer[size - 1]);
+
+                mFileMetaData->setCString(kKeyCompilation, tmp);
+            }
+            break;
+        }
+        case FOURCC('t', 'r', 'k', 'n'):
+        {
+            if (size == 16 && flags == 0) {
+                char tmp[16];
+                uint16_t* pTrack = (uint16_t*)&buffer[10];
+                uint16_t* pTotalTracks = (uint16_t*)&buffer[12];
+                sprintf(tmp, "%d/%d", ntohs(*pTrack), ntohs(*pTotalTracks));
+
+                mFileMetaData->setCString(kKeyCDTrackNumber, tmp);
+            }
+            break;
+        }
+        case FOURCC('d', 'i', 's', 'k'):
+        {
+            if ((size == 14 || size == 16) && flags == 0) {
+                char tmp[16];
+                uint16_t* pDisc = (uint16_t*)&buffer[10];
+                uint16_t* pTotalDiscs = (uint16_t*)&buffer[12];
+                sprintf(tmp, "%d/%d", ntohs(*pDisc), ntohs(*pTotalDiscs));
+
+                mFileMetaData->setCString(kKeyDiscNumber, tmp);
+            }
+            break;
+        }
+        case FOURCC('-', '-', '-', '-'):
+        {
+            buffer[size] = '\0';
+            switch (mPath[5]) {
+                case FOURCC('m', 'e', 'a', 'n'):
+                    mLastCommentMean.setTo((const char *)buffer + 4);
+                    break;
+                case FOURCC('n', 'a', 'm', 'e'):
+                    mLastCommentName.setTo((const char *)buffer + 4);
+                    break;
+                case FOURCC('d', 'a', 't', 'a'):
+                    mLastCommentData.setTo((const char *)buffer + 8);
+                    break;
+            }
+
+            // Once we have a set of mean/name/data info, go ahead and process
+            // it to see if its something we are interested in.  Whether or not
+            // were are interested in the specific tag, make sure to clear out
+            // the set so we can be ready to process another tuple should one
+            // show up later in the file.
+            if ((mLastCommentMean.length() != 0) &&
+                (mLastCommentName.length() != 0) &&
+                (mLastCommentData.length() != 0)) {
+
+                if (mLastCommentMean == "com.apple.iTunes"
+                        && mLastCommentName == "iTunSMPB") {
+                    int32_t delay, padding;
+                    if (sscanf(mLastCommentData,
+                               " %*x %x %x %*x", &delay, &padding) == 2) {
+                        mLastTrack->meta->setInt32(kKeyEncoderDelay, delay);
+                        mLastTrack->meta->setInt32(kKeyEncoderPadding, padding);
+                    }
+                }
+
+                mLastCommentMean.clear();
+                mLastCommentName.clear();
+                mLastCommentData.clear();
+            }
+            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 genre code, iTunes genre codes are
+                // the standard id3 codes, except they start
+                // at 1 instead of 0 (e.g. Pop is 14, not 13)
+                // We use standard id3 numbering, so subtract 1.
+                int genrecode = (int)buffer[size - 1];
+                genrecode--;
+                if (genrecode < 0) {
+                    genrecode = 255; // reserved for 'unknown genre'
+                }
+                char genre[10];
+                sprintf(genre, "%d", genrecode);
+
+                mFileMetaData->setCString(metadataKey, genre);
+            } else if (flags == 1) {
+                // custom genre string
+                buffer[size] = '\0';
+
+                mFileMetaData->setCString(
+                        metadataKey, (const char *)buffer + 8);
+            }
+        } 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) {
+        return NULL;
+    }
+
+    Track *track = mFirstTrack;
+    while (index > 0) {
+        if (track == NULL) {
+            return NULL;
+        }
+
+        track = track->next;
+        --index;
+    }
+
+    if (track == NULL) {
+        return NULL;
+    }
+
+    ALOGV("getTrack called, pssh: %d", mPssh.size());
+
+    return new MPEG4Source(
+            track->meta, mDataSource, track->timescale, track->sampleTable,
+            mSidxEntries, mMoofOffset);
+}
+
+// static
+status_t MPEG4Extractor::verifyTrack(Track *track) {
+    const char *mime;
+    CHECK(track->meta->findCString(kKeyMIMEType, &mime));
+
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+        if (!track->meta->findData(kKeyAVCC, &type, &data, &size)
+                || type != kTypeAVCC) {
+            return ERROR_MALFORMED;
+        }
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)
+            || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
+        if (!track->meta->findData(kKeyESDS, &type, &data, &size)
+                || type != kTypeESDS) {
+            return ERROR_MALFORMED;
+        }
+    }
+
+    if (!track->sampleTable->isValid()) {
+        // Make sure we have all the metadata we need.
+        return ERROR_MALFORMED;
+    }
+
+    return OK;
+}
+
+status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio(
+        const void *esds_data, size_t esds_size) {
+    ESDS esds(esds_data, esds_size);
+
+    uint8_t objectTypeIndication;
+    if (esds.getObjectTypeIndication(&objectTypeIndication) != OK) {
+        return ERROR_MALFORMED;
+    }
+
+    if (objectTypeIndication == 0xe1) {
+        // This isn't MPEG4 audio at all, it's QCELP 14k...
+        mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_QCELP);
+        return OK;
+    }
+
+    if (objectTypeIndication  == 0x6b) {
+        // The media subtype is MP3 audio
+        // Our software MP3 audio decoder may not be able to handle
+        // packetized MP3 audio; for now, lets just return ERROR_UNSUPPORTED
+        ALOGE("MP3 track in MP4/3GPP file is not supported");
+        return ERROR_UNSUPPORTED;
+    }
+
+    const uint8_t *csd;
+    size_t csd_size;
+    if (esds.getCodecSpecificInfo(
+                (const void **)&csd, &csd_size) != OK) {
+        return ERROR_MALFORMED;
+    }
+
+#if 0
+    printf("ESD of size %d\n", csd_size);
+    hexdump(csd, csd_size);
+#endif
+
+    if (csd_size == 0) {
+        // There's no further information, i.e. no codec specific data
+        // Let's assume that the information provided in the mpeg4 headers
+        // is accurate and hope for the best.
+
+        return OK;
+    }
+
+    if (csd_size < 2) {
+        return ERROR_MALFORMED;
+    }
+
+    ABitReader br(csd, csd_size);
+    uint32_t objectType = br.getBits(5);
+
+    if (objectType == 31) {  // AAC-ELD => additional 6 bits
+        objectType = 32 + br.getBits(6);
+    }
+
+    uint32_t freqIndex = br.getBits(4);
+
+    int32_t sampleRate = 0;
+    int32_t numChannels = 0;
+    if (freqIndex == 15) {
+        if (csd_size < 5) {
+            return ERROR_MALFORMED;
+        }
+        sampleRate = br.getBits(24);
+        numChannels = br.getBits(4);
+    } else {
+        numChannels = br.getBits(4);
+        if (objectType == 5) {
+            // SBR specific config per 14496-3 table 1.13
+            freqIndex = br.getBits(4);
+            if (freqIndex == 15) {
+                if (csd_size < 8) {
+                    return ERROR_MALFORMED;
+                }
+                sampleRate = br.getBits(24);
+            }
+        }
+
+        if (sampleRate == 0) {
+            static uint32_t kSamplingRate[] = {
+                96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
+                16000, 12000, 11025, 8000, 7350
+            };
+
+            if (freqIndex == 13 || freqIndex == 14) {
+                return ERROR_MALFORMED;
+            }
+
+            sampleRate = kSamplingRate[freqIndex];
+        }
+    }
+
+    if (numChannels == 0) {
+        return ERROR_UNSUPPORTED;
+    }
+
+    int32_t prevSampleRate;
+    CHECK(mLastTrack->meta->findInt32(kKeySampleRate, &prevSampleRate));
+
+    if (prevSampleRate != sampleRate) {
+        ALOGV("mpeg4 audio sample rate different from previous setting. "
+             "was: %d, now: %d", prevSampleRate, sampleRate);
+    }
+
+    mLastTrack->meta->setInt32(kKeySampleRate, sampleRate);
+
+    int32_t prevChannelCount;
+    CHECK(mLastTrack->meta->findInt32(kKeyChannelCount, &prevChannelCount));
+
+    if (prevChannelCount != numChannels) {
+        ALOGV("mpeg4 audio channel count different from previous setting. "
+             "was: %d, now: %d", prevChannelCount, numChannels);
+    }
+
+    mLastTrack->meta->setInt32(kKeyChannelCount, numChannels);
+
+    return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+MPEG4Source::MPEG4Source(
+        const sp<MetaData> &format,
+        const sp<DataSource> &dataSource,
+        int32_t timeScale,
+        const sp<SampleTable> &sampleTable,
+        Vector<SidxEntry> &sidx,
+        off64_t firstMoofOffset)
+    : mFormat(format),
+      mDataSource(dataSource),
+      mTimescale(timeScale),
+      mSampleTable(sampleTable),
+      mCurrentSampleIndex(0),
+      mCurrentFragmentIndex(0),
+      mSegments(sidx),
+      mFirstMoofOffset(firstMoofOffset),
+      mCurrentMoofOffset(firstMoofOffset),
+      mCurrentTime(0),
+      mCurrentSampleInfoAllocSize(0),
+      mCurrentSampleInfoSizes(NULL),
+      mCurrentSampleInfoOffsetsAllocSize(0),
+      mCurrentSampleInfoOffsets(NULL),
+      mIsAVC(false),
+      mNALLengthSize(0),
+      mStarted(false),
+      mGroup(NULL),
+      mBuffer(NULL),
+      mWantsNALFragments(false),
+      mSrcBuffer(NULL) {
+
+    mFormat->findInt32(kKeyCryptoMode, &mCryptoMode);
+    mDefaultIVSize = 0;
+    mFormat->findInt32(kKeyCryptoDefaultIVSize, &mDefaultIVSize);
+    uint32_t keytype;
+    const void *key;
+    size_t keysize;
+    if (mFormat->findData(kKeyCryptoKey, &keytype, &key, &keysize)) {
+        CHECK(keysize <= 16);
+        memset(mCryptoKey, 0, 16);
+        memcpy(mCryptoKey, key, keysize);
+    }
+
+    const char *mime;
+    bool success = mFormat->findCString(kKeyMIMEType, &mime);
+    CHECK(success);
+
+    mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
+
+    if (mIsAVC) {
+        uint32_t type;
+        const void *data;
+        size_t size;
+        CHECK(format->findData(kKeyAVCC, &type, &data, &size));
+
+        const uint8_t *ptr = (const uint8_t *)data;
+
+        CHECK(size >= 7);
+        CHECK_EQ((unsigned)ptr[0], 1u);  // configurationVersion == 1
+
+        // The number of bytes used to encode the length of a NAL unit.
+        mNALLengthSize = 1 + (ptr[4] & 3);
+    }
+
+    CHECK(format->findInt32(kKeyTrackID, &mTrackId));
+
+    if (mFirstMoofOffset != 0) {
+        off64_t offset = mFirstMoofOffset;
+        parseChunk(&offset);
+    }
+}
+
+MPEG4Source::~MPEG4Source() {
+    if (mStarted) {
+        stop();
+    }
+    free(mCurrentSampleInfoSizes);
+    free(mCurrentSampleInfoOffsets);
+}
+
+status_t MPEG4Source::start(MetaData *params) {
+    Mutex::Autolock autoLock(mLock);
+
+    CHECK(!mStarted);
+
+    int32_t val;
+    if (params && params->findInt32(kKeyWantsNALFragments, &val)
+        && val != 0) {
+        mWantsNALFragments = true;
+    } else {
+        mWantsNALFragments = false;
+    }
+
+    mGroup = new MediaBufferGroup;
+
+    int32_t max_size;
+    CHECK(mFormat->findInt32(kKeyMaxInputSize, &max_size));
+
+    mGroup->add_buffer(new MediaBuffer(max_size));
+
+    mSrcBuffer = new uint8_t[max_size];
+
+    mStarted = true;
+
+    return OK;
+}
+
+status_t MPEG4Source::stop() {
+    Mutex::Autolock autoLock(mLock);
+
+    CHECK(mStarted);
+
+    if (mBuffer != NULL) {
+        mBuffer->release();
+        mBuffer = NULL;
+    }
+
+    delete[] mSrcBuffer;
+    mSrcBuffer = NULL;
+
+    delete mGroup;
+    mGroup = NULL;
+
+    mStarted = false;
+    mCurrentSampleIndex = 0;
+
+    return OK;
+}
+
+status_t MPEG4Source::parseChunk(off64_t *offset) {
+    uint32_t hdr[2];
+    if (mDataSource->readAt(*offset, hdr, 8) < 8) {
+        return ERROR_IO;
+    }
+    uint64_t chunk_size = ntohl(hdr[0]);
+    uint32_t chunk_type = ntohl(hdr[1]);
+    off64_t data_offset = *offset + 8;
+
+    if (chunk_size == 1) {
+        if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) {
+            return ERROR_IO;
+        }
+        chunk_size = ntoh64(chunk_size);
+        data_offset += 8;
+
+        if (chunk_size < 16) {
+            // The smallest valid chunk is 16 bytes long in this case.
+            return ERROR_MALFORMED;
+        }
+    } else if (chunk_size < 8) {
+        // The smallest valid chunk is 8 bytes long.
+        return ERROR_MALFORMED;
+    }
+
+    char chunk[5];
+    MakeFourCCString(chunk_type, chunk);
+    ALOGV("MPEG4Source chunk %s @ %llx", chunk, *offset);
+
+    off64_t chunk_data_size = *offset + chunk_size - data_offset;
+
+    switch(chunk_type) {
+
+        case FOURCC('t', 'r', 'a', 'f'):
+        case FOURCC('m', 'o', 'o', 'f'): {
+            off64_t stop_offset = *offset + chunk_size;
+            *offset = data_offset;
+            while (*offset < stop_offset) {
+                status_t err = parseChunk(offset);
+                if (err != OK) {
+                    return err;
+                }
+            }
+            if (chunk_type == FOURCC('m', 'o', 'o', 'f')) {
+                // *offset points to the mdat box following this moof
+                parseChunk(offset); // doesn't actually parse it, just updates offset
+                mNextMoofOffset = *offset;
+            }
+            break;
+        }
+
+        case FOURCC('t', 'f', 'h', 'd'): {
+                status_t err;
+                if ((err = parseTrackFragmentHeader(data_offset, chunk_data_size)) != OK) {
+                    return err;
+                }
+                *offset += chunk_size;
+                break;
+        }
+
+        case FOURCC('t', 'r', 'u', 'n'): {
+                status_t err;
+                if (mLastParsedTrackId == mTrackId) {
+                    if ((err = parseTrackFragmentRun(data_offset, chunk_data_size)) != OK) {
+                        return err;
+                    }
+                }
+
+                *offset += chunk_size;
+                break;
+        }
+
+        case FOURCC('s', 'a', 'i', 'z'): {
+            status_t err;
+            if ((err = parseSampleAuxiliaryInformationSizes(data_offset, chunk_data_size)) != OK) {
+                return err;
+            }
+            *offset += chunk_size;
+            break;
+        }
+        case FOURCC('s', 'a', 'i', 'o'): {
+            status_t err;
+            if ((err = parseSampleAuxiliaryInformationOffsets(data_offset, chunk_data_size)) != OK) {
+                return err;
+            }
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('m', 'd', 'a', 't'): {
+            // parse DRM info if present
+            ALOGV("MPEG4Source::parseChunk mdat");
+            // if saiz/saoi was previously observed, do something with the sampleinfos
+            *offset += chunk_size;
+            break;
+        }
+
+        default: {
+            *offset += chunk_size;
+            break;
+        }
+    }
+    return OK;
+}
+
+status_t MPEG4Source::parseSampleAuxiliaryInformationSizes(off64_t offset, off64_t size) {
+    ALOGV("parseSampleAuxiliaryInformationSizes");
+    // 14496-12 8.7.12
+    uint8_t version;
+    if (mDataSource->readAt(
+            offset, &version, sizeof(version))
+            < (ssize_t)sizeof(version)) {
+        return ERROR_IO;
+    }
+
+    if (version != 0) {
+        return ERROR_UNSUPPORTED;
+    }
+    offset++;
+
+    uint32_t flags;
+    if (!mDataSource->getUInt24(offset, &flags)) {
+        return ERROR_IO;
+    }
+    offset += 3;
+
+    if (flags & 1) {
+        uint32_t tmp;
+        if (!mDataSource->getUInt32(offset, &tmp)) {
+            return ERROR_MALFORMED;
+        }
+        mCurrentAuxInfoType = tmp;
+        offset += 4;
+        if (!mDataSource->getUInt32(offset, &tmp)) {
+            return ERROR_MALFORMED;
+        }
+        mCurrentAuxInfoTypeParameter = tmp;
+        offset += 4;
+    }
+
+    uint8_t defsize;
+    if (mDataSource->readAt(offset, &defsize, 1) != 1) {
+        return ERROR_MALFORMED;
+    }
+    mCurrentDefaultSampleInfoSize = defsize;
+    offset++;
+
+    uint32_t smplcnt;
+    if (!mDataSource->getUInt32(offset, &smplcnt)) {
+        return ERROR_MALFORMED;
+    }
+    mCurrentSampleInfoCount = smplcnt;
+    offset += 4;
+
+    if (mCurrentDefaultSampleInfoSize != 0) {
+        ALOGV("@@@@ using default sample info size of %d", mCurrentDefaultSampleInfoSize);
+        return OK;
+    }
+    if (smplcnt > mCurrentSampleInfoAllocSize) {
+        mCurrentSampleInfoSizes = (uint8_t*) realloc(mCurrentSampleInfoSizes, smplcnt);
+        mCurrentSampleInfoAllocSize = smplcnt;
+    }
+
+    mDataSource->readAt(offset, mCurrentSampleInfoSizes, smplcnt);
+    return OK;
+}
+
+status_t MPEG4Source::parseSampleAuxiliaryInformationOffsets(off64_t offset, off64_t size) {
+    ALOGV("parseSampleAuxiliaryInformationOffsets");
+    // 14496-12 8.7.13
+    uint8_t version;
+    if (mDataSource->readAt(offset, &version, sizeof(version)) != 1) {
+        return ERROR_IO;
+    }
+    offset++;
+
+    uint32_t flags;
+    if (!mDataSource->getUInt24(offset, &flags)) {
+        return ERROR_IO;
+    }
+    offset += 3;
+
+    uint32_t entrycount;
+    if (!mDataSource->getUInt32(offset, &entrycount)) {
+        return ERROR_IO;
+    }
+    offset += 4;
+
+    if (entrycount > mCurrentSampleInfoOffsetsAllocSize) {
+        mCurrentSampleInfoOffsets = (uint64_t*) realloc(mCurrentSampleInfoOffsets, entrycount * 8);
+        mCurrentSampleInfoOffsetsAllocSize = entrycount;
+    }
+    mCurrentSampleInfoOffsetCount = entrycount;
+
+    for (size_t i = 0; i < entrycount; i++) {
+        if (version == 0) {
+            uint32_t tmp;
+            if (!mDataSource->getUInt32(offset, &tmp)) {
+                return ERROR_IO;
+            }
+            mCurrentSampleInfoOffsets[i] = tmp;
+            offset += 4;
+        } else {
+            uint64_t tmp;
+            if (!mDataSource->getUInt64(offset, &tmp)) {
+                return ERROR_IO;
+            }
+            mCurrentSampleInfoOffsets[i] = tmp;
+            offset += 8;
+        }
+    }
+
+    // parse clear/encrypted data
+
+    off64_t drmoffset = mCurrentSampleInfoOffsets[0]; // from moof
+
+    drmoffset += mCurrentMoofOffset;
+    int ivlength;
+    CHECK(mFormat->findInt32(kKeyCryptoDefaultIVSize, &ivlength));
+
+    // read CencSampleAuxiliaryDataFormats
+    for (size_t i = 0; i < mCurrentSampleInfoCount; i++) {
+        Sample *smpl = &mCurrentSamples.editItemAt(i);
+
+        memset(smpl->iv, 0, 16);
+        if (mDataSource->readAt(drmoffset, smpl->iv, ivlength) != ivlength) {
+            return ERROR_IO;
+        }
+
+        drmoffset += ivlength;
+
+        int32_t smplinfosize = mCurrentDefaultSampleInfoSize;
+        if (smplinfosize == 0) {
+            smplinfosize = mCurrentSampleInfoSizes[i];
+        }
+        if (smplinfosize > ivlength) {
+            uint16_t numsubsamples;
+            if (!mDataSource->getUInt16(drmoffset, &numsubsamples)) {
+                return ERROR_IO;
+            }
+            drmoffset += 2;
+            for (size_t j = 0; j < numsubsamples; j++) {
+                uint16_t numclear;
+                uint32_t numencrypted;
+                if (!mDataSource->getUInt16(drmoffset, &numclear)) {
+                    return ERROR_IO;
+                }
+                drmoffset += 2;
+                if (!mDataSource->getUInt32(drmoffset, &numencrypted)) {
+                    return ERROR_IO;
+                }
+                drmoffset += 4;
+                smpl->clearsizes.add(numclear);
+                smpl->encryptedsizes.add(numencrypted);
+            }
+        } else {
+            smpl->clearsizes.add(0);
+            smpl->encryptedsizes.add(smpl->size);
+        }
+    }
+
+
+    return OK;
+}
+
+status_t MPEG4Source::parseTrackFragmentHeader(off64_t offset, off64_t size) {
+
+    if (size < 8) {
+        return -EINVAL;
+    }
+
+    uint32_t flags;
+    if (!mDataSource->getUInt32(offset, &flags)) { // actually version + flags
+        return ERROR_MALFORMED;
+    }
+
+    if (flags & 0xff000000) {
+        return -EINVAL;
+    }
+
+    if (!mDataSource->getUInt32(offset + 4, (uint32_t*)&mLastParsedTrackId)) {
+        return ERROR_MALFORMED;
+    }
+
+    if (mLastParsedTrackId != mTrackId) {
+        // this is not the right track, skip it
+        return OK;
+    }
+
+    mTrackFragmentHeaderInfo.mFlags = flags;
+    mTrackFragmentHeaderInfo.mTrackID = mLastParsedTrackId;
+    offset += 8;
+    size -= 8;
+
+    ALOGV("fragment header: %08x %08x", flags, mTrackFragmentHeaderInfo.mTrackID);
+
+    if (flags & TrackFragmentHeaderInfo::kBaseDataOffsetPresent) {
+        if (size < 8) {
+            return -EINVAL;
+        }
+
+        if (!mDataSource->getUInt64(offset, &mTrackFragmentHeaderInfo.mBaseDataOffset)) {
+            return ERROR_MALFORMED;
+        }
+        offset += 8;
+        size -= 8;
+    }
+
+    if (flags & TrackFragmentHeaderInfo::kSampleDescriptionIndexPresent) {
+        if (size < 4) {
+            return -EINVAL;
+        }
+
+        if (!mDataSource->getUInt32(offset, &mTrackFragmentHeaderInfo.mSampleDescriptionIndex)) {
+            return ERROR_MALFORMED;
+        }
+        offset += 4;
+        size -= 4;
+    }
+
+    if (flags & TrackFragmentHeaderInfo::kDefaultSampleDurationPresent) {
+        if (size < 4) {
+            return -EINVAL;
+        }
+
+        if (!mDataSource->getUInt32(offset, &mTrackFragmentHeaderInfo.mDefaultSampleDuration)) {
+            return ERROR_MALFORMED;
+        }
+        offset += 4;
+        size -= 4;
+    }
+
+    if (flags & TrackFragmentHeaderInfo::kDefaultSampleSizePresent) {
+        if (size < 4) {
+            return -EINVAL;
+        }
+
+        if (!mDataSource->getUInt32(offset, &mTrackFragmentHeaderInfo.mDefaultSampleSize)) {
+            return ERROR_MALFORMED;
+        }
+        offset += 4;
+        size -= 4;
+    }
+
+    if (flags & TrackFragmentHeaderInfo::kDefaultSampleFlagsPresent) {
+        if (size < 4) {
+            return -EINVAL;
+        }
+
+        if (!mDataSource->getUInt32(offset, &mTrackFragmentHeaderInfo.mDefaultSampleFlags)) {
+            return ERROR_MALFORMED;
+        }
+        offset += 4;
+        size -= 4;
+    }
+
+    if (!(flags & TrackFragmentHeaderInfo::kBaseDataOffsetPresent)) {
+        mTrackFragmentHeaderInfo.mBaseDataOffset = mCurrentMoofOffset;
+    }
+
+    mTrackFragmentHeaderInfo.mDataOffset = 0;
+    return OK;
+}
+
+status_t MPEG4Source::parseTrackFragmentRun(off64_t offset, off64_t size) {
+
+    ALOGV("MPEG4Extractor::parseTrackFragmentRun");
+    if (size < 8) {
+        return -EINVAL;
+    }
+
+    enum {
+        kDataOffsetPresent                  = 0x01,
+        kFirstSampleFlagsPresent            = 0x04,
+        kSampleDurationPresent              = 0x100,
+        kSampleSizePresent                  = 0x200,
+        kSampleFlagsPresent                 = 0x400,
+        kSampleCompositionTimeOffsetPresent = 0x800,
+    };
+
+    uint32_t flags;
+    if (!mDataSource->getUInt32(offset, &flags)) {
+        return ERROR_MALFORMED;
+    }
+    ALOGV("fragment run flags: %08x", flags);
+
+    if (flags & 0xff000000) {
+        return -EINVAL;
+    }
+
+    if ((flags & kFirstSampleFlagsPresent) && (flags & kSampleFlagsPresent)) {
+        // These two shall not be used together.
+        return -EINVAL;
+    }
+
+    uint32_t sampleCount;
+    if (!mDataSource->getUInt32(offset + 4, &sampleCount)) {
+        return ERROR_MALFORMED;
+    }
+    offset += 8;
+    size -= 8;
+
+    uint64_t dataOffset = mTrackFragmentHeaderInfo.mDataOffset;
+
+    uint32_t firstSampleFlags = 0;
+
+    if (flags & kDataOffsetPresent) {
+        if (size < 4) {
+            return -EINVAL;
+        }
+
+        int32_t dataOffsetDelta;
+        if (!mDataSource->getUInt32(offset, (uint32_t*)&dataOffsetDelta)) {
+            return ERROR_MALFORMED;
+        }
+
+        dataOffset = mTrackFragmentHeaderInfo.mBaseDataOffset + dataOffsetDelta;
+
+        offset += 4;
+        size -= 4;
+    }
+
+    if (flags & kFirstSampleFlagsPresent) {
+        if (size < 4) {
+            return -EINVAL;
+        }
+
+        if (!mDataSource->getUInt32(offset, &firstSampleFlags)) {
+            return ERROR_MALFORMED;
+        }
+        offset += 4;
+        size -= 4;
+    }
+
+    uint32_t sampleDuration = 0, sampleSize = 0, sampleFlags = 0,
+             sampleCtsOffset = 0;
+
+    size_t bytesPerSample = 0;
+    if (flags & kSampleDurationPresent) {
+        bytesPerSample += 4;
+    } else if (mTrackFragmentHeaderInfo.mFlags
+            & TrackFragmentHeaderInfo::kDefaultSampleDurationPresent) {
+        sampleDuration = mTrackFragmentHeaderInfo.mDefaultSampleDuration;
+    } else {
+        sampleDuration = mTrackFragmentHeaderInfo.mDefaultSampleDuration;
+    }
+
+    if (flags & kSampleSizePresent) {
+        bytesPerSample += 4;
+    } else if (mTrackFragmentHeaderInfo.mFlags
+            & TrackFragmentHeaderInfo::kDefaultSampleSizePresent) {
+        sampleSize = mTrackFragmentHeaderInfo.mDefaultSampleSize;
+    } else {
+        sampleSize = mTrackFragmentHeaderInfo.mDefaultSampleSize;
+    }
+
+    if (flags & kSampleFlagsPresent) {
+        bytesPerSample += 4;
+    } else if (mTrackFragmentHeaderInfo.mFlags
+            & TrackFragmentHeaderInfo::kDefaultSampleFlagsPresent) {
+        sampleFlags = mTrackFragmentHeaderInfo.mDefaultSampleFlags;
+    } else {
+        sampleFlags = mTrackFragmentHeaderInfo.mDefaultSampleFlags;
+    }
+
+    if (flags & kSampleCompositionTimeOffsetPresent) {
+        bytesPerSample += 4;
+    } else {
+        sampleCtsOffset = 0;
+    }
+
+    if (size < sampleCount * bytesPerSample) {
+        return -EINVAL;
+    }
+
+    Sample tmp;
+    for (uint32_t i = 0; i < sampleCount; ++i) {
+        if (flags & kSampleDurationPresent) {
+            if (!mDataSource->getUInt32(offset, &sampleDuration)) {
+                return ERROR_MALFORMED;
+            }
+            offset += 4;
+        }
+
+        if (flags & kSampleSizePresent) {
+            if (!mDataSource->getUInt32(offset, &sampleSize)) {
+                return ERROR_MALFORMED;
+            }
+            offset += 4;
+        }
+
+        if (flags & kSampleFlagsPresent) {
+            if (!mDataSource->getUInt32(offset, &sampleFlags)) {
+                return ERROR_MALFORMED;
+            }
+            offset += 4;
+        }
+
+        if (flags & kSampleCompositionTimeOffsetPresent) {
+            if (!mDataSource->getUInt32(offset, &sampleCtsOffset)) {
+                return ERROR_MALFORMED;
+            }
+            offset += 4;
+        }
+
+        ALOGV("adding sample %d at offset 0x%08llx, size %u, duration %u, "
+              " flags 0x%08x", i + 1,
+                dataOffset, sampleSize, sampleDuration,
+                (flags & kFirstSampleFlagsPresent) && i == 0
+                    ? firstSampleFlags : sampleFlags);
+        tmp.offset = dataOffset;
+        tmp.size = sampleSize;
+        tmp.duration = sampleDuration;
+        mCurrentSamples.add(tmp);
+
+        dataOffset += sampleSize;
+    }
+
+    mTrackFragmentHeaderInfo.mDataOffset = dataOffset;
+
+    return OK;
+}
+
+sp<MetaData> MPEG4Source::getFormat() {
+    Mutex::Autolock autoLock(mLock);
+
+    return mFormat;
+}
+
+size_t MPEG4Source::parseNALSize(const uint8_t *data) const {
+    switch (mNALLengthSize) {
+        case 1:
+            return *data;
+        case 2:
+            return U16_AT(data);
+        case 3:
+            return ((size_t)data[0] << 16) | U16_AT(&data[1]);
+        case 4:
+            return U32_AT(data);
+    }
+
+    // This cannot happen, mNALLengthSize springs to life by adding 1 to
+    // a 2-bit integer.
+    CHECK(!"Should not be here.");
+
+    return 0;
+}
+
+status_t MPEG4Source::read(
+        MediaBuffer **out, const ReadOptions *options) {
+    Mutex::Autolock autoLock(mLock);
+
+    CHECK(mStarted);
+
+    if (mFirstMoofOffset > 0) {
+        return fragmentedRead(out, options);
+    }
+
+    *out = NULL;
+
+    int64_t targetSampleTimeUs = -1;
+
+    int64_t seekTimeUs;
+    ReadOptions::SeekMode mode;
+    if (options && options->getSeekTo(&seekTimeUs, &mode)) {
+        uint32_t findFlags = 0;
+        switch (mode) {
+            case ReadOptions::SEEK_PREVIOUS_SYNC:
+                findFlags = SampleTable::kFlagBefore;
+                break;
+            case ReadOptions::SEEK_NEXT_SYNC:
+                findFlags = SampleTable::kFlagAfter;
+                break;
+            case ReadOptions::SEEK_CLOSEST_SYNC:
+            case ReadOptions::SEEK_CLOSEST:
+                findFlags = SampleTable::kFlagClosest;
+                break;
+            default:
+                CHECK(!"Should not be here.");
+                break;
+        }
+
+        uint32_t sampleIndex;
+        status_t err = mSampleTable->findSampleAtTime(
+                seekTimeUs * mTimescale / 1000000,
+                &sampleIndex, findFlags);
+
+        if (mode == ReadOptions::SEEK_CLOSEST) {
+            // We found the closest sample already, now we want the sync
+            // sample preceding it (or the sample itself of course), even
+            // if the subsequent sync sample is closer.
+            findFlags = SampleTable::kFlagBefore;
+        }
+
+        uint32_t syncSampleIndex;
+        if (err == OK) {
+            err = mSampleTable->findSyncSampleNear(
+                    sampleIndex, &syncSampleIndex, findFlags);
+        }
+
+        uint32_t sampleTime;
+        if (err == OK) {
+            err = mSampleTable->getMetaDataForSample(
+                    sampleIndex, NULL, NULL, &sampleTime);
+        }
+
+        if (err != OK) {
+            if (err == ERROR_OUT_OF_RANGE) {
+                // An attempt to seek past the end of the stream would
+                // normally cause this ERROR_OUT_OF_RANGE error. Propagating
+                // this all the way to the MediaPlayer would cause abnormal
+                // termination. Legacy behaviour appears to be to behave as if
+                // we had seeked to the end of stream, ending normally.
+                err = ERROR_END_OF_STREAM;
+            }
+            ALOGV("end of stream");
+            return err;
+        }
+
+        if (mode == ReadOptions::SEEK_CLOSEST) {
+            targetSampleTimeUs = (sampleTime * 1000000ll) / mTimescale;
+        }
+
+#if 0
+        uint32_t syncSampleTime;
+        CHECK_EQ(OK, mSampleTable->getMetaDataForSample(
+                    syncSampleIndex, NULL, NULL, &syncSampleTime));
+
+        ALOGI("seek to time %lld us => sample at time %lld us, "
+             "sync sample at time %lld us",
+             seekTimeUs,
+             sampleTime * 1000000ll / mTimescale,
+             syncSampleTime * 1000000ll / mTimescale);
+#endif
+
+        mCurrentSampleIndex = syncSampleIndex;
+        if (mBuffer != NULL) {
+            mBuffer->release();
+            mBuffer = NULL;
+        }
+
+        // fall through
+    }
+
+    off64_t offset;
+    size_t size;
+    uint32_t cts;
+    bool isSyncSample;
+    bool newBuffer = false;
+    if (mBuffer == NULL) {
+        newBuffer = true;
+
+        status_t err =
+            mSampleTable->getMetaDataForSample(
+                    mCurrentSampleIndex, &offset, &size, &cts, &isSyncSample);
+
+        if (err != OK) {
+            return err;
+        }
+
+        err = mGroup->acquire_buffer(&mBuffer);
+
+        if (err != OK) {
+            CHECK(mBuffer == NULL);
+            return err;
+        }
+    }
+
+    if (!mIsAVC || mWantsNALFragments) {
+        if (newBuffer) {
+            ssize_t num_bytes_read =
+                mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size);
+
+            if (num_bytes_read < (ssize_t)size) {
+                mBuffer->release();
+                mBuffer = NULL;
+
+                return ERROR_IO;
+            }
+
+            CHECK(mBuffer != NULL);
+            mBuffer->set_range(0, size);
+            mBuffer->meta_data()->clear();
+            mBuffer->meta_data()->setInt64(
+                    kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
+
+            if (targetSampleTimeUs >= 0) {
+                mBuffer->meta_data()->setInt64(
+                        kKeyTargetTime, targetSampleTimeUs);
+            }
+
+            if (isSyncSample) {
+                mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+            }
+
+            ++mCurrentSampleIndex;
+        }
+
+        if (!mIsAVC) {
+            *out = mBuffer;
+            mBuffer = NULL;
+
+            return OK;
+        }
+
+        // Each NAL unit is split up into its constituent fragments and
+        // each one of them returned in its own buffer.
+
+        CHECK(mBuffer->range_length() >= mNALLengthSize);
+
+        const uint8_t *src =
+            (const uint8_t *)mBuffer->data() + mBuffer->range_offset();
+
+        size_t nal_size = parseNALSize(src);
+        if (mBuffer->range_length() < mNALLengthSize + nal_size) {
+            ALOGE("incomplete NAL unit.");
+
+            mBuffer->release();
+            mBuffer = NULL;
+
+            return ERROR_MALFORMED;
+        }
+
+        MediaBuffer *clone = mBuffer->clone();
+        CHECK(clone != NULL);
+        clone->set_range(mBuffer->range_offset() + mNALLengthSize, nal_size);
+
+        CHECK(mBuffer != NULL);
+        mBuffer->set_range(
+                mBuffer->range_offset() + mNALLengthSize + nal_size,
+                mBuffer->range_length() - mNALLengthSize - nal_size);
+
+        if (mBuffer->range_length() == 0) {
+            mBuffer->release();
+            mBuffer = NULL;
+        }
+
+        *out = clone;
+
+        return OK;
+    } else {
+        // Whole NAL units are returned but each fragment is prefixed by
+        // the start code (0x00 00 00 01).
+        ssize_t num_bytes_read = 0;
+        int32_t drm = 0;
+        bool usesDRM = (mFormat->findInt32(kKeyIsDRM, &drm) && drm != 0);
+        if (usesDRM) {
+            num_bytes_read =
+                mDataSource->readAt(offset, (uint8_t*)mBuffer->data(), size);
+        } else {
+            num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size);
+        }
+
+        if (num_bytes_read < (ssize_t)size) {
+            mBuffer->release();
+            mBuffer = NULL;
+
+            return ERROR_IO;
+        }
+
+        if (usesDRM) {
+            CHECK(mBuffer != NULL);
+            mBuffer->set_range(0, size);
+
+        } else {
+            uint8_t *dstData = (uint8_t *)mBuffer->data();
+            size_t srcOffset = 0;
+            size_t dstOffset = 0;
+
+            while (srcOffset < size) {
+                bool isMalFormed = (srcOffset + mNALLengthSize > size);
+                size_t nalLength = 0;
+                if (!isMalFormed) {
+                    nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
+                    srcOffset += mNALLengthSize;
+                    isMalFormed = srcOffset + nalLength > size;
+                }
+
+                if (isMalFormed) {
+                    ALOGE("Video is malformed");
+                    mBuffer->release();
+                    mBuffer = NULL;
+                    return ERROR_MALFORMED;
+                }
+
+                if (nalLength == 0) {
+                    continue;
+                }
+
+                CHECK(dstOffset + 4 <= mBuffer->size());
+
+                dstData[dstOffset++] = 0;
+                dstData[dstOffset++] = 0;
+                dstData[dstOffset++] = 0;
+                dstData[dstOffset++] = 1;
+                memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
+                srcOffset += nalLength;
+                dstOffset += nalLength;
+            }
+            CHECK_EQ(srcOffset, size);
+            CHECK(mBuffer != NULL);
+            mBuffer->set_range(0, dstOffset);
+        }
+
+        mBuffer->meta_data()->clear();
+        mBuffer->meta_data()->setInt64(
+                kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
+
+        if (targetSampleTimeUs >= 0) {
+            mBuffer->meta_data()->setInt64(
+                    kKeyTargetTime, targetSampleTimeUs);
+        }
+
+        if (isSyncSample) {
+            mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+        }
+
+        ++mCurrentSampleIndex;
+
+        *out = mBuffer;
+        mBuffer = NULL;
+
+        return OK;
+    }
+}
+
+status_t MPEG4Source::fragmentedRead(
+        MediaBuffer **out, const ReadOptions *options) {
+
+    ALOGV("MPEG4Source::fragmentedRead");
+
+    CHECK(mStarted);
+
+    *out = NULL;
+
+    int64_t targetSampleTimeUs = -1;
+
+    int64_t seekTimeUs;
+    ReadOptions::SeekMode mode;
+    if (options && options->getSeekTo(&seekTimeUs, &mode)) {
+
+        int numSidxEntries = mSegments.size();
+        if (numSidxEntries != 0) {
+            int64_t totalTime = 0;
+            off64_t totalOffset = mFirstMoofOffset;
+            for (int i = 0; i < numSidxEntries; i++) {
+                const SidxEntry *se = &mSegments[i];
+                if (totalTime + se->mDurationUs > seekTimeUs) {
+                    // The requested time is somewhere in this segment
+                    if ((mode == ReadOptions::SEEK_NEXT_SYNC) ||
+                        (mode == ReadOptions::SEEK_CLOSEST_SYNC &&
+                        (seekTimeUs - totalTime) > (totalTime + se->mDurationUs - seekTimeUs))) {
+                        // requested next sync, or closest sync and it was closer to the end of
+                        // this segment
+                        totalTime += se->mDurationUs;
+                        totalOffset += se->mSize;
+                    }
+                    break;
+                }
+                totalTime += se->mDurationUs;
+                totalOffset += se->mSize;
+            }
+        mCurrentMoofOffset = totalOffset;
+        mCurrentSamples.clear();
+        mCurrentSampleIndex = 0;
+        parseChunk(&totalOffset);
+        mCurrentTime = totalTime * mTimescale / 1000000ll;
+        }
+
+        if (mBuffer != NULL) {
+            mBuffer->release();
+            mBuffer = NULL;
+        }
+
+        // fall through
+    }
+
+    off64_t offset = 0;
+    size_t size;
+    uint32_t cts = 0;
+    bool isSyncSample = false;
+    bool newBuffer = false;
+    if (mBuffer == NULL) {
+        newBuffer = true;
+
+        if (mCurrentSampleIndex >= mCurrentSamples.size()) {
+            // move to next fragment
+            Sample lastSample = mCurrentSamples[mCurrentSamples.size() - 1];
+            off64_t nextMoof = mNextMoofOffset; // lastSample.offset + lastSample.size;
+            mCurrentMoofOffset = nextMoof;
+            mCurrentSamples.clear();
+            mCurrentSampleIndex = 0;
+            parseChunk(&nextMoof);
+                if (mCurrentSampleIndex >= mCurrentSamples.size()) {
+                    return ERROR_END_OF_STREAM;
+                }
+        }
+
+        const Sample *smpl = &mCurrentSamples[mCurrentSampleIndex];
+        offset = smpl->offset;
+        size = smpl->size;
+        cts = mCurrentTime;
+        mCurrentTime += smpl->duration;
+        isSyncSample = (mCurrentSampleIndex == 0); // XXX
+
+        status_t err = mGroup->acquire_buffer(&mBuffer);
+
+        if (err != OK) {
+            CHECK(mBuffer == NULL);
+            ALOGV("acquire_buffer returned %d", err);
+            return err;
+        }
+    }
+
+    const Sample *smpl = &mCurrentSamples[mCurrentSampleIndex];
+    const sp<MetaData> bufmeta = mBuffer->meta_data();
+    bufmeta->clear();
+    if (smpl->encryptedsizes.size()) {
+        // store clear/encrypted lengths in metadata
+        bufmeta->setData(kKeyPlainSizes, 0,
+                smpl->clearsizes.array(), smpl->clearsizes.size() * 4);
+        bufmeta->setData(kKeyEncryptedSizes, 0,
+                smpl->encryptedsizes.array(), smpl->encryptedsizes.size() * 4);
+        bufmeta->setData(kKeyCryptoIV, 0, smpl->iv, 16); // use 16 or the actual size?
+        bufmeta->setInt32(kKeyCryptoDefaultIVSize, mDefaultIVSize);
+        bufmeta->setInt32(kKeyCryptoMode, mCryptoMode);
+        bufmeta->setData(kKeyCryptoKey, 0, mCryptoKey, 16);
+    }
+
+    if (!mIsAVC || mWantsNALFragments) {
+        if (newBuffer) {
+            ssize_t num_bytes_read =
+                mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size);
+
+            if (num_bytes_read < (ssize_t)size) {
+                mBuffer->release();
+                mBuffer = NULL;
+
+                ALOGV("i/o error");
+                return ERROR_IO;
+            }
+
+            CHECK(mBuffer != NULL);
+            mBuffer->set_range(0, size);
+            mBuffer->meta_data()->setInt64(
+                    kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
+
+            if (targetSampleTimeUs >= 0) {
+                mBuffer->meta_data()->setInt64(
+                        kKeyTargetTime, targetSampleTimeUs);
+            }
+
+            if (isSyncSample) {
+                mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+            }
+
+            ++mCurrentSampleIndex;
+        }
+
+        if (!mIsAVC) {
+            *out = mBuffer;
+            mBuffer = NULL;
+
+            return OK;
+        }
+
+        // Each NAL unit is split up into its constituent fragments and
+        // each one of them returned in its own buffer.
+
+        CHECK(mBuffer->range_length() >= mNALLengthSize);
+
+        const uint8_t *src =
+            (const uint8_t *)mBuffer->data() + mBuffer->range_offset();
+
+        size_t nal_size = parseNALSize(src);
+        if (mBuffer->range_length() < mNALLengthSize + nal_size) {
+            ALOGE("incomplete NAL unit.");
+
+            mBuffer->release();
+            mBuffer = NULL;
+
+            return ERROR_MALFORMED;
+        }
+
+        MediaBuffer *clone = mBuffer->clone();
+        CHECK(clone != NULL);
+        clone->set_range(mBuffer->range_offset() + mNALLengthSize, nal_size);
+
+        CHECK(mBuffer != NULL);
+        mBuffer->set_range(
+                mBuffer->range_offset() + mNALLengthSize + nal_size,
+                mBuffer->range_length() - mNALLengthSize - nal_size);
+
+        if (mBuffer->range_length() == 0) {
+            mBuffer->release();
+            mBuffer = NULL;
+        }
+
+        *out = clone;
+
+        return OK;
+    } else {
+        ALOGV("whole NAL");
+        // Whole NAL units are returned but each fragment is prefixed by
+        // the start code (0x00 00 00 01).
+        ssize_t num_bytes_read = 0;
+        int32_t drm = 0;
+        bool usesDRM = (mFormat->findInt32(kKeyIsDRM, &drm) && drm != 0);
+        if (usesDRM) {
+            num_bytes_read =
+                mDataSource->readAt(offset, (uint8_t*)mBuffer->data(), size);
+        } else {
+            num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size);
+        }
+
+        if (num_bytes_read < (ssize_t)size) {
+            mBuffer->release();
+            mBuffer = NULL;
+
+            ALOGV("i/o error");
+            return ERROR_IO;
+        }
+
+        if (usesDRM) {
+            CHECK(mBuffer != NULL);
+            mBuffer->set_range(0, size);
+
+        } else {
+            uint8_t *dstData = (uint8_t *)mBuffer->data();
+            size_t srcOffset = 0;
+            size_t dstOffset = 0;
+
+            while (srcOffset < size) {
+                bool isMalFormed = (srcOffset + mNALLengthSize > size);
+                size_t nalLength = 0;
+                if (!isMalFormed) {
+                    nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
+                    srcOffset += mNALLengthSize;
+                    isMalFormed = srcOffset + nalLength > size;
+                }
+
+                if (isMalFormed) {
+                    ALOGE("Video is malformed");
+                    mBuffer->release();
+                    mBuffer = NULL;
+                    return ERROR_MALFORMED;
+                }
+
+                if (nalLength == 0) {
+                    continue;
+                }
+
+                CHECK(dstOffset + 4 <= mBuffer->size());
+
+                dstData[dstOffset++] = 0;
+                dstData[dstOffset++] = 0;
+                dstData[dstOffset++] = 0;
+                dstData[dstOffset++] = 1;
+                memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
+                srcOffset += nalLength;
+                dstOffset += nalLength;
+            }
+            CHECK_EQ(srcOffset, size);
+            CHECK(mBuffer != NULL);
+            mBuffer->set_range(0, dstOffset);
+        }
+
+        mBuffer->meta_data()->setInt64(
+                kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
+
+        if (targetSampleTimeUs >= 0) {
+            mBuffer->meta_data()->setInt64(
+                    kKeyTargetTime, targetSampleTimeUs);
+        }
+
+        if (isSyncSample) {
+            mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+        }
+
+        ++mCurrentSampleIndex;
+
+        *out = mBuffer;
+        mBuffer = NULL;
+
+        return OK;
+    }
+}
+
+MPEG4Extractor::Track *MPEG4Extractor::findTrackByMimePrefix(
+        const char *mimePrefix) {
+    for (Track *track = mFirstTrack; track != NULL; track = track->next) {
+        const char *mime;
+        if (track->meta != NULL
+                && track->meta->findCString(kKeyMIMEType, &mime)
+                && !strncasecmp(mime, mimePrefix, strlen(mimePrefix))) {
+            return track;
+        }
+    }
+
+    return NULL;
+}
+
+static bool LegacySniffMPEG4(
+        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+    uint8_t header[8];
+
+    ssize_t n = source->readAt(4, header, sizeof(header));
+    if (n < (ssize_t)sizeof(header)) {
+        return false;
+    }
+
+    if (!memcmp(header, "ftyp3gp", 7) || !memcmp(header, "ftypmp42", 8)
+        || !memcmp(header, "ftyp3gr6", 8) || !memcmp(header, "ftyp3gs6", 8)
+        || !memcmp(header, "ftyp3ge6", 8) || !memcmp(header, "ftyp3gg6", 8)
+        || !memcmp(header, "ftypisom", 8) || !memcmp(header, "ftypM4V ", 8)
+        || !memcmp(header, "ftypM4A ", 8) || !memcmp(header, "ftypf4v ", 8)
+        || !memcmp(header, "ftypkddi", 8) || !memcmp(header, "ftypM4VP", 8)) {
+        *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4;
+        *confidence = 0.4;
+
+        return true;
+    }
+
+    return false;
+}
+
+static bool isCompatibleBrand(uint32_t fourcc) {
+    static const uint32_t kCompatibleBrands[] = {
+        FOURCC('i', 's', 'o', 'm'),
+        FOURCC('i', 's', 'o', '2'),
+        FOURCC('a', 'v', 'c', '1'),
+        FOURCC('3', 'g', 'p', '4'),
+        FOURCC('m', 'p', '4', '1'),
+        FOURCC('m', 'p', '4', '2'),
+
+        // Won't promise that the following file types can be played.
+        // Just give these file types a chance.
+        FOURCC('q', 't', ' ', ' '),  // Apple's QuickTime
+        FOURCC('M', 'S', 'N', 'V'),  // Sony's PSP
+
+        FOURCC('3', 'g', '2', 'a'),  // 3GPP2
+        FOURCC('3', 'g', '2', 'b'),
+    };
+
+    for (size_t i = 0;
+         i < sizeof(kCompatibleBrands) / sizeof(kCompatibleBrands[0]);
+         ++i) {
+        if (kCompatibleBrands[i] == fourcc) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+// Attempt to actually parse the 'ftyp' atom and determine if a suitable
+// compatible brand is present.
+// Also try to identify where this file's metadata ends
+// (end of the 'moov' atom) and report it to the caller as part of
+// the metadata.
+static bool BetterSniffMPEG4(
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *meta) {
+    // We scan up to 128 bytes to identify this file as an MP4.
+    static const off64_t kMaxScanOffset = 128ll;
+
+    off64_t offset = 0ll;
+    bool foundGoodFileType = false;
+    off64_t moovAtomEndOffset = -1ll;
+    bool done = false;
+
+    while (!done && offset < kMaxScanOffset) {
+        uint32_t hdr[2];
+        if (source->readAt(offset, hdr, 8) < 8) {
+            return false;
+        }
+
+        uint64_t chunkSize = ntohl(hdr[0]);
+        uint32_t chunkType = ntohl(hdr[1]);
+        off64_t chunkDataOffset = offset + 8;
+
+        if (chunkSize == 1) {
+            if (source->readAt(offset + 8, &chunkSize, 8) < 8) {
+                return false;
+            }
+
+            chunkSize = ntoh64(chunkSize);
+            chunkDataOffset += 8;
+
+            if (chunkSize < 16) {
+                // The smallest valid chunk is 16 bytes long in this case.
+                return false;
+            }
+        } else if (chunkSize < 8) {
+            // The smallest valid chunk is 8 bytes long.
+            return false;
+        }
+
+        off64_t chunkDataSize = offset + chunkSize - chunkDataOffset;
+
+        char chunkstring[5];
+        MakeFourCCString(chunkType, chunkstring);
+        ALOGV("saw chunk type %s, size %lld @ %lld", chunkstring, chunkSize, offset);
+        switch (chunkType) {
+            case FOURCC('f', 't', 'y', 'p'):
+            {
+                if (chunkDataSize < 8) {
+                    return false;
+                }
+
+                uint32_t numCompatibleBrands = (chunkDataSize - 8) / 4;
+                for (size_t i = 0; i < numCompatibleBrands + 2; ++i) {
+                    if (i == 1) {
+                        // Skip this index, it refers to the minorVersion,
+                        // not a brand.
+                        continue;
+                    }
+
+                    uint32_t brand;
+                    if (source->readAt(
+                                chunkDataOffset + 4 * i, &brand, 4) < 4) {
+                        return false;
+                    }
+
+                    brand = ntohl(brand);
+
+                    if (isCompatibleBrand(brand)) {
+                        foundGoodFileType = true;
+                        break;
+                    }
+                }
+
+                if (!foundGoodFileType) {
+                    return false;
+                }
+
+                break;
+            }
+
+            case FOURCC('m', 'o', 'o', 'v'):
+            {
+                moovAtomEndOffset = offset + chunkSize;
+
+                done = true;
+                break;
+            }
+
+            default:
+                break;
+        }
+
+        offset += chunkSize;
+    }
+
+    if (!foundGoodFileType) {
+        return false;
+    }
+
+    *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4;
+    *confidence = 0.4f;
+
+    if (moovAtomEndOffset >= 0) {
+        *meta = new AMessage;
+        (*meta)->setInt64("meta-data-size", moovAtomEndOffset);
+
+        ALOGV("found metadata size: %lld", moovAtomEndOffset);
+    }
+
+    return true;
+}
+
+bool SniffMPEG4(
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *meta) {
+    if (BetterSniffMPEG4(source, mimeType, confidence, meta)) {
+        return true;
+    }
+
+    if (LegacySniffMPEG4(source, mimeType, confidence)) {
+        ALOGW("Identified supported mpeg4 through LegacySniffMPEG4.");
+        return true;
+    }
+
+    return false;
+}
+
+}  // namespace android
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MediaBuffer.cpp
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MediaBuffer"
+#include <utils/Log.h>
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MetaData.h>
+
+#include <ui/GraphicBuffer.h>
+#include <sys/atomics.h>
+
+namespace android {
+
+MediaBuffer::MediaBuffer(void *data, size_t size)
+    : mObserver(NULL),
+      mNextBuffer(NULL),
+      mRefCount(0),
+      mData(data),
+      mSize(size),
+      mRangeOffset(0),
+      mRangeLength(size),
+      mOwnsData(false),
+      mMetaData(new MetaData),
+      mOriginal(NULL) {
+}
+
+MediaBuffer::MediaBuffer(size_t size)
+    : mObserver(NULL),
+      mNextBuffer(NULL),
+      mRefCount(0),
+      mData(malloc(size)),
+      mSize(size),
+      mRangeOffset(0),
+      mRangeLength(size),
+      mOwnsData(true),
+      mMetaData(new MetaData),
+      mOriginal(NULL) {
+}
+
+MediaBuffer::MediaBuffer(const sp<GraphicBuffer>& graphicBuffer)
+    : mObserver(NULL),
+      mNextBuffer(NULL),
+      mRefCount(0),
+      mData(NULL),
+      mSize(1),
+      mRangeOffset(0),
+      mRangeLength(mSize),
+      mGraphicBuffer(graphicBuffer),
+      mOwnsData(false),
+      mMetaData(new MetaData),
+      mOriginal(NULL) {
+}
+
+MediaBuffer::MediaBuffer(const sp<ABuffer> &buffer)
+    : mObserver(NULL),
+      mNextBuffer(NULL),
+      mRefCount(0),
+      mData(buffer->data()),
+      mSize(buffer->size()),
+      mRangeOffset(0),
+      mRangeLength(mSize),
+      mBuffer(buffer),
+      mOwnsData(false),
+      mMetaData(new MetaData),
+      mOriginal(NULL) {
+}
+
+void MediaBuffer::release() {
+    if (mObserver == NULL) {
+        CHECK_EQ(mRefCount, 0);
+        delete this;
+        return;
+    }
+
+    int prevCount = __atomic_dec(&mRefCount);
+    if (prevCount == 1) {
+        if (mObserver == NULL) {
+            delete this;
+            return;
+        }
+
+        mObserver->signalBufferReturned(this);
+    }
+    CHECK(prevCount > 0);
+}
+
+void MediaBuffer::claim() {
+    CHECK(mObserver != NULL);
+    CHECK_EQ(mRefCount, 1);
+
+    mRefCount = 0;
+}
+
+void MediaBuffer::add_ref() {
+    (void) __atomic_inc(&mRefCount);
+}
+
+void *MediaBuffer::data() const {
+    CHECK(mGraphicBuffer == NULL);
+    return mData;
+}
+
+size_t MediaBuffer::size() const {
+    CHECK(mGraphicBuffer == NULL);
+    return mSize;
+}
+
+size_t MediaBuffer::range_offset() const {
+    return mRangeOffset;
+}
+
+size_t MediaBuffer::range_length() const {
+    return mRangeLength;
+}
+
+void MediaBuffer::set_range(size_t offset, size_t length) {
+    if ((mGraphicBuffer == NULL) && (offset + length > mSize)) {
+        ALOGE("offset = %d, length = %d, mSize = %d", offset, length, mSize);
+    }
+    CHECK((mGraphicBuffer != NULL) || (offset + length <= mSize));
+
+    mRangeOffset = offset;
+    mRangeLength = length;
+}
+
+sp<GraphicBuffer> MediaBuffer::graphicBuffer() const {
+    return mGraphicBuffer;
+}
+
+sp<MetaData> MediaBuffer::meta_data() {
+    return mMetaData;
+}
+
+void MediaBuffer::reset() {
+    mMetaData->clear();
+    set_range(0, mSize);
+}
+
+MediaBuffer::~MediaBuffer() {
+    CHECK(mObserver == NULL);
+
+    if (mOwnsData && mData != NULL) {
+        free(mData);
+        mData = NULL;
+    }
+
+    if (mOriginal != NULL) {
+        mOriginal->release();
+        mOriginal = NULL;
+    }
+}
+
+void MediaBuffer::setObserver(MediaBufferObserver *observer) {
+    CHECK(observer == NULL || mObserver == NULL);
+    mObserver = observer;
+}
+
+void MediaBuffer::setNextBuffer(MediaBuffer *buffer) {
+    mNextBuffer = buffer;
+}
+
+MediaBuffer *MediaBuffer::nextBuffer() {
+    return mNextBuffer;
+}
+
+int MediaBuffer::refcount() const {
+    return mRefCount;
+}
+
+MediaBuffer *MediaBuffer::clone() {
+    CHECK(mGraphicBuffer == NULL);
+
+    MediaBuffer *buffer = new MediaBuffer(mData, mSize);
+    buffer->set_range(mRangeOffset, mRangeLength);
+    buffer->mMetaData = new MetaData(*mMetaData.get());
+
+    add_ref();
+    buffer->mOriginal = this;
+
+    return buffer;
+}
+
+}  // namespace android
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MediaBufferGroup.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MediaBufferGroup"
+#include <utils/Log.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+
+namespace android {
+
+MediaBufferGroup::MediaBufferGroup()
+    : mFirstBuffer(NULL),
+      mLastBuffer(NULL) {
+}
+
+MediaBufferGroup::~MediaBufferGroup() {
+    MediaBuffer *next;
+    for (MediaBuffer *buffer = mFirstBuffer; buffer != NULL;
+         buffer = next) {
+        next = buffer->nextBuffer();
+
+        CHECK_EQ(buffer->refcount(), 0);
+
+        buffer->setObserver(NULL);
+        buffer->release();
+    }
+}
+
+void MediaBufferGroup::add_buffer(MediaBuffer *buffer) {
+    Mutex::Autolock autoLock(mLock);
+
+    buffer->setObserver(this);
+
+    if (mLastBuffer) {
+        mLastBuffer->setNextBuffer(buffer);
+    } else {
+        mFirstBuffer = buffer;
+    }
+
+    mLastBuffer = buffer;
+}
+
+status_t MediaBufferGroup::acquire_buffer(MediaBuffer **out) {
+    Mutex::Autolock autoLock(mLock);
+
+    for (;;) {
+        for (MediaBuffer *buffer = mFirstBuffer;
+             buffer != NULL; buffer = buffer->nextBuffer()) {
+            if (buffer->refcount() == 0) {
+                buffer->add_ref();
+                buffer->reset();
+
+                *out = buffer;
+                goto exit;
+            }
+        }
+
+        // All buffers are in use. Block until one of them is returned to us.
+        mCondition.wait(mLock);
+    }
+
+exit:
+    return OK;
+}
+
+void MediaBufferGroup::signalBufferReturned(MediaBuffer *) {
+    Mutex::Autolock autoLock(mLock);
+    mCondition.signal();
+}
+
+}  // namespace android
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MediaDefs.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/stagefright/MediaDefs.h>
+
+namespace android {
+
+const char *MEDIA_MIMETYPE_IMAGE_JPEG = "image/jpeg";
+
+const char *MEDIA_MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
+const char *MEDIA_MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
+const char *MEDIA_MIMETYPE_VIDEO_AVC = "video/avc";
+const char *MEDIA_MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es";
+const char *MEDIA_MIMETYPE_VIDEO_H263 = "video/3gpp";
+const char *MEDIA_MIMETYPE_VIDEO_MPEG2 = "video/mpeg2";
+const char *MEDIA_MIMETYPE_VIDEO_RAW = "video/raw";
+
+const char *MEDIA_MIMETYPE_AUDIO_AMR_NB = "audio/3gpp";
+const char *MEDIA_MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
+const char *MEDIA_MIMETYPE_AUDIO_MPEG = "audio/mpeg";
+const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I = "audio/mpeg-L1";
+const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II = "audio/mpeg-L2";
+const char *MEDIA_MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
+const char *MEDIA_MIMETYPE_AUDIO_QCELP = "audio/qcelp";
+const char *MEDIA_MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
+const char *MEDIA_MIMETYPE_AUDIO_G711_ALAW = "audio/g711-alaw";
+const char *MEDIA_MIMETYPE_AUDIO_G711_MLAW = "audio/g711-mlaw";
+const char *MEDIA_MIMETYPE_AUDIO_RAW = "audio/raw";
+const char *MEDIA_MIMETYPE_AUDIO_FLAC = "audio/flac";
+const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS = "audio/aac-adts";
+const char *MEDIA_MIMETYPE_AUDIO_MSGSM = "audio/gsm";
+
+const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mp4";
+const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/x-wav";
+const char *MEDIA_MIMETYPE_CONTAINER_OGG = "application/ogg";
+const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA = "video/x-matroska";
+const char *MEDIA_MIMETYPE_CONTAINER_MPEG2TS = "video/mp2ts";
+const char *MEDIA_MIMETYPE_CONTAINER_AVI = "video/avi";
+const char *MEDIA_MIMETYPE_CONTAINER_MPEG2PS = "video/mp2p";
+
+const char *MEDIA_MIMETYPE_CONTAINER_WVM = "video/wvm";
+
+const char *MEDIA_MIMETYPE_TEXT_3GPP = "text/3gpp-tt";
+const char *MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
+
+}  // namespace android
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MediaSource.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/stagefright/MediaSource.h>
+
+namespace android {
+
+MediaSource::MediaSource() {}
+
+MediaSource::~MediaSource() {}
+
+////////////////////////////////////////////////////////////////////////////////
+
+MediaSource::ReadOptions::ReadOptions() {
+    reset();
+}
+
+void MediaSource::ReadOptions::reset() {
+    mOptions = 0;
+    mSeekTimeUs = 0;
+    mLatenessUs = 0;
+}
+
+void MediaSource::ReadOptions::setSeekTo(int64_t time_us, SeekMode mode) {
+    mOptions |= kSeekTo_Option;
+    mSeekTimeUs = time_us;
+    mSeekMode = mode;
+}
+
+void MediaSource::ReadOptions::clearSeekTo() {
+    mOptions &= ~kSeekTo_Option;
+    mSeekTimeUs = 0;
+    mSeekMode = SEEK_CLOSEST_SYNC;
+}
+
+bool MediaSource::ReadOptions::getSeekTo(
+        int64_t *time_us, SeekMode *mode) const {
+    *time_us = mSeekTimeUs;
+    *mode = mSeekMode;
+    return (mOptions & kSeekTo_Option) != 0;
+}
+
+void MediaSource::ReadOptions::setLateBy(int64_t lateness_us) {
+    mLatenessUs = lateness_us;
+}
+
+int64_t MediaSource::ReadOptions::getLateBy() const {
+    return mLatenessUs;
+}
+
+}  // namespace android
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MetaData.cpp
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MetaData"
+#include <utils/Log.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+MetaData::MetaData() {
+}
+
+MetaData::MetaData(const MetaData &from)
+    : RefBase(),
+      mItems(from.mItems) {
+}
+
+MetaData::~MetaData() {
+    clear();
+}
+
+void MetaData::clear() {
+    mItems.clear();
+}
+
+bool MetaData::remove(uint32_t key) {
+    ssize_t i = mItems.indexOfKey(key);
+
+    if (i < 0) {
+        return false;
+    }
+
+    mItems.removeItemsAt(i);
+
+    return true;
+}
+
+bool MetaData::setCString(uint32_t key, const char *value) {
+    return setData(key, TYPE_C_STRING, value, strlen(value) + 1);
+}
+
+bool MetaData::setInt32(uint32_t key, int32_t value) {
+    return setData(key, TYPE_INT32, &value, sizeof(value));
+}
+
+bool MetaData::setInt64(uint32_t key, int64_t value) {
+    return setData(key, TYPE_INT64, &value, sizeof(value));
+}
+
+bool MetaData::setFloat(uint32_t key, float value) {
+    return setData(key, TYPE_FLOAT, &value, sizeof(value));
+}
+
+bool MetaData::setPointer(uint32_t key, void *value) {
+    return setData(key, TYPE_POINTER, &value, sizeof(value));
+}
+
+bool MetaData::setRect(
+        uint32_t key,
+        int32_t left, int32_t top,
+        int32_t right, int32_t bottom) {
+    Rect r;
+    r.mLeft = left;
+    r.mTop = top;
+    r.mRight = right;
+    r.mBottom = bottom;
+
+    return setData(key, TYPE_RECT, &r, sizeof(r));
+}
+
+bool MetaData::findCString(uint32_t key, const char **value) {
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (!findData(key, &type, &data, &size) || type != TYPE_C_STRING) {
+        return false;
+    }
+
+    *value = (const char *)data;
+
+    return true;
+}
+
+bool MetaData::findInt32(uint32_t key, int32_t *value) {
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (!findData(key, &type, &data, &size) || type != TYPE_INT32) {
+        return false;
+    }
+
+    CHECK_EQ(size, sizeof(*value));
+
+    *value = *(int32_t *)data;
+
+    return true;
+}
+
+bool MetaData::findInt64(uint32_t key, int64_t *value) {
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (!findData(key, &type, &data, &size) || type != TYPE_INT64) {
+        return false;
+    }
+
+    CHECK_EQ(size, sizeof(*value));
+
+    *value = *(int64_t *)data;
+
+    return true;
+}
+
+bool MetaData::findFloat(uint32_t key, float *value) {
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (!findData(key, &type, &data, &size) || type != TYPE_FLOAT) {
+        return false;
+    }
+
+    CHECK_EQ(size, sizeof(*value));
+
+    *value = *(float *)data;
+
+    return true;
+}
+
+bool MetaData::findPointer(uint32_t key, void **value) {
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (!findData(key, &type, &data, &size) || type != TYPE_POINTER) {
+        return false;
+    }
+
+    CHECK_EQ(size, sizeof(*value));
+
+    *value = *(void **)data;
+
+    return true;
+}
+
+bool MetaData::findRect(
+        uint32_t key,
+        int32_t *left, int32_t *top,
+        int32_t *right, int32_t *bottom) {
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (!findData(key, &type, &data, &size) || type != TYPE_RECT) {
+        return false;
+    }
+
+    CHECK_EQ(size, sizeof(Rect));
+
+    const Rect *r = (const Rect *)data;
+    *left = r->mLeft;
+    *top = r->mTop;
+    *right = r->mRight;
+    *bottom = r->mBottom;
+
+    return true;
+}
+
+bool MetaData::setData(
+        uint32_t key, uint32_t type, const void *data, size_t size) {
+    bool overwrote_existing = true;
+
+    ssize_t i = mItems.indexOfKey(key);
+    if (i < 0) {
+        typed_data item;
+        i = mItems.add(key, item);
+
+        overwrote_existing = false;
+    }
+
+    typed_data &item = mItems.editValueAt(i);
+
+    item.setData(type, data, size);
+
+    return overwrote_existing;
+}
+
+bool MetaData::findData(uint32_t key, uint32_t *type,
+                        const void **data, size_t *size) const {
+    ssize_t i = mItems.indexOfKey(key);
+
+    if (i < 0) {
+        return false;
+    }
+
+    const typed_data &item = mItems.valueAt(i);
+
+    item.getData(type, data, size);
+
+    return true;
+}
+
+MetaData::typed_data::typed_data()
+    : mType(0),
+      mSize(0) {
+}
+
+MetaData::typed_data::~typed_data() {
+    clear();
+}
+
+MetaData::typed_data::typed_data(const typed_data &from)
+    : mType(from.mType),
+      mSize(0) {
+    allocateStorage(from.mSize);
+    memcpy(storage(), from.storage(), mSize);
+}
+
+MetaData::typed_data &MetaData::typed_data::operator=(
+        const MetaData::typed_data &from) {
+    if (this != &from) {
+        clear();
+        mType = from.mType;
+        allocateStorage(from.mSize);
+        memcpy(storage(), from.storage(), mSize);
+    }
+
+    return *this;
+}
+
+void MetaData::typed_data::clear() {
+    freeStorage();
+
+    mType = 0;
+}
+
+void MetaData::typed_data::setData(
+        uint32_t type, const void *data, size_t size) {
+    clear();
+
+    mType = type;
+    allocateStorage(size);
+    memcpy(storage(), data, size);
+}
+
+void MetaData::typed_data::getData(
+        uint32_t *type, const void **data, size_t *size) const {
+    *type = mType;
+    *size = mSize;
+    *data = storage();
+}
+
+void MetaData::typed_data::allocateStorage(size_t size) {
+    mSize = size;
+
+    if (usesReservoir()) {
+        return;
+    }
+
+    u.ext_data = malloc(mSize);
+}
+
+void MetaData::typed_data::freeStorage() {
+    if (!usesReservoir()) {
+        if (u.ext_data) {
+            free(u.ext_data);
+            u.ext_data = NULL;
+        }
+    }
+
+    mSize = 0;
+}
+
+String8 MetaData::typed_data::asString() const {
+    String8 out;
+    const void *data = storage();
+    switch(mType) {
+        case TYPE_NONE:
+            out = String8::format("no type, size %d)", mSize);
+            break;
+        case TYPE_C_STRING:
+            out = String8::format("(char*) %s", (const char *)data);
+            break;
+        case TYPE_INT32:
+            out = String8::format("(int32_t) %d", *(int32_t *)data);
+            break;
+        case TYPE_INT64:
+            out = String8::format("(int64_t) %lld", *(int64_t *)data);
+            break;
+        case TYPE_FLOAT:
+            out = String8::format("(float) %f", *(float *)data);
+            break;
+        case TYPE_POINTER:
+            out = String8::format("(void*) %p", *(void **)data);
+            break;
+        case TYPE_RECT:
+        {
+            const Rect *r = (const Rect *)data;
+            out = String8::format("Rect(%d, %d, %d, %d)",
+                                  r->mLeft, r->mTop, r->mRight, r->mBottom);
+            break;
+        }
+
+        default:
+            out = String8::format("(unknown type %d, size %d)", mType, mSize);
+            if (mSize <= 48) { // if it's less than three lines of hex data, dump it
+                AString foo;
+                hexdump(data, mSize, 0, &foo);
+                out.append("\n");
+                out.append(foo.c_str());
+            }
+            break;
+    }
+    return out;
+}
+
+static void MakeFourCCString(uint32_t x, char *s) {
+    s[0] = x >> 24;
+    s[1] = (x >> 16) & 0xff;
+    s[2] = (x >> 8) & 0xff;
+    s[3] = x & 0xff;
+    s[4] = '\0';
+}
+
+void MetaData::dumpToLog() const {
+    for (int i = mItems.size(); --i >= 0;) {
+        int32_t key = mItems.keyAt(i);
+        char cc[5];
+        MakeFourCCString(key, cc);
+        const typed_data &item = mItems.valueAt(i);
+        ALOGI("%s: %s", cc, item.asString().string());
+    }
+}
+
+}  // namespace android
+
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "SampleIterator"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "include/SampleIterator.h"
+
+#include <arpa/inet.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/Utils.h>
+
+#include "include/SampleTable.h"
+
+namespace android {
+
+SampleIterator::SampleIterator(SampleTable *table)
+    : mTable(table),
+      mInitialized(false),
+      mTimeToSampleIndex(0),
+      mTTSSampleIndex(0),
+      mTTSSampleTime(0),
+      mTTSCount(0),
+      mTTSDuration(0) {
+    reset();
+}
+
+void SampleIterator::reset() {
+    mSampleToChunkIndex = 0;
+    mFirstChunk = 0;
+    mFirstChunkSampleIndex = 0;
+    mStopChunk = 0;
+    mStopChunkSampleIndex = 0;
+    mSamplesPerChunk = 0;
+    mChunkDesc = 0;
+}
+
+status_t SampleIterator::seekTo(uint32_t sampleIndex) {
+    ALOGV("seekTo(%d)", sampleIndex);
+
+    if (sampleIndex >= mTable->mNumSampleSizes) {
+        return ERROR_END_OF_STREAM;
+    }
+
+    if (mTable->mSampleToChunkOffset < 0
+            || mTable->mChunkOffsetOffset < 0
+            || mTable->mSampleSizeOffset < 0
+            || mTable->mTimeToSampleCount == 0) {
+
+        return ERROR_MALFORMED;
+    }
+
+    if (mInitialized && mCurrentSampleIndex == sampleIndex) {
+        return OK;
+    }
+
+    if (!mInitialized || sampleIndex < mFirstChunkSampleIndex) {
+        reset();
+    }
+
+    if (sampleIndex >= mStopChunkSampleIndex) {
+        status_t err;
+        if ((err = findChunkRange(sampleIndex)) != OK) {
+            ALOGE("findChunkRange failed");
+            return err;
+        }
+    }
+
+    CHECK(sampleIndex < mStopChunkSampleIndex);
+
+    uint32_t chunk =
+        (sampleIndex - mFirstChunkSampleIndex) / mSamplesPerChunk
+        + mFirstChunk;
+
+    if (!mInitialized || chunk != mCurrentChunkIndex) {
+        mCurrentChunkIndex = chunk;
+
+        status_t err;
+        if ((err = getChunkOffset(chunk, &mCurrentChunkOffset)) != OK) {
+            ALOGE("getChunkOffset return error");
+            return err;
+        }
+
+        mCurrentChunkSampleSizes.clear();
+
+        uint32_t firstChunkSampleIndex =
+            mFirstChunkSampleIndex
+                + mSamplesPerChunk * (mCurrentChunkIndex - mFirstChunk);
+
+        for (uint32_t i = 0; i < mSamplesPerChunk; ++i) {
+            size_t sampleSize;
+            if ((err = getSampleSizeDirect(
+                            firstChunkSampleIndex + i, &sampleSize)) != OK) {
+                ALOGE("getSampleSizeDirect return error");
+                return err;
+            }
+
+            mCurrentChunkSampleSizes.push(sampleSize);
+        }
+    }
+
+    uint32_t chunkRelativeSampleIndex =
+        (sampleIndex - mFirstChunkSampleIndex) % mSamplesPerChunk;
+
+    mCurrentSampleOffset = mCurrentChunkOffset;
+    for (uint32_t i = 0; i < chunkRelativeSampleIndex; ++i) {
+        mCurrentSampleOffset += mCurrentChunkSampleSizes[i];
+    }
+
+    mCurrentSampleSize = mCurrentChunkSampleSizes[chunkRelativeSampleIndex];
+    if (sampleIndex < mTTSSampleIndex) {
+        mTimeToSampleIndex = 0;
+        mTTSSampleIndex = 0;
+        mTTSSampleTime = 0;
+        mTTSCount = 0;
+        mTTSDuration = 0;
+    }
+
+    status_t err;
+    if ((err = findSampleTime(sampleIndex, &mCurrentSampleTime)) != OK) {
+        ALOGE("findSampleTime return error");
+        return err;
+    }
+
+    mCurrentSampleIndex = sampleIndex;
+
+    mInitialized = true;
+
+    return OK;
+}
+
+status_t SampleIterator::findChunkRange(uint32_t sampleIndex) {
+    CHECK(sampleIndex >= mFirstChunkSampleIndex);
+
+    while (sampleIndex >= mStopChunkSampleIndex) {
+        if (mSampleToChunkIndex == mTable->mNumSampleToChunkOffsets) {
+            return ERROR_OUT_OF_RANGE;
+        }
+
+        mFirstChunkSampleIndex = mStopChunkSampleIndex;
+
+        const SampleTable::SampleToChunkEntry *entry =
+            &mTable->mSampleToChunkEntries[mSampleToChunkIndex];
+
+        mFirstChunk = entry->startChunk;
+        mSamplesPerChunk = entry->samplesPerChunk;
+        mChunkDesc = entry->chunkDesc;
+
+        if (mSampleToChunkIndex + 1 < mTable->mNumSampleToChunkOffsets) {
+            mStopChunk = entry[1].startChunk;
+
+            mStopChunkSampleIndex =
+                mFirstChunkSampleIndex
+                    + (mStopChunk - mFirstChunk) * mSamplesPerChunk;
+        } else {
+            mStopChunk = 0xffffffff;
+            mStopChunkSampleIndex = 0xffffffff;
+        }
+
+        ++mSampleToChunkIndex;
+    }
+
+    return OK;
+}
+
+status_t SampleIterator::getChunkOffset(uint32_t chunk, off64_t *offset) {
+    *offset = 0;
+
+    if (chunk >= mTable->mNumChunkOffsets) {
+        return ERROR_OUT_OF_RANGE;
+    }
+
+    if (mTable->mChunkOffsetType == SampleTable::kChunkOffsetType32) {
+        uint32_t offset32;
+
+        if (mTable->mDataSource->readAt(
+                    mTable->mChunkOffsetOffset + 8 + 4 * chunk,
+                    &offset32,
+                    sizeof(offset32)) < (ssize_t)sizeof(offset32)) {
+            return ERROR_IO;
+        }
+
+        *offset = ntohl(offset32);
+    } else {
+        CHECK_EQ(mTable->mChunkOffsetType, SampleTable::kChunkOffsetType64);
+
+        uint64_t offset64;
+        if (mTable->mDataSource->readAt(
+                    mTable->mChunkOffsetOffset + 8 + 8 * chunk,
+                    &offset64,
+                    sizeof(offset64)) < (ssize_t)sizeof(offset64)) {
+            return ERROR_IO;
+        }
+
+        *offset = ntoh64(offset64);
+    }
+
+    return OK;
+}
+
+status_t SampleIterator::getSampleSizeDirect(
+        uint32_t sampleIndex, size_t *size) {
+    *size = 0;
+
+    if (sampleIndex >= mTable->mNumSampleSizes) {
+        return ERROR_OUT_OF_RANGE;
+    }
+
+    if (mTable->mDefaultSampleSize > 0) {
+        *size = mTable->mDefaultSampleSize;
+        return OK;
+    }
+
+    switch (mTable->mSampleSizeFieldSize) {
+        case 32:
+        {
+            if (mTable->mDataSource->readAt(
+                        mTable->mSampleSizeOffset + 12 + 4 * sampleIndex,
+                        size, sizeof(*size)) < (ssize_t)sizeof(*size)) {
+                return ERROR_IO;
+            }
+
+            *size = ntohl(*size);
+            break;
+        }
+
+        case 16:
+        {
+            uint16_t x;
+            if (mTable->mDataSource->readAt(
+                        mTable->mSampleSizeOffset + 12 + 2 * sampleIndex,
+                        &x, sizeof(x)) < (ssize_t)sizeof(x)) {
+                return ERROR_IO;
+            }
+
+            *size = ntohs(x);
+            break;
+        }
+
+        case 8:
+        {
+            uint8_t x;
+            if (mTable->mDataSource->readAt(
+                        mTable->mSampleSizeOffset + 12 + sampleIndex,
+                        &x, sizeof(x)) < (ssize_t)sizeof(x)) {
+                return ERROR_IO;
+            }
+
+            *size = x;
+            break;
+        }
+
+        default:
+        {
+            CHECK_EQ(mTable->mSampleSizeFieldSize, 4);
+
+            uint8_t x;
+            if (mTable->mDataSource->readAt(
+                        mTable->mSampleSizeOffset + 12 + sampleIndex / 2,
+                        &x, sizeof(x)) < (ssize_t)sizeof(x)) {
+                return ERROR_IO;
+            }
+
+            *size = (sampleIndex & 1) ? x & 0x0f : x >> 4;
+            break;
+        }
+    }
+
+    return OK;
+}
+
+status_t SampleIterator::findSampleTime(
+        uint32_t sampleIndex, uint32_t *time) {
+    if (sampleIndex >= mTable->mNumSampleSizes) {
+        return ERROR_OUT_OF_RANGE;
+    }
+
+    while (sampleIndex >= mTTSSampleIndex + mTTSCount) {
+        if (mTimeToSampleIndex == mTable->mTimeToSampleCount) {
+            return ERROR_OUT_OF_RANGE;
+        }
+
+        mTTSSampleIndex += mTTSCount;
+        mTTSSampleTime += mTTSCount * mTTSDuration;
+
+        mTTSCount = mTable->mTimeToSample[2 * mTimeToSampleIndex];
+        mTTSDuration = mTable->mTimeToSample[2 * mTimeToSampleIndex + 1];
+
+        ++mTimeToSampleIndex;
+    }
+
+    *time = mTTSSampleTime + mTTSDuration * (sampleIndex - mTTSSampleIndex);
+
+    *time += mTable->getCompositionTimeOffset(sampleIndex);
+
+    return OK;
+}
+
+}  // namespace android
+
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp
@@ -0,0 +1,831 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "SampleTable"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "include/SampleTable.h"
+#include "include/SampleIterator.h"
+
+#include <arpa/inet.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+// static
+const uint32_t SampleTable::kChunkOffsetType32 = FOURCC('s', 't', 'c', 'o');
+// static
+const uint32_t SampleTable::kChunkOffsetType64 = FOURCC('c', 'o', '6', '4');
+// static
+const uint32_t SampleTable::kSampleSizeType32 = FOURCC('s', 't', 's', 'z');
+// static
+const uint32_t SampleTable::kSampleSizeTypeCompact = FOURCC('s', 't', 'z', '2');
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct SampleTable::CompositionDeltaLookup {
+    CompositionDeltaLookup();
+
+    void setEntries(
+            const uint32_t *deltaEntries, size_t numDeltaEntries);
+
+    uint32_t getCompositionTimeOffset(uint32_t sampleIndex);
+
+private:
+    Mutex mLock;
+
+    const uint32_t *mDeltaEntries;
+    size_t mNumDeltaEntries;
+
+    size_t mCurrentDeltaEntry;
+    size_t mCurrentEntrySampleIndex;
+
+    DISALLOW_EVIL_CONSTRUCTORS(CompositionDeltaLookup);
+};
+
+SampleTable::CompositionDeltaLookup::CompositionDeltaLookup()
+    : mDeltaEntries(NULL),
+      mNumDeltaEntries(0),
+      mCurrentDeltaEntry(0),
+      mCurrentEntrySampleIndex(0) {
+}
+
+void SampleTable::CompositionDeltaLookup::setEntries(
+        const uint32_t *deltaEntries, size_t numDeltaEntries) {
+    Mutex::Autolock autolock(mLock);
+
+    mDeltaEntries = deltaEntries;
+    mNumDeltaEntries = numDeltaEntries;
+    mCurrentDeltaEntry = 0;
+    mCurrentEntrySampleIndex = 0;
+}
+
+uint32_t SampleTable::CompositionDeltaLookup::getCompositionTimeOffset(
+        uint32_t sampleIndex) {
+    Mutex::Autolock autolock(mLock);
+
+    if (mDeltaEntries == NULL) {
+        return 0;
+    }
+
+    if (sampleIndex < mCurrentEntrySampleIndex) {
+        mCurrentDeltaEntry = 0;
+        mCurrentEntrySampleIndex = 0;
+    }
+
+    while (mCurrentDeltaEntry < mNumDeltaEntries) {
+        uint32_t sampleCount = mDeltaEntries[2 * mCurrentDeltaEntry];
+        if (sampleIndex < mCurrentEntrySampleIndex + sampleCount) {
+            return mDeltaEntries[2 * mCurrentDeltaEntry + 1];
+        }
+
+        mCurrentEntrySampleIndex += sampleCount;
+        ++mCurrentDeltaEntry;
+    }
+
+    return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+SampleTable::SampleTable(const sp<DataSource> &source)
+    : mDataSource(source),
+      mChunkOffsetOffset(-1),
+      mChunkOffsetType(0),
+      mNumChunkOffsets(0),
+      mSampleToChunkOffset(-1),
+      mNumSampleToChunkOffsets(0),
+      mSampleSizeOffset(-1),
+      mSampleSizeFieldSize(0),
+      mDefaultSampleSize(0),
+      mNumSampleSizes(0),
+      mTimeToSampleCount(0),
+      mTimeToSample(NULL),
+      mSampleTimeEntries(NULL),
+      mCompositionTimeDeltaEntries(NULL),
+      mNumCompositionTimeDeltaEntries(0),
+      mCompositionDeltaLookup(new CompositionDeltaLookup),
+      mSyncSampleOffset(-1),
+      mNumSyncSamples(0),
+      mSyncSamples(NULL),
+      mLastSyncSampleIndex(0),
+      mSampleToChunkEntries(NULL) {
+    mSampleIterator = new SampleIterator(this);
+}
+
+SampleTable::~SampleTable() {
+    delete[] mSampleToChunkEntries;
+    mSampleToChunkEntries = NULL;
+
+    delete[] mSyncSamples;
+    mSyncSamples = NULL;
+
+    delete mCompositionDeltaLookup;
+    mCompositionDeltaLookup = NULL;
+
+    delete[] mCompositionTimeDeltaEntries;
+    mCompositionTimeDeltaEntries = NULL;
+
+    delete[] mSampleTimeEntries;
+    mSampleTimeEntries = NULL;
+
+    delete[] mTimeToSample;
+    mTimeToSample = NULL;
+
+    delete mSampleIterator;
+    mSampleIterator = NULL;
+}
+
+bool SampleTable::isValid() const {
+    return mChunkOffsetOffset >= 0
+        && mSampleToChunkOffset >= 0
+        && mSampleSizeOffset >= 0
+        && mTimeToSample != NULL;
+}
+
+status_t SampleTable::setChunkOffsetParams(
+        uint32_t type, off64_t data_offset, size_t data_size) {
+    if (mChunkOffsetOffset >= 0) {
+        return ERROR_MALFORMED;
+    }
+
+    CHECK(type == kChunkOffsetType32 || type == kChunkOffsetType64);
+
+    mChunkOffsetOffset = data_offset;
+    mChunkOffsetType = type;
+
+    if (data_size < 8) {
+        return ERROR_MALFORMED;
+    }
+
+    uint8_t header[8];
+    if (mDataSource->readAt(
+                data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+        return ERROR_IO;
+    }
+
+    if (U32_AT(header) != 0) {
+        // Expected version = 0, flags = 0.
+        return ERROR_MALFORMED;
+    }
+
+    mNumChunkOffsets = U32_AT(&header[4]);
+
+    if (mChunkOffsetType == kChunkOffsetType32) {
+        if (data_size < 8 + mNumChunkOffsets * 4) {
+            return ERROR_MALFORMED;
+        }
+    } else {
+        if (data_size < 8 + mNumChunkOffsets * 8) {
+            return ERROR_MALFORMED;
+        }
+    }
+
+    return OK;
+}
+
+status_t SampleTable::setSampleToChunkParams(
+        off64_t data_offset, size_t data_size) {
+    if (mSampleToChunkOffset >= 0) {
+        return ERROR_MALFORMED;
+    }
+
+    mSampleToChunkOffset = data_offset;
+
+    if (data_size < 8) {
+        return ERROR_MALFORMED;
+    }
+
+    uint8_t header[8];
+    if (mDataSource->readAt(
+                data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+        return ERROR_IO;
+    }
+
+    if (U32_AT(header) != 0) {
+        // Expected version = 0, flags = 0.
+        return ERROR_MALFORMED;
+    }
+
+    mNumSampleToChunkOffsets = U32_AT(&header[4]);
+
+    if (data_size < 8 + mNumSampleToChunkOffsets * 12) {
+        return ERROR_MALFORMED;
+    }
+
+    mSampleToChunkEntries =
+        new SampleToChunkEntry[mNumSampleToChunkOffsets];
+
+    for (uint32_t i = 0; i < mNumSampleToChunkOffsets; ++i) {
+        uint8_t buffer[12];
+        if (mDataSource->readAt(
+                    mSampleToChunkOffset + 8 + i * 12, buffer, sizeof(buffer))
+                != (ssize_t)sizeof(buffer)) {
+            return ERROR_IO;
+        }
+
+        CHECK(U32_AT(buffer) >= 1);  // chunk index is 1 based in the spec.
+
+        // We want the chunk index to be 0-based.
+        mSampleToChunkEntries[i].startChunk = U32_AT(buffer) - 1;
+        mSampleToChunkEntries[i].samplesPerChunk = U32_AT(&buffer[4]);
+        mSampleToChunkEntries[i].chunkDesc = U32_AT(&buffer[8]);
+    }
+
+    return OK;
+}
+
+status_t SampleTable::setSampleSizeParams(
+        uint32_t type, off64_t data_offset, size_t data_size) {
+    if (mSampleSizeOffset >= 0) {
+        return ERROR_MALFORMED;
+    }
+
+    CHECK(type == kSampleSizeType32 || type == kSampleSizeTypeCompact);
+
+    mSampleSizeOffset = data_offset;
+
+    if (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;
+    }
+
+    if (U32_AT(header) != 0) {
+        // Expected version = 0, flags = 0.
+        return ERROR_MALFORMED;
+    }
+
+    mDefaultSampleSize = U32_AT(&header[4]);
+    mNumSampleSizes = U32_AT(&header[8]);
+
+    if (type == kSampleSizeType32) {
+        mSampleSizeFieldSize = 32;
+
+        if (mDefaultSampleSize != 0) {
+            return OK;
+        }
+
+        if (data_size < 12 + mNumSampleSizes * 4) {
+            return ERROR_MALFORMED;
+        }
+    } else {
+        if ((mDefaultSampleSize & 0xffffff00) != 0) {
+            // The high 24 bits are reserved and must be 0.
+            return ERROR_MALFORMED;
+        }
+
+        mSampleSizeFieldSize = mDefaultSampleSize & 0xff;
+        mDefaultSampleSize = 0;
+
+        if (mSampleSizeFieldSize != 4 && mSampleSizeFieldSize != 8
+            && mSampleSizeFieldSize != 16) {
+            return ERROR_MALFORMED;
+        }
+
+        if (data_size < 12 + (mNumSampleSizes * mSampleSizeFieldSize + 4) / 8) {
+            return ERROR_MALFORMED;
+        }
+    }
+
+    return OK;
+}
+
+status_t SampleTable::setTimeToSampleParams(
+        off64_t data_offset, size_t data_size) {
+    if (mTimeToSample != NULL || data_size < 8) {
+        return ERROR_MALFORMED;
+    }
+
+    uint8_t header[8];
+    if (mDataSource->readAt(
+                data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+        return ERROR_IO;
+    }
+
+    if (U32_AT(header) != 0) {
+        // Expected version = 0, flags = 0.
+        return ERROR_MALFORMED;
+    }
+
+    mTimeToSampleCount = U32_AT(&header[4]);
+    mTimeToSample = new uint32_t[mTimeToSampleCount * 2];
+
+    size_t size = sizeof(uint32_t) * mTimeToSampleCount * 2;
+    if (mDataSource->readAt(
+                data_offset + 8, mTimeToSample, size) < (ssize_t)size) {
+        return ERROR_IO;
+    }
+
+    for (uint32_t i = 0; i < mTimeToSampleCount * 2; ++i) {
+        mTimeToSample[i] = ntohl(mTimeToSample[i]);
+    }
+
+    return OK;
+}
+
+status_t SampleTable::setCompositionTimeToSampleParams(
+        off64_t data_offset, size_t data_size) {
+    ALOGI("There are reordered frames present.");
+
+    if (mCompositionTimeDeltaEntries != NULL || data_size < 8) {
+        return ERROR_MALFORMED;
+    }
+
+    uint8_t header[8];
+    if (mDataSource->readAt(
+                data_offset, header, sizeof(header))
+            < (ssize_t)sizeof(header)) {
+        return ERROR_IO;
+    }
+
+    if (U32_AT(header) != 0) {
+        // Expected version = 0, flags = 0.
+        return ERROR_MALFORMED;
+    }
+
+    size_t numEntries = U32_AT(&header[4]);
+
+    if (data_size != (numEntries + 1) * 8) {
+        return ERROR_MALFORMED;
+    }
+
+    mNumCompositionTimeDeltaEntries = numEntries;
+    mCompositionTimeDeltaEntries = new uint32_t[2 * numEntries];
+
+    if (mDataSource->readAt(
+                data_offset + 8, mCompositionTimeDeltaEntries, numEntries * 8)
+            < (ssize_t)numEntries * 8) {
+        delete[] mCompositionTimeDeltaEntries;
+        mCompositionTimeDeltaEntries = NULL;
+
+        return ERROR_IO;
+    }
+
+    for (size_t i = 0; i < 2 * numEntries; ++i) {
+        mCompositionTimeDeltaEntries[i] = ntohl(mCompositionTimeDeltaEntries[i]);
+    }
+
+    mCompositionDeltaLookup->setEntries(
+            mCompositionTimeDeltaEntries, mNumCompositionTimeDeltaEntries);
+
+    return OK;
+}
+
+status_t SampleTable::setSyncSampleParams(off64_t data_offset, size_t data_size) {
+    if (mSyncSampleOffset >= 0 || data_size < 8) {
+        return ERROR_MALFORMED;
+    }
+
+    mSyncSampleOffset = data_offset;
+
+    uint8_t header[8];
+    if (mDataSource->readAt(
+                data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+        return ERROR_IO;
+    }
+
+    if (U32_AT(header) != 0) {
+        // Expected version = 0, flags = 0.
+        return ERROR_MALFORMED;
+    }
+
+    mNumSyncSamples = U32_AT(&header[4]);
+
+    if (mNumSyncSamples < 2) {
+        ALOGV("Table of sync samples is empty or has only a single entry!");
+    }
+
+    mSyncSamples = new uint32_t[mNumSyncSamples];
+    size_t size = mNumSyncSamples * sizeof(uint32_t);
+    if (mDataSource->readAt(mSyncSampleOffset + 8, mSyncSamples, size)
+            != (ssize_t)size) {
+        return ERROR_IO;
+    }
+
+    for (size_t i = 0; i < mNumSyncSamples; ++i) {
+        mSyncSamples[i] = ntohl(mSyncSamples[i]) - 1;
+    }
+
+    return OK;
+}
+
+uint32_t SampleTable::countChunkOffsets() const {
+    return mNumChunkOffsets;
+}
+
+uint32_t SampleTable::countSamples() const {
+    return mNumSampleSizes;
+}
+
+status_t SampleTable::getMaxSampleSize(size_t *max_size) {
+    Mutex::Autolock autoLock(mLock);
+
+    *max_size = 0;
+
+    for (uint32_t i = 0; i < mNumSampleSizes; ++i) {
+        size_t sample_size;
+        status_t err = getSampleSize_l(i, &sample_size);
+
+        if (err != OK) {
+            return err;
+        }
+
+        if (sample_size > *max_size) {
+            *max_size = sample_size;
+        }
+    }
+
+    return OK;
+}
+
+uint32_t abs_difference(uint32_t time1, uint32_t time2) {
+    return time1 > time2 ? time1 - time2 : time2 - time1;
+}
+
+// static
+int SampleTable::CompareIncreasingTime(const void *_a, const void *_b) {
+    const SampleTimeEntry *a = (const SampleTimeEntry *)_a;
+    const SampleTimeEntry *b = (const SampleTimeEntry *)_b;
+
+    if (a->mCompositionTime < b->mCompositionTime) {
+        return -1;
+    } else if (a->mCompositionTime > b->mCompositionTime) {
+        return 1;
+    }
+
+    return 0;
+}
+
+void SampleTable::buildSampleEntriesTable() {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mSampleTimeEntries != NULL) {
+        return;
+    }
+
+    mSampleTimeEntries = new SampleTimeEntry[mNumSampleSizes];
+
+    uint32_t sampleIndex = 0;
+    uint32_t sampleTime = 0;
+
+    for (uint32_t i = 0; i < mTimeToSampleCount; ++i) {
+        uint32_t n = mTimeToSample[2 * i];
+        uint32_t delta = mTimeToSample[2 * i + 1];
+
+        for (uint32_t j = 0; j < n; ++j) {
+            if (sampleIndex < mNumSampleSizes) {
+                // Technically this should always be the case if the file
+                // is well-formed, but you know... there's (gasp) malformed
+                // content out there.
+
+                mSampleTimeEntries[sampleIndex].mSampleIndex = sampleIndex;
+
+                uint32_t compTimeDelta =
+                    mCompositionDeltaLookup->getCompositionTimeOffset(
+                            sampleIndex);
+
+                mSampleTimeEntries[sampleIndex].mCompositionTime =
+                    sampleTime + compTimeDelta;
+            }
+
+            ++sampleIndex;
+            sampleTime += delta;
+        }
+    }
+
+    qsort(mSampleTimeEntries, mNumSampleSizes, sizeof(SampleTimeEntry),
+          CompareIncreasingTime);
+}
+
+status_t SampleTable::findSampleAtTime(
+        uint32_t req_time, uint32_t *sample_index, uint32_t flags) {
+    buildSampleEntriesTable();
+
+    uint32_t left = 0;
+    uint32_t right = mNumSampleSizes;
+    while (left < right) {
+        uint32_t center = (left + right) / 2;
+        uint32_t centerTime = mSampleTimeEntries[center].mCompositionTime;
+
+        if (req_time < centerTime) {
+            right = center;
+        } else if (req_time > centerTime) {
+            left = center + 1;
+        } else {
+            left = center;
+            break;
+        }
+    }
+
+    if (left == mNumSampleSizes) {
+        if (flags == kFlagAfter) {
+            return ERROR_OUT_OF_RANGE;
+        }
+
+        --left;
+    }
+
+    uint32_t closestIndex = left;
+
+    switch (flags) {
+        case kFlagBefore:
+        {
+            while (closestIndex > 0
+                    && mSampleTimeEntries[closestIndex].mCompositionTime
+                            > req_time) {
+                --closestIndex;
+            }
+            break;
+        }
+
+        case kFlagAfter:
+        {
+            while (closestIndex + 1 < mNumSampleSizes
+                    && mSampleTimeEntries[closestIndex].mCompositionTime
+                            < req_time) {
+                ++closestIndex;
+            }
+            break;
+        }
+
+        default:
+        {
+            CHECK(flags == kFlagClosest);
+
+            if (closestIndex > 0) {
+                // Check left neighbour and pick closest.
+                uint32_t absdiff1 =
+                    abs_difference(
+                            mSampleTimeEntries[closestIndex].mCompositionTime,
+                            req_time);
+
+                uint32_t absdiff2 =
+                    abs_difference(
+                            mSampleTimeEntries[closestIndex - 1].mCompositionTime,
+                            req_time);
+
+                if (absdiff1 > absdiff2) {
+                    closestIndex = closestIndex - 1;
+                }
+            }
+
+            break;
+        }
+    }
+
+    *sample_index = mSampleTimeEntries[closestIndex].mSampleIndex;
+
+    return OK;
+}
+
+status_t SampleTable::findSyncSampleNear(
+        uint32_t start_sample_index, uint32_t *sample_index, uint32_t flags) {
+    Mutex::Autolock autoLock(mLock);
+
+    *sample_index = 0;
+
+    if (mSyncSampleOffset < 0) {
+        // All samples are sync-samples.
+        *sample_index = start_sample_index;
+        return OK;
+    }
+
+    if (mNumSyncSamples == 0) {
+        *sample_index = 0;
+        return OK;
+    }
+
+    uint32_t left = 0;
+    uint32_t right = mNumSyncSamples;
+    while (left < right) {
+        uint32_t center = left + (right - left) / 2;
+        uint32_t x = mSyncSamples[center];
+
+        if (start_sample_index < x) {
+            right = center;
+        } else if (start_sample_index > x) {
+            left = center + 1;
+        } else {
+            left = center;
+            break;
+        }
+    }
+    if (left == mNumSyncSamples) {
+        if (flags == kFlagAfter) {
+            ALOGE("tried to find a sync frame after the last one: %d", left);
+            return ERROR_OUT_OF_RANGE;
+        }
+        left = left - 1;
+    }
+
+    // Now ssi[left] is the sync sample index just before (or at)
+    // start_sample_index.
+    // Also start_sample_index < ssi[left + 1], if left + 1 < mNumSyncSamples.
+
+    uint32_t x = mSyncSamples[left];
+
+    if (left + 1 < mNumSyncSamples) {
+        uint32_t y = mSyncSamples[left + 1];
+
+        // our sample lies between sync samples x and y.
+
+        status_t err = mSampleIterator->seekTo(start_sample_index);
+        if (err != OK) {
+            return err;
+        }
+
+        uint32_t sample_time = mSampleIterator->getSampleTime();
+
+        err = mSampleIterator->seekTo(x);
+        if (err != OK) {
+            return err;
+        }
+        uint32_t x_time = mSampleIterator->getSampleTime();
+
+        err = mSampleIterator->seekTo(y);
+        if (err != OK) {
+            return err;
+        }
+
+        uint32_t y_time = mSampleIterator->getSampleTime();
+
+        if (abs_difference(x_time, sample_time)
+                > abs_difference(y_time, sample_time)) {
+            // Pick the sync sample closest (timewise) to the start-sample.
+            x = y;
+            ++left;
+        }
+    }
+
+    switch (flags) {
+        case kFlagBefore:
+        {
+            if (x > start_sample_index) {
+                CHECK(left > 0);
+
+                x = mSyncSamples[left - 1];
+
+                if (x > start_sample_index) {
+                    // The table of sync sample indices was not sorted
+                    // properly.
+                    return ERROR_MALFORMED;
+                }
+            }
+            break;
+        }
+
+        case kFlagAfter:
+        {
+            if (x < start_sample_index) {
+                if (left + 1 >= mNumSyncSamples) {
+                    return ERROR_OUT_OF_RANGE;
+                }
+
+                x = mSyncSamples[left + 1];
+
+                if (x < start_sample_index) {
+                    // The table of sync sample indices was not sorted
+                    // properly.
+                    return ERROR_MALFORMED;
+                }
+            }
+
+            break;
+        }
+
+        default:
+            break;
+    }
+
+    *sample_index = x;
+
+    return OK;
+}
+
+status_t SampleTable::findThumbnailSample(uint32_t *sample_index) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mSyncSampleOffset < 0) {
+        // All samples are sync-samples.
+        *sample_index = 0;
+        return OK;
+    }
+
+    uint32_t bestSampleIndex = 0;
+    size_t maxSampleSize = 0;
+
+    static const size_t kMaxNumSyncSamplesToScan = 20;
+
+    // Consider the first kMaxNumSyncSamplesToScan sync samples and
+    // pick the one with the largest (compressed) size as the thumbnail.
+
+    size_t numSamplesToScan = mNumSyncSamples;
+    if (numSamplesToScan > kMaxNumSyncSamplesToScan) {
+        numSamplesToScan = kMaxNumSyncSamplesToScan;
+    }
+
+    for (size_t i = 0; i < numSamplesToScan; ++i) {
+        uint32_t x = mSyncSamples[i];
+
+        // Now x is a sample index.
+        size_t sampleSize;
+        status_t err = getSampleSize_l(x, &sampleSize);
+        if (err != OK) {
+            return err;
+        }
+
+        if (i == 0 || sampleSize > maxSampleSize) {
+            bestSampleIndex = x;
+            maxSampleSize = sampleSize;
+        }
+    }
+
+    *sample_index = bestSampleIndex;
+
+    return OK;
+}
+
+status_t SampleTable::getSampleSize_l(
+        uint32_t sampleIndex, size_t *sampleSize) {
+    return mSampleIterator->getSampleSizeDirect(
+            sampleIndex, sampleSize);
+}
+
+status_t SampleTable::getMetaDataForSample(
+        uint32_t sampleIndex,
+        off64_t *offset,
+        size_t *size,
+        uint32_t *compositionTime,
+        bool *isSyncSample) {
+    Mutex::Autolock autoLock(mLock);
+
+    status_t err;
+    if ((err = mSampleIterator->seekTo(sampleIndex)) != OK) {
+        return err;
+    }
+
+    if (offset) {
+        *offset = mSampleIterator->getSampleOffset();
+    }
+
+    if (size) {
+        *size = mSampleIterator->getSampleSize();
+    }
+
+    if (compositionTime) {
+        *compositionTime = mSampleIterator->getSampleTime();
+    }
+
+    if (isSyncSample) {
+        *isSyncSample = false;
+        if (mSyncSampleOffset < 0) {
+            // Every sample is a sync sample.
+            *isSyncSample = true;
+        } else {
+            size_t i = (mLastSyncSampleIndex < mNumSyncSamples)
+                    && (mSyncSamples[mLastSyncSampleIndex] <= sampleIndex)
+                ? mLastSyncSampleIndex : 0;
+
+            while (i < mNumSyncSamples && mSyncSamples[i] < sampleIndex) {
+                ++i;
+            }
+
+            if (i < mNumSyncSamples && mSyncSamples[i] == sampleIndex) {
+                *isSyncSample = true;
+            }
+
+            mLastSyncSampleIndex = i;
+        }
+    }
+
+    return OK;
+}
+
+uint32_t SampleTable::getCompositionTimeOffset(uint32_t sampleIndex) {
+    return mCompositionDeltaLookup->getCompositionTimeOffset(sampleIndex);
+}
+
+}  // namespace android
+
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/Utils.cpp
@@ -0,0 +1,607 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Utils"
+#include <utils/Log.h>
+
+#include "include/ESDS.h"
+
+#include <arpa/inet.h>
+#include <cutils/properties.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/AudioSystem.h>
+#include <media/MediaPlayerInterface.h>
+#include <hardware/audio.h>
+#include <media/stagefright/Utils.h>
+#include <media/AudioParameter.h>
+
+namespace android {
+
+uint16_t U16_AT(const uint8_t *ptr) {
+    return ptr[0] << 8 | ptr[1];
+}
+
+uint32_t U32_AT(const uint8_t *ptr) {
+    return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
+}
+
+uint64_t U64_AT(const uint8_t *ptr) {
+    return ((uint64_t)U32_AT(ptr)) << 32 | U32_AT(ptr + 4);
+}
+
+uint16_t U16LE_AT(const uint8_t *ptr) {
+    return ptr[0] | (ptr[1] << 8);
+}
+
+uint32_t U32LE_AT(const uint8_t *ptr) {
+    return ptr[3] << 24 | ptr[2] << 16 | ptr[1] << 8 | ptr[0];
+}
+
+uint64_t U64LE_AT(const uint8_t *ptr) {
+    return ((uint64_t)U32LE_AT(ptr + 4)) << 32 | U32LE_AT(ptr);
+}
+
+// XXX warning: these won't work on big-endian host.
+uint64_t ntoh64(uint64_t x) {
+    return ((uint64_t)ntohl(x & 0xffffffff) << 32) | ntohl(x >> 32);
+}
+
+uint64_t hton64(uint64_t x) {
+    return ((uint64_t)htonl(x & 0xffffffff) << 32) | htonl(x >> 32);
+}
+
+status_t convertMetaDataToMessage(
+        const sp<MetaData> &meta, sp<AMessage> *format) {
+    format->clear();
+
+    const char *mime;
+    CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+    sp<AMessage> msg = new AMessage;
+    msg->setString("mime", mime);
+
+    int64_t durationUs;
+    if (meta->findInt64(kKeyDuration, &durationUs)) {
+        msg->setInt64("durationUs", durationUs);
+    }
+
+    int32_t isSync;
+    if (meta->findInt32(kKeyIsSyncFrame, &isSync) && isSync != 0) {
+        msg->setInt32("is-sync-frame", 1);
+    }
+
+    if (!strncasecmp("video/", mime, 6)) {
+        int32_t width, height;
+        CHECK(meta->findInt32(kKeyWidth, &width));
+        CHECK(meta->findInt32(kKeyHeight, &height));
+
+        msg->setInt32("width", width);
+        msg->setInt32("height", height);
+
+        int32_t sarWidth, sarHeight;
+        if (meta->findInt32(kKeySARWidth, &sarWidth)
+                && meta->findInt32(kKeySARHeight, &sarHeight)) {
+            msg->setInt32("sar-width", sarWidth);
+            msg->setInt32("sar-height", sarHeight);
+        }
+    } else if (!strncasecmp("audio/", mime, 6)) {
+        int32_t numChannels, sampleRate;
+        CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
+        CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+
+        msg->setInt32("channel-count", numChannels);
+        msg->setInt32("sample-rate", sampleRate);
+
+        int32_t channelMask;
+        if (meta->findInt32(kKeyChannelMask, &channelMask)) {
+            msg->setInt32("channel-mask", channelMask);
+        }
+
+        int32_t delay = 0;
+        if (meta->findInt32(kKeyEncoderDelay, &delay)) {
+            msg->setInt32("encoder-delay", delay);
+        }
+        int32_t padding = 0;
+        if (meta->findInt32(kKeyEncoderPadding, &padding)) {
+            msg->setInt32("encoder-padding", padding);
+        }
+
+        int32_t isADTS;
+        if (meta->findInt32(kKeyIsADTS, &isADTS)) {
+            msg->setInt32("is-adts", true);
+        }
+    }
+
+    int32_t maxInputSize;
+    if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) {
+        msg->setInt32("max-input-size", maxInputSize);
+    }
+
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (meta->findData(kKeyAVCC, &type, &data, &size)) {
+        // Parse the AVCDecoderConfigurationRecord
+
+        const uint8_t *ptr = (const uint8_t *)data;
+
+        CHECK(size >= 7);
+        CHECK_EQ((unsigned)ptr[0], 1u);  // configurationVersion == 1
+        uint8_t profile = ptr[1];
+        uint8_t level = ptr[3];
+
+        // There is decodable content out there that fails the following
+        // assertion, let's be lenient for now...
+        // CHECK((ptr[4] >> 2) == 0x3f);  // reserved
+
+        size_t lengthSize = 1 + (ptr[4] & 3);
+
+        // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
+        // violates it...
+        // CHECK((ptr[5] >> 5) == 7);  // reserved
+
+        size_t numSeqParameterSets = ptr[5] & 31;
+
+        ptr += 6;
+        size -= 6;
+
+        sp<ABuffer> buffer = new ABuffer(1024);
+        buffer->setRange(0, 0);
+
+        for (size_t i = 0; i < numSeqParameterSets; ++i) {
+            CHECK(size >= 2);
+            size_t length = U16_AT(ptr);
+
+            ptr += 2;
+            size -= 2;
+
+            CHECK(size >= length);
+
+            memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
+            memcpy(buffer->data() + buffer->size() + 4, ptr, length);
+            buffer->setRange(0, buffer->size() + 4 + length);
+
+            ptr += length;
+            size -= length;
+        }
+
+        buffer->meta()->setInt32("csd", true);
+        buffer->meta()->setInt64("timeUs", 0);
+
+        msg->setBuffer("csd-0", buffer);
+
+        buffer = new ABuffer(1024);
+        buffer->setRange(0, 0);
+
+        CHECK(size >= 1);
+        size_t numPictureParameterSets = *ptr;
+        ++ptr;
+        --size;
+
+        for (size_t i = 0; i < numPictureParameterSets; ++i) {
+            CHECK(size >= 2);
+            size_t length = U16_AT(ptr);
+
+            ptr += 2;
+            size -= 2;
+
+            CHECK(size >= length);
+
+            memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
+            memcpy(buffer->data() + buffer->size() + 4, ptr, length);
+            buffer->setRange(0, buffer->size() + 4 + length);
+
+            ptr += length;
+            size -= length;
+        }
+
+        buffer->meta()->setInt32("csd", true);
+        buffer->meta()->setInt64("timeUs", 0);
+        msg->setBuffer("csd-1", buffer);
+    } else if (meta->findData(kKeyESDS, &type, &data, &size)) {
+        ESDS esds((const char *)data, size);
+        CHECK_EQ(esds.InitCheck(), (status_t)OK);
+
+        const void *codec_specific_data;
+        size_t codec_specific_data_size;
+        esds.getCodecSpecificInfo(
+                &codec_specific_data, &codec_specific_data_size);
+
+        sp<ABuffer> buffer = new ABuffer(codec_specific_data_size);
+
+        memcpy(buffer->data(), codec_specific_data,
+               codec_specific_data_size);
+
+        buffer->meta()->setInt32("csd", true);
+        buffer->meta()->setInt64("timeUs", 0);
+        msg->setBuffer("csd-0", buffer);
+    } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) {
+        sp<ABuffer> buffer = new ABuffer(size);
+        memcpy(buffer->data(), data, size);
+
+        buffer->meta()->setInt32("csd", true);
+        buffer->meta()->setInt64("timeUs", 0);
+        msg->setBuffer("csd-0", buffer);
+
+        if (!meta->findData(kKeyVorbisBooks, &type, &data, &size)) {
+            return -EINVAL;
+        }
+
+        buffer = new ABuffer(size);
+        memcpy(buffer->data(), data, size);
+
+        buffer->meta()->setInt32("csd", true);
+        buffer->meta()->setInt64("timeUs", 0);
+        msg->setBuffer("csd-1", buffer);
+    }
+
+    *format = msg;
+
+    return OK;
+}
+
+static size_t reassembleAVCC(const sp<ABuffer> &csd0, const sp<ABuffer> csd1, char *avcc) {
+
+    avcc[0] = 1;        // version
+    avcc[1] = 0x64;     // profile
+    avcc[2] = 0;        // unused (?)
+    avcc[3] = 0xd;      // level
+    avcc[4] = 0xff;     // reserved+size
+
+    size_t i = 0;
+    int numparams = 0;
+    int lastparamoffset = 0;
+    int avccidx = 6;
+    do {
+        if (i >= csd0->size() - 4 ||
+                memcmp(csd0->data() + i, "\x00\x00\x00\x01", 4) == 0) {
+            if (i >= csd0->size() - 4) {
+                // there can't be another param here, so use all the rest
+                i = csd0->size();
+            }
+            ALOGV("block at %d, last was %d", i, lastparamoffset);
+            if (lastparamoffset > 0) {
+                int size = i - lastparamoffset;
+                avcc[avccidx++] = size >> 8;
+                avcc[avccidx++] = size & 0xff;
+                memcpy(avcc+avccidx, csd0->data() + lastparamoffset, size);
+                avccidx += size;
+                numparams++;
+            }
+            i += 4;
+            lastparamoffset = i;
+        } else {
+            i++;
+        }
+    } while(i < csd0->size());
+    ALOGV("csd0 contains %d params", numparams);
+
+    avcc[5] = 0xe0 | numparams;
+    //and now csd-1
+    i = 0;
+    numparams = 0;
+    lastparamoffset = 0;
+    int numpicparamsoffset = avccidx;
+    avccidx++;
+    do {
+        if (i >= csd1->size() - 4 ||
+                memcmp(csd1->data() + i, "\x00\x00\x00\x01", 4) == 0) {
+            if (i >= csd1->size() - 4) {
+                // there can't be another param here, so use all the rest
+                i = csd1->size();
+            }
+            ALOGV("block at %d, last was %d", i, lastparamoffset);
+            if (lastparamoffset > 0) {
+                int size = i - lastparamoffset;
+                avcc[avccidx++] = size >> 8;
+                avcc[avccidx++] = size & 0xff;
+                memcpy(avcc+avccidx, csd1->data() + lastparamoffset, size);
+                avccidx += size;
+                numparams++;
+            }
+            i += 4;
+            lastparamoffset = i;
+        } else {
+            i++;
+        }
+    } while(i < csd1->size());
+    avcc[numpicparamsoffset] = numparams;
+    return avccidx;
+}
+
+static void reassembleESDS(const sp<ABuffer> &csd0, char *esds) {
+    int csd0size = csd0->size();
+    esds[0] = 3; // kTag_ESDescriptor;
+    int esdescriptorsize = 26 + csd0size;
+    CHECK(esdescriptorsize < 268435456); // 7 bits per byte, so max is 2^28-1
+    esds[1] = 0x80 | (esdescriptorsize >> 21);
+    esds[2] = 0x80 | ((esdescriptorsize >> 14) & 0x7f);
+    esds[3] = 0x80 | ((esdescriptorsize >> 7) & 0x7f);
+    esds[4] = (esdescriptorsize & 0x7f);
+    esds[5] = esds[6] = 0; // es id
+    esds[7] = 0; // flags
+    esds[8] = 4; // kTag_DecoderConfigDescriptor
+    int configdescriptorsize = 18 + csd0size;
+    esds[9] = 0x80 | (configdescriptorsize >> 21);
+    esds[10] = 0x80 | ((configdescriptorsize >> 14) & 0x7f);
+    esds[11] = 0x80 | ((configdescriptorsize >> 7) & 0x7f);
+    esds[12] = (configdescriptorsize & 0x7f);
+    esds[13] = 0x40; // objectTypeIndication
+    esds[14] = 0x15; // not sure what 14-25 mean, they are ignored by ESDS.cpp,
+    esds[15] = 0x00; // but the actual values here were taken from a real file.
+    esds[16] = 0x18;
+    esds[17] = 0x00;
+    esds[18] = 0x00;
+    esds[19] = 0x00;
+    esds[20] = 0xfa;
+    esds[21] = 0x00;
+    esds[22] = 0x00;
+    esds[23] = 0x00;
+    esds[24] = 0xfa;
+    esds[25] = 0x00;
+    esds[26] = 5; // kTag_DecoderSpecificInfo;
+    esds[27] = 0x80 | (csd0size >> 21);
+    esds[28] = 0x80 | ((csd0size >> 14) & 0x7f);
+    esds[29] = 0x80 | ((csd0size >> 7) & 0x7f);
+    esds[30] = (csd0size & 0x7f);
+    memcpy((void*)&esds[31], csd0->data(), csd0size);
+    // data following this is ignored, so don't bother appending it
+
+}
+
+void convertMessageToMetaData(const sp<AMessage> &msg, sp<MetaData> &meta) {
+    AString mime;
+    if (msg->findString("mime", &mime)) {
+        meta->setCString(kKeyMIMEType, mime.c_str());
+    } else {
+        ALOGW("did not find mime type");
+    }
+
+    int64_t durationUs;
+    if (msg->findInt64("durationUs", &durationUs)) {
+        meta->setInt64(kKeyDuration, durationUs);
+    }
+
+    int32_t isSync;
+    if (msg->findInt32("is-sync-frame", &isSync) && isSync != 0) {
+        meta->setInt32(kKeyIsSyncFrame, 1);
+    }
+
+    if (mime.startsWith("video/")) {
+        int32_t width;
+        int32_t height;
+        if (msg->findInt32("width", &width) && msg->findInt32("height", &height)) {
+            meta->setInt32(kKeyWidth, width);
+            meta->setInt32(kKeyHeight, height);
+        } else {
+            ALOGW("did not find width and/or height");
+        }
+
+        int32_t sarWidth, sarHeight;
+        if (msg->findInt32("sar-width", &sarWidth)
+                && msg->findInt32("sar-height", &sarHeight)) {
+            meta->setInt32(kKeySARWidth, sarWidth);
+            meta->setInt32(kKeySARHeight, sarHeight);
+        }
+    } else if (mime.startsWith("audio/")) {
+        int32_t numChannels;
+        if (msg->findInt32("channel-count", &numChannels)) {
+            meta->setInt32(kKeyChannelCount, numChannels);
+        }
+        int32_t sampleRate;
+        if (msg->findInt32("sample-rate", &sampleRate)) {
+            meta->setInt32(kKeySampleRate, sampleRate);
+        }
+        int32_t channelMask;
+        if (msg->findInt32("channel-mask", &channelMask)) {
+            meta->setInt32(kKeyChannelMask, channelMask);
+        }
+        int32_t delay = 0;
+        if (msg->findInt32("encoder-delay", &delay)) {
+            meta->setInt32(kKeyEncoderDelay, delay);
+        }
+        int32_t padding = 0;
+        if (msg->findInt32("encoder-padding", &padding)) {
+            meta->setInt32(kKeyEncoderPadding, padding);
+        }
+
+        int32_t isADTS;
+        if (msg->findInt32("is-adts", &isADTS)) {
+            meta->setInt32(kKeyIsADTS, isADTS);
+        }
+    }
+
+    int32_t maxInputSize;
+    if (msg->findInt32("max-input-size", &maxInputSize)) {
+        meta->setInt32(kKeyMaxInputSize, maxInputSize);
+    }
+
+    // reassemble the csd data into its original form
+    sp<ABuffer> csd0;
+    if (msg->findBuffer("csd-0", &csd0)) {
+        if (mime.startsWith("video/")) { // do we need to be stricter than this?
+            sp<ABuffer> csd1;
+            if (msg->findBuffer("csd-1", &csd1)) {
+                char avcc[1024]; // that oughta be enough, right?
+                size_t outsize = reassembleAVCC(csd0, csd1, avcc);
+                meta->setData(kKeyAVCC, kKeyAVCC, avcc, outsize);
+            }
+        } else if (mime.startsWith("audio/")) {
+            int csd0size = csd0->size();
+            char esds[csd0size + 31];
+            reassembleESDS(csd0, esds);
+            meta->setData(kKeyESDS, kKeyESDS, esds, sizeof(esds));
+        }
+    }
+
+    // XXX TODO add whatever other keys there are
+
+#if 0
+    ALOGI("converted %s to:", msg->debugString(0).c_str());
+    meta->dumpToLog();
+#endif
+}
+
+AString MakeUserAgent() {
+    AString ua;
+    ua.append("stagefright/1.2 (Linux;Android ");
+
+#if (PROPERTY_VALUE_MAX < 8)
+#error "PROPERTY_VALUE_MAX must be at least 8"
+#endif
+
+    char value[PROPERTY_VALUE_MAX];
+    property_get("ro.build.version.release", value, "Unknown");
+    ua.append(value);
+    ua.append(")");
+
+    return ua;
+}
+
+status_t sendMetaDataToHal(sp<MediaPlayerBase::AudioSink>& sink,
+                           const sp<MetaData>& meta)
+{
+    int32_t sampleRate = 0;
+    int32_t bitRate = 0;
+    int32_t channelMask = 0;
+    int32_t delaySamples = 0;
+    int32_t paddingSamples = 0;
+
+    AudioParameter param = AudioParameter();
+
+    if (meta->findInt32(kKeySampleRate, &sampleRate)) {
+        param.addInt(String8(AUDIO_OFFLOAD_CODEC_SAMPLE_RATE), sampleRate);
+    }
+    if (meta->findInt32(kKeyChannelMask, &channelMask)) {
+        param.addInt(String8(AUDIO_OFFLOAD_CODEC_NUM_CHANNEL), channelMask);
+    }
+    if (meta->findInt32(kKeyBitRate, &bitRate)) {
+        param.addInt(String8(AUDIO_OFFLOAD_CODEC_AVG_BIT_RATE), bitRate);
+    }
+    if (meta->findInt32(kKeyEncoderDelay, &delaySamples)) {
+        param.addInt(String8(AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES), delaySamples);
+    }
+    if (meta->findInt32(kKeyEncoderPadding, &paddingSamples)) {
+        param.addInt(String8(AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES), paddingSamples);
+    }
+
+    ALOGV("sendMetaDataToHal: bitRate %d, sampleRate %d, chanMask %d,"
+          "delaySample %d, paddingSample %d", bitRate, sampleRate,
+          channelMask, delaySamples, paddingSamples);
+
+    sink->setParameters(param.toString());
+    return OK;
+}
+
+struct mime_conv_t {
+    const char* mime;
+    audio_format_t format;
+};
+
+static const struct mime_conv_t mimeLookup[] = {
+    { MEDIA_MIMETYPE_AUDIO_MPEG,        AUDIO_FORMAT_MP3 },
+    { MEDIA_MIMETYPE_AUDIO_RAW,         AUDIO_FORMAT_PCM_16_BIT },
+    { MEDIA_MIMETYPE_AUDIO_AMR_NB,      AUDIO_FORMAT_AMR_NB },
+    { MEDIA_MIMETYPE_AUDIO_AMR_WB,      AUDIO_FORMAT_AMR_WB },
+    { MEDIA_MIMETYPE_AUDIO_AAC,         AUDIO_FORMAT_AAC },
+    { MEDIA_MIMETYPE_AUDIO_VORBIS,      AUDIO_FORMAT_VORBIS },
+    { 0, AUDIO_FORMAT_INVALID }
+};
+
+status_t mapMimeToAudioFormat( audio_format_t& format, const char* mime )
+{
+const struct mime_conv_t* p = &mimeLookup[0];
+    while (p->mime != NULL) {
+        if (0 == strcasecmp(mime, p->mime)) {
+            format = p->format;
+            return OK;
+        }
+        ++p;
+    }
+
+    return BAD_VALUE;
+}
+
+bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo, bool isStreaming)
+{
+    const char *mime;
+    CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+    audio_offload_info_t info = AUDIO_INFO_INITIALIZER;
+
+    info.format = AUDIO_FORMAT_INVALID;
+    if (mapMimeToAudioFormat(info.format, mime) != OK) {
+        ALOGE(" Couldn't map mime type \"%s\" to a valid AudioSystem::audio_format !", mime);
+        return false;
+    } else {
+        ALOGV("Mime type \"%s\" mapped to audio_format %d", mime, info.format);
+    }
+
+    if (AUDIO_FORMAT_INVALID == info.format) {
+        // can't offload if we don't know what the source format is
+        ALOGE("mime type \"%s\" not a known audio format", mime);
+        return false;
+    }
+
+    int32_t srate = -1;
+    if (!meta->findInt32(kKeySampleRate, &srate)) {
+        ALOGV("track of type '%s' does not publish sample rate", mime);
+    }
+    info.sample_rate = srate;
+
+    int32_t cmask = 0;
+    if (!meta->findInt32(kKeyChannelMask, &cmask)) {
+        ALOGV("track of type '%s' does not publish channel mask", mime);
+
+        // Try a channel count instead
+        int32_t channelCount;
+        if (!meta->findInt32(kKeyChannelCount, &channelCount)) {
+            ALOGV("track of type '%s' does not publish channel count", mime);
+        } else {
+            cmask = audio_channel_out_mask_from_count(channelCount);
+        }
+    }
+    info.channel_mask = cmask;
+
+    int64_t duration = 0;
+    if (!meta->findInt64(kKeyDuration, &duration)) {
+        ALOGV("track of type '%s' does not publish duration", mime);
+    }
+    info.duration_us = duration;
+
+    int32_t brate = -1;
+    if (!meta->findInt32(kKeyBitRate, &brate)) {
+        ALOGV("track of type '%s' does not publish bitrate", mime);
+     }
+    info.bit_rate = brate;
+
+
+    info.stream_type = AUDIO_STREAM_MUSIC;
+    info.has_video = hasVideo;
+    info.is_streaming = isStreaming;
+
+    // Check if offload is possible for given format, stream type, sample rate,
+    // bit rate, duration, video and streaming
+    return AudioSystem::isOffloadSupported(info);
+}
+
+}  // namespace android
+
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/foundation/AAtomizer.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+
+#include "AAtomizer.h"
+
+namespace android {
+
+// static
+AAtomizer AAtomizer::gAtomizer;
+
+// static
+const char *AAtomizer::Atomize(const char *name) {
+    return gAtomizer.atomize(name);
+}
+
+AAtomizer::AAtomizer() {
+    for (size_t i = 0; i < 128; ++i) {
+        mAtoms.push(List<AString>());
+    }
+}
+
+const char *AAtomizer::atomize(const char *name) {
+    Mutex::Autolock autoLock(mLock);
+
+    const size_t n = mAtoms.size();
+    size_t index = AAtomizer::Hash(name) % n;
+    List<AString> &entry = mAtoms.editItemAt(index);
+    List<AString>::iterator it = entry.begin();
+    while (it != entry.end()) {
+        if ((*it) == name) {
+            return (*it).c_str();
+        }
+        ++it;
+    }
+
+    entry.push_back(AString(name));
+
+    return (*--entry.end()).c_str();
+}
+
+// static
+uint32_t AAtomizer::Hash(const char *s) {
+    uint32_t sum = 0;
+    while (*s != '\0') {
+        sum = (sum * 31) + *s;
+        ++s;
+    }
+
+    return sum;
+}
+
+}  // namespace android
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/foundation/ABitReader.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ABitReader.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+ABitReader::ABitReader(const uint8_t *data, size_t size)
+    : mData(data),
+      mSize(size),
+      mReservoir(0),
+      mNumBitsLeft(0) {
+}
+
+void ABitReader::fillReservoir() {
+    CHECK_GT(mSize, 0u);
+
+    mReservoir = 0;
+    size_t i;
+    for (i = 0; mSize > 0 && i < 4; ++i) {
+        mReservoir = (mReservoir << 8) | *mData;
+
+        ++mData;
+        --mSize;
+    }
+
+    mNumBitsLeft = 8 * i;
+    mReservoir <<= 32 - mNumBitsLeft;
+}
+
+uint32_t ABitReader::getBits(size_t n) {
+    CHECK_LE(n, 32u);
+
+    uint32_t result = 0;
+    while (n > 0) {
+        if (mNumBitsLeft == 0) {
+            fillReservoir();
+        }
+
+        size_t m = n;
+        if (m > mNumBitsLeft) {
+            m = mNumBitsLeft;
+        }
+
+        result = (result << m) | (mReservoir >> (32 - m));
+        mReservoir <<= m;
+        mNumBitsLeft -= m;
+
+        n -= m;
+    }
+
+    return result;
+}
+
+void ABitReader::skipBits(size_t n) {
+    while (n > 32) {
+        getBits(32);
+        n -= 32;
+    }
+
+    if (n > 0) {
+        getBits(n);
+    }
+}
+
+void ABitReader::putBits(uint32_t x, size_t n) {
+    CHECK_LE(n, 32u);
+
+    while (mNumBitsLeft + n > 32) {
+        mNumBitsLeft -= 8;
+        --mData;
+        ++mSize;
+    }
+
+    mReservoir = (mReservoir >> n) | (x << (32 - n));
+    mNumBitsLeft += n;
+}
+
+size_t ABitReader::numBitsLeft() const {
+    return mSize * 8 + mNumBitsLeft;
+}
+
+const uint8_t *ABitReader::data() const {
+    return mData - (mNumBitsLeft + 7) / 8;
+}
+
+}  // namespace android
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/foundation/ABuffer.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ABuffer.h"
+
+#include "ADebug.h"
+#include "ALooper.h"
+#include "AMessage.h"
+
+namespace android {
+
+ABuffer::ABuffer(size_t capacity)
+    : mData(malloc(capacity)),
+      mCapacity(capacity),
+      mRangeOffset(0),
+      mRangeLength(capacity),
+      mInt32Data(0),
+      mOwnsData(true) {
+}
+
+ABuffer::ABuffer(void *data, size_t capacity)
+    : mData(data),
+      mCapacity(capacity),
+      mRangeOffset(0),
+      mRangeLength(capacity),
+      mInt32Data(0),
+      mOwnsData(false) {
+}
+
+ABuffer::~ABuffer() {
+    if (mOwnsData) {
+        if (mData != NULL) {
+            free(mData);
+            mData = NULL;
+        }
+    }
+
+    if (mFarewell != NULL) {
+        mFarewell->post();
+    }
+}
+
+void ABuffer::setRange(size_t offset, size_t size) {
+    CHECK_LE(offset, mCapacity);
+    CHECK_LE(offset + size, mCapacity);
+
+    mRangeOffset = offset;
+    mRangeLength = size;
+}
+
+void ABuffer::setFarewellMessage(const sp<AMessage> msg) {
+    mFarewell = msg;
+}
+
+sp<AMessage> ABuffer::meta() {
+    if (mMeta == NULL) {
+        mMeta = new AMessage;
+    }
+    return mMeta;
+}
+
+}  // namespace android
+
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/foundation/AString.cpp
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ADebug.h"
+#include "AString.h"
+
+namespace android {
+
+// static
+const char *AString::kEmptyString = "";
+
+AString::AString()
+    : mData((char *)kEmptyString),
+      mSize(0),
+      mAllocSize(1) {
+}
+
+AString::AString(const char *s)
+    : mData(NULL),
+      mSize(0),
+      mAllocSize(1) {
+    setTo(s);
+}
+
+AString::AString(const char *s, size_t size)
+    : mData(NULL),
+      mSize(0),
+      mAllocSize(1) {
+    setTo(s, size);
+}
+
+AString::AString(const AString &from)
+    : mData(NULL),
+      mSize(0),
+      mAllocSize(1) {
+    setTo(from, 0, from.size());
+}
+
+AString::AString(const AString &from, size_t offset, size_t n)
+    : mData(NULL),
+      mSize(0),
+      mAllocSize(1) {
+    setTo(from, offset, n);
+}
+
+AString::~AString() {
+    clear();
+}
+
+AString &AString::operator=(const AString &from) {
+    if (&from != this) {
+        setTo(from, 0, from.size());
+    }
+
+    return *this;
+}
+
+size_t AString::size() const {
+    return mSize;
+}
+
+const char *AString::c_str() const {
+    return mData;
+}
+
+bool AString::empty() const {
+    return mSize == 0;
+}
+
+void AString::setTo(const char *s) {
+    setTo(s, strlen(s));
+}
+
+void AString::setTo(const char *s, size_t size) {
+    clear();
+    append(s, size);
+}
+
+void AString::setTo(const AString &from, size_t offset, size_t n) {
+    CHECK(&from != this);
+
+    clear();
+    setTo(from.mData + offset, n);
+}
+
+void AString::clear() {
+    if (mData && mData != kEmptyString) {
+        free(mData);
+        mData = NULL;
+    }
+
+    mData = (char *)kEmptyString;
+    mSize = 0;
+    mAllocSize = 1;
+}
+
+size_t AString::hash() const {
+    size_t x = 0;
+    for (size_t i = 0; i < mSize; ++i) {
+        x = (x * 31) + mData[i];
+    }
+
+    return x;
+}
+
+bool AString::operator==(const AString &other) const {
+    return mSize == other.mSize && !memcmp(mData, other.mData, mSize);
+}
+
+void AString::trim() {
+    makeMutable();
+
+    size_t i = 0;
+    while (i < mSize && isspace(mData[i])) {
+        ++i;
+    }
+
+    size_t j = mSize;
+    while (j > i && isspace(mData[j - 1])) {
+        --j;
+    }
+
+    memmove(mData, &mData[i], j - i);
+    mSize = j - i;
+    mData[mSize] = '\0';
+}
+
+void AString::erase(size_t start, size_t n) {
+    CHECK_LT(start, mSize);
+    CHECK_LE(start + n, mSize);
+
+    makeMutable();
+
+    memmove(&mData[start], &mData[start + n], mSize - start - n);
+    mSize -= n;
+    mData[mSize] = '\0';
+}
+
+void AString::makeMutable() {
+    if (mData == kEmptyString) {
+        mData = strdup(kEmptyString);
+    }
+}
+
+void AString::append(const char *s) {
+    append(s, strlen(s));
+}
+
+void AString::append(const char *s, size_t size) {
+    makeMutable();
+
+    if (mSize + size + 1 > mAllocSize) {
+        mAllocSize = (mAllocSize + size + 31) & -32;
+        mData = (char *)realloc(mData, mAllocSize);
+        CHECK(mData != NULL);
+    }
+
+    memcpy(&mData[mSize], s, size);
+    mSize += size;
+    mData[mSize] = '\0';
+}
+
+void AString::append(const AString &from) {
+    append(from.c_str(), from.size());
+}
+
+void AString::append(const AString &from, size_t offset, size_t n) {
+    append(from.c_str() + offset, n);
+}
+
+void AString::append(int x) {
+    char s[16];
+    sprintf(s, "%d", x);
+
+    append(s);
+}
+
+void AString::append(unsigned x) {
+    char s[16];
+    sprintf(s, "%u", x);
+
+    append(s);
+}
+
+void AString::append(long x) {
+    char s[16];
+    sprintf(s, "%ld", x);
+
+    append(s);
+}
+
+void AString::append(unsigned long x) {
+    char s[16];
+    sprintf(s, "%lu", x);
+
+    append(s);
+}
+
+void AString::append(long long x) {
+    char s[32];
+    sprintf(s, "%lld", x);
+
+    append(s);
+}
+
+void AString::append(unsigned long long x) {
+    char s[32];
+    sprintf(s, "%llu", x);
+
+    append(s);
+}
+
+void AString::append(float x) {
+    char s[16];
+    sprintf(s, "%f", x);
+
+    append(s);
+}
+
+void AString::append(double x) {
+    char s[16];
+    sprintf(s, "%f", x);
+
+    append(s);
+}
+
+void AString::append(void *x) {
+    char s[16];
+    sprintf(s, "%p", x);
+
+    append(s);
+}
+
+ssize_t AString::find(const char *substring, size_t start) const {
+    CHECK_LE(start, size());
+
+    const char *match = strstr(mData + start, substring);
+
+    if (match == NULL) {
+        return -1;
+    }
+
+    return match - mData;
+}
+
+void AString::insert(const AString &from, size_t insertionPos) {
+    insert(from.c_str(), from.size(), insertionPos);
+}
+
+void AString::insert(const char *from, size_t size, size_t insertionPos) {
+    CHECK_GE(insertionPos, 0u);
+    CHECK_LE(insertionPos, mSize);
+
+    makeMutable();
+
+    if (mSize + size + 1 > mAllocSize) {
+        mAllocSize = (mAllocSize + size + 31) & -32;
+        mData = (char *)realloc(mData, mAllocSize);
+        CHECK(mData != NULL);
+    }
+
+    memmove(&mData[insertionPos + size],
+            &mData[insertionPos], mSize - insertionPos + 1);
+
+    memcpy(&mData[insertionPos], from, size);
+
+    mSize += size;
+}
+
+bool AString::operator<(const AString &other) const {
+    return compare(other) < 0;
+}
+
+bool AString::operator>(const AString &other) const {
+    return compare(other) > 0;
+}
+
+int AString::compare(const AString &other) const {
+    return strcmp(mData, other.mData);
+}
+
+void AString::tolower() {
+    makeMutable();
+
+    for (size_t i = 0; i < mSize; ++i) {
+        mData[i] = ::tolower(mData[i]);
+    }
+}
+
+bool AString::startsWith(const char *prefix) const {
+    return !strncmp(mData, prefix, strlen(prefix));
+}
+
+bool AString::endsWith(const char *suffix) const {
+    size_t suffixLen = strlen(suffix);
+
+    if (mSize < suffixLen) {
+        return false;
+    }
+
+    return !strcmp(mData + mSize - suffixLen, suffix);
+}
+
+AString StringPrintf(const char *format, ...) {
+    va_list ap;
+    va_start(ap, format);
+
+    char *buffer;
+    vasprintf(&buffer, format, ap);
+
+    va_end(ap);
+
+    AString result(buffer);
+
+    free(buffer);
+    buffer = NULL;
+
+    return result;
+}
+
+}  // namespace android
+
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/foundation/hexdump.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "hexdump"
+#include <utils/Log.h>
+
+#include "hexdump.h"
+
+#include "ADebug.h"
+#include "AString.h"
+
+#include <ctype.h>
+#include <stdint.h>
+#include <stdio.h>
+
+namespace android {
+
+static void appendIndent(AString *s, int32_t indent) {
+    static const char kWhitespace[] =
+        "                                        "
+        "                                        ";
+
+    CHECK_LT((size_t)indent, sizeof(kWhitespace));
+
+    s->append(kWhitespace, indent);
+}
+
+void hexdump(const void *_data, size_t size, size_t indent, AString *appendTo) {
+    const uint8_t *data = (const uint8_t *)_data;
+
+    size_t offset = 0;
+    while (offset < size) {
+        AString line;
+
+        appendIndent(&line, indent);
+
+        char tmp[32];
+        sprintf(tmp, "%08lx:  ", (unsigned long)offset);
+
+        line.append(tmp);
+
+        for (size_t i = 0; i < 16; ++i) {
+            if (i == 8) {
+                line.append(' ');
+            }
+            if (offset + i >= size) {
+                line.append("   ");
+            } else {
+                sprintf(tmp, "%02x ", data[offset + i]);
+                line.append(tmp);
+            }
+        }
+
+        line.append(' ');
+
+        for (size_t i = 0; i < 16; ++i) {
+            if (offset + i >= size) {
+                break;
+            }
+
+            if (isprint(data[offset + i])) {
+                line.append((char)data[offset + i]);
+            } else {
+                line.append('.');
+            }
+        }
+
+        if (appendTo != NULL) {
+            appendTo->append(line);
+            appendTo->append("\n");
+        } else {
+            ALOGI("%s", line.c_str());
+        }
+
+        offset += 16;
+    }
+}
+
+}  // namespace android
+
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/id3/ID3.cpp
@@ -0,0 +1,900 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ID3"
+#include <utils/Log.h>
+
+#include "../include/ID3.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/Utils.h>
+#include <utils/String8.h>
+#include <byteswap.h>
+
+namespace android {
+
+static const size_t kMaxMetadataSize = 3 * 1024 * 1024;
+
+struct MemorySource : public DataSource {
+    MemorySource(const uint8_t *data, size_t size)
+        : mData(data),
+          mSize(size) {
+    }
+
+    virtual status_t initCheck() const {
+        return OK;
+    }
+
+    virtual ssize_t readAt(off64_t offset, void *data, size_t size) {
+        off64_t available = (offset >= mSize) ? 0ll : mSize - offset;
+
+        size_t copy = (available > size) ? size : available;
+        memcpy(data, mData + offset, copy);
+
+        return copy;
+    }
+
+private:
+    const uint8_t *mData;
+    size_t mSize;
+
+    DISALLOW_EVIL_CONSTRUCTORS(MemorySource);
+};
+
+ID3::ID3(const sp<DataSource> &source, bool ignoreV1)
+    : mIsValid(false),
+      mData(NULL),
+      mSize(0),
+      mFirstFrameOffset(0),
+      mVersion(ID3_UNKNOWN),
+      mRawSize(0) {
+    mIsValid = parseV2(source);
+
+    if (!mIsValid && !ignoreV1) {
+        mIsValid = parseV1(source);
+    }
+}
+
+ID3::ID3(const uint8_t *data, size_t size, bool ignoreV1)
+    : mIsValid(false),
+      mData(NULL),
+      mSize(0),
+      mFirstFrameOffset(0),
+      mVersion(ID3_UNKNOWN),
+      mRawSize(0) {
+    sp<MemorySource> source = new MemorySource(data, size);
+
+    mIsValid = parseV2(source);
+
+    if (!mIsValid && !ignoreV1) {
+        mIsValid = parseV1(source);
+    }
+}
+
+ID3::~ID3() {
+    if (mData) {
+        free(mData);
+        mData = NULL;
+    }
+}
+
+bool ID3::isValid() const {
+    return mIsValid;
+}
+
+ID3::Version ID3::version() const {
+    return mVersion;
+}
+
+// static
+bool ID3::ParseSyncsafeInteger(const uint8_t encoded[4], size_t *x) {
+    *x = 0;
+    for (int32_t i = 0; i < 4; ++i) {
+        if (encoded[i] & 0x80) {
+            return false;
+        }
+
+        *x = ((*x) << 7) | encoded[i];
+    }
+
+    return true;
+}
+
+bool ID3::parseV2(const sp<DataSource> &source) {
+struct id3_header {
+    char id[3];
+    uint8_t version_major;
+    uint8_t version_minor;
+    uint8_t flags;
+    uint8_t enc_size[4];
+    };
+
+    id3_header header;
+    if (source->readAt(
+                0, &header, sizeof(header)) != (ssize_t)sizeof(header)) {
+        return false;
+    }
+
+    if (memcmp(header.id, "ID3", 3)) {
+        return false;
+    }
+
+    if (header.version_major == 0xff || header.version_minor == 0xff) {
+        return false;
+    }
+
+    if (header.version_major == 2) {
+        if (header.flags & 0x3f) {
+            // We only support the 2 high bits, if any of the lower bits are
+            // set, we cannot guarantee to understand the tag format.
+            return false;
+        }
+
+        if (header.flags & 0x40) {
+            // No compression scheme has been decided yet, ignore the
+            // tag if compression is indicated.
+
+            return false;
+        }
+    } else if (header.version_major == 3) {
+        if (header.flags & 0x1f) {
+            // We only support the 3 high bits, if any of the lower bits are
+            // set, we cannot guarantee to understand the tag format.
+            return false;
+        }
+    } else if (header.version_major == 4) {
+        if (header.flags & 0x0f) {
+            // The lower 4 bits are undefined in this spec.
+            return false;
+        }
+    } else {
+        return false;
+    }
+
+    size_t size;
+    if (!ParseSyncsafeInteger(header.enc_size, &size)) {
+        return false;
+    }
+
+    if (size > kMaxMetadataSize) {
+        ALOGE("skipping huge ID3 metadata of size %d", size);
+        return false;
+    }
+
+    mData = (uint8_t *)malloc(size);
+
+    if (mData == NULL) {
+        return false;
+    }
+
+    mSize = size;
+    mRawSize = mSize + sizeof(header);
+
+    if (source->readAt(sizeof(header), mData, mSize) != (ssize_t)mSize) {
+        free(mData);
+        mData = NULL;
+
+        return false;
+    }
+
+    if (header.version_major == 4) {
+        void *copy = malloc(size);
+        memcpy(copy, mData, size);
+
+        bool success = removeUnsynchronizationV2_4(false /* iTunesHack */);
+        if (!success) {
+            memcpy(mData, copy, size);
+            mSize = size;
+
+            success = removeUnsynchronizationV2_4(true /* iTunesHack */);
+
+            if (success) {
+                ALOGV("Had to apply the iTunes hack to parse this ID3 tag");
+            }
+        }
+
+        free(copy);
+        copy = NULL;
+
+        if (!success) {
+            free(mData);
+            mData = NULL;
+
+            return false;
+        }
+    } else if (header.flags & 0x80) {
+        ALOGV("removing unsynchronization");
+
+        removeUnsynchronization();
+    }
+
+    mFirstFrameOffset = 0;
+    if (header.version_major == 3 && (header.flags & 0x40)) {
+        // Version 2.3 has an optional extended header.
+
+        if (mSize < 4) {
+            free(mData);
+            mData = NULL;
+
+            return false;
+        }
+
+        size_t extendedHeaderSize = U32_AT(&mData[0]) + 4;
+
+        if (extendedHeaderSize > mSize) {
+            free(mData);
+            mData = NULL;
+
+            return false;
+        }
+
+        mFirstFrameOffset = extendedHeaderSize;
+
+        uint16_t extendedFlags = 0;
+        if (extendedHeaderSize >= 6) {
+            extendedFlags = U16_AT(&mData[4]);
+
+            if (extendedHeaderSize >= 10) {
+                size_t paddingSize = U32_AT(&mData[6]);
+
+                if (mFirstFrameOffset + paddingSize > mSize) {
+                    free(mData);
+                    mData = NULL;
+
+                    return false;
+                }
+
+                mSize -= paddingSize;
+            }
+
+            if (extendedFlags & 0x8000) {
+                ALOGV("have crc");
+            }
+        }
+    } else if (header.version_major == 4 && (header.flags & 0x40)) {
+        // Version 2.4 has an optional extended header, that's different
+        // from Version 2.3's...
+
+        if (mSize < 4) {
+            free(mData);
+            mData = NULL;
+
+            return false;
+        }
+
+        size_t ext_size;
+        if (!ParseSyncsafeInteger(mData, &ext_size)) {
+            free(mData);
+            mData = NULL;
+
+            return false;
+        }
+
+        if (ext_size < 6 || ext_size > mSize) {
+            free(mData);
+            mData = NULL;
+
+            return false;
+        }
+
+        mFirstFrameOffset = ext_size;
+    }
+
+    if (header.version_major == 2) {
+        mVersion = ID3_V2_2;
+    } else if (header.version_major == 3) {
+        mVersion = ID3_V2_3;
+    } else {
+        CHECK_EQ(header.version_major, 4);
+        mVersion = ID3_V2_4;
+    }
+
+    return true;
+}
+
+void ID3::removeUnsynchronization() {
+    for (size_t i = 0; i + 1 < mSize; ++i) {
+        if (mData[i] == 0xff && mData[i + 1] == 0x00) {
+            memmove(&mData[i + 1], &mData[i + 2], mSize - i - 2);
+            --mSize;
+        }
+    }
+}
+
+static void WriteSyncsafeInteger(uint8_t *dst, size_t x) {
+    for (size_t i = 0; i < 4; ++i) {
+        dst[3 - i] = (x & 0x7f);
+        x >>= 7;
+    }
+}
+
+bool ID3::removeUnsynchronizationV2_4(bool iTunesHack) {
+    size_t oldSize = mSize;
+
+    size_t offset = 0;
+    while (offset + 10 <= mSize) {
+        i