experimental support for fragmented mp4 playback in nuplayer

cherry picked from change 170999

Change-Id: I407775f0290154ad4961134839a15c9f296424c0
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index 8687fab..1247588 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -104,7 +104,7 @@
 
 LOCAL_SHARED_LIBRARIES := \
 	libstagefright liblog libutils libbinder libgui \
-        libstagefright_foundation libmedia libmedia_native
+        libstagefright_foundation libmedia libmedia_native libcutils
 
 LOCAL_C_INCLUDES:= \
 	frameworks/av/media/libstagefright \
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index a9f0ab2..8e7861e 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -19,6 +19,7 @@
 #include "utils/Log.h"
 
 #include <binder/ProcessState.h>
+#include <cutils/properties.h> // for property_get
 
 #include <media/IStreamSource.h>
 #include <media/mediaplayer.h>
@@ -342,8 +343,13 @@
 
     sp<IStreamSource> source;
 
+    char prop[PROPERTY_VALUE_MAX];
+    bool usemp4 = property_get("media.stagefright.use-mp4source", prop, NULL) &&
+            (!strcmp(prop, "1") || !strcasecmp(prop, "true"));
+
     size_t len = strlen(argv[1]);
-    if (len >= 3 && !strcasecmp(".ts", &argv[1][len - 3])) {
+    if ((!usemp4 && len >= 3 && !strcasecmp(".ts", &argv[1][len - 3])) ||
+        (usemp4 && len >= 4 && !strcasecmp(".mp4", &argv[1][len - 4]))) {
         int fd = open(argv[1], O_RDONLY);
 
         if (fd < 0) {
diff --git a/include/media/stagefright/foundation/hexdump.h b/include/media/stagefright/foundation/hexdump.h
index f6083a9..8360c5a 100644
--- a/include/media/stagefright/foundation/hexdump.h
+++ b/include/media/stagefright/foundation/hexdump.h
@@ -22,7 +22,11 @@
 
 namespace android {
 
-void hexdump(const void *_data, size_t size);
+struct AString;
+
+void hexdump(
+        const void *_data, size_t size,
+        size_t indent = 0, AString *appendTo = NULL);
 
 }  // namespace android
 
diff --git a/media/libmediaplayerservice/nuplayer/Android.mk b/media/libmediaplayerservice/nuplayer/Android.mk
index f97ba57..f469054 100644
--- a/media/libmediaplayerservice/nuplayer/Android.mk
+++ b/media/libmediaplayerservice/nuplayer/Android.mk
@@ -11,6 +11,9 @@
         NuPlayerStreamListener.cpp      \
         RTSPSource.cpp                  \
         StreamingSource.cpp             \
+        mp4/MP4Source.cpp               \
+        mp4/Parser.cpp                  \
+        mp4/TrackFragment.cpp           \
 
 LOCAL_C_INCLUDES := \
 	$(TOP)/frameworks/av/media/libstagefright/httplive            \
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 99569c9..f0c3240 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -128,7 +128,7 @@
     return OK;
 }
 
-sp<MetaData> NuPlayer::GenericSource::getFormat(bool audio) {
+sp<MetaData> NuPlayer::GenericSource::getFormatMeta(bool audio) {
     sp<MediaSource> source = audio ? mAudioTrack.mSource : mVideoTrack.mSource;
 
     if (source == NULL) {
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index aaa5876..e50b855 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -43,7 +43,6 @@
 
     virtual status_t feedMoreTSData();
 
-    virtual sp<MetaData> getFormat(bool audio);
     virtual status_t dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit);
 
     virtual status_t getDuration(int64_t *durationUs);
@@ -53,6 +52,8 @@
 protected:
     virtual ~GenericSource();
 
+    virtual sp<MetaData> getFormatMeta(bool audio);
+
 private:
     struct Track {
         sp<MediaSource> mSource;
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
index 22b8847..1e98f35 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
@@ -81,7 +81,7 @@
     mTSParser = new ATSParser;
 }
 
-sp<MetaData> NuPlayer::HTTPLiveSource::getFormat(bool audio) {
+sp<MetaData> NuPlayer::HTTPLiveSource::getFormatMeta(bool audio) {
     ATSParser::SourceType type =
         audio ? ATSParser::AUDIO : ATSParser::VIDEO;
 
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
index f22af5b..9950a9e 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
@@ -37,7 +37,6 @@
 
     virtual status_t feedMoreTSData();
 
-    virtual sp<MetaData> getFormat(bool audio);
     virtual status_t dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit);
 
     virtual status_t getDuration(int64_t *durationUs);
@@ -47,6 +46,8 @@
 protected:
     virtual ~HTTPLiveSource();
 
+    virtual sp<MetaData> getFormatMeta(bool audio);
+
 private:
     enum Flags {
         // Don't log any URLs.
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index daf60f6..a02732b 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -28,9 +28,11 @@
 #include "RTSPSource.h"
 #include "StreamingSource.h"
 #include "GenericSource.h"
+#include "mp4/MP4Source.h"
 
 #include "ATSParser.h"
 
+#include <cutils/properties.h> // for property_get
 #include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -43,6 +45,9 @@
 
 #include "avc_utils.h"
 
+#include "ESDS.h"
+#include <media/stagefright/Utils.h>
+
 namespace android {
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -81,7 +86,14 @@
 void NuPlayer::setDataSource(const sp<IStreamSource> &source) {
     sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
 
-    msg->setObject("source", new StreamingSource(source));
+    char prop[PROPERTY_VALUE_MAX];
+    if (property_get("media.stagefright.use-mp4source", prop, NULL)
+            && (!strcmp(prop, "1") || !strcasecmp(prop, "true"))) {
+        msg->setObject("source", new MP4Source(source));
+    } else {
+        msg->setObject("source", new StreamingSource(source));
+    }
+
     msg->post();
 }
 
@@ -679,16 +691,16 @@
         return OK;
     }
 
-    sp<MetaData> meta = mSource->getFormat(audio);
+    sp<AMessage> format = mSource->getFormat(audio);
 
-    if (meta == NULL) {
+    if (format == NULL) {
         return -EWOULDBLOCK;
     }
 
     if (!audio) {
-        const char *mime;
-        CHECK(meta->findCString(kKeyMIMEType, &mime));
-        mVideoIsAVC = !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime);
+        AString mime;
+        CHECK(format->findString("mime", &mime));
+        mVideoIsAVC = !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str());
     }
 
     sp<AMessage> notify =
@@ -699,7 +711,7 @@
                        new Decoder(notify, mNativeWindow);
     looper()->registerHandler(*decoder);
 
-    (*decoder)->configure(meta);
+    (*decoder)->configure(format);
 
     int64_t durationUs;
     if (mDriver != NULL && mSource->getDuration(&durationUs) == OK) {
@@ -930,4 +942,19 @@
     }
 }
 
+sp<AMessage> NuPlayer::Source::getFormat(bool audio) {
+    sp<MetaData> meta = getFormatMeta(audio);
+
+    if (meta == NULL) {
+        return NULL;
+    }
+
+    sp<AMessage> msg = new AMessage;
+
+    if(convertMetaDataToMessage(meta, &msg) == OK) {
+        return msg;
+    }
+    return NULL;
+}
+
 }  // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index 25766e0..996806e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -60,14 +60,16 @@
 
     virtual void onMessageReceived(const sp<AMessage> &msg);
 
+public:
+    struct NuPlayerStreamListener;
+    struct Source;
+
 private:
     struct Decoder;
     struct GenericSource;
     struct HTTPLiveSource;
-    struct NuPlayerStreamListener;
     struct Renderer;
     struct RTSPSource;
-    struct Source;
     struct StreamingSource;
 
     enum {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 0e53662..22f699e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -20,15 +20,11 @@
 
 #include "NuPlayerDecoder.h"
 
-#include "ESDS.h"
-
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/ACodec.h>
 #include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MetaData.h>
-#include <media/stagefright/Utils.h>
 
 namespace android {
 
@@ -42,16 +38,24 @@
 NuPlayer::Decoder::~Decoder() {
 }
 
-void NuPlayer::Decoder::configure(const sp<MetaData> &meta) {
+void NuPlayer::Decoder::configure(const sp<AMessage> &format) {
     CHECK(mCodec == NULL);
 
-    const char *mime;
-    CHECK(meta->findCString(kKeyMIMEType, &mime));
+    AString mime;
+    CHECK(format->findString("mime", &mime));
 
     sp<AMessage> notifyMsg =
         new AMessage(kWhatCodecNotify, id());
 
-    sp<AMessage> format = makeFormat(meta);
+    mCSDIndex = 0;
+    for (size_t i = 0;; ++i) {
+        sp<ABuffer> csd;
+        if (!format->findBuffer(StringPrintf("csd-%d", i).c_str(), &csd)) {
+            break;
+        }
+
+        mCSD.push(csd);
+    }
 
     if (mNativeWindow != NULL) {
         format->setObject("native-window", mNativeWindow);
@@ -61,7 +65,7 @@
     // quickly, violating the OpenMAX specs, until that is remedied
     // we need to invest in an extra looper to free the main event
     // queue.
-    bool needDedicatedLooper = !strncasecmp(mime, "video/", 6);
+    bool needDedicatedLooper = !strncasecmp(mime.c_str(), "video/", 6);
 
     mCodec = new ACodec;
 
@@ -100,25 +104,6 @@
     }
 }
 
-sp<AMessage> NuPlayer::Decoder::makeFormat(const sp<MetaData> &meta) {
-    CHECK(mCSD.isEmpty());
-
-    sp<AMessage> msg;
-    CHECK_EQ(convertMetaDataToMessage(meta, &msg), (status_t)OK);
-
-    mCSDIndex = 0;
-    for (size_t i = 0;; ++i) {
-        sp<ABuffer> csd;
-        if (!msg->findBuffer(StringPrintf("csd-%d", i).c_str(), &csd)) {
-            break;
-        }
-
-        mCSD.push(csd);
-    }
-
-    return msg;
-}
-
 void NuPlayer::Decoder::onFillThisBuffer(const sp<AMessage> &msg) {
     sp<AMessage> reply;
     CHECK(msg->findMessage("reply", &reply));
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index 3ab1fcf..a876148 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -30,7 +30,7 @@
     Decoder(const sp<AMessage> &notify,
             const sp<NativeWindowWrapper> &nativeWindow = NULL);
 
-    void configure(const sp<MetaData> &meta);
+    void configure(const sp<AMessage> &format);
 
     void signalFlush();
     void signalResume();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
index 531b29f..66aeff3 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
@@ -34,7 +34,7 @@
     // an error or ERROR_END_OF_STREAM if not.
     virtual status_t feedMoreTSData() = 0;
 
-    virtual sp<MetaData> getFormat(bool audio) = 0;
+    virtual sp<AMessage> getFormat(bool audio);
 
     virtual status_t dequeueAccessUnit(
             bool audio, sp<ABuffer> *accessUnit) = 0;
@@ -54,6 +54,8 @@
 protected:
     virtual ~Source() {}
 
+    virtual sp<MetaData> getFormatMeta(bool audio) { return NULL; }
+
 private:
     DISALLOW_EVIL_CONSTRUCTORS(Source);
 };
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
index c910488..4a704e3 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -95,7 +95,7 @@
     return mFinalResult;
 }
 
-sp<MetaData> NuPlayer::RTSPSource::getFormat(bool audio) {
+sp<MetaData> NuPlayer::RTSPSource::getFormatMeta(bool audio) {
     sp<AnotherPacketSource> source = getSource(audio);
 
     if (source == NULL) {
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.h b/media/libmediaplayerservice/nuplayer/RTSPSource.h
index e11e304..c8409e5 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.h
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.h
@@ -40,7 +40,6 @@
 
     virtual status_t feedMoreTSData();
 
-    virtual sp<MetaData> getFormat(bool audio);
     virtual status_t dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit);
 
     virtual status_t getDuration(int64_t *durationUs);
@@ -52,6 +51,8 @@
 protected:
     virtual ~RTSPSource();
 
+    virtual sp<MetaData> getFormatMeta(bool audio);
+
 private:
     enum {
         kWhatNotify          = 'noti',
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
index 7c9bc5e..b696aa4 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
@@ -107,7 +107,7 @@
     return OK;
 }
 
-sp<MetaData> NuPlayer::StreamingSource::getFormat(bool audio) {
+sp<MetaData> NuPlayer::StreamingSource::getFormatMeta(bool audio) {
     ATSParser::SourceType type =
         audio ? ATSParser::AUDIO : ATSParser::VIDEO;
 
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.h b/media/libmediaplayerservice/nuplayer/StreamingSource.h
index ca00ef9..3971e2a 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.h
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.h
@@ -33,12 +33,13 @@
 
     virtual status_t feedMoreTSData();
 
-    virtual sp<MetaData> getFormat(bool audio);
     virtual status_t dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit);
 
 protected:
     virtual ~StreamingSource();
 
+    virtual sp<MetaData> getFormatMeta(bool audio);
+
 private:
     sp<IStreamSource> mSource;
     status_t mFinalResult;
diff --git a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp
new file mode 100644
index 0000000..25c91e9
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2012 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 "MP4Source.h"
+
+#include "Parser.h"
+#include "../NuPlayerStreamListener.h"
+
+#include <media/IStreamSource.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+struct StreamSource : public Parser::Source {
+    StreamSource(const sp<IStreamSource> &source)
+        : mListener(new NuPlayer::NuPlayerStreamListener(source, 0)),
+          mPosition(0) {
+        mListener->start();
+    }
+
+    virtual ssize_t readAt(off64_t offset, void *data, size_t size) {
+        if (offset < mPosition) {
+            return -EPIPE;
+        }
+
+        while (offset > mPosition) {
+            char buffer[1024];
+            off64_t skipBytes = offset - mPosition;
+            if (skipBytes > sizeof(buffer)) {
+                skipBytes = sizeof(buffer);
+            }
+
+            sp<AMessage> extra;
+            ssize_t n;
+            for (;;) {
+                n = mListener->read(buffer, skipBytes, &extra);
+
+                if (n == -EWOULDBLOCK) {
+                    usleep(10000);
+                    continue;
+                }
+
+                break;
+            }
+
+            ALOGV("skipped %ld bytes at offset %lld", n, mPosition);
+
+            if (n < 0) {
+                return n;
+            }
+
+            mPosition += n;
+        }
+
+        sp<AMessage> extra;
+        size_t total = 0;
+        while (total < size) {
+            ssize_t n = mListener->read(
+                    (uint8_t *)data + total, size - total, &extra);
+
+            if (n == -EWOULDBLOCK) {
+                usleep(10000);
+                continue;
+            } else if (n == 0) {
+                break;
+            } else if (n < 0) {
+                mPosition += total;
+                return n;
+            }
+
+            total += n;
+        }
+
+        ALOGV("read %ld bytes at offset %lld", n, mPosition);
+
+        mPosition += total;
+
+        return total;
+    }
+
+private:
+    sp<NuPlayer::NuPlayerStreamListener> mListener;
+    off64_t mPosition;
+
+    DISALLOW_EVIL_CONSTRUCTORS(StreamSource);
+};
+
+MP4Source::MP4Source(const sp<IStreamSource> &source)
+    : mSource(source),
+      mLooper(new ALooper),
+      mParser(new Parser),
+      mEOS(false) {
+    mLooper->registerHandler(mParser);
+}
+
+MP4Source::~MP4Source() {
+}
+
+void MP4Source::start() {
+    mLooper->start(false /* runOnCallingThread */);
+    mParser->start(new StreamSource(mSource));
+}
+
+status_t MP4Source::feedMoreTSData() {
+    return mEOS ? ERROR_END_OF_STREAM : (status_t)OK;
+}
+
+sp<AMessage> MP4Source::getFormat(bool audio) {
+    return mParser->getFormat(audio);
+}
+
+status_t MP4Source::dequeueAccessUnit(
+        bool audio, sp<ABuffer> *accessUnit) {
+    return mParser->dequeueAccessUnit(audio, accessUnit);
+}
+
+}  // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h
new file mode 100644
index 0000000..57430aa
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2012 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 MP4_SOURCE_H
+#define MP4_SOURCE_H
+
+#include "NuPlayerSource.h"
+
+namespace android {
+
+struct Parser;
+
+struct MP4Source : public NuPlayer::Source {
+    MP4Source(const sp<IStreamSource> &source);
+
+    virtual void start();
+
+    virtual status_t feedMoreTSData();
+
+    virtual sp<AMessage> getFormat(bool audio);
+
+    virtual status_t dequeueAccessUnit(
+            bool audio, sp<ABuffer> *accessUnit);
+
+protected:
+    virtual ~MP4Source();
+
+private:
+    sp<IStreamSource> mSource;
+    sp<ALooper> mLooper;
+    sp<Parser> mParser;
+    bool mEOS;
+
+    DISALLOW_EVIL_CONSTRUCTORS(MP4Source);
+};
+
+}  // namespace android
+
+#endif // MP4_SOURCE_H
diff --git a/media/libmediaplayerservice/nuplayer/mp4/Parser.cpp b/media/libmediaplayerservice/nuplayer/mp4/Parser.cpp
new file mode 100644
index 0000000..b9fe819
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/mp4/Parser.cpp
@@ -0,0 +1,1643 @@
+/*
+ * Copyright (C) 2012 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 "Parser"
+#include <utils/Log.h>
+
+#include "Parser.h"
+#include "TrackFragment.h"
+
+#include "ESDS.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/Utils.h>
+
+#include "../NuPlayerStreamListener.h"
+
+namespace android {
+
+static const char *Fourcc2String(uint32_t fourcc) {
+    static char buffer[5];
+    buffer[4] = '\0';
+    buffer[0] = fourcc >> 24;
+    buffer[1] = (fourcc >> 16) & 0xff;
+    buffer[2] = (fourcc >> 8) & 0xff;
+    buffer[3] = fourcc & 0xff;
+
+    return buffer;
+}
+
+static const char *IndentString(size_t n) {
+    static const char kSpace[] = "                              ";
+    return kSpace + sizeof(kSpace) - 2 * n - 1;
+}
+
+// static
+const Parser::DispatchEntry Parser::kDispatchTable[] = {
+    { FOURCC('m', 'o', 'o', 'v'), 0, NULL },
+    { FOURCC('t', 'r', 'a', 'k'), FOURCC('m', 'o', 'o', 'v'), NULL },
+    { FOURCC('u', 'd', 't', 'a'), FOURCC('t', 'r', 'a', 'k'), NULL },
+    { FOURCC('u', 'd', 't', 'a'), FOURCC('m', 'o', 'o', 'v'), NULL },
+    { FOURCC('m', 'e', 't', 'a'), FOURCC('u', 'd', 't', 'a'), NULL },
+    { FOURCC('i', 'l', 's', 't'), FOURCC('m', 'e', 't', 'a'), NULL },
+
+    { FOURCC('t', 'k', 'h', 'd'), FOURCC('t', 'r', 'a', 'k'),
+        &Parser::parseTrackHeader
+    },
+
+    { FOURCC('m', 'v', 'e', 'x'), FOURCC('m', 'o', 'o', 'v'), NULL },
+
+    { FOURCC('t', 'r', 'e', 'x'), FOURCC('m', 'v', 'e', 'x'),
+        &Parser::parseTrackExtends
+    },
+
+    { FOURCC('e', 'd', 't', 's'), FOURCC('t', 'r', 'a', 'k'), NULL },
+    { FOURCC('m', 'd', 'i', 'a'), FOURCC('t', 'r', 'a', 'k'), NULL },
+
+    { FOURCC('m', 'd', 'h', 'd'), FOURCC('m', 'd', 'i', 'a'),
+        &Parser::parseMediaHeader
+    },
+
+    { FOURCC('h', 'd', 'l', 'r'), FOURCC('m', 'd', 'i', 'a'),
+        &Parser::parseMediaHandler
+    },
+
+    { FOURCC('m', 'i', 'n', 'f'), FOURCC('m', 'd', 'i', 'a'), NULL },
+    { FOURCC('d', 'i', 'n', 'f'), FOURCC('m', 'i', 'n', 'f'), NULL },
+    { FOURCC('s', 't', 'b', 'l'), FOURCC('m', 'i', 'n', 'f'), NULL },
+    { FOURCC('s', 't', 's', 'd'), FOURCC('s', 't', 'b', 'l'), NULL },
+
+    { FOURCC('s', 't', 's', 'z'), FOURCC('s', 't', 'b', 'l'),
+        &Parser::parseSampleSizes },
+
+    { FOURCC('s', 't', 'z', '2'), FOURCC('s', 't', 'b', 'l'),
+        &Parser::parseCompactSampleSizes },
+
+    { FOURCC('s', 't', 's', 'c'), FOURCC('s', 't', 'b', 'l'),
+        &Parser::parseSampleToChunk },
+
+    { FOURCC('s', 't', 'c', 'o'), FOURCC('s', 't', 'b', 'l'),
+        &Parser::parseChunkOffsets },
+
+    { FOURCC('c', 'o', '6', '4'), FOURCC('s', 't', 'b', 'l'),
+        &Parser::parseChunkOffsets64 },
+
+    { FOURCC('a', 'v', 'c', 'C'), FOURCC('a', 'v', 'c', '1'),
+        &Parser::parseAVCCodecSpecificData },
+
+    { FOURCC('e', 's', 'd', 's'), FOURCC('m', 'p', '4', 'a'),
+        &Parser::parseESDSCodecSpecificData },
+
+    { FOURCC('e', 's', 'd', 's'), FOURCC('m', 'p', '4', 'v'),
+        &Parser::parseESDSCodecSpecificData },
+
+    { FOURCC('m', 'd', 'a', 't'), 0, &Parser::parseMediaData },
+
+    { FOURCC('m', 'o', 'o', 'f'), 0, NULL },
+    { FOURCC('t', 'r', 'a', 'f'), FOURCC('m', 'o', 'o', 'f'), NULL },
+
+    { FOURCC('t', 'f', 'h', 'd'), FOURCC('t', 'r', 'a', 'f'),
+        &Parser::parseTrackFragmentHeader
+    },
+    { FOURCC('t', 'r', 'u', 'n'), FOURCC('t', 'r', 'a', 'f'),
+        &Parser::parseTrackFragmentRun
+    },
+
+    { FOURCC('m', 'f', 'r', 'a'), 0, NULL },
+};
+
+struct FileSource : public Parser::Source {
+    FileSource(const char *filename)
+        : mFile(fopen(filename, "rb")) {
+            CHECK(mFile != NULL);
+        }
+
+    virtual ssize_t readAt(off64_t offset, void *data, size_t size) {
+        fseek(mFile, offset, SEEK_SET);
+        return fread(data, 1, size, mFile);
+    }
+
+    private:
+    FILE *mFile;
+
+    DISALLOW_EVIL_CONSTRUCTORS(FileSource);
+};
+
+Parser::Parser()
+    : mBufferPos(0),
+      mSuspended(false) {
+}
+
+Parser::~Parser() {
+}
+
+void Parser::start(const char *filename) {
+    sp<AMessage> msg = new AMessage(kWhatStart, id());
+    msg->setObject("source", new FileSource(filename));
+    msg->post();
+}
+
+void Parser::start(const sp<Source> &source) {
+    sp<AMessage> msg = new AMessage(kWhatStart, id());
+    msg->setObject("source", source);
+    msg->post();
+}
+
+sp<AMessage> Parser::getFormat(bool audio) {
+    sp<AMessage> msg = new AMessage(kWhatGetFormat, id());
+    msg->setInt32("audio", audio);
+
+    sp<AMessage> response;
+    status_t err = msg->postAndAwaitResponse(&response);
+
+    if (err != OK) {
+        return NULL;
+    }
+
+    if (response->findInt32("err", &err) && err != OK) {
+        return NULL;
+    }
+
+    sp<AMessage> format;
+    CHECK(response->findMessage("format", &format));
+
+    ALOGV("returning format %s", format->debugString().c_str());
+    return format;
+}
+
+status_t Parser::dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit) {
+    sp<AMessage> msg = new AMessage(kWhatDequeueAccessUnit, id());
+    msg->setInt32("audio", audio);
+
+    sp<AMessage> response;
+    status_t err = msg->postAndAwaitResponse(&response);
+
+    if (err != OK) {
+        return err;
+    }
+
+    if (response->findInt32("err", &err) && err != OK) {
+        return err;
+    }
+
+    CHECK(response->findBuffer("accessUnit", accessUnit));
+
+    return OK;
+}
+
+ssize_t Parser::findTrack(bool wantAudio) const {
+    for (size_t i = 0; i < mTracks.size(); ++i) {
+        const TrackInfo *info = &mTracks.valueAt(i);
+
+        bool isAudio =
+            info->mMediaHandlerType == FOURCC('s', 'o', 'u', 'n');
+
+        bool isVideo =
+            info->mMediaHandlerType == FOURCC('v', 'i', 'd', 'e');
+
+        if ((wantAudio && isAudio) || (!wantAudio && !isAudio)) {
+            if (info->mSampleDescs.empty()) {
+                break;
+            }
+
+            return i;
+        }
+    }
+
+    return -EWOULDBLOCK;
+}
+
+void Parser::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatStart:
+        {
+            sp<RefBase> obj;
+            CHECK(msg->findObject("source", &obj));
+
+            mSource = static_cast<Source *>(obj.get());
+
+            mBuffer = new ABuffer(512 * 1024);
+            mBuffer->setRange(0, 0);
+
+            enter(0, 0);
+
+            (new AMessage(kWhatProceed, id()))->post();
+            break;
+        }
+
+        case kWhatProceed:
+        {
+            CHECK(!mSuspended);
+
+            status_t err = onProceed();
+
+            if (err == OK) {
+                if (!mSuspended) {
+                    msg->post();
+                }
+            } else if (err != -EAGAIN) {
+                ALOGE("onProceed returned error %d", err);
+            }
+
+            break;
+        }
+
+        case kWhatReadMore:
+        {
+            size_t needed;
+            CHECK(msg->findSize("needed", &needed));
+
+            memmove(mBuffer->base(), mBuffer->data(), mBuffer->size());
+            mBufferPos += mBuffer->offset();
+            mBuffer->setRange(0, mBuffer->size());
+
+            size_t maxBytesToRead = mBuffer->capacity() - mBuffer->size();
+            CHECK_GE(maxBytesToRead, needed);
+
+            ssize_t n = mSource->readAt(
+                    mBufferPos + mBuffer->size(),
+                    mBuffer->data() + mBuffer->size(), needed);
+
+            if (n < (ssize_t)needed) {
+                ALOGI("%s", "Reached EOF");
+            } else {
+                mBuffer->setRange(0, mBuffer->size() + n);
+                (new AMessage(kWhatProceed, id()))->post();
+            }
+
+            break;
+        }
+
+        case kWhatGetFormat:
+        {
+            int32_t wantAudio;
+            CHECK(msg->findInt32("audio", &wantAudio));
+
+            status_t err = -EWOULDBLOCK;
+            sp<AMessage> response = new AMessage;
+
+            ssize_t trackIndex = findTrack(wantAudio);
+
+            if (trackIndex < 0) {
+                err = trackIndex;
+            } else {
+                TrackInfo *info = &mTracks.editValueAt(trackIndex);
+
+                response->setMessage(
+                        "format", info->mSampleDescs.itemAt(0).mFormat);
+
+                err = OK;
+            }
+
+            response->setInt32("err", err);
+
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatDequeueAccessUnit:
+        {
+            int32_t wantAudio;
+            CHECK(msg->findInt32("audio", &wantAudio));
+
+            status_t err = -EWOULDBLOCK;
+            sp<AMessage> response = new AMessage;
+
+            ssize_t trackIndex = findTrack(wantAudio);
+
+            if (trackIndex < 0) {
+                err = trackIndex;
+            } else {
+                sp<ABuffer> accessUnit;
+                err = onDequeueAccessUnit(trackIndex, &accessUnit);
+
+                if (err == OK) {
+                    response->setBuffer("accessUnit", accessUnit);
+                }
+            }
+
+            response->setInt32("err", err);
+
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            response->postReply(replyID);
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+status_t Parser::onProceed() {
+    status_t err;
+
+    if ((err = need(8)) != OK) {
+        return err;
+    }
+
+    uint64_t size = readU32(0);
+    uint32_t type = readU32(4);
+
+    size_t offset = 8;
+
+    if (size == 1) {
+        if ((err = need(16)) != OK) {
+            return err;
+        }
+
+        size = readU64(offset);
+        offset += 8;
+    }
+
+    uint8_t userType[16];
+
+    if (type == FOURCC('u', 'u', 'i', 'd')) {
+        if ((err = need(offset + 16)) != OK) {
+            return err;
+        }
+
+        memcpy(userType, mBuffer->data() + offset, 16);
+        offset += 16;
+    }
+
+    CHECK(!mStack.isEmpty());
+    uint32_t ptype = mStack.itemAt(mStack.size() - 1).mType;
+
+    static const size_t kNumDispatchers =
+        sizeof(kDispatchTable) / sizeof(kDispatchTable[0]);
+
+    size_t i;
+    for (i = 0; i < kNumDispatchers; ++i) {
+        if (kDispatchTable[i].mType == type
+                && kDispatchTable[i].mParentType == ptype) {
+            break;
+        }
+    }
+
+    // SampleEntry boxes are container boxes that start with a variable
+    // amount of data depending on the media handler type.
+    // We don't look inside 'hint' type SampleEntry boxes.
+
+    bool isSampleEntryBox =
+        (ptype == FOURCC('s', 't', 's', 'd'))
+        && editTrack(mCurrentTrackID)->mMediaHandlerType
+        != FOURCC('h', 'i', 'n', 't');
+
+    if ((i < kNumDispatchers && kDispatchTable[i].mHandler == 0)
+            || isSampleEntryBox || ptype == FOURCC('i', 'l', 's', 't')) {
+        // This is a container box.
+        if (type == FOURCC('m', 'e', 't', 'a')) {
+            if ((err = need(offset + 4)) < OK) {
+                return err;
+            }
+
+            if (readU32(offset) != 0) {
+                return -EINVAL;
+            }
+
+            offset += 4;
+        } else if (type == FOURCC('s', 't', 's', 'd')) {
+            if ((err = need(offset + 8)) < OK) {
+                return err;
+            }
+
+            if (readU32(offset) != 0) {
+                return -EINVAL;
+            }
+
+            if (readU32(offset + 4) == 0) {
+                // We need at least some entries.
+                return -EINVAL;
+            }
+
+            offset += 8;
+        } else if (isSampleEntryBox) {
+            size_t headerSize;
+
+            switch (editTrack(mCurrentTrackID)->mMediaHandlerType) {
+                case FOURCC('v', 'i', 'd', 'e'):
+                {
+                    // 8 bytes SampleEntry + 70 bytes VisualSampleEntry
+                    headerSize = 78;
+                    break;
+                }
+
+                case FOURCC('s', 'o', 'u', 'n'):
+                {
+                    // 8 bytes SampleEntry + 20 bytes AudioSampleEntry
+                    headerSize = 28;
+                    break;
+                }
+
+                case FOURCC('m', 'e', 't', 'a'):
+                {
+                    headerSize = 8;  // 8 bytes SampleEntry
+                    break;
+                }
+
+                default:
+                    TRESPASS();
+            }
+
+            if (offset + headerSize > size) {
+                return -EINVAL;
+            }
+
+            if ((err = need(offset + headerSize)) != OK) {
+                return err;
+            }
+
+            switch (editTrack(mCurrentTrackID)->mMediaHandlerType) {
+                case FOURCC('v', 'i', 'd', 'e'):
+                {
+                    err = parseVisualSampleEntry(
+                            type, offset, offset + headerSize);
+                    break;
+                }
+
+                case FOURCC('s', 'o', 'u', 'n'):
+                {
+                    err = parseAudioSampleEntry(
+                            type, offset, offset + headerSize);
+                    break;
+                }
+
+                case FOURCC('m', 'e', 't', 'a'):
+                {
+                    err = OK;
+                    break;
+                }
+
+                default:
+                    TRESPASS();
+            }
+
+            if (err != OK) {
+                return err;
+            }
+
+            offset += headerSize;
+        }
+
+        skip(offset);
+
+        ALOGV("%sentering box of type '%s'",
+                IndentString(mStack.size()), Fourcc2String(type));
+
+        enter(type, size - offset);
+    } else {
+        if (!fitsContainer(size)) {
+            return -EINVAL;
+        }
+
+        if (i < kNumDispatchers && kDispatchTable[i].mHandler != 0) {
+            // We have a handler for this box type.
+
+            if ((err = need(size)) != OK) {
+                return err;
+            }
+
+            ALOGV("%sparsing box of type '%s'",
+                    IndentString(mStack.size()), Fourcc2String(type));
+
+            if ((err = (this->*kDispatchTable[i].mHandler)(
+                            type, offset, size)) != OK) {
+                return err;
+            }
+        } else {
+            // Unknown box type
+
+            ALOGV("%sskipping box of type '%s', size %llu",
+                    IndentString(mStack.size()),
+                    Fourcc2String(type), size);
+
+        }
+
+        skip(size);
+    }
+
+    return OK;
+}
+
+// static
+int Parser::CompareSampleLocation(
+        const SampleInfo &sample, const MediaDataInfo &mdatInfo) {
+    if (sample.mOffset + sample.mSize < mdatInfo.mOffset) {
+        return -1;
+    }
+
+    if (sample.mOffset >= mdatInfo.mOffset + mdatInfo.mBuffer->size()) {
+        return 1;
+    }
+
+    // Otherwise make sure the sample is completely contained within this
+    // media data block.
+
+    CHECK_GE(sample.mOffset, mdatInfo.mOffset);
+
+    CHECK_LE(sample.mOffset + sample.mSize,
+             mdatInfo.mOffset + mdatInfo.mBuffer->size());
+
+    return 0;
+}
+
+void Parser::resumeIfNecessary() {
+    if (!mSuspended) {
+        return;
+    }
+
+    ALOGI("resuming.");
+
+    mSuspended = false;
+    (new AMessage(kWhatProceed, id()))->post();
+}
+
+status_t Parser::getSample(
+        TrackInfo *info, sp<TrackFragment> *fragment, SampleInfo *sampleInfo) {
+    for (;;) {
+        if (info->mFragments.empty()) {
+            resumeIfNecessary();
+            return -EWOULDBLOCK;
+        }
+
+        *fragment = *info->mFragments.begin();
+
+        status_t err = (*fragment)->getSample(sampleInfo);
+
+        if (err == OK) {
+            return OK;
+        } else if (err != ERROR_END_OF_STREAM) {
+            return err;
+        }
+
+        // Really, end of this fragment...
+
+        info->mFragments.erase(info->mFragments.begin());
+    }
+}
+
+status_t Parser::onDequeueAccessUnit(
+        size_t trackIndex, sp<ABuffer> *accessUnit) {
+    TrackInfo *info = &mTracks.editValueAt(trackIndex);
+
+    sp<TrackFragment> fragment;
+    SampleInfo sampleInfo;
+    status_t err = getSample(info, &fragment, &sampleInfo);
+
+    if (err == -EWOULDBLOCK) {
+        resumeIfNecessary();
+        return err;
+    } else if (err != OK) {
+        return err;
+    }
+
+    err = -EWOULDBLOCK;
+
+    bool checkDroppable = false;
+
+    for (size_t i = 0; i < mMediaData.size(); ++i) {
+        const MediaDataInfo &mdatInfo = mMediaData.itemAt(i);
+
+        int cmp = CompareSampleLocation(sampleInfo, mdatInfo);
+
+        if (cmp < 0) {
+            return -EPIPE;
+        } else if (cmp == 0) {
+            if (i > 0) {
+                checkDroppable = true;
+            }
+
+            err = makeAccessUnit(info, sampleInfo, mdatInfo, accessUnit);
+            break;
+        }
+    }
+
+    if (err != OK) {
+        return err;
+    }
+
+    fragment->advance();
+
+    if (!mMediaData.empty() && checkDroppable) {
+        size_t numDroppable = 0;
+        bool done = false;
+
+        for (size_t i = 0; !done && i < mMediaData.size(); ++i) {
+            const MediaDataInfo &mdatInfo = mMediaData.itemAt(i);
+
+            for (size_t j = 0; j < mTracks.size(); ++j) {
+                TrackInfo *info = &mTracks.editValueAt(j);
+
+                sp<TrackFragment> fragment;
+                SampleInfo sampleInfo;
+                err = getSample(info, &fragment, &sampleInfo);
+
+                if (err != OK) {
+                    done = true;
+                    break;
+                }
+
+                int cmp = CompareSampleLocation(sampleInfo, mdatInfo);
+
+                if (cmp <= 0) {
+                    done = true;
+                    break;
+                }
+            }
+
+            if (!done) {
+                ++numDroppable;
+            }
+        }
+
+        if (numDroppable > 0) {
+            mMediaData.removeItemsAt(0, numDroppable);
+
+            if (mMediaData.size() < 5) {
+                resumeIfNecessary();
+            }
+        }
+    }
+
+    return err;
+}
+
+static size_t parseNALSize(size_t nalLengthSize, const uint8_t *data) {
+    switch (nalLengthSize) {
+        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.
+    TRESPASS();
+
+    return 0;
+}
+
+status_t Parser::makeAccessUnit(
+        TrackInfo *info,
+        const SampleInfo &sample,
+        const MediaDataInfo &mdatInfo,
+        sp<ABuffer> *accessUnit) {
+    if (sample.mSampleDescIndex < 1
+            || sample.mSampleDescIndex > info->mSampleDescs.size()) {
+        return ERROR_MALFORMED;
+    }
+
+    int64_t presentationTimeUs =
+        1000000ll * sample.mPresentationTime / info->mMediaTimeScale;
+
+    const SampleDescription &sampleDesc =
+        info->mSampleDescs.itemAt(sample.mSampleDescIndex - 1);
+
+    size_t nalLengthSize;
+    if (!sampleDesc.mFormat->findSize("nal-length-size", &nalLengthSize)) {
+        *accessUnit = new ABuffer(sample.mSize);
+
+        memcpy((*accessUnit)->data(),
+               mdatInfo.mBuffer->data() + (sample.mOffset - mdatInfo.mOffset),
+               sample.mSize);
+
+        (*accessUnit)->meta()->setInt64("timeUs", presentationTimeUs);
+        return OK;
+    }
+
+    const uint8_t *srcPtr =
+        mdatInfo.mBuffer->data() + (sample.mOffset - mdatInfo.mOffset);
+
+    for (int i = 0; i < 2 ; ++i) {
+        size_t srcOffset = 0;
+        size_t dstOffset = 0;
+
+        while (srcOffset < sample.mSize) {
+            if (srcOffset + nalLengthSize > sample.mSize) {
+                return ERROR_MALFORMED;
+            }
+
+            size_t nalSize = parseNALSize(nalLengthSize, &srcPtr[srcOffset]);
+            srcOffset += nalLengthSize;
+
+            if (srcOffset + nalSize > sample.mSize) {
+                return ERROR_MALFORMED;
+            }
+
+            if (i == 1) {
+                memcpy((*accessUnit)->data() + dstOffset,
+                       "\x00\x00\x00\x01",
+                       4);
+
+                memcpy((*accessUnit)->data() + dstOffset + 4,
+                       srcPtr + srcOffset,
+                       nalSize);
+            }
+
+            srcOffset += nalSize;
+            dstOffset += nalSize + 4;
+        }
+
+        if (i == 0) {
+            (*accessUnit) = new ABuffer(dstOffset);
+            (*accessUnit)->meta()->setInt64(
+                    "timeUs", presentationTimeUs);
+        }
+    }
+
+    return OK;
+}
+
+status_t Parser::need(size_t size) {
+    if (!fitsContainer(size)) {
+        return -EINVAL;
+    }
+
+    if (size <= mBuffer->size()) {
+        return OK;
+    }
+
+    sp<AMessage> msg = new AMessage(kWhatReadMore, id());
+    msg->setSize("needed", size - mBuffer->size());
+    msg->post();
+
+    // ALOGV("need(%d) returning -EAGAIN, only have %d", size, mBuffer->size());
+
+    return -EAGAIN;
+}
+
+void Parser::enter(uint32_t type, uint64_t size) {
+    Container container;
+    container.mType = type;
+    container.mExtendsToEOF = (size == 0);
+    container.mBytesRemaining = size;
+
+    mStack.push(container);
+}
+
+bool Parser::fitsContainer(uint64_t size) const {
+    CHECK(!mStack.isEmpty());
+    const Container &container = mStack.itemAt(mStack.size() - 1);
+
+    return container.mExtendsToEOF || size <= container.mBytesRemaining;
+}
+
+uint16_t Parser::readU16(size_t offset) {
+    CHECK_LE(offset + 2, mBuffer->size());
+
+    const uint8_t *ptr = mBuffer->data() + offset;
+    return (ptr[0] << 8) | ptr[1];
+}
+
+uint32_t Parser::readU32(size_t offset) {
+    CHECK_LE(offset + 4, mBuffer->size());
+
+    const uint8_t *ptr = mBuffer->data() + offset;
+    return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
+}
+
+uint64_t Parser::readU64(size_t offset) {
+    return (((uint64_t)readU32(offset)) << 32) | readU32(offset + 4);
+}
+
+void Parser::skip(off_t distance) {
+    CHECK(!mStack.isEmpty());
+    for (size_t i = mStack.size(); i-- > 0;) {
+        Container *container = &mStack.editItemAt(i);
+        if (!container->mExtendsToEOF) {
+            CHECK_LE(distance, (off_t)container->mBytesRemaining);
+
+            container->mBytesRemaining -= distance;
+
+            if (container->mBytesRemaining == 0) {
+                ALOGV("%sleaving box of type '%s'",
+                        IndentString(mStack.size() - 1),
+                        Fourcc2String(container->mType));
+
+#if 0
+                if (container->mType == FOURCC('s', 't', 's', 'd')) {
+                    TrackInfo *trackInfo = editTrack(mCurrentTrackID);
+                    for (size_t i = 0;
+                            i < trackInfo->mSampleDescs.size(); ++i) {
+                        ALOGI("format #%d: %s",
+                              i,
+                              trackInfo->mSampleDescs.itemAt(i)
+                                .mFormat->debugString().c_str());
+                    }
+                }
+#endif
+
+                if (container->mType == FOURCC('s', 't', 'b', 'l')) {
+                    TrackInfo *trackInfo = editTrack(mCurrentTrackID);
+
+                    trackInfo->mStaticFragment->signalCompletion();
+
+                    CHECK(trackInfo->mFragments.empty());
+                    trackInfo->mFragments.push_back(trackInfo->mStaticFragment);
+                    trackInfo->mStaticFragment.clear();
+                } else if (container->mType == FOURCC('t', 'r', 'a', 'f')) {
+                    TrackInfo *trackInfo =
+                        editTrack(mTrackFragmentHeaderInfo.mTrackID);
+
+                    const sp<TrackFragment> &fragment =
+                        *--trackInfo->mFragments.end();
+
+                    static_cast<DynamicTrackFragment *>(
+                            fragment.get())->signalCompletion();
+                }
+
+                container = NULL;
+                mStack.removeItemsAt(i);
+            }
+        }
+    }
+
+    if (distance < (off_t)mBuffer->size()) {
+        mBuffer->setRange(mBuffer->offset() + distance, mBuffer->size() - distance);
+        mBufferPos += distance;
+        return;
+    }
+
+    mBuffer->setRange(0, 0);
+    mBufferPos += distance;
+}
+
+status_t Parser::parseTrackHeader(
+        uint32_t type, size_t offset, uint64_t size) {
+    if (offset + 4 > size) {
+        return -EINVAL;
+    }
+
+    uint32_t flags = readU32(offset);
+
+    uint32_t version = flags >> 24;
+    flags &= 0xffffff;
+
+    uint32_t trackID;
+    uint64_t duration;
+
+    if (version == 1) {
+        if (offset + 36 > size) {
+            return -EINVAL;
+        }
+
+        trackID = readU32(offset + 20);
+        duration = readU64(offset + 28);
+
+        offset += 36;
+    } else if (version == 0) {
+        if (offset + 24 > size) {
+            return -EINVAL;
+        }
+
+        trackID = readU32(offset + 12);
+        duration = readU32(offset + 20);
+
+        offset += 24;
+    } else {
+        return -EINVAL;
+    }
+
+    TrackInfo *info = editTrack(trackID, true /* createIfNecessary */);
+    info->mFlags = flags;
+    info->mDuration = duration;
+
+    info->mStaticFragment = new StaticTrackFragment;
+
+    mCurrentTrackID = trackID;
+
+    return OK;
+}
+
+status_t Parser::parseMediaHeader(
+        uint32_t type, size_t offset, uint64_t size) {
+    if (offset + 4 > size) {
+        return -EINVAL;
+    }
+
+    uint32_t versionAndFlags = readU32(offset);
+
+    if (versionAndFlags & 0xffffff) {
+        return ERROR_MALFORMED;
+    }
+
+    uint32_t version = versionAndFlags >> 24;
+
+    TrackInfo *info = editTrack(mCurrentTrackID);
+
+    if (version == 1) {
+        if (offset + 4 + 32 > size) {
+            return -EINVAL;
+        }
+        info->mMediaTimeScale = U32_AT(mBuffer->data() + offset + 20);
+    } else if (version == 0) {
+        if (offset + 4 + 20 > size) {
+            return -EINVAL;
+        }
+        info->mMediaTimeScale = U32_AT(mBuffer->data() + offset + 12);
+    } else {
+        return ERROR_MALFORMED;
+    }
+
+    return OK;
+}
+
+status_t Parser::parseMediaHandler(
+        uint32_t type, size_t offset, uint64_t size) {
+    if (offset + 12 > size) {
+        return -EINVAL;
+    }
+
+    if (readU32(offset) != 0) {
+        return -EINVAL;
+    }
+
+    uint32_t handlerType = readU32(offset + 8);
+
+    switch (handlerType) {
+        case FOURCC('v', 'i', 'd', 'e'):
+        case FOURCC('s', 'o', 'u', 'n'):
+        case FOURCC('h', 'i', 'n', 't'):
+        case FOURCC('m', 'e', 't', 'a'):
+            break;
+
+        default:
+            return -EINVAL;
+    }
+
+    editTrack(mCurrentTrackID)->mMediaHandlerType = handlerType;
+
+    return OK;
+}
+
+status_t Parser::parseVisualSampleEntry(
+        uint32_t type, size_t offset, uint64_t size) {
+    if (offset + 78 > size) {
+        return -EINVAL;
+    }
+
+    TrackInfo *trackInfo = editTrack(mCurrentTrackID);
+
+    trackInfo->mSampleDescs.push();
+    SampleDescription *sampleDesc =
+        &trackInfo->mSampleDescs.editItemAt(
+                trackInfo->mSampleDescs.size() - 1);
+
+    sampleDesc->mType = type;
+    sampleDesc->mDataRefIndex = readU16(offset + 6);
+
+    sp<AMessage> format = new AMessage;
+
+    switch (type) {
+        case FOURCC('a', 'v', 'c', '1'):
+            format->setString("mime", MEDIA_MIMETYPE_VIDEO_AVC);
+            break;
+        case FOURCC('m', 'p', '4', 'v'):
+            format->setString("mime", MEDIA_MIMETYPE_VIDEO_MPEG4);
+            break;
+        default:
+            format->setString("mime", "application/octet-stream");
+            break;
+    }
+
+    format->setInt32("width", readU16(offset + 8 + 16));
+    format->setInt32("height", readU16(offset + 8 + 18));
+
+    sampleDesc->mFormat = format;
+
+    return OK;
+}
+
+status_t Parser::parseAudioSampleEntry(
+        uint32_t type, size_t offset, uint64_t size) {
+    if (offset + 28 > size) {
+        return -EINVAL;
+    }
+
+    TrackInfo *trackInfo = editTrack(mCurrentTrackID);
+
+    trackInfo->mSampleDescs.push();
+    SampleDescription *sampleDesc =
+        &trackInfo->mSampleDescs.editItemAt(
+                trackInfo->mSampleDescs.size() - 1);
+
+    sampleDesc->mType = type;
+    sampleDesc->mDataRefIndex = readU16(offset + 6);
+
+    sp<AMessage> format = new AMessage;
+
+    format->setInt32("channel-count", readU16(offset + 8 + 8));
+    format->setInt32("sample-size", readU16(offset + 8 + 10));
+    format->setInt32("sample-rate", readU32(offset + 8 + 16) / 65536.0f);
+
+    switch (type) {
+        case FOURCC('m', 'p', '4', 'a'):
+            format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC);
+            break;
+        case FOURCC('s', 'a', 'm', 'r'):
+            format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_NB);
+            format->setInt32("channel-count", 1);
+            format->setInt32("sample-rate", 8000);
+            break;
+        case FOURCC('s', 'a', 'w', 'b'):
+            format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_WB);
+            format->setInt32("channel-count", 1);
+            format->setInt32("sample-rate", 16000);
+            break;
+        default:
+            format->setString("mime", "application/octet-stream");
+            break;
+    }
+
+    sampleDesc->mFormat = format;
+
+    return OK;
+}
+
+static void addCodecSpecificData(
+        const sp<AMessage> &format, int32_t index,
+        const void *data, size_t size,
+        bool insertStartCode = false) {
+    sp<ABuffer> csd = new ABuffer(insertStartCode ? size + 4 : size);
+
+    memcpy(csd->data() + (insertStartCode ? 4 : 0), data, size);
+
+    if (insertStartCode) {
+        memcpy(csd->data(), "\x00\x00\x00\x01", 4);
+    }
+
+    csd->meta()->setInt32("csd", true);
+    csd->meta()->setInt64("timeUs", 0ll);
+
+    format->setBuffer(StringPrintf("csd-%d", index).c_str(), csd);
+}
+
+status_t Parser::parseSampleSizes(
+        uint32_t type, size_t offset, uint64_t size) {
+    return editTrack(mCurrentTrackID)->mStaticFragment->parseSampleSizes(
+            this, type, offset, size);
+}
+
+status_t Parser::parseCompactSampleSizes(
+        uint32_t type, size_t offset, uint64_t size) {
+    return editTrack(mCurrentTrackID)->mStaticFragment->parseCompactSampleSizes(
+            this, type, offset, size);
+}
+
+status_t Parser::parseSampleToChunk(
+        uint32_t type, size_t offset, uint64_t size) {
+    return editTrack(mCurrentTrackID)->mStaticFragment->parseSampleToChunk(
+            this, type, offset, size);
+}
+
+status_t Parser::parseChunkOffsets(
+        uint32_t type, size_t offset, uint64_t size) {
+    return editTrack(mCurrentTrackID)->mStaticFragment->parseChunkOffsets(
+            this, type, offset, size);
+}
+
+status_t Parser::parseChunkOffsets64(
+        uint32_t type, size_t offset, uint64_t size) {
+    return editTrack(mCurrentTrackID)->mStaticFragment->parseChunkOffsets64(
+            this, type, offset, size);
+}
+
+status_t Parser::parseAVCCodecSpecificData(
+        uint32_t type, size_t offset, uint64_t size) {
+    TrackInfo *trackInfo = editTrack(mCurrentTrackID);
+
+    SampleDescription *sampleDesc =
+        &trackInfo->mSampleDescs.editItemAt(
+                trackInfo->mSampleDescs.size() - 1);
+
+    if (sampleDesc->mType != FOURCC('a', 'v', 'c', '1')) {
+        return -EINVAL;
+    }
+
+    const uint8_t *ptr = mBuffer->data() + offset;
+
+    size -= offset;
+    offset = 0;
+
+    if (size < 7 || ptr[0] != 0x01) {
+        return ERROR_MALFORMED;
+    }
+
+    sampleDesc->mFormat->setSize("nal-length-size", 1 + (ptr[4] & 3));
+
+    size_t numSPS = ptr[5] & 31;
+
+    ptr += 6;
+    size -= 6;
+
+    for (size_t i = 0; i < numSPS; ++i) {
+        if (size < 2) {
+            return ERROR_MALFORMED;
+        }
+
+        size_t length = U16_AT(ptr);
+
+        ptr += 2;
+        size -= 2;
+
+        if (size < length) {
+            return ERROR_MALFORMED;
+        }
+
+        addCodecSpecificData(
+                sampleDesc->mFormat, i, ptr, length,
+                true /* insertStartCode */);
+
+        ptr += length;
+        size -= length;
+    }
+
+    if (size < 1) {
+        return ERROR_MALFORMED;
+    }
+
+    size_t numPPS = *ptr;
+    ++ptr;
+    --size;
+
+    for (size_t i = 0; i < numPPS; ++i) {
+        if (size < 2) {
+            return ERROR_MALFORMED;
+        }
+
+        size_t length = U16_AT(ptr);
+
+        ptr += 2;
+        size -= 2;
+
+        if (size < length) {
+            return ERROR_MALFORMED;
+        }
+
+        addCodecSpecificData(
+                sampleDesc->mFormat, numSPS + i, ptr, length,
+                true /* insertStartCode */);
+
+        ptr += length;
+        size -= length;
+    }
+
+    return OK;
+}
+
+status_t Parser::parseESDSCodecSpecificData(
+        uint32_t type, size_t offset, uint64_t size) {
+    TrackInfo *trackInfo = editTrack(mCurrentTrackID);
+
+    SampleDescription *sampleDesc =
+        &trackInfo->mSampleDescs.editItemAt(
+                trackInfo->mSampleDescs.size() - 1);
+
+    if (sampleDesc->mType != FOURCC('m', 'p', '4', 'a')
+            && sampleDesc->mType != FOURCC('m', 'p', '4', 'v')) {
+        return -EINVAL;
+    }
+
+    const uint8_t *ptr = mBuffer->data() + offset;
+
+    size -= offset;
+    offset = 0;
+
+    if (size < 4) {
+        return -EINVAL;
+    }
+
+    if (U32_AT(ptr) != 0) {
+        return -EINVAL;
+    }
+
+    ptr += 4;
+    size -=4;
+
+    ESDS esds(ptr, size);
+
+    uint8_t objectTypeIndication;
+    if (esds.getObjectTypeIndication(&objectTypeIndication) != OK) {
+        return ERROR_MALFORMED;
+    }
+
+    const uint8_t *csd;
+    size_t csd_size;
+    if (esds.getCodecSpecificInfo(
+                (const void **)&csd, &csd_size) != OK) {
+        return ERROR_MALFORMED;
+    }
+
+    addCodecSpecificData(sampleDesc->mFormat, 0, csd, csd_size);
+
+    if (sampleDesc->mType != FOURCC('m', 'p', '4', 'a')) {
+        return OK;
+    }
+
+    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;
+    }
+
+    uint32_t objectType = csd[0] >> 3;
+
+    if (objectType == 31) {
+        return ERROR_UNSUPPORTED;
+    }
+
+    uint32_t freqIndex = (csd[0] & 7) << 1 | (csd[1] >> 7);
+    int32_t sampleRate = 0;
+    int32_t numChannels = 0;
+    if (freqIndex == 15) {
+        if (csd_size < 5) {
+            return ERROR_MALFORMED;
+        }
+
+        sampleRate = (csd[1] & 0x7f) << 17
+                        | csd[2] << 9
+                        | csd[3] << 1
+                        | (csd[4] >> 7);
+
+        numChannels = (csd[4] >> 3) & 15;
+    } else {
+        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];
+        numChannels = (csd[1] >> 3) & 15;
+    }
+
+    if (numChannels == 0) {
+        return ERROR_UNSUPPORTED;
+    }
+
+    sampleDesc->mFormat->setInt32("sample-rate", sampleRate);
+    sampleDesc->mFormat->setInt32("channel-count", numChannels);
+
+    return OK;
+}
+
+status_t Parser::parseMediaData(
+        uint32_t type, size_t offset, uint64_t size) {
+    ALOGV("skipping 'mdat' chunk at offsets 0x%08lx-0x%08llx.",
+          mBufferPos + offset, mBufferPos + size);
+
+    sp<ABuffer> buffer = new ABuffer(size - offset);
+    memcpy(buffer->data(), mBuffer->data() + offset, size - offset);
+
+    mMediaData.push();
+    MediaDataInfo *info = &mMediaData.editItemAt(mMediaData.size() - 1);
+    info->mBuffer = buffer;
+    info->mOffset = mBufferPos + offset;
+
+    if (mMediaData.size() > 10) {
+        ALOGI("suspending for now.");
+        mSuspended = true;
+    }
+
+    return OK;
+}
+
+status_t Parser::parseTrackExtends(
+        uint32_t type, size_t offset, uint64_t size) {
+    if (offset + 24 > size) {
+        return -EINVAL;
+    }
+
+    if (readU32(offset) != 0) {
+        return -EINVAL;
+    }
+
+    uint32_t trackID = readU32(offset + 4);
+
+    TrackInfo *info = editTrack(trackID, true /* createIfNecessary */);
+    info->mDefaultSampleDescriptionIndex = readU32(offset + 8);
+    info->mDefaultSampleDuration = readU32(offset + 12);
+    info->mDefaultSampleSize = readU32(offset + 16);
+    info->mDefaultSampleFlags = readU32(offset + 20);
+
+    return OK;
+}
+
+Parser::TrackInfo *Parser::editTrack(
+        uint32_t trackID, bool createIfNecessary) {
+    ssize_t i = mTracks.indexOfKey(trackID);
+
+    if (i >= 0) {
+        return &mTracks.editValueAt(i);
+    }
+
+    if (!createIfNecessary) {
+        return NULL;
+    }
+
+    TrackInfo info;
+    info.mTrackID = trackID;
+    info.mFlags = 0;
+    info.mDuration = 0xffffffff;
+    info.mMediaTimeScale = 0;
+    info.mMediaHandlerType = 0;
+    info.mDefaultSampleDescriptionIndex = 0;
+    info.mDefaultSampleDuration = 0;
+    info.mDefaultSampleSize = 0;
+    info.mDefaultSampleFlags = 0;
+
+    info.mDecodingTime = 0;
+
+    mTracks.add(trackID, info);
+    return &mTracks.editValueAt(mTracks.indexOfKey(trackID));
+}
+
+status_t Parser::parseTrackFragmentHeader(
+        uint32_t type, size_t offset, uint64_t size) {
+    if (offset + 8 > size) {
+        return -EINVAL;
+    }
+
+    uint32_t flags = readU32(offset);
+
+    if (flags & 0xff000000) {
+        return -EINVAL;
+    }
+
+    mTrackFragmentHeaderInfo.mFlags = flags;
+
+    mTrackFragmentHeaderInfo.mTrackID = readU32(offset + 4);
+    offset += 8;
+
+    if (flags & TrackFragmentHeaderInfo::kBaseDataOffsetPresent) {
+        if (offset + 8 > size) {
+            return -EINVAL;
+        }
+
+        mTrackFragmentHeaderInfo.mBaseDataOffset = readU64(offset);
+        offset += 8;
+    }
+
+    if (flags & TrackFragmentHeaderInfo::kSampleDescriptionIndexPresent) {
+        if (offset + 4 > size) {
+            return -EINVAL;
+        }
+
+        mTrackFragmentHeaderInfo.mSampleDescriptionIndex = readU32(offset);
+        offset += 4;
+    }
+
+    if (flags & TrackFragmentHeaderInfo::kDefaultSampleDurationPresent) {
+        if (offset + 4 > size) {
+            return -EINVAL;
+        }
+
+        mTrackFragmentHeaderInfo.mDefaultSampleDuration = readU32(offset);
+        offset += 4;
+    }
+
+    if (flags & TrackFragmentHeaderInfo::kDefaultSampleSizePresent) {
+        if (offset + 4 > size) {
+            return -EINVAL;
+        }
+
+        mTrackFragmentHeaderInfo.mDefaultSampleSize = readU32(offset);
+        offset += 4;
+    }
+
+    if (flags & TrackFragmentHeaderInfo::kDefaultSampleFlagsPresent) {
+        if (offset + 4 > size) {
+            return -EINVAL;
+        }
+
+        mTrackFragmentHeaderInfo.mDefaultSampleFlags = readU32(offset);
+        offset += 4;
+    }
+
+    if (!(flags & TrackFragmentHeaderInfo::kBaseDataOffsetPresent)) {
+        CHECK(!mStack.isEmpty());
+
+        // This should point to the start of the data inside the 'mdat' box
+        // following the current 'moof' box.
+
+        mTrackFragmentHeaderInfo.mBaseDataOffset =
+            mBufferPos + mStack.itemAt(mStack.size() - 1).mBytesRemaining + 8;
+    }
+
+    mTrackFragmentHeaderInfo.mDataOffset =
+        mTrackFragmentHeaderInfo.mBaseDataOffset;
+
+    TrackInfo *trackInfo = editTrack(mTrackFragmentHeaderInfo.mTrackID);
+
+    if (trackInfo->mFragments.empty()
+            || (*trackInfo->mFragments.begin())->complete()) {
+        trackInfo->mFragments.push_back(new DynamicTrackFragment);
+    }
+
+    return OK;
+}
+
+status_t Parser::parseTrackFragmentRun(
+        uint32_t type, size_t offset, uint64_t size) {
+    if (offset + 8 > size) {
+        return -EINVAL;
+    }
+
+    enum {
+        kDataOffsetPresent                  = 0x01,
+        kFirstSampleFlagsPresent            = 0x04,
+        kSampleDurationPresent              = 0x100,
+        kSampleSizePresent                  = 0x200,
+        kSampleFlagsPresent                 = 0x400,
+        kSampleCompositionTimeOffsetPresent = 0x800,
+    };
+
+    uint32_t flags = readU32(offset);
+
+    if (flags & 0xff000000) {
+        return -EINVAL;
+    }
+
+    if ((flags & kFirstSampleFlagsPresent) && (flags & kSampleFlagsPresent)) {
+        // These two shall not be used together.
+        return -EINVAL;
+    }
+
+    uint32_t sampleCount = readU32(offset + 4);
+    offset += 8;
+
+    uint64_t dataOffset = mTrackFragmentHeaderInfo.mDataOffset;
+
+    uint32_t firstSampleFlags = 0;
+
+    if (flags & kDataOffsetPresent) {
+        if (offset + 4 > size) {
+            return -EINVAL;
+        }
+
+        int32_t dataOffsetDelta = (int32_t)readU32(offset);
+
+        dataOffset = mTrackFragmentHeaderInfo.mBaseDataOffset + dataOffsetDelta;
+
+        offset += 4;
+    }
+
+    if (flags & kFirstSampleFlagsPresent) {
+        if (offset + 4 > size) {
+            return -EINVAL;
+        }
+
+        firstSampleFlags = readU32(offset);
+        offset += 4;
+    }
+
+    TrackInfo *info = editTrack(mTrackFragmentHeaderInfo.mTrackID);
+
+    if (info == NULL) {
+        return -EINVAL;
+    }
+
+    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 = info->mDefaultSampleDuration;
+    }
+
+    if (flags & kSampleSizePresent) {
+        bytesPerSample += 4;
+    } else if (mTrackFragmentHeaderInfo.mFlags
+            & TrackFragmentHeaderInfo::kDefaultSampleSizePresent) {
+        sampleSize = mTrackFragmentHeaderInfo.mDefaultSampleSize;
+    } else {
+        sampleSize = info->mDefaultSampleSize;
+    }
+
+    if (flags & kSampleFlagsPresent) {
+        bytesPerSample += 4;
+    } else if (mTrackFragmentHeaderInfo.mFlags
+            & TrackFragmentHeaderInfo::kDefaultSampleFlagsPresent) {
+        sampleFlags = mTrackFragmentHeaderInfo.mDefaultSampleFlags;
+    } else {
+        sampleFlags = info->mDefaultSampleFlags;
+    }
+
+    if (flags & kSampleCompositionTimeOffsetPresent) {
+        bytesPerSample += 4;
+    } else {
+        sampleCtsOffset = 0;
+    }
+
+    if (offset + sampleCount * bytesPerSample > size) {
+        return -EINVAL;
+    }
+
+    uint32_t sampleDescIndex =
+        (mTrackFragmentHeaderInfo.mFlags
+            & TrackFragmentHeaderInfo::kSampleDescriptionIndexPresent)
+            ? mTrackFragmentHeaderInfo.mSampleDescriptionIndex
+            : info->mDefaultSampleDescriptionIndex;
+
+    for (uint32_t i = 0; i < sampleCount; ++i) {
+        if (flags & kSampleDurationPresent) {
+            sampleDuration = readU32(offset);
+            offset += 4;
+        }
+
+        if (flags & kSampleSizePresent) {
+            sampleSize = readU32(offset);
+            offset += 4;
+        }
+
+        if (flags & kSampleFlagsPresent) {
+            sampleFlags = readU32(offset);
+            offset += 4;
+        }
+
+        if (flags & kSampleCompositionTimeOffsetPresent) {
+            sampleCtsOffset = readU32(offset);
+            offset += 4;
+        }
+
+        ALOGV("adding sample at offset 0x%08llx, size %u, duration %u, "
+              "sampleDescIndex=%u, flags 0x%08x",
+                dataOffset, sampleSize, sampleDuration,
+                sampleDescIndex,
+                (flags & kFirstSampleFlagsPresent) && i == 0
+                    ? firstSampleFlags : sampleFlags);
+
+        const sp<TrackFragment> &fragment = *--info->mFragments.end();
+
+        uint32_t decodingTime = info->mDecodingTime;
+        info->mDecodingTime += sampleDuration;
+        uint32_t presentationTime = decodingTime + sampleCtsOffset;
+
+        static_cast<DynamicTrackFragment *>(
+                fragment.get())->addSample(
+                    dataOffset,
+                    sampleSize,
+                    presentationTime,
+                    sampleDescIndex,
+                    ((flags & kFirstSampleFlagsPresent) && i == 0)
+                        ? firstSampleFlags : sampleFlags);
+
+        dataOffset += sampleSize;
+    }
+
+    mTrackFragmentHeaderInfo.mDataOffset = dataOffset;
+
+    return OK;
+}
+
+void Parser::copyBuffer(
+        sp<ABuffer> *dst, size_t offset, uint64_t size, size_t extra) const {
+    sp<ABuffer> buf = new ABuffer(size + extra);
+    memcpy(buf->data(), mBuffer->data() + offset, size);
+
+    *dst = buf;
+}
+
+}  // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/mp4/Parser.h b/media/libmediaplayerservice/nuplayer/mp4/Parser.h
new file mode 100644
index 0000000..50c96bb
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/mp4/Parser.h
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2012 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 PARSER_H_
+
+#define PARSER_H_
+
+#include <media/stagefright/foundation/AHandler.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+struct ABuffer;
+
+struct Parser : public AHandler {
+    struct Source : public RefBase {
+        Source() {}
+
+        virtual ssize_t readAt(off64_t offset, void *data, size_t size) = 0;
+
+        protected:
+        virtual ~Source() {}
+
+        private:
+        DISALLOW_EVIL_CONSTRUCTORS(Source);
+    };
+
+    Parser();
+
+    void start(const char *filename);
+    void start(const sp<Source> &source);
+
+    sp<AMessage> getFormat(bool audio);
+    status_t dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit);
+
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+protected:
+    virtual ~Parser();
+
+private:
+    enum {
+        kWhatStart,
+        kWhatProceed,
+        kWhatReadMore,
+        kWhatGetFormat,
+        kWhatDequeueAccessUnit,
+    };
+
+    struct TrackFragment;
+    struct DynamicTrackFragment;
+    struct StaticTrackFragment;
+
+    struct DispatchEntry {
+        uint32_t mType;
+        uint32_t mParentType;
+        status_t (Parser::*mHandler)(uint32_t, size_t, uint64_t);
+    };
+
+    struct Container {
+        uint64_t mBytesRemaining;
+        uint32_t mType;
+        bool mExtendsToEOF;
+    };
+
+    struct SampleDescription {
+        uint32_t mType;
+        uint16_t mDataRefIndex;
+
+        sp<AMessage> mFormat;
+    };
+
+    struct SampleInfo {
+        off64_t mOffset;
+        size_t mSize;
+        uint32_t mPresentationTime;
+        size_t mSampleDescIndex;
+        uint32_t mFlags;
+    };
+
+    struct MediaDataInfo {
+        sp<ABuffer> mBuffer;
+        off64_t mOffset;
+    };
+
+    struct TrackInfo {
+        enum Flags {
+            kTrackEnabled     = 0x01,
+            kTrackInMovie     = 0x02,
+            kTrackInPreview   = 0x04,
+        };
+
+        uint32_t mTrackID;
+        uint32_t mFlags;
+        uint32_t mDuration;  // This is the duration in terms of movie timescale!
+
+        uint32_t mMediaTimeScale;
+
+        uint32_t mMediaHandlerType;
+        Vector<SampleDescription> mSampleDescs;
+
+        // from track extends:
+        uint32_t mDefaultSampleDescriptionIndex;
+        uint32_t mDefaultSampleDuration;
+        uint32_t mDefaultSampleSize;
+        uint32_t mDefaultSampleFlags;
+
+        uint32_t mDecodingTime;
+
+        sp<StaticTrackFragment> mStaticFragment;
+        List<sp<TrackFragment> > mFragments;
+    };
+
+    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;
+    };
+
+    static const DispatchEntry kDispatchTable[];
+
+    sp<Source> mSource;
+    off_t mBufferPos;
+    bool mSuspended;
+    sp<ABuffer> mBuffer;
+    Vector<Container> mStack;
+    KeyedVector<uint32_t, TrackInfo> mTracks;  // TrackInfo by trackID
+    Vector<MediaDataInfo> mMediaData;
+
+    uint32_t mCurrentTrackID;
+
+    TrackFragmentHeaderInfo mTrackFragmentHeaderInfo;
+
+    status_t onProceed();
+    status_t onDequeueAccessUnit(size_t trackIndex, sp<ABuffer> *accessUnit);
+
+    void enter(uint32_t type, uint64_t size);
+
+    uint16_t readU16(size_t offset);
+    uint32_t readU32(size_t offset);
+    uint64_t readU64(size_t offset);
+    void skip(off_t distance);
+    status_t need(size_t size);
+    bool fitsContainer(uint64_t size) const;
+
+    status_t parseTrackHeader(
+            uint32_t type, size_t offset, uint64_t size);
+
+    status_t parseMediaHeader(
+            uint32_t type, size_t offset, uint64_t size);
+
+    status_t parseMediaHandler(
+            uint32_t type, size_t offset, uint64_t size);
+
+    status_t parseTrackExtends(
+            uint32_t type, size_t offset, uint64_t size);
+
+    status_t parseTrackFragmentHeader(
+            uint32_t type, size_t offset, uint64_t size);
+
+    status_t parseTrackFragmentRun(
+            uint32_t type, size_t offset, uint64_t size);
+
+    status_t parseVisualSampleEntry(
+            uint32_t type, size_t offset, uint64_t size);
+
+    status_t parseAudioSampleEntry(
+            uint32_t type, size_t offset, uint64_t size);
+
+    status_t parseSampleSizes(
+            uint32_t type, size_t offset, uint64_t size);
+
+    status_t parseCompactSampleSizes(
+            uint32_t type, size_t offset, uint64_t size);
+
+    status_t parseSampleToChunk(
+            uint32_t type, size_t offset, uint64_t size);
+
+    status_t parseChunkOffsets(
+            uint32_t type, size_t offset, uint64_t size);
+
+    status_t parseChunkOffsets64(
+            uint32_t type, size_t offset, uint64_t size);
+
+    status_t parseAVCCodecSpecificData(
+            uint32_t type, size_t offset, uint64_t size);
+
+    status_t parseESDSCodecSpecificData(
+            uint32_t type, size_t offset, uint64_t size);
+
+    status_t parseMediaData(
+            uint32_t type, size_t offset, uint64_t size);
+
+    TrackInfo *editTrack(uint32_t trackID, bool createIfNecessary = false);
+
+    ssize_t findTrack(bool wantAudio) const;
+
+    status_t makeAccessUnit(
+            TrackInfo *info,
+            const SampleInfo &sample,
+            const MediaDataInfo &mdatInfo,
+            sp<ABuffer> *accessUnit);
+
+    status_t getSample(
+            TrackInfo *info,
+            sp<TrackFragment> *fragment,
+            SampleInfo *sampleInfo);
+
+    static int CompareSampleLocation(
+        const SampleInfo &sample, const MediaDataInfo &mdatInfo);
+
+    void resumeIfNecessary();
+
+    void copyBuffer(
+            sp<ABuffer> *dst,
+            size_t offset, uint64_t size, size_t extra = 0) const;
+
+    DISALLOW_EVIL_CONSTRUCTORS(Parser);
+};
+
+}  // namespace android
+
+#endif  // PARSER_H_
+
diff --git a/media/libmediaplayerservice/nuplayer/mp4/TrackFragment.cpp b/media/libmediaplayerservice/nuplayer/mp4/TrackFragment.cpp
new file mode 100644
index 0000000..e2df468
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/mp4/TrackFragment.cpp
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2012 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 "TrackFragment"
+#include <utils/Log.h>
+
+#include "TrackFragment.h"
+
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/Utils.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+Parser::DynamicTrackFragment::DynamicTrackFragment()
+    : mComplete(false),
+      mSampleIndex(0) {
+}
+
+Parser::DynamicTrackFragment::~DynamicTrackFragment() {
+}
+
+status_t Parser::DynamicTrackFragment::getSample(SampleInfo *info) {
+    if (mSampleIndex >= mSamples.size()) {
+        return mComplete ? ERROR_END_OF_STREAM : -EWOULDBLOCK;
+    }
+
+    *info = mSamples.itemAt(mSampleIndex);
+
+    return OK;
+}
+
+void Parser::DynamicTrackFragment::advance() {
+    ++mSampleIndex;
+}
+
+void Parser::DynamicTrackFragment::addSample(
+        off64_t dataOffset, size_t sampleSize,
+        uint32_t presentationTime,
+        size_t sampleDescIndex,
+        uint32_t flags) {
+    mSamples.push();
+    SampleInfo *sampleInfo = &mSamples.editItemAt(mSamples.size() - 1);
+
+    sampleInfo->mOffset = dataOffset;
+    sampleInfo->mSize = sampleSize;
+    sampleInfo->mPresentationTime = presentationTime;
+    sampleInfo->mSampleDescIndex = sampleDescIndex;
+    sampleInfo->mFlags = flags;
+}
+
+status_t Parser::DynamicTrackFragment::signalCompletion() {
+    mComplete = true;
+
+    return OK;
+}
+
+bool Parser::DynamicTrackFragment::complete() const {
+    return mComplete;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+Parser::StaticTrackFragment::StaticTrackFragment()
+    : mSampleIndex(0),
+      mSampleCount(0),
+      mSampleToChunkIndex(-1),
+      mSampleToChunkRemaining(0),
+      mPrevChunkIndex(0xffffffff),
+      mNextSampleOffset(0) {
+}
+
+Parser::StaticTrackFragment::~StaticTrackFragment() {
+}
+
+status_t Parser::StaticTrackFragment::getSample(SampleInfo *info) {
+    if (mSampleIndex >= mSampleCount) {
+        return ERROR_END_OF_STREAM;
+    }
+
+    *info = mSampleInfo;
+
+    ALOGV("returning sample %d at [0x%08llx, 0x%08llx)",
+          mSampleIndex,
+          info->mOffset, info->mOffset + info->mSize);
+
+    return OK;
+}
+
+void Parser::StaticTrackFragment::updateSampleInfo() {
+    if (mSampleIndex >= mSampleCount) {
+        return;
+    }
+
+    if (mSampleSizes != NULL) {
+        uint32_t defaultSampleSize = U32_AT(mSampleSizes->data() + 4);
+        if (defaultSampleSize > 0) {
+            mSampleInfo.mSize = defaultSampleSize;
+        } else {
+            mSampleInfo.mSize= U32_AT(mSampleSizes->data() + 12 + 4 * mSampleIndex);
+        }
+    } else {
+        CHECK(mCompactSampleSizes != NULL);
+
+        uint32_t fieldSize = U32_AT(mCompactSampleSizes->data() + 4);
+
+        switch (fieldSize) {
+            case 4:
+            {
+                unsigned byte = mCompactSampleSizes->data()[12 + mSampleIndex / 2];
+                mSampleInfo.mSize = (mSampleIndex & 1) ? byte & 0x0f : byte >> 4;
+                break;
+            }
+
+            case 8:
+            {
+                mSampleInfo.mSize = mCompactSampleSizes->data()[12 + mSampleIndex];
+                break;
+            }
+
+            default:
+            {
+                CHECK_EQ(fieldSize, 16);
+                mSampleInfo.mSize =
+                    U16_AT(mCompactSampleSizes->data() + 12 + mSampleIndex * 2);
+                break;
+            }
+        }
+    }
+
+    CHECK_GT(mSampleToChunkRemaining, 0);
+
+    // The sample desc index is 1-based... XXX
+    mSampleInfo.mSampleDescIndex =
+        U32_AT(mSampleToChunk->data() + 8 + 12 * mSampleToChunkIndex + 8);
+
+    uint32_t chunkIndex =
+        U32_AT(mSampleToChunk->data() + 8 + 12 * mSampleToChunkIndex);
+
+    CHECK_GE(chunkIndex, 1);
+    --chunkIndex;
+
+    if (chunkIndex != mPrevChunkIndex) {
+        mPrevChunkIndex = chunkIndex;
+
+        if (mChunkOffsets != NULL) {
+            uint32_t entryCount = U32_AT(mChunkOffsets->data() + 4);
+
+            if (chunkIndex >= entryCount) {
+                mSampleIndex = mSampleCount;
+                return;
+            }
+
+            mNextSampleOffset =
+                U32_AT(mChunkOffsets->data() + 8 + 4 * chunkIndex);
+        } else {
+            CHECK(mChunkOffsets64 != NULL);
+
+            uint32_t entryCount = U32_AT(mChunkOffsets64->data() + 4);
+
+            if (chunkIndex >= entryCount) {
+                mSampleIndex = mSampleCount;
+                return;
+            }
+
+            mNextSampleOffset =
+                U64_AT(mChunkOffsets64->data() + 8 + 8 * chunkIndex);
+        }
+    }
+
+    mSampleInfo.mOffset = mNextSampleOffset;
+
+    mSampleInfo.mPresentationTime = 0;
+    mSampleInfo.mFlags = 0;
+}
+
+void Parser::StaticTrackFragment::advance() {
+    mNextSampleOffset += mSampleInfo.mSize;
+
+    ++mSampleIndex;
+    if (--mSampleToChunkRemaining == 0) {
+        uint32_t entryCount = U32_AT(mSampleToChunk->data() + 4);
+
+        if ((uint32_t)(mSampleToChunkIndex + 1) == entryCount) {
+            mSampleIndex = mSampleCount;  // EOS.
+            return;
+        }
+
+        ++mSampleToChunkIndex;
+        mSampleToChunkRemaining =
+            U32_AT(mSampleToChunk->data() + 8 + 12 * mSampleToChunkIndex + 4);
+    }
+
+    updateSampleInfo();
+}
+
+static void setU32At(uint8_t *ptr, uint32_t x) {
+    ptr[0] = x >> 24;
+    ptr[1] = (x >> 16) & 0xff;
+    ptr[2] = (x >> 8) & 0xff;
+    ptr[3] = x & 0xff;
+}
+
+void Parser::StaticTrackFragment::fixSampleToChunkTableIfNecessary() {
+    uint32_t entryCount = U32_AT(mSampleToChunk->data() + 4);
+    uint32_t totalSamples = 0;
+    for (uint32_t i = 0; i < entryCount; ++i) {
+        totalSamples += U32_AT(mSampleToChunk->data() + 8 + 12 * i + 4);
+    }
+
+    if (totalSamples < mSampleCount) {
+        // Some samples are not accounted for in the sample-to-chunk
+        // data. Fabricate an extra chunk adjacent to the last one
+        // in the table with the same sample desription index.
+
+        ALOGW("Faking an extra sample-to-chunk entry for %d samples.",
+              mSampleCount - totalSamples);
+
+        uint32_t lastChunkIndex =
+            U32_AT(mSampleToChunk->data() + 8 + 12 * (entryCount - 1));
+
+        uint32_t lastSampleDescriptionIndex =
+            U32_AT(mSampleToChunk->data() + 8 + 12 * (entryCount - 1) + 8);
+
+        uint8_t *ptr = mSampleToChunk->data() + 8 + 12 * entryCount;
+
+        setU32At(ptr, lastChunkIndex + 1);
+        setU32At(ptr + 4, mSampleCount - totalSamples);
+        setU32At(ptr + 8, lastSampleDescriptionIndex);
+        setU32At(mSampleToChunk->data() + 4, entryCount + 1);
+    }
+}
+
+status_t Parser::StaticTrackFragment::signalCompletion() {
+    fixSampleToChunkTableIfNecessary();
+
+    mSampleToChunkIndex = 0;
+
+    mSampleToChunkRemaining =
+        U32_AT(mSampleToChunk->data() + 8 + 12 * mSampleToChunkIndex + 4);
+
+    updateSampleInfo();
+
+    return OK;
+}
+
+bool Parser::StaticTrackFragment::complete() const {
+    return true;
+}
+
+status_t Parser::StaticTrackFragment::parseSampleSizes(
+        Parser *parser, uint32_t type, size_t offset, uint64_t size) {
+    if (offset + 12 > size) {
+        return ERROR_MALFORMED;
+    }
+
+    if (parser->readU32(offset) != 0) {
+        return ERROR_MALFORMED;
+    }
+
+    uint32_t sampleSize = parser->readU32(offset + 4);
+    uint32_t sampleCount = parser->readU32(offset + 8);
+
+    if (sampleSize == 0 && offset + 12 + sampleCount * 4 != size) {
+        return ERROR_MALFORMED;
+    }
+
+    parser->copyBuffer(&mSampleSizes, offset, size);
+
+    mSampleCount = sampleCount;
+
+    return OK;
+}
+
+status_t Parser::StaticTrackFragment::parseCompactSampleSizes(
+        Parser *parser, uint32_t type, size_t offset, uint64_t size) {
+    if (offset + 12 > size) {
+        return ERROR_MALFORMED;
+    }
+
+    if (parser->readU32(offset) != 0) {
+        return ERROR_MALFORMED;
+    }
+
+    uint32_t fieldSize = parser->readU32(offset + 4);
+
+    if (fieldSize != 4 && fieldSize != 8 && fieldSize != 16) {
+        return ERROR_MALFORMED;
+    }
+
+    uint32_t sampleCount = parser->readU32(offset + 8);
+
+    if (offset + 12 + (sampleCount * fieldSize + 4) / 8 != size) {
+        return ERROR_MALFORMED;
+    }
+
+    parser->copyBuffer(&mCompactSampleSizes, offset, size);
+
+    mSampleCount = sampleCount;
+
+    return OK;
+}
+
+status_t Parser::StaticTrackFragment::parseSampleToChunk(
+        Parser *parser, uint32_t type, size_t offset, uint64_t size) {
+    if (offset + 8 > size) {
+        return ERROR_MALFORMED;
+    }
+
+    if (parser->readU32(offset) != 0) {
+        return ERROR_MALFORMED;
+    }
+
+    uint32_t entryCount = parser->readU32(offset + 4);
+
+    if (entryCount == 0 || offset + 8 + entryCount * 12 != size) {
+        return ERROR_MALFORMED;
+    }
+
+    parser->copyBuffer(&mSampleToChunk, offset, size, 12 /* extra */);
+
+    return OK;
+}
+
+status_t Parser::StaticTrackFragment::parseChunkOffsets(
+        Parser *parser, uint32_t type, size_t offset, uint64_t size) {
+    if (offset + 8 > size) {
+        return ERROR_MALFORMED;
+    }
+
+    if (parser->readU32(offset) != 0) {
+        return ERROR_MALFORMED;
+    }
+
+    uint32_t entryCount = parser->readU32(offset + 4);
+
+    if (offset + 8 + entryCount * 4 != size) {
+        return ERROR_MALFORMED;
+    }
+
+    parser->copyBuffer(&mChunkOffsets, offset, size);
+
+    return OK;
+}
+
+status_t Parser::StaticTrackFragment::parseChunkOffsets64(
+        Parser *parser, uint32_t type, size_t offset, uint64_t size) {
+    if (offset + 8 > size) {
+        return ERROR_MALFORMED;
+    }
+
+    if (parser->readU32(offset) != 0) {
+        return ERROR_MALFORMED;
+    }
+
+    uint32_t entryCount = parser->readU32(offset + 4);
+
+    if (offset + 8 + entryCount * 8 != size) {
+        return ERROR_MALFORMED;
+    }
+
+    parser->copyBuffer(&mChunkOffsets64, offset, size);
+
+    return OK;
+}
+
+}  // namespace android
+
diff --git a/media/libmediaplayerservice/nuplayer/mp4/TrackFragment.h b/media/libmediaplayerservice/nuplayer/mp4/TrackFragment.h
new file mode 100644
index 0000000..e5945ac
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/mp4/TrackFragment.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2012 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 TRACK_FRAGMENT_H_
+
+#define TRACK_FRAGMENT_H_
+
+#include "Parser.h"
+
+namespace android {
+
+struct Parser::TrackFragment : public RefBase {
+    TrackFragment() {}
+
+    virtual status_t getSample(SampleInfo *info) = 0;
+    virtual void advance() = 0;
+
+    virtual status_t signalCompletion() = 0;
+    virtual bool complete() const = 0;
+
+protected:
+    virtual ~TrackFragment() {}
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(TrackFragment);
+};
+
+struct Parser::DynamicTrackFragment : public Parser::TrackFragment {
+    DynamicTrackFragment();
+
+    virtual status_t getSample(SampleInfo *info);
+    virtual void advance();
+
+    void addSample(
+            off64_t dataOffset, size_t sampleSize,
+            uint32_t presentationTime,
+            size_t sampleDescIndex,
+            uint32_t flags);
+
+    // No more samples will be added to this fragment.
+    virtual status_t signalCompletion();
+
+    virtual bool complete() const;
+
+protected:
+    virtual ~DynamicTrackFragment();
+
+private:
+    bool mComplete;
+    size_t mSampleIndex;
+    Vector<SampleInfo> mSamples;
+
+    DISALLOW_EVIL_CONSTRUCTORS(DynamicTrackFragment);
+};
+
+struct Parser::StaticTrackFragment : public Parser::TrackFragment {
+    StaticTrackFragment();
+
+    virtual status_t getSample(SampleInfo *info);
+    virtual void advance();
+
+    virtual status_t signalCompletion();
+    virtual bool complete() const;
+
+    status_t parseSampleSizes(
+            Parser *parser, uint32_t type, size_t offset, uint64_t size);
+
+    status_t parseCompactSampleSizes(
+            Parser *parser, uint32_t type, size_t offset, uint64_t size);
+
+    status_t parseSampleToChunk(
+            Parser *parser, uint32_t type, size_t offset, uint64_t size);
+
+    status_t parseChunkOffsets(
+            Parser *parser, uint32_t type, size_t offset, uint64_t size);
+
+    status_t parseChunkOffsets64(
+            Parser *parser, uint32_t type, size_t offset, uint64_t size);
+
+protected:
+    virtual ~StaticTrackFragment();
+
+private:
+    size_t mSampleIndex;
+    size_t mSampleCount;
+
+    SampleInfo mSampleInfo;
+
+    sp<ABuffer> mSampleSizes;
+    sp<ABuffer> mCompactSampleSizes;
+
+    sp<ABuffer> mSampleToChunk;
+    ssize_t mSampleToChunkIndex;
+    size_t mSampleToChunkRemaining;
+
+    sp<ABuffer> mChunkOffsets;
+    sp<ABuffer> mChunkOffsets64;
+    uint32_t mPrevChunkIndex;
+    uint64_t mNextSampleOffset;
+
+    void updateSampleInfo();
+    void fixSampleToChunkTableIfNecessary();
+
+    DISALLOW_EVIL_CONSTRUCTORS(StaticTrackFragment);
+};
+
+}  // namespace android
+
+#endif  // TRACK_FRAGMENT_H_
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index 8b01ac6..dc42f91 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -25,6 +25,7 @@
 #include "AString.h"
 
 #include <binder/Parcel.h>
+#include <media/stagefright/foundation/hexdump.h>
 
 namespace android {
 
@@ -399,9 +400,20 @@
                         "RefBase *%s = %p", item.mName, item.u.refValue);
                 break;
             case kTypeBuffer:
-                tmp = StringPrintf(
-                        "ABuffer *%s = %p", item.mName, item.u.refValue);
+            {
+                sp<ABuffer> buffer = static_cast<ABuffer *>(item.u.refValue);
+
+                if (buffer != NULL && buffer->size() <= 64) {
+                    tmp = StringPrintf("Buffer %s = {\n", item.mName);
+                    hexdump(buffer->data(), buffer->size(), indent + 4, &tmp);
+                    appendIndent(&tmp, indent + 2);
+                    tmp.append("}");
+                } else {
+                    tmp = StringPrintf(
+                            "Buffer *%s = %p", item.mName, buffer.get());
+                }
                 break;
+            }
             case kTypeMessage:
                 tmp = StringPrintf(
                         "AMessage %s = %s",
diff --git a/media/libstagefright/foundation/hexdump.cpp b/media/libstagefright/foundation/hexdump.cpp
index 16c1ca5..a44d832 100644
--- a/media/libstagefright/foundation/hexdump.cpp
+++ b/media/libstagefright/foundation/hexdump.cpp
@@ -29,13 +29,25 @@
 
 namespace android {
 
-void hexdump(const void *_data, size_t size) {
+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);
 
@@ -67,7 +79,12 @@
             }
         }
 
-        ALOGI("%s", line.c_str());
+        if (appendTo != NULL) {
+            appendTo->append(line);
+            appendTo->append("\n");
+        } else {
+            ALOGI("%s", line.c_str());
+        }
 
         offset += 16;
     }