Add webrtc streamer

Bug: 141887532
Test: locally
Change-Id: I2a0d927470ba1f68cc94b94987a0e0de8b74333e
diff --git a/host/frontend/gcastv2/libandroid/AAtomizer.cpp b/host/frontend/gcastv2/libandroid/AAtomizer.cpp
new file mode 100644
index 0000000..f8486c6
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/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 <media/stagefright/foundation/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_back(std::list<std::string>());
+    }
+}
+
+const char *AAtomizer::atomize(const char *name) {
+    Mutex::Autolock autoLock(mLock);
+
+    const size_t n = mAtoms.size();
+    size_t index = AAtomizer::Hash(name) % n;
+    std::list<std::string> &entry = mAtoms[index];
+    std::list<std::string>::iterator it = entry.begin();
+    while (it != entry.end()) {
+        if ((*it) == name) {
+            return (*it).c_str();
+        }
+        ++it;
+    }
+
+    entry.push_back(std::string(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
diff --git a/host/frontend/gcastv2/libandroid/ABitReader.cpp b/host/frontend/gcastv2/libandroid/ABitReader.cpp
new file mode 100644
index 0000000..efb5385
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/ABitReader.cpp
@@ -0,0 +1,98 @@
+/*
+ * 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 <media/stagefright/foundation/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(mNumBitsLeft + n, 32u);
+
+    mReservoir = (mReservoir >> n) | (x << (32 - n));
+    mNumBitsLeft += n;
+}
+
+size_t ABitReader::numBitsLeft() const {
+    return mSize * 8 + mNumBitsLeft;
+}
+
+const uint8_t *ABitReader::data() const {
+    CHECK_EQ(mNumBitsLeft % 8, 0u);
+
+    return mData - mNumBitsLeft / 8;
+}
+
+}  // namespace android
diff --git a/host/frontend/gcastv2/libandroid/ABuffer.cpp b/host/frontend/gcastv2/libandroid/ABuffer.cpp
new file mode 100644
index 0000000..146bfaa
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/ABuffer.cpp
@@ -0,0 +1,97 @@
+/*
+ * 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 <media/stagefright/foundation/ABuffer.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/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;
+}
+
+void ABuffer::reserve(size_t size) {
+    CHECK(mOwnsData);
+
+    if (mCapacity >= size) {
+        return;
+    }
+
+    mCapacity = size;
+    void *newData = realloc(mData, mCapacity);
+    if (!newData) {
+        newData = malloc(mCapacity);
+
+        memcpy(static_cast<uint8_t *>(newData) + mRangeOffset,
+               this->data(),
+               this->size());
+
+        free(mData);
+    }
+    mData = newData;
+}
+
+}  // namespace android
+
diff --git a/host/frontend/gcastv2/libandroid/ADebug.cpp b/host/frontend/gcastv2/libandroid/ADebug.cpp
new file mode 100644
index 0000000..fae0970
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/ADebug.cpp
@@ -0,0 +1,61 @@
+
+#include <media/stagefright/foundation/ADebug.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef ANDROID
+#include <cutils/log.h>
+#endif
+
+namespace android {
+
+Logger::Logger(LogType type)
+    : mLogType(type) {
+    switch (mLogType) {
+        case VERBOSE:
+            mMessage = "V ";
+            break;
+        case INFO:
+            mMessage = "I ";
+            break;
+        case WARNING:
+            mMessage = "W ";
+            break;
+        case ERROR:
+            mMessage = "E ";
+            break;
+        case FATAL:
+            mMessage = "F ";
+            break;
+
+        default:
+            break;
+    }
+}
+
+Logger::~Logger() {
+    mMessage.append("\n");
+
+#if defined(TARGET_ANDROID_DEVICE)
+    if (mLogType == VERBOSE) {
+        return;
+    }
+
+    LOG_PRI(ANDROID_LOG_INFO, "ADebug", "%s", mMessage.c_str());
+#else
+    fprintf(stderr, "%s", mMessage.c_str());
+    fflush(stderr);
+#endif
+
+    if (mLogType == FATAL) {
+        abort();
+    }
+}
+
+const char *LeafName(const char *s) {
+    const char *lastSlash = strrchr(s, '/');
+    return lastSlash != NULL ? lastSlash + 1 : s;
+}
+
+}  // namespace android
diff --git a/host/frontend/gcastv2/libandroid/ALooper.cpp b/host/frontend/gcastv2/libandroid/ALooper.cpp
new file mode 100644
index 0000000..136bdd9
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/ALooper.cpp
@@ -0,0 +1,199 @@
+/*
+ * 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 "ALooper"
+#include <utils/Log.h>
+
+#include <time.h>
+
+#include <media/stagefright/foundation/ALooper.h>
+
+#include <media/stagefright/foundation/AHandler.h>
+#include <media/stagefright/foundation/ALooperRoster.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+namespace android {
+
+ALooperRoster gLooperRoster;
+
+struct ALooper::LooperThread : public Thread {
+    LooperThread(ALooper *looper)
+        : mLooper(looper) {
+    }
+
+    virtual bool threadLoop() {
+        return mLooper->loop();
+    }
+
+protected:
+    virtual ~LooperThread() {}
+
+private:
+    ALooper *mLooper;
+
+    DISALLOW_EVIL_CONSTRUCTORS(LooperThread);
+};
+
+// static
+int64_t ALooper::GetNowUs() {
+    struct timespec ts;
+    int res = clock_gettime(CLOCK_MONOTONIC, &ts);
+    CHECK(!res);
+
+    return static_cast<int64_t>(ts.tv_sec) * 1000000
+        + (ts.tv_nsec + 500) / 1000;
+}
+
+ALooper::ALooper()
+    : mRunningLocally(false) {
+}
+
+ALooper::~ALooper() {
+    stop();
+}
+
+ALooper::handler_id ALooper::registerHandler(const sp<AHandler> &handler) {
+    return gLooperRoster.registerHandler(this, handler);
+}
+
+void ALooper::unregisterHandler(handler_id handlerID) {
+    gLooperRoster.unregisterHandler(handlerID);
+}
+
+status_t ALooper::start(bool runOnCallingThread) {
+    if (runOnCallingThread) {
+        {
+            Mutex::Autolock autoLock(mLock);
+
+            if (mThread != NULL || mRunningLocally) {
+                return INVALID_OPERATION;
+            }
+
+            mRunningLocally = true;
+        }
+
+        do {
+        } while (loop());
+
+        return OK;
+    }
+
+    Mutex::Autolock autoLock(mLock);
+
+    if (mThread != NULL || mRunningLocally) {
+        return INVALID_OPERATION;
+    }
+
+    mThread = new LooperThread(this);
+
+    status_t err = mThread->run("ALooper");
+    if (err != OK) {
+        mThread.clear();
+    }
+
+    return err;
+}
+
+status_t ALooper::stop() {
+    sp<LooperThread> thread;
+    bool runningLocally;
+
+    {
+        Mutex::Autolock autoLock(mLock);
+
+        thread = mThread;
+        runningLocally = mRunningLocally;
+        mThread.clear();
+        mRunningLocally = false;
+    }
+
+    if (thread == NULL && !runningLocally) {
+        return INVALID_OPERATION;
+    }
+
+    mQueueChangedCondition.signal();
+
+    if (thread != NULL) {
+        thread->requestExit();
+    }
+
+    return OK;
+}
+
+void ALooper::post(const sp<AMessage> &msg, int64_t delayUs) {
+    Mutex::Autolock autoLock(mLock);
+
+    int64_t whenUs;
+    if (delayUs > 0) {
+        whenUs = GetNowUs() + delayUs;
+    } else {
+        whenUs = GetNowUs();
+    }
+
+    auto it = mEventQueue.begin();
+    while (it != mEventQueue.end() && (*it).mWhenUs <= whenUs) {
+        ++it;
+    }
+
+    Event event;
+    event.mWhenUs = whenUs;
+    event.mMessage = msg;
+
+    if (it == mEventQueue.begin()) {
+        mQueueChangedCondition.signal();
+    }
+
+    mEventQueue.insert(it, event);
+}
+
+bool ALooper::loop() {
+    Event event;
+
+    {
+        Mutex::Autolock autoLock(mLock);
+        if (mThread == NULL && !mRunningLocally) {
+            return false;
+        }
+        if (mEventQueue.empty()) {
+            mQueueChangedCondition.wait(mLock);
+            return true;
+        }
+        int64_t whenUs = (*mEventQueue.begin()).mWhenUs;
+        int64_t nowUs = GetNowUs();
+
+        if (whenUs > nowUs) {
+            int64_t delayUs = whenUs - nowUs;
+            mQueueChangedCondition.waitRelative(mLock, delayUs * 1000ll);
+
+            return true;
+        }
+
+        event = mEventQueue.front();
+        mEventQueue.pop_front();
+    }
+
+    gLooperRoster.deliverMessage(event.mMessage);
+
+    // NOTE: It's important to note that at this point our "ALooper" object
+    // may no longer exist (its final reference may have gone away while
+    // delivering the message). We have made sure, however, that loop()
+    // won't be called again.
+
+    return true;
+}
+
+}  // namespace android
diff --git a/host/frontend/gcastv2/libandroid/ALooperRoster.cpp b/host/frontend/gcastv2/libandroid/ALooperRoster.cpp
new file mode 100644
index 0000000..d701c9e
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/ALooperRoster.cpp
@@ -0,0 +1,111 @@
+/*
+ * 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 "ALooperRoster"
+#include <utils/Log.h>
+
+#include <media/stagefright/foundation/ALooperRoster.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AHandler.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+namespace android {
+
+ALooperRoster::ALooperRoster()
+    : mNextHandlerID(1) {
+}
+
+ALooper::handler_id ALooperRoster::registerHandler(
+        const sp<ALooper> looper, const sp<AHandler> &handler) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (handler->id() != 0) {
+        CHECK(!"A handler must only be registered once.");
+        return INVALID_OPERATION;
+    }
+
+    handler->mLooper = looper.get();
+
+    HandlerInfo info;
+    info.mLooper = looper;
+    info.mHandler = handler.get();
+    ALooper::handler_id handlerID = mNextHandlerID++;
+    mHandlers[handlerID] = info;
+
+    handler->setID(handlerID);
+
+    return handlerID;
+}
+
+void ALooperRoster::unregisterHandler(ALooper::handler_id handlerID) {
+    Mutex::Autolock autoLock(mLock);
+
+    auto it = mHandlers.find(handlerID);
+    CHECK(it != mHandlers.end());
+
+    const HandlerInfo &info = it->second;
+    sp<AHandler> handler = info.mHandler.promote();
+
+    if (handler != NULL) {
+        handler->setID(0);
+    }
+
+    mHandlers.erase(it);
+}
+
+void ALooperRoster::postMessage(
+        const sp<AMessage> &msg, int64_t delayUs) {
+    Mutex::Autolock autoLock(mLock);
+
+    auto it = mHandlers.find(msg->target());
+
+    if (it == mHandlers.end()) {
+        LOG(WARNING) << "failed to post message. Target handler not registered.";
+        return;
+    }
+
+    const HandlerInfo &info = it->second;
+    info.mLooper->post(msg, delayUs);
+}
+
+void ALooperRoster::deliverMessage(const sp<AMessage> &msg) {
+    sp<AHandler> handler;
+
+    {
+        Mutex::Autolock autoLock(mLock);
+
+        auto it = mHandlers.find(msg->target());
+
+        if (it == mHandlers.end()) {
+            LOG(WARNING) << "failed to deliver message. Target handler not registered.";
+            return;
+        }
+
+        const HandlerInfo &info = it->second;
+        handler = info.mHandler.promote();
+
+        if (handler == NULL) {
+            mHandlers.erase(it);
+            return;
+        }
+    }
+
+    handler->onMessageReceived(msg);
+}
+
+}  // namespace android
diff --git a/host/frontend/gcastv2/libandroid/AMessage.cpp b/host/frontend/gcastv2/libandroid/AMessage.cpp
new file mode 100644
index 0000000..93d8f84
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/AMessage.cpp
@@ -0,0 +1,388 @@
+/*
+ * 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 <media/stagefright/foundation/AMessage.h>
+
+#include <media/stagefright/Utils.h>
+#include <media/stagefright/foundation/AAtomizer.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooperRoster.h>
+#include <media/stagefright/foundation/hexdump.h>
+
+namespace android {
+
+AMessage::AMessage(uint32_t what, ALooper::handler_id target)
+    : mWhat(what),
+      mTarget(target),
+      mNumItems(0) {
+}
+
+AMessage::~AMessage() {
+    clear();
+}
+
+void AMessage::setWhat(uint32_t what) {
+    mWhat = what;
+}
+
+uint32_t AMessage::what() const {
+    return mWhat;
+}
+
+void AMessage::setTarget(ALooper::handler_id handlerID) {
+    mTarget = handlerID;
+}
+
+ALooper::handler_id AMessage::target() const {
+    return mTarget;
+}
+
+void AMessage::clear() {
+    for (size_t i = 0; i < mNumItems; ++i) {
+        Item *item = &mItems[i];
+        freeItem(item);
+    }
+    mNumItems = 0;
+}
+
+void AMessage::freeItem(Item *item) {
+    switch (item->mType) {
+        case kTypeString:
+        {
+            delete item->u.stringValue;
+            break;
+        }
+
+        case kTypeObject:
+        case kTypeMessage:
+        case kTypeBuffer:
+        {
+            if (item->u.refValue != NULL) {
+                item->u.refValue->decStrong(this);
+            }
+            break;
+        }
+
+        default:
+            break;
+    }
+}
+
+AMessage::Item *AMessage::allocateItem(const char *name) {
+    name = AAtomizer::Atomize(name);
+
+    size_t i = 0;
+    while (i < mNumItems && mItems[i].mName != name) {
+        ++i;
+    }
+
+    Item *item;
+
+    if (i < mNumItems) {
+        item = &mItems[i];
+        freeItem(item);
+    } else {
+        CHECK(mNumItems < kMaxNumItems);
+        i = mNumItems++;
+        item = &mItems[i];
+
+        item->mName = name;
+    }
+
+    return item;
+}
+
+const AMessage::Item *AMessage::findItem(
+        const char *name, Type type) const {
+    name = AAtomizer::Atomize(name);
+
+    for (size_t i = 0; i < mNumItems; ++i) {
+        const Item *item = &mItems[i];
+
+        if (item->mName == name) {
+            return item->mType == type ? item : NULL;
+        }
+    }
+
+    return NULL;
+}
+
+#define BASIC_TYPE(NAME,FIELDNAME,TYPENAME)                             \
+void AMessage::set##NAME(const char *name, TYPENAME value) {            \
+    Item *item = allocateItem(name);                                    \
+                                                                        \
+    item->mType = kType##NAME;                                          \
+    item->u.FIELDNAME = value;                                          \
+}                                                                       \
+                                                                        \
+bool AMessage::find##NAME(const char *name, TYPENAME *value) const {    \
+    const Item *item = findItem(name, kType##NAME);                     \
+    if (item) {                                                         \
+        *value = item->u.FIELDNAME;                                     \
+        return true;                                                    \
+    }                                                                   \
+    return false;                                                       \
+}
+
+BASIC_TYPE(Int32,int32Value,int32_t)
+BASIC_TYPE(Int64,int64Value,int64_t)
+BASIC_TYPE(Size,sizeValue,size_t)
+BASIC_TYPE(Float,floatValue,float)
+BASIC_TYPE(Double,doubleValue,double)
+BASIC_TYPE(Pointer,ptrValue,void *)
+
+#undef BASIC_TYPE
+
+void AMessage::setString(
+        const char *name, const char *s, ssize_t len) {
+    Item *item = allocateItem(name);
+    item->mType = kTypeString;
+    item->u.stringValue = new std::string(s, len < 0 ? strlen(s) : len);
+}
+
+void AMessage::setObject(const char *name, const sp<RefBase> &obj) {
+    Item *item = allocateItem(name);
+    item->mType = kTypeObject;
+
+    if (obj != NULL) { obj->incStrong(this); }
+    item->u.refValue = obj.get();
+}
+
+void AMessage::setMessage(const char *name, const sp<AMessage> &obj) {
+    Item *item = allocateItem(name);
+    item->mType = kTypeMessage;
+
+    if (obj != NULL) { obj->incStrong(this); }
+    item->u.refValue = obj.get();
+}
+
+void AMessage::setBuffer(const char *name, const sp<ABuffer> &obj) {
+    Item *item = allocateItem(name);
+    item->mType = kTypeBuffer;
+
+    if (obj != NULL) { obj->incStrong(this); }
+    item->u.refValue = obj.get();
+}
+
+bool AMessage::findString(const char *name, std::string *value) const {
+    const Item *item = findItem(name, kTypeString);
+    if (item) {
+        *value = *item->u.stringValue;
+        return true;
+    }
+    return false;
+}
+
+bool AMessage::findObject(const char *name, sp<RefBase> *obj) const {
+    const Item *item = findItem(name, kTypeObject);
+    if (item) {
+        *obj = item->u.refValue;
+        return true;
+    }
+    return false;
+}
+
+bool AMessage::findMessage(const char *name, sp<AMessage> *obj) const {
+    const Item *item = findItem(name, kTypeMessage);
+    if (item) {
+        *obj = static_cast<AMessage *>(item->u.refValue);
+        return true;
+    }
+    return false;
+}
+
+bool AMessage::findBuffer(const char *name, sp<ABuffer> *obj) const {
+    const Item *item = findItem(name, kTypeBuffer);
+    if (item) {
+        *obj = static_cast<ABuffer *>(item->u.refValue);
+        return true;
+    }
+    return false;
+}
+
+void AMessage::post(int64_t delayUs) {
+    extern ALooperRoster gLooperRoster;
+
+    gLooperRoster.postMessage(this, delayUs);
+}
+
+sp<AMessage> AMessage::dup() const {
+    sp<AMessage> msg = new AMessage(mWhat, mTarget);
+    msg->mNumItems = mNumItems;
+
+    for (size_t i = 0; i < mNumItems; ++i) {
+        const Item *from = &mItems[i];
+        Item *to = &msg->mItems[i];
+
+        to->mName = from->mName;
+        to->mType = from->mType;
+
+        switch (from->mType) {
+            case kTypeString:
+            {
+                to->u.stringValue = new std::string(*from->u.stringValue);
+                break;
+            }
+
+            case kTypeObject:
+            case kTypeMessage:
+            case kTypeBuffer:
+            {
+                to->u.refValue = from->u.refValue;
+                to->u.refValue->incStrong(this);
+                break;
+            }
+
+            default:
+            {
+                to->u = from->u;
+                break;
+            }
+        }
+    }
+
+    return msg;
+}
+
+static bool isFourcc(uint32_t what) {
+    return isprint(what & 0xff)
+        && isprint((what >> 8) & 0xff)
+        && isprint((what >> 16) & 0xff)
+        && isprint((what >> 24) & 0xff);
+}
+
+static void appendIndent(std::string *s, size_t indent) {
+    static const char kWhitespace[] =
+        "                                        "
+        "                                        ";
+
+    CHECK_LT((size_t)indent, sizeof(kWhitespace));
+
+    s->append(kWhitespace, indent);
+}
+
+std::string AMessage::debugString(size_t indent) const {
+    std::string s = "AMessage(what = ";
+
+    std::string tmp;
+    if (isFourcc(mWhat)) {
+        tmp = StringPrintf(
+                "'%c%c%c%c'",
+                (char)(mWhat >> 24),
+                (char)((mWhat >> 16) & 0xff),
+                (char)((mWhat >> 8) & 0xff),
+                (char)(mWhat & 0xff));
+    } else {
+        tmp = StringPrintf("0x%08x", mWhat);
+    }
+    s.append(tmp);
+
+    if (mTarget != 0) {
+        tmp = StringPrintf(", target = %d", mTarget);
+        s.append(tmp);
+    }
+    s.append(") = {\n");
+
+    for (size_t i = 0; i < mNumItems; ++i) {
+        const Item &item = mItems[i];
+
+        switch (item.mType) {
+            case kTypeInt32:
+                tmp = StringPrintf(
+                        "int32_t %s = %d", item.mName, item.u.int32Value);
+                break;
+            case kTypeInt64:
+                tmp = StringPrintf(
+                        "int64_t %s = %lld", item.mName, item.u.int64Value);
+                break;
+            case kTypeSize:
+                tmp = StringPrintf(
+                        "size_t %s = %d", item.mName, item.u.sizeValue);
+                break;
+            case kTypeFloat:
+                tmp = StringPrintf(
+                        "float %s = %f", item.mName, item.u.floatValue);
+                break;
+            case kTypeDouble:
+                tmp = StringPrintf(
+                        "double %s = %f", item.mName, item.u.doubleValue);
+                break;
+            case kTypePointer:
+                tmp = StringPrintf(
+                        "void *%s = %p", item.mName, item.u.ptrValue);
+                break;
+            case kTypeString:
+                tmp = StringPrintf(
+                        "string %s = \"%s\"",
+                        item.mName,
+                        item.u.stringValue->c_str());
+                break;
+            case kTypeObject:
+                tmp = StringPrintf(
+                        "RefBase *%s = %p", item.mName, item.u.refValue);
+                break;
+            case kTypeBuffer:
+            {
+                sp<ABuffer> buffer = static_cast<ABuffer *>(item.u.refValue);
+
+                if (buffer != NULL && buffer->data() != NULL && buffer->size() <= 1024) {
+                    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",
+                        item.mName,
+                        static_cast<AMessage *>(
+                            item.u.refValue)->debugString(
+                                indent + strlen(item.mName) + 14).c_str());
+                break;
+            default:
+                TRESPASS();
+        }
+
+        appendIndent(&s, indent);
+        s.append("  ");
+        s.append(tmp);
+        s.append("\n");
+    }
+
+    appendIndent(&s, indent);
+    s.append("}");
+
+    return s;
+}
+
+size_t AMessage::countEntries() const {
+    return mNumItems;
+}
+
+const char *AMessage::getEntryNameAt(size_t i, Type *type) const {
+    CHECK_LT(i, mNumItems);
+    *type = mItems[i].mType;
+
+    return mItems[i].mName;
+}
+
+}  // namespace android
diff --git a/host/frontend/gcastv2/libandroid/ANetworkSession.cpp b/host/frontend/gcastv2/libandroid/ANetworkSession.cpp
new file mode 100644
index 0000000..f422a34
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/ANetworkSession.cpp
@@ -0,0 +1,1410 @@
+/*
+ * Copyright 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 "NetworkSession"
+#include <utils/Log.h>
+
+#include <media/stagefright/foundation/ANetworkSession.h>
+
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <media/stagefright/Utils.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/foundation/ParsedMessage.h>
+
+#include <list>
+
+namespace android {
+
+static const size_t kMaxUDPSize = 1500;
+static const int32_t kMaxUDPRetries = 200;
+
+struct ANetworkSession::NetworkThread : public Thread {
+    NetworkThread(ANetworkSession *session);
+
+protected:
+    virtual ~NetworkThread();
+
+private:
+    ANetworkSession *mSession;
+
+    virtual bool threadLoop();
+
+    DISALLOW_EVIL_CONSTRUCTORS(NetworkThread);
+};
+
+struct ANetworkSession::Session : public RefBase {
+    enum Mode {
+        MODE_RTSP,
+        MODE_DATAGRAM,
+        MODE_WEBSOCKET,
+    };
+
+    enum State {
+        CONNECTING,
+        CONNECTED,
+        LISTENING_RTSP,
+        LISTENING_TCP_DGRAMS,
+        DATAGRAM,
+    };
+
+    Session(int32_t sessionID,
+            State state,
+            int s,
+            const sp<AMessage> &notify);
+
+    int32_t sessionID() const;
+    int socket() const;
+    sp<AMessage> getNotificationMessage() const;
+
+    bool isRTSPServer() const;
+    bool isTCPDatagramServer() const;
+
+    bool wantsToRead();
+    bool wantsToWrite();
+
+    status_t readMore();
+    status_t writeMore();
+
+    status_t sendRequest(
+            const void *data, ssize_t size, bool timeValid, int64_t timeUs);
+
+    void setMode(Mode mode);
+
+    status_t switchToWebSocketMode();
+
+protected:
+    virtual ~Session();
+
+private:
+    enum {
+        FRAGMENT_FLAG_TIME_VALID = 1,
+    };
+    struct Fragment {
+        uint32_t mFlags;
+        int64_t mTimeUs;
+        sp<ABuffer> mBuffer;
+    };
+
+    int32_t mSessionID;
+    State mState;
+    Mode mMode;
+    int mSocket;
+    sp<AMessage> mNotify;
+    bool mSawReceiveFailure, mSawSendFailure;
+    int32_t mUDPRetries;
+
+    std::list<Fragment> mOutFragments;
+
+    std::string mInBuffer;
+
+    int64_t mLastStallReportUs;
+
+    void notifyError(bool send, status_t err, const char *detail);
+    void notify(NotificationReason reason);
+
+    void dumpFragmentStats(const Fragment &frag);
+
+    DISALLOW_EVIL_CONSTRUCTORS(Session);
+};
+////////////////////////////////////////////////////////////////////////////////
+
+ANetworkSession::NetworkThread::NetworkThread(ANetworkSession *session)
+    : mSession(session) {
+}
+
+ANetworkSession::NetworkThread::~NetworkThread() {
+}
+
+bool ANetworkSession::NetworkThread::threadLoop() {
+    mSession->threadLoop();
+
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ANetworkSession::Session::Session(
+        int32_t sessionID,
+        State state,
+        int s,
+        const sp<AMessage> &notify)
+    : mSessionID(sessionID),
+      mState(state),
+      mMode(MODE_DATAGRAM),
+      mSocket(s),
+      mNotify(notify),
+      mSawReceiveFailure(false),
+      mSawSendFailure(false),
+      mUDPRetries(kMaxUDPRetries),
+      mLastStallReportUs(-1ll) {
+    if (mState == CONNECTED) {
+        struct sockaddr_in localAddr;
+        socklen_t localAddrLen = sizeof(localAddr);
+
+        int res = getsockname(
+                mSocket, (struct sockaddr *)&localAddr, &localAddrLen);
+        CHECK_GE(res, 0);
+
+        struct sockaddr_in remoteAddr;
+        socklen_t remoteAddrLen = sizeof(remoteAddr);
+
+        res = getpeername(
+                mSocket, (struct sockaddr *)&remoteAddr, &remoteAddrLen);
+        CHECK_GE(res, 0);
+
+        in_addr_t addr = ntohl(localAddr.sin_addr.s_addr);
+        std::string localAddrString = StringPrintf(
+                "%d.%d.%d.%d",
+                (addr >> 24),
+                (addr >> 16) & 0xff,
+                (addr >> 8) & 0xff,
+                addr & 0xff);
+
+        addr = ntohl(remoteAddr.sin_addr.s_addr);
+        std::string remoteAddrString = StringPrintf(
+                "%d.%d.%d.%d",
+                (addr >> 24),
+                (addr >> 16) & 0xff,
+                (addr >> 8) & 0xff,
+                addr & 0xff);
+
+        sp<AMessage> msg = mNotify->dup();
+        msg->setInt32("sessionID", mSessionID);
+        msg->setInt32("reason", kWhatClientConnected);
+        msg->setString("server-ip", localAddrString.c_str());
+        msg->setInt32("server-port", ntohs(localAddr.sin_port));
+        msg->setString("client-ip", remoteAddrString.c_str());
+        msg->setInt32("client-port", ntohs(remoteAddr.sin_port));
+        msg->post();
+    }
+}
+
+ANetworkSession::Session::~Session() {
+    ALOGV("Session %d gone", mSessionID);
+
+    close(mSocket);
+    mSocket = -1;
+}
+
+int32_t ANetworkSession::Session::sessionID() const {
+    return mSessionID;
+}
+
+int ANetworkSession::Session::socket() const {
+    return mSocket;
+}
+
+void ANetworkSession::Session::setMode(Mode mode) {
+    mMode = mode;
+}
+
+status_t ANetworkSession::Session::switchToWebSocketMode() {
+    if (mState != CONNECTED || mMode != MODE_RTSP) {
+        return INVALID_OPERATION;
+    }
+
+    mMode = MODE_WEBSOCKET;
+
+    return OK;
+}
+
+sp<AMessage> ANetworkSession::Session::getNotificationMessage() const {
+    return mNotify;
+}
+
+bool ANetworkSession::Session::isRTSPServer() const {
+    return mState == LISTENING_RTSP;
+}
+
+bool ANetworkSession::Session::isTCPDatagramServer() const {
+    return mState == LISTENING_TCP_DGRAMS;
+}
+
+bool ANetworkSession::Session::wantsToRead() {
+    return !mSawReceiveFailure && mState != CONNECTING;
+}
+
+bool ANetworkSession::Session::wantsToWrite() {
+    return !mSawSendFailure
+        && (mState == CONNECTING
+            || (mState == CONNECTED && !mOutFragments.empty())
+            || (mState == DATAGRAM && !mOutFragments.empty()));
+}
+
+status_t ANetworkSession::Session::readMore() {
+    if (mState == DATAGRAM) {
+        CHECK_EQ(mMode, MODE_DATAGRAM);
+
+        status_t err;
+        do {
+            sp<ABuffer> buf = new ABuffer(kMaxUDPSize);
+
+            struct sockaddr_in remoteAddr;
+            socklen_t remoteAddrLen = sizeof(remoteAddr);
+
+            ssize_t n;
+            do {
+                n = recvfrom(
+                        mSocket, buf->data(), buf->capacity(), 0,
+                        (struct sockaddr *)&remoteAddr, &remoteAddrLen);
+            } while (n < 0 && errno == EINTR);
+
+            err = OK;
+            if (n < 0) {
+                err = -errno;
+            } else if (n == 0) {
+                err = -ECONNRESET;
+            } else {
+                buf->setRange(0, n);
+
+                int64_t nowUs = ALooper::GetNowUs();
+                buf->meta()->setInt64("arrivalTimeUs", nowUs);
+
+                sp<AMessage> notify = mNotify->dup();
+                notify->setInt32("sessionID", mSessionID);
+                notify->setInt32("reason", kWhatDatagram);
+
+                uint32_t ip = ntohl(remoteAddr.sin_addr.s_addr);
+                notify->setString(
+                        "fromAddr",
+                        StringPrintf(
+                            "%u.%u.%u.%u",
+                            ip >> 24,
+                            (ip >> 16) & 0xff,
+                            (ip >> 8) & 0xff,
+                            ip & 0xff).c_str());
+
+                notify->setInt32("fromPort", ntohs(remoteAddr.sin_port));
+
+                notify->setBuffer("data", buf);
+                notify->post();
+            }
+        } while (err == OK);
+
+        if (err == -EAGAIN) {
+            err = OK;
+        }
+
+        if (err != OK) {
+            if (!mUDPRetries) {
+                notifyError(false /* send */, err, "Recvfrom failed.");
+                mSawReceiveFailure = true;
+            } else {
+                mUDPRetries--;
+                ALOGE("Recvfrom failed, %d/%d retries left",
+                        mUDPRetries, kMaxUDPRetries);
+                err = OK;
+            }
+        } else {
+            mUDPRetries = kMaxUDPRetries;
+        }
+
+        return err;
+    }
+
+    char tmp[512];
+    ssize_t n;
+    do {
+        n = recv(mSocket, tmp, sizeof(tmp), 0);
+    } while (n < 0 && errno == EINTR);
+
+    status_t err = OK;
+
+    if (n > 0) {
+        mInBuffer.append(tmp, n);
+
+#if 0
+        ALOGI("in:");
+        hexdump(tmp, n);
+#endif
+    } else if (n < 0) {
+        err = -errno;
+    } else {
+        err = -ECONNRESET;
+    }
+
+    if (mMode == MODE_DATAGRAM) {
+        // TCP stream carrying 16-bit length-prefixed datagrams.
+
+        while (mInBuffer.size() >= 2) {
+            size_t packetSize = U16_AT((const uint8_t *)mInBuffer.c_str());
+
+            if (mInBuffer.size() < packetSize + 2) {
+                break;
+            }
+
+            sp<ABuffer> packet = new ABuffer(packetSize);
+            memcpy(packet->data(), mInBuffer.c_str() + 2, packetSize);
+
+            int64_t nowUs = ALooper::GetNowUs();
+            packet->meta()->setInt64("arrivalTimeUs", nowUs);
+
+            sp<AMessage> notify = mNotify->dup();
+            notify->setInt32("sessionID", mSessionID);
+            notify->setInt32("reason", kWhatDatagram);
+            notify->setBuffer("data", packet);
+            notify->post();
+
+            mInBuffer.erase(0, packetSize + 2);
+        }
+    } else if (mMode == MODE_RTSP) {
+        for (;;) {
+            size_t length;
+
+            if (mInBuffer.size() > 0 && mInBuffer.c_str()[0] == '$') {
+                if (mInBuffer.size() < 4) {
+                    break;
+                }
+
+                length = U16_AT((const uint8_t *)mInBuffer.c_str() + 2);
+
+                if (mInBuffer.size() < 4 + length) {
+                    break;
+                }
+
+                sp<AMessage> notify = mNotify->dup();
+                notify->setInt32("sessionID", mSessionID);
+                notify->setInt32("reason", kWhatBinaryData);
+                notify->setInt32("channel", mInBuffer.c_str()[1]);
+
+                sp<ABuffer> data = new ABuffer(length);
+                memcpy(data->data(), mInBuffer.c_str() + 4, length);
+
+                int64_t nowUs = ALooper::GetNowUs();
+                data->meta()->setInt64("arrivalTimeUs", nowUs);
+
+                notify->setBuffer("data", data);
+                notify->post();
+
+                mInBuffer.erase(0, 4 + length);
+                continue;
+            }
+
+            sp<ParsedMessage> msg =
+                ParsedMessage::Parse(
+                        mInBuffer.c_str(), mInBuffer.size(), err != OK, &length);
+
+            if (msg == NULL) {
+                break;
+            }
+
+            sp<AMessage> notify = mNotify->dup();
+            notify->setInt32("sessionID", mSessionID);
+            notify->setInt32("reason", kWhatData);
+            notify->setObject("data", msg);
+            notify->post();
+
+#if 1
+            // XXX The (old) dongle sends the wrong content length header on a
+            // SET_PARAMETER request that signals a "wfd_idr_request".
+            // (17 instead of 19).
+            const char *content = msg->getContent();
+            if (content
+                    && !memcmp(content, "wfd_idr_request\r\n", 17)
+                    && length >= 19
+                    && mInBuffer.c_str()[length] == '\r'
+                    && mInBuffer.c_str()[length + 1] == '\n') {
+                length += 2;
+            }
+#endif
+
+            mInBuffer.erase(0, length);
+
+            if (err != OK) {
+                break;
+            }
+        }
+    } else {
+        CHECK_EQ(mMode, MODE_WEBSOCKET);
+
+        const uint8_t *data = (const uint8_t *)mInBuffer.c_str();
+        // hexdump(data, mInBuffer.size());
+
+        while (mInBuffer.size() >= 2) {
+            size_t offset = 2;
+
+            size_t payloadLen = data[1] & 0x7f;
+            if (payloadLen == 126) {
+                if (offset + 2 > mInBuffer.size()) {
+                    break;
+                }
+
+                payloadLen = U16_AT(&data[offset]);
+                offset += 2;
+            } else if (payloadLen == 127) {
+                if (offset + 8 > mInBuffer.size()) {
+                    break;
+                }
+
+                payloadLen = U64_AT(&data[offset]);
+                
+                offset += 8;
+            }
+
+            uint32_t mask = 0;
+            if (data[1] & 0x80) {
+                // MASK==1
+                if (offset + 4 > mInBuffer.size()) {
+                    break;
+                }
+
+                mask = U32_AT(&data[offset]);
+                offset += 4;
+            }
+
+            if (offset + payloadLen > mInBuffer.size()) {
+                break;
+            }
+
+            // We have the full message.
+
+            sp<ABuffer> packet = new ABuffer(payloadLen);
+            memcpy(packet->data(), &data[offset], payloadLen);
+
+            if (mask != 0) {
+                for (size_t i = 0; i < payloadLen; ++i) {
+                    packet->data()[i] =
+                        data[offset + i]
+                            ^ ((mask >> (8 * (3 - (i % 4)))) & 0xff);
+                }
+            }
+
+            sp<AMessage> notify = mNotify->dup();
+            notify->setInt32("sessionID", mSessionID);
+            notify->setInt32("reason", kWhatWebSocketMessage);
+            notify->setBuffer("data", packet);
+            notify->setInt32("headerByte", data[0]);
+            notify->post();
+
+            mInBuffer.erase(0, offset + payloadLen);
+        }
+    }
+
+    if (err != OK) {
+        notifyError(false /* send */, err, "Recv failed.");
+        mSawReceiveFailure = true;
+    }
+
+    return err;
+}
+
+void ANetworkSession::Session::dumpFragmentStats(const Fragment & /* frag */) {
+#if 0
+    int64_t nowUs = ALooper::GetNowUs();
+    int64_t delayMs = (nowUs - frag.mTimeUs) / 1000ll;
+
+    static const int64_t kMinDelayMs = 0;
+    static const int64_t kMaxDelayMs = 300;
+
+    const char *kPattern = "########################################";
+    size_t kPatternSize = strlen(kPattern);
+
+    int n = (kPatternSize * (delayMs - kMinDelayMs))
+                / (kMaxDelayMs - kMinDelayMs);
+
+    if (n < 0) {
+        n = 0;
+    } else if ((size_t)n > kPatternSize) {
+        n = kPatternSize;
+    }
+
+    ALOGI("[%lld]: (%4lld ms) %s\n",
+          frag.mTimeUs / 1000,
+          delayMs,
+          kPattern + kPatternSize - n);
+#endif
+}
+
+status_t ANetworkSession::Session::writeMore() {
+    if (mState == DATAGRAM) {
+        CHECK(!mOutFragments.empty());
+
+        status_t err;
+        do {
+            const Fragment &frag = *mOutFragments.begin();
+            const sp<ABuffer> &datagram = frag.mBuffer;
+
+            ssize_t n;
+            do {
+                n = send(mSocket, datagram->data(), datagram->size(), 0);
+            } while (n < 0 && errno == EINTR);
+
+            err = OK;
+
+            if (n > 0) {
+                if (frag.mFlags & FRAGMENT_FLAG_TIME_VALID) {
+                    dumpFragmentStats(frag);
+                }
+
+                mOutFragments.erase(mOutFragments.begin());
+            } else if (n < 0) {
+                err = -errno;
+            } else if (n == 0) {
+                err = -ECONNRESET;
+            }
+        } while (err == OK && !mOutFragments.empty());
+
+        if (err == -EAGAIN) {
+            if (!mOutFragments.empty()) {
+                ALOGI("%d datagrams remain queued.", mOutFragments.size());
+            }
+            err = OK;
+        }
+
+        if (err != OK) {
+            if (!mUDPRetries) {
+                notifyError(true /* send */, err, "Send datagram failed.");
+                mSawSendFailure = true;
+            } else {
+                mUDPRetries--;
+                ALOGE("Send datagram failed, %d/%d retries left",
+                        mUDPRetries, kMaxUDPRetries);
+                err = OK;
+            }
+        } else {
+            mUDPRetries = kMaxUDPRetries;
+        }
+
+        return err;
+    }
+
+    if (mState == CONNECTING) {
+        int err;
+        socklen_t optionLen = sizeof(err);
+        CHECK_EQ(getsockopt(mSocket, SOL_SOCKET, SO_ERROR, &err, &optionLen), 0);
+        CHECK_EQ(optionLen, (socklen_t)sizeof(err));
+
+        if (err != 0) {
+            notifyError(kWhatError, -err, "Connection failed");
+            mSawSendFailure = true;
+
+            return -err;
+        }
+
+        mState = CONNECTED;
+        notify(kWhatConnected);
+
+        return OK;
+    }
+
+    CHECK_EQ(mState, CONNECTED);
+    CHECK(!mOutFragments.empty());
+
+    ssize_t n = 0;
+    while (!mOutFragments.empty()) {
+        const Fragment &frag = *mOutFragments.begin();
+
+        do {
+            n = send(mSocket, frag.mBuffer->data(), frag.mBuffer->size(), 0);
+        } while (n < 0 && errno == EINTR);
+
+        if (n <= 0) {
+            break;
+        }
+
+        frag.mBuffer->setRange(
+                frag.mBuffer->offset() + n, frag.mBuffer->size() - n);
+
+        if (frag.mBuffer->size() > 0) {
+            break;
+        }
+
+        if (frag.mFlags & FRAGMENT_FLAG_TIME_VALID) {
+            dumpFragmentStats(frag);
+        }
+
+        mOutFragments.erase(mOutFragments.begin());
+    }
+
+    status_t err = OK;
+
+    if (n < 0) {
+        err = -errno;
+    } else if (n == 0) {
+        err = -ECONNRESET;
+    }
+
+    if (err != OK) {
+        notifyError(true /* send */, err, "Send failed.");
+        mSawSendFailure = true;
+    }
+
+#if 0
+    int numBytesQueued;
+    int res = ioctl(mSocket, SIOCOUTQ, &numBytesQueued);
+    if (res == 0 && numBytesQueued > 50 * 1024) {
+        if (numBytesQueued > 409600) {
+            ALOGW("!!! numBytesQueued = %d", numBytesQueued);
+        }
+
+        int64_t nowUs = ALooper::GetNowUs();
+
+        if (mLastStallReportUs < 0ll
+                || nowUs > mLastStallReportUs + 100000ll) {
+            sp<AMessage> msg = mNotify->dup();
+            msg->setInt32("sessionID", mSessionID);
+            msg->setInt32("reason", kWhatNetworkStall);
+            msg->setSize("numBytesQueued", numBytesQueued);
+            msg->post();
+
+            mLastStallReportUs = nowUs;
+        }
+    }
+#endif
+
+    return err;
+}
+
+status_t ANetworkSession::Session::sendRequest(
+        const void *data, ssize_t size, bool timeValid, int64_t timeUs) {
+    CHECK(mState == CONNECTED || mState == DATAGRAM);
+
+    if (size < 0) {
+        size = strlen((const char *)data);
+    }
+
+    if (size == 0) {
+        return OK;
+    }
+
+    sp<ABuffer> buffer;
+
+    if (mState == CONNECTED && mMode == MODE_DATAGRAM) {
+        CHECK_LE(size, 65535);
+
+        buffer = new ABuffer(size + 2);
+        buffer->data()[0] = size >> 8;
+        buffer->data()[1] = size & 0xff;
+        memcpy(buffer->data() + 2, data, size);
+    } else if (mState == CONNECTED && mMode == MODE_WEBSOCKET) {
+        static const bool kUseMask = false;  // Chromium doesn't like it.
+
+        size_t numHeaderBytes = 2 + (kUseMask ? 4 : 0);
+        if (size > 65535) {
+            numHeaderBytes += 8;
+        } else if (size > 125) {
+            numHeaderBytes += 2;
+        }
+
+        buffer = new ABuffer(numHeaderBytes + size);
+        buffer->data()[0] = 0x81;  // FIN==1 | opcode=1 (text)
+        buffer->data()[1] = kUseMask ? 0x80 : 0x00;
+
+        if (size > 65535) {
+            buffer->data()[1] |= 127;
+            buffer->data()[2] = 0x00;
+            buffer->data()[3] = 0x00;
+            buffer->data()[4] = 0x00;
+            buffer->data()[5] = 0x00;
+            buffer->data()[6] = (size >> 24) & 0xff;
+            buffer->data()[7] = (size >> 16) & 0xff;
+            buffer->data()[8] = (size >> 8) & 0xff;
+            buffer->data()[9] = size & 0xff;
+        } else if (size > 125) {
+            buffer->data()[1] |= 126;
+            buffer->data()[2] = (size >> 8) & 0xff;
+            buffer->data()[3] = size & 0xff;
+        } else {
+            buffer->data()[1] |= size;
+        }
+
+        if (kUseMask) {
+            uint32_t mask = rand();
+
+            buffer->data()[numHeaderBytes - 4] = (mask >> 24) & 0xff;
+            buffer->data()[numHeaderBytes - 3] = (mask >> 16) & 0xff;
+            buffer->data()[numHeaderBytes - 2] = (mask >> 8) & 0xff;
+            buffer->data()[numHeaderBytes - 1] = mask & 0xff;
+
+            for (size_t i = 0; i < (size_t)size; ++i) {
+                buffer->data()[numHeaderBytes + i] =
+                    ((const uint8_t *)data)[i]
+                        ^ ((mask >> (8 * (3 - (i % 4)))) & 0xff);
+            }
+        } else {
+            memcpy(buffer->data() + numHeaderBytes, data, size);
+        }
+    } else {
+        buffer = new ABuffer(size);
+        memcpy(buffer->data(), data, size);
+    }
+
+    Fragment frag;
+
+    frag.mFlags = 0;
+    if (timeValid) {
+        frag.mFlags = FRAGMENT_FLAG_TIME_VALID;
+        frag.mTimeUs = timeUs;
+    }
+
+    frag.mBuffer = buffer;
+
+    mOutFragments.push_back(frag);
+
+    return OK;
+}
+
+void ANetworkSession::Session::notifyError(
+        bool send, status_t err, const char *detail) {
+    sp<AMessage> msg = mNotify->dup();
+    msg->setInt32("sessionID", mSessionID);
+    msg->setInt32("reason", kWhatError);
+    msg->setInt32("send", send);
+    msg->setInt32("err", err);
+    msg->setString("detail", detail);
+    msg->post();
+}
+
+void ANetworkSession::Session::notify(NotificationReason reason) {
+    sp<AMessage> msg = mNotify->dup();
+    msg->setInt32("sessionID", mSessionID);
+    msg->setInt32("reason", reason);
+    msg->post();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ANetworkSession::ANetworkSession()
+    : mNextSessionID(1) {
+    mPipeFd[0] = mPipeFd[1] = -1;
+}
+
+ANetworkSession::~ANetworkSession() {
+    stop();
+}
+
+status_t ANetworkSession::start() {
+    if (mThread != NULL) {
+        return INVALID_OPERATION;
+    }
+
+    int res = pipe(mPipeFd);
+    if (res != 0) {
+        mPipeFd[0] = mPipeFd[1] = -1;
+        return -errno;
+    }
+
+    mThread = new NetworkThread(this);
+
+    status_t err = mThread->run("ANetworkSession");
+
+    if (err != OK) {
+        mThread.clear();
+
+        close(mPipeFd[0]);
+        close(mPipeFd[1]);
+        mPipeFd[0] = mPipeFd[1] = -1;
+
+        return err;
+    }
+
+    return OK;
+}
+
+status_t ANetworkSession::stop() {
+    if (mThread == NULL) {
+        return INVALID_OPERATION;
+    }
+
+    mThread->requestExit();
+    interrupt();
+    mThread->requestExitAndWait();
+
+    mThread.clear();
+
+    close(mPipeFd[0]);
+    close(mPipeFd[1]);
+    mPipeFd[0] = mPipeFd[1] = -1;
+
+    return OK;
+}
+
+status_t ANetworkSession::createRTSPClient(
+        const char *host, unsigned port, const sp<AMessage> &notify,
+        int32_t *sessionID) {
+    return createClientOrServer(
+            kModeCreateRTSPClient,
+            NULL /* addr */,
+            0 /* port */,
+            host,
+            port,
+            notify,
+            sessionID);
+}
+
+status_t ANetworkSession::createRTSPServer(
+        const struct in_addr &addr, unsigned port,
+        const sp<AMessage> &notify, int32_t *sessionID) {
+    return createClientOrServer(
+            kModeCreateRTSPServer,
+            &addr,
+            port,
+            NULL /* remoteHost */,
+            0 /* remotePort */,
+            notify,
+            sessionID);
+}
+
+status_t ANetworkSession::createUDPSession(
+        unsigned localPort, const sp<AMessage> &notify, int32_t *sessionID) {
+    return createUDPSession(localPort, NULL, 0, notify, sessionID);
+}
+
+status_t ANetworkSession::createUDPSession(
+        unsigned localPort,
+        const char *remoteHost,
+        unsigned remotePort,
+        const sp<AMessage> &notify,
+        int32_t *sessionID) {
+    return createClientOrServer(
+            kModeCreateUDPSession,
+            NULL /* addr */,
+            localPort,
+            remoteHost,
+            remotePort,
+            notify,
+            sessionID);
+}
+
+status_t ANetworkSession::createTCPDatagramSession(
+        const struct in_addr &addr, unsigned port,
+        const sp<AMessage> &notify, int32_t *sessionID) {
+    return createClientOrServer(
+            kModeCreateTCPDatagramSessionPassive,
+            &addr,
+            port,
+            NULL /* remoteHost */,
+            0 /* remotePort */,
+            notify,
+            sessionID);
+}
+
+status_t ANetworkSession::createTCPDatagramSession(
+        unsigned localPort,
+        const char *remoteHost,
+        unsigned remotePort,
+        const sp<AMessage> &notify,
+        int32_t *sessionID) {
+    return createClientOrServer(
+            kModeCreateTCPDatagramSessionActive,
+            NULL /* addr */,
+            localPort,
+            remoteHost,
+            remotePort,
+            notify,
+            sessionID);
+}
+
+status_t ANetworkSession::destroySession(int32_t sessionID) {
+    Mutex::Autolock autoLock(mLock);
+
+    auto it = mSessions.find(sessionID);
+
+    if (it == mSessions.end()) {
+        return -ENOENT;
+    }
+
+    mSessions.erase(it);
+
+    interrupt();
+
+    return OK;
+}
+
+// static
+status_t ANetworkSession::MakeSocketNonBlocking(int s) {
+    int flags = fcntl(s, F_GETFL, 0);
+    if (flags < 0) {
+        flags = 0;
+    }
+
+    int res = fcntl(s, F_SETFL, flags | O_NONBLOCK);
+    if (res < 0) {
+        return -errno;
+    }
+
+    return OK;
+}
+
+status_t ANetworkSession::createClientOrServer(
+        Mode mode,
+        const struct in_addr *localAddr,
+        unsigned port,
+        const char *remoteHost,
+        unsigned remotePort,
+        const sp<AMessage> &notify,
+        int32_t *sessionID) {
+    Mutex::Autolock autoLock(mLock);
+
+    *sessionID = 0;
+    status_t err = OK;
+    int s, res;
+    sp<Session> session;
+
+    s = socket(
+            AF_INET,
+            (mode == kModeCreateUDPSession) ? SOCK_DGRAM : SOCK_STREAM,
+            0);
+
+    if (s < 0) {
+        err = -errno;
+        goto bail;
+    }
+
+    if (mode == kModeCreateRTSPServer
+            || mode == kModeCreateTCPDatagramSessionPassive) {
+        const int yes = 1;
+        res = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
+
+        if (res < 0) {
+            err = -errno;
+            goto bail2;
+        }
+    }
+
+    if (mode == kModeCreateUDPSession) {
+        int size = 256 * 1024;
+
+        res = setsockopt(s, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
+
+        if (res < 0) {
+            err = -errno;
+            goto bail2;
+        }
+
+        res = setsockopt(s, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
+
+        if (res < 0) {
+            err = -errno;
+            goto bail2;
+        }
+    } else if (mode == kModeCreateTCPDatagramSessionActive) {
+#if 0
+        int flag = 1;
+        res = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
+
+        if (res < 0) {
+            err = -errno;
+            goto bail2;
+        }
+#endif
+
+        int tos = 224;  // VOICE
+        res = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
+
+        if (res < 0) {
+            err = -errno;
+            goto bail2;
+        }
+    }
+
+    err = MakeSocketNonBlocking(s);
+
+    if (err != OK) {
+        goto bail2;
+    }
+
+    struct sockaddr_in addr;
+    memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
+    addr.sin_family = AF_INET;
+
+    if (mode == kModeCreateRTSPClient
+            || mode == kModeCreateTCPDatagramSessionActive) {
+        struct hostent *ent= gethostbyname(remoteHost);
+        if (ent == NULL) {
+            err = -h_errno;
+            goto bail2;
+        }
+
+        addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
+        addr.sin_port = htons(remotePort);
+    } else if (localAddr != NULL) {
+        addr.sin_addr = *localAddr;
+        addr.sin_port = htons(port);
+    } else {
+        addr.sin_addr.s_addr = htonl(INADDR_ANY);
+        addr.sin_port = htons(port);
+    }
+
+    if (mode == kModeCreateRTSPClient
+            || mode == kModeCreateTCPDatagramSessionActive) {
+        in_addr_t x = ntohl(addr.sin_addr.s_addr);
+        ALOGI("connecting socket %d to %d.%d.%d.%d:%d",
+              s,
+              (x >> 24),
+              (x >> 16) & 0xff,
+              (x >> 8) & 0xff,
+              x & 0xff,
+              ntohs(addr.sin_port));
+
+        res = connect(s, (const struct sockaddr *)&addr, sizeof(addr));
+
+        CHECK_LT(res, 0);
+        if (errno == EINPROGRESS) {
+            res = 0;
+        }
+    } else {
+        res = bind(s, (const struct sockaddr *)&addr, sizeof(addr));
+
+        if (res == 0) {
+            if (mode == kModeCreateRTSPServer
+                    || mode == kModeCreateTCPDatagramSessionPassive) {
+                res = listen(s, 4);
+            } else {
+                CHECK_EQ(mode, kModeCreateUDPSession);
+
+                if (remoteHost != NULL) {
+                    struct sockaddr_in remoteAddr;
+                    memset(remoteAddr.sin_zero, 0, sizeof(remoteAddr.sin_zero));
+                    remoteAddr.sin_family = AF_INET;
+                    remoteAddr.sin_port = htons(remotePort);
+
+                    struct hostent *ent= gethostbyname(remoteHost);
+                    if (ent == NULL) {
+                        err = -h_errno;
+                        goto bail2;
+                    }
+
+                    remoteAddr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
+
+                    res = connect(
+                            s,
+                            (const struct sockaddr *)&remoteAddr,
+                            sizeof(remoteAddr));
+                }
+            }
+        }
+    }
+
+    if (res < 0) {
+        err = -errno;
+        goto bail2;
+    }
+
+    Session::State state;
+    switch (mode) {
+        case kModeCreateRTSPClient:
+            state = Session::CONNECTING;
+            break;
+
+        case kModeCreateTCPDatagramSessionActive:
+            state = Session::CONNECTING;
+            break;
+
+        case kModeCreateTCPDatagramSessionPassive:
+            state = Session::LISTENING_TCP_DGRAMS;
+            break;
+
+        case kModeCreateRTSPServer:
+            state = Session::LISTENING_RTSP;
+            break;
+
+        default:
+            CHECK_EQ(mode, kModeCreateUDPSession);
+            state = Session::DATAGRAM;
+            break;
+    }
+
+    session = new Session(
+            mNextSessionID++,
+            state,
+            s,
+            notify);
+
+    if (mode == kModeCreateTCPDatagramSessionActive) {
+        session->setMode(Session::MODE_DATAGRAM);
+    } else if (mode == kModeCreateRTSPClient) {
+        session->setMode(Session::MODE_RTSP);
+    }
+
+    mSessions[session->sessionID()] = session;
+
+    interrupt();
+
+    *sessionID = session->sessionID();
+
+    goto bail;
+
+bail2:
+    close(s);
+    s = -1;
+
+bail:
+    return err;
+}
+
+status_t ANetworkSession::connectUDPSession(
+        int32_t sessionID, const char *remoteHost, unsigned remotePort) {
+    Mutex::Autolock autoLock(mLock);
+
+    auto it = mSessions.find(sessionID);
+
+    if (it == mSessions.end()) {
+        return -ENOENT;
+    }
+
+    const sp<Session> session = it->second;
+    int s = session->socket();
+
+    struct sockaddr_in remoteAddr;
+    memset(remoteAddr.sin_zero, 0, sizeof(remoteAddr.sin_zero));
+    remoteAddr.sin_family = AF_INET;
+    remoteAddr.sin_port = htons(remotePort);
+
+    status_t err = OK;
+    struct hostent *ent = gethostbyname(remoteHost);
+    if (ent == NULL) {
+        err = -h_errno;
+    } else {
+        remoteAddr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
+
+        int res = connect(
+                s,
+                (const struct sockaddr *)&remoteAddr,
+                sizeof(remoteAddr));
+
+        if (res < 0) {
+            err = -errno;
+        }
+    }
+
+    return err;
+}
+
+status_t ANetworkSession::sendRequest(
+        int32_t sessionID, const void *data, ssize_t size,
+        bool timeValid, int64_t timeUs) {
+    Mutex::Autolock autoLock(mLock);
+
+    auto it = mSessions.find(sessionID);
+
+    if (it == mSessions.end()) {
+        return -ENOENT;
+    }
+
+    const sp<Session> session = it->second;
+
+    status_t err = session->sendRequest(data, size, timeValid, timeUs);
+
+    interrupt();
+
+    return err;
+}
+
+status_t ANetworkSession::switchToWebSocketMode(int32_t sessionID) {
+    Mutex::Autolock autoLock(mLock);
+
+    auto it = mSessions.find(sessionID);
+
+    if (it == mSessions.end()) {
+        return -ENOENT;
+    }
+
+    const sp<Session> session = it->second;
+    return session->switchToWebSocketMode();
+}
+
+void ANetworkSession::interrupt() {
+    static const char dummy = 0;
+
+    ssize_t n;
+    do {
+        n = write(mPipeFd[1], &dummy, 1);
+    } while (n < 0 && errno == EINTR);
+
+    if (n < 0) {
+        ALOGW("Error writing to pipe (%s)", strerror(errno));
+    }
+}
+
+void ANetworkSession::threadLoop() {
+    fd_set rs, ws;
+    FD_ZERO(&rs);
+    FD_ZERO(&ws);
+
+    FD_SET(mPipeFd[0], &rs);
+    int maxFd = mPipeFd[0];
+
+    {
+        Mutex::Autolock autoLock(mLock);
+
+        for (const auto &pair : mSessions) {
+            const sp<Session> &session = pair.second;
+
+            int s = session->socket();
+
+            if (s < 0) {
+                continue;
+            }
+
+            if (session->wantsToRead()) {
+                FD_SET(s, &rs);
+                if (s > maxFd) {
+                    maxFd = s;
+                }
+            }
+
+            if (session->wantsToWrite()) {
+                FD_SET(s, &ws);
+                if (s > maxFd) {
+                    maxFd = s;
+                }
+            }
+        }
+    }
+
+    int res = select(maxFd + 1, &rs, &ws, NULL, NULL /* tv */);
+
+    if (res == 0) {
+        return;
+    }
+
+    if (res < 0) {
+        if (errno == EINTR) {
+            return;
+        }
+
+        ALOGE("select failed w/ error %d (%s)", errno, strerror(errno));
+        return;
+    }
+
+    if (FD_ISSET(mPipeFd[0], &rs)) {
+        char c;
+        ssize_t n;
+        do {
+            n = read(mPipeFd[0], &c, 1);
+        } while (n < 0 && errno == EINTR);
+
+        if (n < 0) {
+            ALOGW("Error reading from pipe (%s)", strerror(errno));
+        }
+
+        --res;
+    }
+
+    {
+        Mutex::Autolock autoLock(mLock);
+
+        std::list<sp<Session>> sessionsToAdd;
+
+        for (const auto &pair : mSessions) {
+            if (res <= 0) {
+                break;
+            }
+
+            const sp<Session> &session = pair.second;
+
+            int s = session->socket();
+
+            if (s < 0) {
+                continue;
+            }
+
+            if (FD_ISSET(s, &rs) || FD_ISSET(s, &ws)) {
+                --res;
+            }
+
+            if (FD_ISSET(s, &rs)) {
+                if (session->isRTSPServer() || session->isTCPDatagramServer()) {
+                    struct sockaddr_in remoteAddr;
+                    socklen_t remoteAddrLen = sizeof(remoteAddr);
+
+                    int clientSocket = accept(
+                            s, (struct sockaddr *)&remoteAddr, &remoteAddrLen);
+
+                    if (clientSocket >= 0) {
+                        status_t err = MakeSocketNonBlocking(clientSocket);
+
+                        if (err != OK) {
+                            ALOGE("Unable to make client socket non blocking, "
+                                  "failed w/ error %d (%s)",
+                                  err, strerror(-err));
+
+                            close(clientSocket);
+                            clientSocket = -1;
+                        } else {
+                            in_addr_t addr = ntohl(remoteAddr.sin_addr.s_addr);
+
+                            ALOGI("incoming connection from %d.%d.%d.%d:%d "
+                                  "(socket %d)",
+                                  (addr >> 24),
+                                  (addr >> 16) & 0xff,
+                                  (addr >> 8) & 0xff,
+                                  addr & 0xff,
+                                  ntohs(remoteAddr.sin_port),
+                                  clientSocket);
+
+                            sp<Session> clientSession =
+                                new Session(
+                                        mNextSessionID++,
+                                        Session::CONNECTED,
+                                        clientSocket,
+                                        session->getNotificationMessage());
+
+                            clientSession->setMode(
+                                    session->isRTSPServer()
+                                        ? Session::MODE_RTSP
+                                        : Session::MODE_DATAGRAM);
+
+                            sessionsToAdd.push_back(clientSession);
+                        }
+                    } else {
+                        ALOGE("accept returned error %d (%s)",
+                              errno, strerror(errno));
+                    }
+                } else {
+                    status_t err = session->readMore();
+                    if (err != OK) {
+                        ALOGE("readMore on socket %d failed w/ error %d (%s)",
+                              s, err, strerror(-err));
+                    }
+                }
+            }
+
+            if (FD_ISSET(s, &ws)) {
+                status_t err = session->writeMore();
+                if (err != OK) {
+                    ALOGE("writeMore on socket %d failed w/ error %d (%s)",
+                          s, err, strerror(-err));
+                }
+            }
+        }
+
+        while (!sessionsToAdd.empty()) {
+            sp<Session> session = *sessionsToAdd.begin();
+            sessionsToAdd.erase(sessionsToAdd.begin());
+
+            mSessions[session->sessionID()] = session;
+
+            ALOGI("added clientSession %d", session->sessionID());
+        }
+    }
+}
+
+}  // namespace android
+
diff --git a/host/frontend/gcastv2/libandroid/ATSParser.cpp b/host/frontend/gcastv2/libandroid/ATSParser.cpp
new file mode 100644
index 0000000..2275f5d
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/ATSParser.cpp
@@ -0,0 +1,1308 @@
+/*
+ * 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 "ATSParser"
+#include <utils/Log.h>
+
+#include <media/stagefright/ATSParser.h>
+
+#include "ESQueue.h"
+
+#include <media/stagefright/avc_utils.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/foundation/hexdump.h>
+#include <media/stagefright/AnotherPacketSource.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+#include <map>
+#include <vector>
+
+namespace android {
+
+// I want the expression "y" evaluated even if verbose logging is off.
+#define MY_LOGV(x, y) \
+    do { unsigned tmp = y; ALOGV(x, tmp); tmp = tmp; } while (0)
+
+static const size_t kTSPacketSize = 188;
+
+struct ATSParser::Program : public RefBase {
+    Program(ATSParser *parser, unsigned programNumber, unsigned programMapPID);
+
+    bool parsePSISection(
+            unsigned pid, ABitReader *br, status_t *err);
+
+    bool parsePID(
+            unsigned pid, unsigned continuity_counter,
+            unsigned payload_unit_start_indicator,
+            ABitReader *br, status_t *err);
+
+    void signalDiscontinuity(
+            DiscontinuityType type, const sp<AMessage> &extra);
+
+    void signalEOS(status_t finalResult);
+
+    sp<AnotherPacketSource> getSource(SourceType type);
+
+    int64_t convertPTSToTimestamp(uint64_t PTS);
+
+    bool PTSTimeDeltaEstablished() const {
+        return mFirstPTSValid;
+    }
+
+    unsigned number() const { return mProgramNumber; }
+
+    void updateProgramMapPID(unsigned programMapPID) {
+        mProgramMapPID = programMapPID;
+    }
+
+    unsigned programMapPID() const {
+        return mProgramMapPID;
+    }
+
+    uint32_t parserFlags() const {
+        return mParser->mFlags;
+    }
+
+private:
+    ATSParser *mParser;
+    unsigned mProgramNumber;
+    unsigned mProgramMapPID;
+    std::map<unsigned, sp<Stream>> mStreams;
+    bool mFirstPTSValid;
+    uint64_t mFirstPTS;
+
+    status_t parseProgramMap(ABitReader *br);
+
+    DISALLOW_EVIL_CONSTRUCTORS(Program);
+};
+
+struct ATSParser::Stream : public RefBase {
+    Stream(Program *program,
+           unsigned elementaryPID,
+           unsigned streamType,
+           unsigned PCR_PID);
+
+    unsigned type() const { return mStreamType; }
+    unsigned pid() const { return mElementaryPID; }
+    void setPID(unsigned pid) { mElementaryPID = pid; }
+
+    status_t parse(
+            unsigned continuity_counter,
+            unsigned payload_unit_start_indicator,
+            ABitReader *br);
+
+    void signalDiscontinuity(
+            DiscontinuityType type, const sp<AMessage> &extra);
+
+    void signalEOS(status_t finalResult);
+
+    sp<AnotherPacketSource> getSource(SourceType type);
+
+protected:
+    virtual ~Stream();
+
+private:
+    Program *mProgram;
+    unsigned mElementaryPID;
+    unsigned mStreamType;
+    unsigned mPCR_PID;
+    int32_t mExpectedContinuityCounter;
+
+    sp<ABuffer> mBuffer;
+    sp<AnotherPacketSource> mSource;
+    bool mPayloadStarted;
+
+    uint64_t mPrevPTS;
+
+    ElementaryStreamQueue *mQueue;
+
+    status_t flush();
+    status_t parsePES(ABitReader *br);
+
+    void onPayloadData(
+            unsigned PTS_DTS_flags, uint64_t PTS, uint64_t DTS,
+            const uint8_t *data, size_t size);
+
+    void extractAACFrames(const sp<ABuffer> &buffer);
+
+    bool isAudio() const;
+    bool isVideo() const;
+
+    DISALLOW_EVIL_CONSTRUCTORS(Stream);
+};
+
+struct ATSParser::PSISection : public RefBase {
+    PSISection();
+
+    status_t append(const void *data, size_t size);
+    void clear();
+
+    bool isComplete() const;
+    bool isEmpty() const;
+
+    const uint8_t *data() const;
+    size_t size() const;
+
+protected:
+    virtual ~PSISection();
+
+private:
+    sp<ABuffer> mBuffer;
+
+    DISALLOW_EVIL_CONSTRUCTORS(PSISection);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+ATSParser::Program::Program(
+        ATSParser *parser, unsigned programNumber, unsigned programMapPID)
+    : mParser(parser),
+      mProgramNumber(programNumber),
+      mProgramMapPID(programMapPID),
+      mFirstPTSValid(false),
+      mFirstPTS(0) {
+    ALOGV("new program number %u", programNumber);
+}
+
+bool ATSParser::Program::parsePSISection(
+        unsigned pid, ABitReader *br, status_t *err) {
+    *err = OK;
+
+    if (pid != mProgramMapPID) {
+        return false;
+    }
+
+    *err = parseProgramMap(br);
+
+    return true;
+}
+
+bool ATSParser::Program::parsePID(
+        unsigned pid, unsigned continuity_counter,
+        unsigned payload_unit_start_indicator,
+        ABitReader *br, status_t *err) {
+    *err = OK;
+
+    auto it = mStreams.find(pid);
+    if (it == mStreams.end()) {
+        return false;
+    }
+
+    *err = it->second->parse(
+            continuity_counter, payload_unit_start_indicator, br);
+
+    return true;
+}
+
+void ATSParser::Program::signalDiscontinuity(
+        DiscontinuityType type, const sp<AMessage> &extra) {
+    for (const auto &pair : mStreams) {
+        pair.second->signalDiscontinuity(type, extra);
+    }
+}
+
+void ATSParser::Program::signalEOS(status_t finalResult) {
+    for (const auto &pair : mStreams) {
+        pair.second->signalEOS(finalResult);
+    }
+}
+
+struct StreamInfo {
+    unsigned mType;
+    unsigned mPID;
+};
+
+status_t ATSParser::Program::parseProgramMap(ABitReader *br) {
+    unsigned table_id = br->getBits(8);
+    ALOGV("  table_id = %u", table_id);
+    CHECK_EQ(table_id, 0x02u);
+
+    unsigned section_syntax_indicator = br->getBits(1);
+    ALOGV("  section_syntax_indicator = %u", section_syntax_indicator);
+    CHECK_EQ(section_syntax_indicator, 1u);
+
+    CHECK_EQ(br->getBits(1), 0u);
+    MY_LOGV("  reserved = %u", br->getBits(2));
+
+    unsigned section_length = br->getBits(12);
+    ALOGV("  section_length = %u", section_length);
+    CHECK_EQ(section_length & 0xc00, 0u);
+    CHECK_LE(section_length, 1021u);
+
+    MY_LOGV("  program_number = %u", br->getBits(16));
+    MY_LOGV("  reserved = %u", br->getBits(2));
+    MY_LOGV("  version_number = %u", br->getBits(5));
+    MY_LOGV("  current_next_indicator = %u", br->getBits(1));
+    MY_LOGV("  section_number = %u", br->getBits(8));
+    MY_LOGV("  last_section_number = %u", br->getBits(8));
+    MY_LOGV("  reserved = %u", br->getBits(3));
+
+    unsigned PCR_PID = br->getBits(13);
+    ALOGV("  PCR_PID = 0x%04x", PCR_PID);
+
+    MY_LOGV("  reserved = %u", br->getBits(4));
+
+    unsigned program_info_length = br->getBits(12);
+    ALOGV("  program_info_length = %u", program_info_length);
+    CHECK_EQ(program_info_length & 0xc00, 0u);
+
+    br->skipBits(program_info_length * 8);  // skip descriptors
+
+    std::vector<StreamInfo> infos;
+
+    // infoBytesRemaining is the number of bytes that make up the
+    // variable length section of ES_infos. It does not include the
+    // final CRC.
+    size_t infoBytesRemaining = section_length - 9 - program_info_length - 4;
+
+    while (infoBytesRemaining > 0) {
+        CHECK_GE(infoBytesRemaining, 5u);
+
+        unsigned streamType = br->getBits(8);
+        ALOGV("    stream_type = 0x%02x", streamType);
+
+        MY_LOGV("    reserved = %u", br->getBits(3));
+
+        unsigned elementaryPID = br->getBits(13);
+        ALOGV("    elementary_PID = 0x%04x", elementaryPID);
+
+        MY_LOGV("    reserved = %u", br->getBits(4));
+
+        unsigned ES_info_length = br->getBits(12);
+        ALOGV("    ES_info_length = %u", ES_info_length);
+        CHECK_EQ(ES_info_length & 0xc00, 0u);
+
+        CHECK_GE(infoBytesRemaining - 5, ES_info_length);
+
+#if 0
+        br->skipBits(ES_info_length * 8);  // skip descriptors
+#else
+        unsigned info_bytes_remaining = ES_info_length;
+        while (info_bytes_remaining >= 2) {
+            MY_LOGV("      tag = 0x%02x", br->getBits(8));
+
+            unsigned descLength = br->getBits(8);
+            ALOGV("      len = %u", descLength);
+
+            CHECK_GE(info_bytes_remaining, 2 + descLength);
+
+            br->skipBits(descLength * 8);
+
+            info_bytes_remaining -= descLength + 2;
+        }
+        CHECK_EQ(info_bytes_remaining, 0u);
+#endif
+
+        StreamInfo info;
+        info.mType = streamType;
+        info.mPID = elementaryPID;
+        infos.push_back(info);
+
+        infoBytesRemaining -= 5 + ES_info_length;
+    }
+
+    CHECK_EQ(infoBytesRemaining, 0u);
+    MY_LOGV("  CRC = 0x%08x", br->getBits(32));
+
+    bool PIDsChanged = false;
+    for (const auto &info : infos) {
+        auto it = mStreams.find(info.mPID);
+        if (it != mStreams.end() && it->second->type() != info.mType) {
+            ALOGI("%s", "uh oh. stream PIDs have changed.");
+            PIDsChanged = true;
+            break;
+        }
+    }
+
+    if (PIDsChanged) {
+#if 0
+        ALOGI("before:");
+        for (size_t i = 0; i < mStreams.size(); ++i) {
+            sp<Stream> stream = mStreams.editValueAt(i);
+
+            ALOGI("PID 0x%08x => type 0x%02x", stream->pid(), stream->type());
+        }
+
+        ALOGI("after:");
+        for (size_t i = 0; i < infos.size(); ++i) {
+            StreamInfo &info = infos.editItemAt(i);
+
+            ALOGI("PID 0x%08x => type 0x%02x", info.mPID, info.mType);
+        }
+#endif
+
+        // The only case we can recover from is if we have two streams
+        // and they switched PIDs.
+
+        bool success = false;
+
+        if (mStreams.size() == 2 && infos.size() == 2) {
+            const StreamInfo &info1 = infos.at(0);
+            const StreamInfo &info2 = infos.at(1);
+
+            sp<Stream> s1 = mStreams.begin()->second;
+            sp<Stream> s2 = (++mStreams.begin())->second;
+
+            bool caseA =
+                info1.mPID == s1->pid() && info1.mType == s2->type()
+                    && info2.mPID == s2->pid() && info2.mType == s1->type();
+
+            bool caseB =
+                info1.mPID == s2->pid() && info1.mType == s1->type()
+                    && info2.mPID == s1->pid() && info2.mType == s2->type();
+
+            if (caseA || caseB) {
+                unsigned pid1 = s1->pid();
+                unsigned pid2 = s2->pid();
+                s1->setPID(pid2);
+                s2->setPID(pid1);
+
+                mStreams.clear();
+                mStreams[s1->pid()] = s1;
+                mStreams[s2->pid()] = s2;
+
+                success = true;
+            }
+        }
+
+        if (!success) {
+            ALOGI("%s", "Stream PIDs changed and we cannot recover.");
+            return ERROR_MALFORMED;
+        }
+    }
+
+    for (const StreamInfo &info : infos) {
+        auto it = mStreams.find(info.mPID);
+        if (it == mStreams.end()) {
+            sp<Stream> stream = new Stream(
+                    this, info.mPID, info.mType, PCR_PID);
+
+            mStreams[info.mPID] = stream;
+        }
+    }
+
+    return OK;
+}
+
+sp<AnotherPacketSource> ATSParser::Program::getSource(SourceType type) {
+    size_t index = (type == AUDIO) ? 0 : 0;
+
+    for (const auto &pair : mStreams) {
+        sp<AnotherPacketSource> source = pair.second->getSource(type);
+        if (source != NULL) {
+            if (index == 0) {
+                return source;
+            }
+            --index;
+        }
+    }
+
+    return NULL;
+}
+
+int64_t ATSParser::Program::convertPTSToTimestamp(uint64_t PTS) {
+    if (mParser->mFlags & DUMP_PTS) {
+        ALOGI("PTS = 0x%016llx", PTS);
+    }
+
+    if (!(mParser->mFlags & TS_TIMESTAMPS_ARE_ABSOLUTE)) {
+        if (!mFirstPTSValid) {
+            mFirstPTSValid = true;
+            mFirstPTS = PTS;
+            PTS = 0;
+        } else if (PTS < mFirstPTS) {
+            PTS = 0;
+        } else {
+            PTS -= mFirstPTS;
+        }
+    }
+
+    int64_t timeUs = (PTS * 100) / 9;
+
+    if (mParser->mAbsoluteTimeAnchorUs >= 0ll) {
+        timeUs += mParser->mAbsoluteTimeAnchorUs;
+    }
+
+    if (mParser->mTimeOffsetValid) {
+        timeUs += mParser->mTimeOffsetUs;
+    }
+
+    return timeUs;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ATSParser::Stream::Stream(
+        Program *program,
+        unsigned elementaryPID,
+        unsigned streamType,
+        unsigned PCR_PID)
+    : mProgram(program),
+      mElementaryPID(elementaryPID),
+      mStreamType(streamType),
+      mPCR_PID(PCR_PID),
+      mExpectedContinuityCounter(-1),
+      mPayloadStarted(false),
+      mPrevPTS(0),
+      mQueue(NULL) {
+    switch (mStreamType) {
+        case STREAMTYPE_H264:
+            mQueue = new ElementaryStreamQueue(
+                    ElementaryStreamQueue::H264,
+                    (mProgram->parserFlags() & ALIGNED_VIDEO_DATA)
+                        ? ElementaryStreamQueue::kFlag_AlignedData : 0);
+            break;
+        case STREAMTYPE_MPEG2_AUDIO_ADTS:
+            mQueue = new ElementaryStreamQueue(ElementaryStreamQueue::AAC);
+            break;
+        case STREAMTYPE_MPEG1_AUDIO:
+        case STREAMTYPE_MPEG2_AUDIO:
+            mQueue = new ElementaryStreamQueue(
+                    ElementaryStreamQueue::MPEG_AUDIO);
+            break;
+
+        case STREAMTYPE_MPEG1_VIDEO:
+        case STREAMTYPE_MPEG2_VIDEO:
+            mQueue = new ElementaryStreamQueue(
+                    ElementaryStreamQueue::MPEG_VIDEO);
+            break;
+
+        case STREAMTYPE_MPEG4_VIDEO:
+            mQueue = new ElementaryStreamQueue(
+                    ElementaryStreamQueue::MPEG4_VIDEO);
+            break;
+
+        case STREAMTYPE_PCM_AUDIO:
+            mQueue = new ElementaryStreamQueue(
+                    ElementaryStreamQueue::PCM_AUDIO);
+            break;
+
+        default:
+            break;
+    }
+
+    ALOGV("new stream PID 0x%02x, type 0x%02x", elementaryPID, streamType);
+
+    if (mQueue != NULL) {
+        mBuffer = new ABuffer(192 * 1024);
+        mBuffer->setRange(0, 0);
+    }
+}
+
+ATSParser::Stream::~Stream() {
+    delete mQueue;
+    mQueue = NULL;
+}
+
+status_t ATSParser::Stream::parse(
+        unsigned continuity_counter,
+        unsigned payload_unit_start_indicator, ABitReader *br) {
+    if (mQueue == NULL) {
+        return OK;
+    }
+
+    if (mExpectedContinuityCounter >= 0
+            && (unsigned)mExpectedContinuityCounter != continuity_counter) {
+        ALOGI("discontinuity on stream pid 0x%04x", mElementaryPID);
+
+        mPayloadStarted = false;
+        mBuffer->setRange(0, 0);
+        mExpectedContinuityCounter = -1;
+
+#if 0
+        // Uncomment this if you'd rather see no corruption whatsoever on
+        // screen and suspend updates until we come across another IDR frame.
+
+        if (mStreamType == STREAMTYPE_H264) {
+            ALOGI("clearing video queue");
+            mQueue->clear(true /* clearFormat */);
+        }
+#endif
+
+        return OK;
+    }
+
+    mExpectedContinuityCounter = (continuity_counter + 1) & 0x0f;
+
+    if (payload_unit_start_indicator) {
+        if (mPayloadStarted) {
+            // Otherwise we run the danger of receiving the trailing bytes
+            // of a PES packet that we never saw the start of and assuming
+            // we have a a complete PES packet.
+
+            status_t err = flush();
+
+            if (err != OK) {
+                return err;
+            }
+        }
+
+        mPayloadStarted = true;
+    }
+
+    if (!mPayloadStarted) {
+        return OK;
+    }
+
+    size_t payloadSizeBits = br->numBitsLeft();
+    CHECK_EQ(payloadSizeBits % 8, 0u);
+
+    size_t neededSize = mBuffer->size() + payloadSizeBits / 8;
+    if (mBuffer->capacity() < neededSize) {
+        // Increment in multiples of 64K.
+        neededSize = (neededSize + 65535) & ~65535;
+
+        ALOGI("resizing buffer to %ld bytes", neededSize);
+
+        sp<ABuffer> newBuffer = new ABuffer(neededSize);
+        memcpy(newBuffer->data(), mBuffer->data(), mBuffer->size());
+        newBuffer->setRange(0, mBuffer->size());
+        mBuffer = newBuffer;
+    }
+
+    memcpy(mBuffer->data() + mBuffer->size(), br->data(), payloadSizeBits / 8);
+    mBuffer->setRange(0, mBuffer->size() + payloadSizeBits / 8);
+
+    return OK;
+}
+
+bool ATSParser::Stream::isVideo() const {
+    switch (mStreamType) {
+        case STREAMTYPE_H264:
+        case STREAMTYPE_MPEG1_VIDEO:
+        case STREAMTYPE_MPEG2_VIDEO:
+        case STREAMTYPE_MPEG4_VIDEO:
+            return true;
+
+        default:
+            return false;
+    }
+}
+
+bool ATSParser::Stream::isAudio() const {
+    switch (mStreamType) {
+        case STREAMTYPE_MPEG1_AUDIO:
+        case STREAMTYPE_MPEG2_AUDIO:
+        case STREAMTYPE_MPEG2_AUDIO_ADTS:
+        case STREAMTYPE_PCM_AUDIO:
+            return true;
+
+        default:
+            return false;
+    }
+}
+
+void ATSParser::Stream::signalDiscontinuity(
+        DiscontinuityType type, const sp<AMessage> &extra) {
+    mExpectedContinuityCounter = -1;
+
+    if (mQueue == NULL) {
+        return;
+    }
+
+    mPayloadStarted = false;
+    mBuffer->setRange(0, 0);
+
+    bool clearFormat = false;
+    if (isAudio()) {
+        if (type & DISCONTINUITY_AUDIO_FORMAT) {
+            clearFormat = true;
+        }
+    } else {
+        if (type & DISCONTINUITY_VIDEO_FORMAT) {
+            clearFormat = true;
+        }
+    }
+
+    mQueue->clear(clearFormat);
+
+    if (mSource != NULL) {
+        mSource->queueDiscontinuity(type, extra);
+    }
+}
+
+void ATSParser::Stream::signalEOS(status_t finalResult) {
+    if (mSource != NULL) {
+        mSource->signalEOS(finalResult);
+    }
+}
+
+status_t ATSParser::Stream::parsePES(ABitReader *br) {
+    unsigned packet_startcode_prefix = br->getBits(24);
+
+    ALOGV("packet_startcode_prefix = 0x%08x", packet_startcode_prefix);
+
+    if (packet_startcode_prefix != 1) {
+        ALOGV("Supposedly payload_unit_start=1 unit does not start "
+             "with startcode.");
+
+        return ERROR_MALFORMED;
+    }
+
+    CHECK_EQ(packet_startcode_prefix, 0x000001u);
+
+    unsigned stream_id = br->getBits(8);
+    ALOGV("stream_id = 0x%02x", stream_id);
+
+    unsigned PES_packet_length = br->getBits(16);
+    ALOGV("PES_packet_length = %u", PES_packet_length);
+
+    if (stream_id != 0xbc  // program_stream_map
+            && stream_id != 0xbe  // padding_stream
+            && stream_id != 0xbf  // private_stream_2
+            && stream_id != 0xf0  // ECM
+            && stream_id != 0xf1  // EMM
+            && stream_id != 0xff  // program_stream_directory
+            && stream_id != 0xf2  // DSMCC
+            && stream_id != 0xf8) {  // H.222.1 type E
+        CHECK_EQ(br->getBits(2), 2u);
+
+        MY_LOGV("PES_scrambling_control = %u", br->getBits(2));
+        MY_LOGV("PES_priority = %u", br->getBits(1));
+        MY_LOGV("data_alignment_indicator = %u", br->getBits(1));
+        MY_LOGV("copyright = %u", br->getBits(1));
+        MY_LOGV("original_or_copy = %u", br->getBits(1));
+
+        unsigned PTS_DTS_flags = br->getBits(2);
+        ALOGV("PTS_DTS_flags = %u", PTS_DTS_flags);
+
+        unsigned ESCR_flag = br->getBits(1);
+        ALOGV("ESCR_flag = %u", ESCR_flag);
+
+        unsigned ES_rate_flag = br->getBits(1);
+        ALOGV("ES_rate_flag = %u", ES_rate_flag);
+
+        MY_LOGV("DSM_trick_mode_flag = %u", br->getBits(1));
+        MY_LOGV("additional_copy_info_flag = %u", br->getBits(1));
+        MY_LOGV("PES_CRC_flag = %u", br->getBits(1));
+        MY_LOGV("PES_extension_flag = %u", br->getBits(1));
+
+        unsigned PES_header_data_length = br->getBits(8);
+        ALOGV("PES_header_data_length = %u", PES_header_data_length);
+
+        unsigned optional_bytes_remaining = PES_header_data_length;
+
+        uint64_t PTS = 0, DTS = 0;
+
+        if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) {
+            CHECK_GE(optional_bytes_remaining, 5u);
+
+            CHECK_EQ(br->getBits(4), PTS_DTS_flags);
+
+            PTS = ((uint64_t)br->getBits(3)) << 30;
+            CHECK_EQ(br->getBits(1), 1u);
+            PTS |= ((uint64_t)br->getBits(15)) << 15;
+            CHECK_EQ(br->getBits(1), 1u);
+            PTS |= br->getBits(15);
+            CHECK_EQ(br->getBits(1), 1u);
+
+            ALOGV("PTS = 0x%016llx (%.2f)", PTS, PTS / 90000.0);
+
+            optional_bytes_remaining -= 5;
+
+            if (PTS_DTS_flags == 3) {
+                CHECK_GE(optional_bytes_remaining, 5u);
+
+                CHECK_EQ(br->getBits(4), 1u);
+
+                DTS = ((uint64_t)br->getBits(3)) << 30;
+                CHECK_EQ(br->getBits(1), 1u);
+                DTS |= ((uint64_t)br->getBits(15)) << 15;
+                CHECK_EQ(br->getBits(1), 1u);
+                DTS |= br->getBits(15);
+                CHECK_EQ(br->getBits(1), 1u);
+
+                ALOGV("DTS = %llu", DTS);
+
+                optional_bytes_remaining -= 5;
+            }
+        }
+
+        if (ESCR_flag) {
+            CHECK_GE(optional_bytes_remaining, 6u);
+
+            br->getBits(2);
+
+            uint64_t ESCR = ((uint64_t)br->getBits(3)) << 30;
+            CHECK_EQ(br->getBits(1), 1u);
+            ESCR |= ((uint64_t)br->getBits(15)) << 15;
+            CHECK_EQ(br->getBits(1), 1u);
+            ESCR |= br->getBits(15);
+            CHECK_EQ(br->getBits(1), 1u);
+
+            ALOGV("ESCR = %llu", ESCR);
+            MY_LOGV("ESCR_extension = %u", br->getBits(9));
+
+            CHECK_EQ(br->getBits(1), 1u);
+
+            optional_bytes_remaining -= 6;
+        }
+
+        if (ES_rate_flag) {
+            CHECK_GE(optional_bytes_remaining, 3u);
+
+            CHECK_EQ(br->getBits(1), 1u);
+            MY_LOGV("ES_rate = %u", br->getBits(22));
+            CHECK_EQ(br->getBits(1), 1u);
+
+            optional_bytes_remaining -= 3;
+        }
+
+        br->skipBits(optional_bytes_remaining * 8);
+
+        // ES data follows.
+
+        if (PES_packet_length != 0) {
+            CHECK_GE(PES_packet_length, PES_header_data_length + 3);
+
+            unsigned dataLength =
+                PES_packet_length - 3 - PES_header_data_length;
+
+            if (br->numBitsLeft() < dataLength * 8) {
+                ALOGE("PES packet does not carry enough data to contain "
+                     "payload. (numBitsLeft = %ld, required = %d)",
+                     br->numBitsLeft(), dataLength * 8);
+
+                return ERROR_MALFORMED;
+            }
+
+            CHECK_GE(br->numBitsLeft(), dataLength * 8);
+
+            onPayloadData(
+                    PTS_DTS_flags, PTS, DTS, br->data(), dataLength);
+
+            br->skipBits(dataLength * 8);
+        } else {
+            onPayloadData(
+                    PTS_DTS_flags, PTS, DTS,
+                    br->data(), br->numBitsLeft() / 8);
+
+            size_t payloadSizeBits = br->numBitsLeft();
+            CHECK_EQ(payloadSizeBits % 8, 0u);
+
+            ALOGV("There's %d bytes of payload.", payloadSizeBits / 8);
+        }
+    } else if (stream_id == 0xbe) {  // padding_stream
+        CHECK_NE(PES_packet_length, 0u);
+        br->skipBits(PES_packet_length * 8);
+    } else {
+        CHECK_NE(PES_packet_length, 0u);
+        br->skipBits(PES_packet_length * 8);
+    }
+
+    return OK;
+}
+
+status_t ATSParser::Stream::flush() {
+    if (mBuffer->size() == 0) {
+        return OK;
+    }
+
+    ALOGV("flushing stream 0x%04x size = %d", mElementaryPID, mBuffer->size());
+
+    ABitReader br(mBuffer->data(), mBuffer->size());
+
+    status_t err = parsePES(&br);
+
+    mBuffer->setRange(0, 0);
+
+    return err;
+}
+
+void ATSParser::Stream::onPayloadData(
+        unsigned PTS_DTS_flags, uint64_t PTS, uint64_t /* DTS */,
+        const uint8_t *data, size_t size) {
+#if 0
+    ALOGI("payload streamType 0x%02x, PTS = 0x%016llx, dPTS = %lld",
+          mStreamType,
+          PTS,
+          (int64_t)PTS - mPrevPTS);
+    mPrevPTS = PTS;
+#endif
+
+    ALOGV("onPayloadData mStreamType=0x%02x", mStreamType);
+
+    int64_t timeUs = 0ll;  // no presentation timestamp available.
+    if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) {
+        timeUs = mProgram->convertPTSToTimestamp(PTS);
+    }
+
+    status_t err = mQueue->appendData(data, size, timeUs);
+
+    if (err != OK) {
+        return;
+    }
+
+    sp<ABuffer> accessUnit;
+    while ((accessUnit = mQueue->dequeueAccessUnit()) != NULL) {
+        if (mSource == NULL) {
+            sp<MetaData> meta = mQueue->getFormat();
+
+            if (meta != NULL) {
+                ALOGV("Stream PID 0x%08x of type 0x%02x now has data.",
+                     mElementaryPID, mStreamType);
+
+                mSource = new AnotherPacketSource(meta);
+                mSource->queueAccessUnit(accessUnit);
+            }
+        } else if (mQueue->getFormat() != NULL) {
+            // After a discontinuity we invalidate the queue's format
+            // and won't enqueue any access units to the source until
+            // the queue has reestablished the new format.
+
+            if (mSource->getFormat() == NULL) {
+                mSource->setFormat(mQueue->getFormat());
+            }
+            mSource->queueAccessUnit(accessUnit);
+        }
+    }
+}
+
+sp<AnotherPacketSource> ATSParser::Stream::getSource(SourceType type) {
+    switch (type) {
+        case VIDEO:
+        {
+            if (isVideo()) {
+                return mSource;
+            }
+            break;
+        }
+
+        case AUDIO:
+        {
+            if (isAudio()) {
+                return mSource;
+            }
+            break;
+        }
+
+        default:
+            break;
+    }
+
+    return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ATSParser::ATSParser(uint32_t flags)
+    : mFlags(flags),
+      mAbsoluteTimeAnchorUs(-1ll),
+      mTimeOffsetValid(false),
+      mTimeOffsetUs(0ll),
+      mNumTSPacketsParsed(0),
+      mNumPCRs(0) {
+    mPSISections[0 /* PID */] = new PSISection;
+}
+
+ATSParser::~ATSParser() {
+}
+
+status_t ATSParser::feedTSPacket(const void *data, size_t size) {
+    CHECK_EQ(size, kTSPacketSize);
+
+    ABitReader br((const uint8_t *)data, kTSPacketSize);
+    return parseTS(&br);
+}
+
+void ATSParser::signalDiscontinuity(
+        DiscontinuityType type, const sp<AMessage> &extra) {
+    if (type == DISCONTINUITY_ABSOLUTE_TIME) {
+        int64_t timeUs;
+        CHECK(extra->findInt64("timeUs", &timeUs));
+
+        CHECK(mPrograms.empty());
+        mAbsoluteTimeAnchorUs = timeUs;
+        return;
+    } else if (type == DISCONTINUITY_TIME_OFFSET) {
+        int64_t offset;
+        CHECK(extra->findInt64("offset", &offset));
+
+        mTimeOffsetValid = true;
+        mTimeOffsetUs = offset;
+        return;
+    }
+
+    for (const auto &program : mPrograms) {
+        program->signalDiscontinuity(type, extra);
+    }
+}
+
+void ATSParser::signalEOS(status_t finalResult) {
+    CHECK_NE(finalResult, (status_t)OK);
+
+    for (const auto &program : mPrograms) {
+        program->signalEOS(finalResult);
+    }
+}
+
+void ATSParser::parseProgramAssociationTable(ABitReader *br) {
+    unsigned table_id = br->getBits(8);
+    ALOGV("  table_id = %u", table_id);
+    CHECK_EQ(table_id, 0x00u);
+
+    unsigned section_syntax_indictor = br->getBits(1);
+    ALOGV("  section_syntax_indictor = %u", section_syntax_indictor);
+    CHECK_EQ(section_syntax_indictor, 1u);
+
+    CHECK_EQ(br->getBits(1), 0u);
+    MY_LOGV("  reserved = %u", br->getBits(2));
+
+    unsigned section_length = br->getBits(12);
+    ALOGV("  section_length = %u", section_length);
+    CHECK_EQ(section_length & 0xc00, 0u);
+
+    MY_LOGV("  transport_stream_id = %u", br->getBits(16));
+    MY_LOGV("  reserved = %u", br->getBits(2));
+    MY_LOGV("  version_number = %u", br->getBits(5));
+    MY_LOGV("  current_next_indicator = %u", br->getBits(1));
+    MY_LOGV("  section_number = %u", br->getBits(8));
+    MY_LOGV("  last_section_number = %u", br->getBits(8));
+
+    size_t numProgramBytes = (section_length - 5 /* header */ - 4 /* crc */);
+    CHECK_EQ((numProgramBytes % 4), 0u);
+
+    for (size_t i = 0; i < numProgramBytes / 4; ++i) {
+        unsigned program_number = br->getBits(16);
+        ALOGV("    program_number = %u", program_number);
+
+        MY_LOGV("    reserved = %u", br->getBits(3));
+
+        if (program_number == 0) {
+            MY_LOGV("    network_PID = 0x%04x", br->getBits(13));
+        } else {
+            unsigned programMapPID = br->getBits(13);
+
+            ALOGV("    program_map_PID = 0x%04x", programMapPID);
+
+            bool found = false;
+            for (const auto &program : mPrograms) {
+                if (program->number() == program_number) {
+                    program->updateProgramMapPID(programMapPID);
+                    found = true;
+                    break;
+                }
+            }
+
+            if (!found) {
+                mPrograms.push_back(
+                        new Program(this, program_number, programMapPID));
+            }
+
+            if (mPSISections.find(programMapPID) == mPSISections.end()) {
+                mPSISections[programMapPID] = new PSISection;
+            }
+        }
+    }
+
+    MY_LOGV("  CRC = 0x%08x", br->getBits(32));
+}
+
+status_t ATSParser::parsePID(
+        ABitReader *br, unsigned PID,
+        unsigned continuity_counter,
+        unsigned payload_unit_start_indicator) {
+    auto it = mPSISections.find(PID);
+
+    if (it != mPSISections.end()) {
+        const sp<PSISection> &section = it->second;
+
+        if (payload_unit_start_indicator) {
+            CHECK(section->isEmpty());
+
+            unsigned skip = br->getBits(8);
+            br->skipBits(skip * 8);
+        }
+
+
+        CHECK((br->numBitsLeft() % 8) == 0);
+        status_t err = section->append(br->data(), br->numBitsLeft() / 8);
+
+        if (err != OK) {
+            return err;
+        }
+
+        if (!section->isComplete()) {
+            return OK;
+        }
+
+        ABitReader sectionBits(section->data(), section->size());
+
+        if (PID == 0) {
+            parseProgramAssociationTable(&sectionBits);
+        } else {
+            bool handled = false;
+            for (const auto &program : mPrograms) {
+                status_t err;
+                if (!program->parsePSISection(PID, &sectionBits, &err)) {
+                    continue;
+                }
+
+                if (err != OK) {
+                    return err;
+                }
+
+                handled = true;
+                break;
+            }
+
+            if (!handled) {
+                mPSISections.erase(it);
+            }
+        }
+
+        section->clear();
+
+        return OK;
+    }
+
+    bool handled = false;
+    for (const auto &program : mPrograms) {
+        status_t err;
+        if (program->parsePID(
+                    PID, continuity_counter, payload_unit_start_indicator,
+                    br, &err)) {
+            if (err != OK) {
+                return err;
+            }
+
+            handled = true;
+            break;
+        }
+    }
+
+    if (!handled) {
+        ALOGV("PID 0x%04x not handled.", PID);
+    }
+
+    return OK;
+}
+
+void ATSParser::parseAdaptationField(ABitReader *br, unsigned PID) {
+    unsigned adaptation_field_length = br->getBits(8);
+
+    if (adaptation_field_length > 0) {
+        unsigned discontinuity_indicator = br->getBits(1);
+
+        if (discontinuity_indicator) {
+            ALOGV("PID 0x%04x: discontinuity_indicator = 1 (!!!)", PID);
+        }
+
+        br->skipBits(2);
+        unsigned PCR_flag = br->getBits(1);
+
+        size_t numBitsRead = 4;
+
+        if (PCR_flag) {
+            br->skipBits(4);
+            uint64_t PCR_base = br->getBits(32);
+            PCR_base = (PCR_base << 1) | br->getBits(1);
+
+            br->skipBits(6);
+            unsigned PCR_ext = br->getBits(9);
+
+            // The number of bytes from the start of the current
+            // MPEG2 transport stream packet up and including
+            // the final byte of this PCR_ext field.
+            size_t byteOffsetFromStartOfTSPacket =
+                (188 - br->numBitsLeft() / 8);
+
+            uint64_t PCR = PCR_base * 300 + PCR_ext;
+
+            ALOGV("PID 0x%04x: PCR = 0x%016llx (%.2f)",
+                  PID, PCR, PCR / 27E6);
+
+            // The number of bytes received by this parser up to and
+            // including the final byte of this PCR_ext field.
+            size_t byteOffsetFromStart =
+                mNumTSPacketsParsed * 188 + byteOffsetFromStartOfTSPacket;
+
+            for (size_t i = 0; i < mPrograms.size(); ++i) {
+                updatePCR(PID, PCR, byteOffsetFromStart);
+            }
+
+            numBitsRead += 52;
+        }
+
+        CHECK_GE(adaptation_field_length * 8, numBitsRead);
+
+        br->skipBits(adaptation_field_length * 8 - numBitsRead);
+    }
+}
+
+status_t ATSParser::parseTS(ABitReader *br) {
+    ALOGV("---");
+
+    unsigned sync_byte = br->getBits(8);
+    CHECK_EQ(sync_byte, 0x47u);
+
+    MY_LOGV("transport_error_indicator = %u", br->getBits(1));
+
+    unsigned payload_unit_start_indicator = br->getBits(1);
+    ALOGV("payload_unit_start_indicator = %u", payload_unit_start_indicator);
+
+    MY_LOGV("transport_priority = %u", br->getBits(1));
+
+    unsigned PID = br->getBits(13);
+    ALOGV("PID = 0x%04x", PID);
+
+    MY_LOGV("transport_scrambling_control = %u", br->getBits(2));
+
+    unsigned adaptation_field_control = br->getBits(2);
+    ALOGV("adaptation_field_control = %u", adaptation_field_control);
+
+    unsigned continuity_counter = br->getBits(4);
+    ALOGV("PID = 0x%04x, continuity_counter = %u", PID, continuity_counter);
+
+    // ALOGI("PID = 0x%04x, continuity_counter = %u", PID, continuity_counter);
+
+    if (adaptation_field_control == 2 || adaptation_field_control == 3) {
+        parseAdaptationField(br, PID);
+    }
+
+    status_t err = OK;
+
+    if (adaptation_field_control == 1 || adaptation_field_control == 3) {
+        err = parsePID(
+                br, PID, continuity_counter, payload_unit_start_indicator);
+    }
+
+    ++mNumTSPacketsParsed;
+
+    return err;
+}
+
+sp<AnotherPacketSource> ATSParser::getSource(SourceType type) {
+    int which = -1;  // any
+
+    for (const auto &program : mPrograms) {
+        if (which >= 0 && (int)program->number() != which) {
+            continue;
+        }
+
+        sp<AnotherPacketSource> source = program->getSource(type);
+
+        if (source != NULL) {
+            return source;
+        }
+    }
+
+    return NULL;
+}
+
+bool ATSParser::PTSTimeDeltaEstablished() {
+    if (mPrograms.empty()) {
+        return false;
+    }
+
+    return mPrograms.front()->PTSTimeDeltaEstablished();
+}
+
+void ATSParser::updatePCR(
+        unsigned /* PID */, uint64_t PCR, size_t byteOffsetFromStart) {
+    ALOGV("PCR 0x%016llx @ %d", PCR, byteOffsetFromStart);
+
+    if (mNumPCRs == 2) {
+        mPCR[0] = mPCR[1];
+        mPCRBytes[0] = mPCRBytes[1];
+        mSystemTimeUs[0] = mSystemTimeUs[1];
+        mNumPCRs = 1;
+    }
+
+    mPCR[mNumPCRs] = PCR;
+    mPCRBytes[mNumPCRs] = byteOffsetFromStart;
+    mSystemTimeUs[mNumPCRs] = ALooper::GetNowUs();
+
+    ++mNumPCRs;
+
+#if 0
+    if (mNumPCRs == 2) {
+        double transportRate =
+            (mPCRBytes[1] - mPCRBytes[0]) * 27E6 / (mPCR[1] - mPCR[0]);
+
+        ALOGV("transportRate = %.2f bytes/sec", transportRate);
+    }
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ATSParser::PSISection::PSISection() {
+}
+
+ATSParser::PSISection::~PSISection() {
+}
+
+status_t ATSParser::PSISection::append(const void *data, size_t size) {
+    if (mBuffer == NULL || mBuffer->size() + size > mBuffer->capacity()) {
+        size_t newCapacity =
+            (mBuffer == NULL) ? size : mBuffer->capacity() + size;
+
+        newCapacity = (newCapacity + 1023) & ~1023;
+
+        sp<ABuffer> newBuffer = new ABuffer(newCapacity);
+
+        if (mBuffer != NULL) {
+            memcpy(newBuffer->data(), mBuffer->data(), mBuffer->size());
+            newBuffer->setRange(0, mBuffer->size());
+        } else {
+            newBuffer->setRange(0, 0);
+        }
+
+        mBuffer = newBuffer;
+    }
+
+    memcpy(mBuffer->data() + mBuffer->size(), data, size);
+    mBuffer->setRange(0, mBuffer->size() + size);
+
+    return OK;
+}
+
+void ATSParser::PSISection::clear() {
+    if (mBuffer != NULL) {
+        mBuffer->setRange(0, 0);
+    }
+}
+
+bool ATSParser::PSISection::isComplete() const {
+    if (mBuffer == NULL || mBuffer->size() < 3) {
+        return false;
+    }
+
+    unsigned sectionLength = U16_AT(mBuffer->data() + 1) & 0xfff;
+    return mBuffer->size() >= sectionLength + 3;
+}
+
+bool ATSParser::PSISection::isEmpty() const {
+    return mBuffer == NULL || mBuffer->size() == 0;
+}
+
+const uint8_t *ATSParser::PSISection::data() const {
+    return mBuffer == NULL ? NULL : mBuffer->data();
+}
+
+size_t ATSParser::PSISection::size() const {
+    return mBuffer == NULL ? 0 : mBuffer->size();
+}
+
+}  // namespace android
diff --git a/host/frontend/gcastv2/libandroid/Android.bp b/host/frontend/gcastv2/libandroid/Android.bp
new file mode 100644
index 0000000..7c781ef
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/Android.bp
@@ -0,0 +1,67 @@
+// Copyright (C) 2018 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.
+
+cc_library_static {
+    name: "libandroidglue",
+    host_supported: true,
+    srcs: [
+        "AAtomizer.cpp",
+        "ABitReader.cpp",
+        "ABuffer.cpp",
+        "ALooper.cpp",
+        "ALooperRoster.cpp",
+        "AMessage.cpp",
+        "ANetworkSession.cpp",
+        "AnotherPacketSource.cpp",
+        "ATSParser.cpp",
+        "ESQueue.cpp",
+        "FileSource.cpp",
+        "JSONObject.cpp",
+        "KeyStore.cpp",
+        "MyScopedByteArray.cpp",
+        "MyScopedUTF8String.cpp",
+        "NuMediaExtractor.cpp",
+        "Utils.cpp",
+        "avc_utils.cpp",
+        "base64.cpp",
+        "hexdump.cpp",
+        "ParsedMessage.cpp",
+        "RefBase.cpp",
+        "TSPacketizer.cpp",
+    ],
+    target: {
+        host: {
+            cflags: [
+                "-DTARGET_ANDROID",
+            ],
+        },
+        android: {
+            cflags: [
+                "-DTARGET_ANDROID_DEVICE",
+            ],
+            srcs: [
+                "ADebug.cpp",
+                "JavaThread.cpp",
+                "MyAndroidRuntime.cpp",
+                "MyJNIHelpers.cpp",
+            ],
+        },
+    },
+    shared_libs: [
+        "libbase",
+    ],
+    local_include_dirs: ["include"],
+    export_include_dirs: ["include"],
+}
+
diff --git a/host/frontend/gcastv2/libandroid/AnotherPacketSource.cpp b/host/frontend/gcastv2/libandroid/AnotherPacketSource.cpp
new file mode 100644
index 0000000..5f0722d
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/AnotherPacketSource.cpp
@@ -0,0 +1,264 @@
+/*
+ * 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 <media/stagefright/AnotherPacketSource.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/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+const int64_t kNearEOSMarkUs = 2000000ll; // 2 secs
+
+AnotherPacketSource::AnotherPacketSource(const sp<MetaData> &meta)
+    : mIsAudio(false),
+      mFormat(meta),
+      mLastQueuedTimeUs(0),
+      mEOSResult(OK) {
+    const char *mime;
+    CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+    if (!strncasecmp("audio/", mime, 6)) {
+        mIsAudio = true;
+    } else {
+        CHECK(!strncasecmp("video/", mime, 6));
+    }
+}
+
+void AnotherPacketSource::setFormat(const sp<MetaData> &meta) {
+    CHECK(mFormat == NULL);
+    mFormat = meta;
+}
+
+AnotherPacketSource::~AnotherPacketSource() {
+}
+
+status_t AnotherPacketSource::start(MetaData * /* params */) {
+    return OK;
+}
+
+status_t AnotherPacketSource::stop() {
+    return OK;
+}
+
+sp<MetaData> AnotherPacketSource::getFormat() {
+    return mFormat;
+}
+
+status_t AnotherPacketSource::dequeueAccessUnit(sp<ABuffer> *buffer) {
+    buffer->clear();
+
+    Mutex::Autolock autoLock(mLock);
+    while (mEOSResult == OK && mBuffers.empty()) {
+        mCondition.wait(mLock);
+    }
+
+    if (!mBuffers.empty()) {
+        *buffer = *mBuffers.begin();
+        mBuffers.erase(mBuffers.begin());
+
+        int32_t discontinuity;
+        if ((*buffer)->meta()->findInt32("discontinuity", &discontinuity)) {
+            if (wasFormatChange(discontinuity)) {
+                mFormat.clear();
+            }
+
+            return INFO_DISCONTINUITY;
+        }
+
+        return OK;
+    }
+
+    return mEOSResult;
+}
+
+status_t AnotherPacketSource::read(
+        MediaBuffer **out, const ReadOptions *) {
+    *out = NULL;
+
+    Mutex::Autolock autoLock(mLock);
+    while (mEOSResult == OK && mBuffers.empty()) {
+        mCondition.wait(mLock);
+    }
+
+    if (!mBuffers.empty()) {
+        const sp<ABuffer> buffer = *mBuffers.begin();
+        mBuffers.erase(mBuffers.begin());
+
+        int32_t discontinuity;
+        if (buffer->meta()->findInt32("discontinuity", &discontinuity)) {
+            if (wasFormatChange(discontinuity)) {
+                mFormat.clear();
+            }
+
+            return INFO_DISCONTINUITY;
+        } else {
+            int64_t timeUs;
+            CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
+
+            MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size());
+            memcpy(mediaBuffer->data(), buffer->data(), buffer->size());
+
+            mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
+
+            *out = mediaBuffer;
+            return OK;
+        }
+    }
+
+    return mEOSResult;
+}
+
+bool AnotherPacketSource::wasFormatChange(
+        int32_t discontinuityType) const {
+    if (mIsAudio) {
+        return (discontinuityType & ATSParser::DISCONTINUITY_AUDIO_FORMAT) != 0;
+    }
+
+    return (discontinuityType & ATSParser::DISCONTINUITY_VIDEO_FORMAT) != 0;
+}
+
+void AnotherPacketSource::queueAccessUnit(const sp<ABuffer> &buffer) {
+    int32_t damaged;
+    if (buffer->meta()->findInt32("damaged", &damaged) && damaged) {
+        // LOG(VERBOSE) << "discarding damaged AU";
+        return;
+    }
+
+    CHECK(buffer->meta()->findInt64("timeUs", &mLastQueuedTimeUs));
+    ALOGV("queueAccessUnit timeUs=%lld us (%.2f secs)", mLastQueuedTimeUs, mLastQueuedTimeUs / 1E6);
+
+    Mutex::Autolock autoLock(mLock);
+    mBuffers.push_back(buffer);
+    mCondition.signal();
+}
+
+void AnotherPacketSource::queueDiscontinuity(
+        ATSParser::DiscontinuityType type,
+        const sp<AMessage> &extra) {
+    Mutex::Autolock autoLock(mLock);
+
+    // Leave only discontinuities in the queue.
+    auto it = mBuffers.begin();
+    while (it != mBuffers.end()) {
+        sp<ABuffer> oldBuffer = *it;
+
+        int32_t oldDiscontinuityType;
+        if (!oldBuffer->meta()->findInt32(
+                    "discontinuity", &oldDiscontinuityType)) {
+            it = mBuffers.erase(it);
+            continue;
+        }
+
+        ++it;
+    }
+
+    mEOSResult = OK;
+    mLastQueuedTimeUs = 0;
+
+    sp<ABuffer> buffer = new ABuffer(0);
+    buffer->meta()->setInt32("discontinuity", static_cast<int32_t>(type));
+    buffer->meta()->setMessage("extra", extra);
+
+    mBuffers.push_back(buffer);
+    mCondition.signal();
+}
+
+void AnotherPacketSource::signalEOS(status_t result) {
+    CHECK(result != OK);
+
+    Mutex::Autolock autoLock(mLock);
+    mEOSResult = result;
+    mCondition.signal();
+}
+
+bool AnotherPacketSource::hasBufferAvailable(status_t *finalResult) {
+    Mutex::Autolock autoLock(mLock);
+    if (!mBuffers.empty()) {
+        return true;
+    }
+
+    *finalResult = mEOSResult;
+    return false;
+}
+
+int64_t AnotherPacketSource::getBufferedDurationUs(status_t *finalResult) {
+    Mutex::Autolock autoLock(mLock);
+
+    *finalResult = mEOSResult;
+
+    if (mBuffers.empty()) {
+        return 0;
+    }
+
+    int64_t time1 = -1;
+    int64_t time2 = -1;
+
+    auto it = mBuffers.begin();
+    while (it != mBuffers.end()) {
+        const sp<ABuffer> &buffer = *it;
+
+        int64_t timeUs;
+        if (buffer->meta()->findInt64("timeUs", &timeUs)) {
+            if (time1 < 0) {
+                time1 = timeUs;
+            }
+
+            time2 = timeUs;
+        } else {
+            // This is a discontinuity, reset everything.
+            time1 = time2 = -1;
+        }
+
+        ++it;
+    }
+
+    return time2 - time1;
+}
+
+status_t AnotherPacketSource::nextBufferTime(int64_t *timeUs) {
+    *timeUs = 0;
+
+    Mutex::Autolock autoLock(mLock);
+
+    if (mBuffers.empty()) {
+        return mEOSResult != OK ? mEOSResult : -EWOULDBLOCK;
+    }
+
+    sp<ABuffer> buffer = *mBuffers.begin();
+    CHECK(buffer->meta()->findInt64("timeUs", timeUs));
+
+    return OK;
+}
+
+bool AnotherPacketSource::isFinished(int64_t duration) const {
+    if (duration > 0) {
+        int64_t diff = duration - mLastQueuedTimeUs;
+        if (diff < kNearEOSMarkUs && diff > -kNearEOSMarkUs) {
+            ALOGV("Detecting EOS due to near end");
+            return true;
+        }
+    }
+    return (mEOSResult != OK);
+}
+
+}  // namespace android
diff --git a/host/frontend/gcastv2/libandroid/ESQueue.cpp b/host/frontend/gcastv2/libandroid/ESQueue.cpp
new file mode 100644
index 0000000..cd50663
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/ESQueue.cpp
@@ -0,0 +1,1021 @@
+/*
+ * 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 "ESQueue"
+#include <media/stagefright/foundation/ADebug.h>
+
+#include "ESQueue.h"
+
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/foundation/ABitReader.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/avc_utils.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+#include <netinet/in.h>
+
+#include <vector>
+
+namespace android {
+
+ElementaryStreamQueue::ElementaryStreamQueue(Mode mode, uint32_t flags)
+    : mMode(mode),
+      mFlags(flags) {
+}
+
+sp<MetaData> ElementaryStreamQueue::getFormat() {
+    return mFormat;
+}
+
+void ElementaryStreamQueue::clear(bool clearFormat) {
+    if (mBuffer != NULL) {
+        mBuffer->setRange(0, 0);
+    }
+
+    mRangeInfos.clear();
+
+    if (clearFormat) {
+        mFormat.clear();
+    }
+}
+
+static bool IsSeeminglyValidADTSHeader(const uint8_t *ptr, size_t size) {
+    if (size < 3) {
+        // Not enough data to verify header.
+        return false;
+    }
+
+    if (ptr[0] != 0xff || (ptr[1] >> 4) != 0x0f) {
+        return false;
+    }
+
+    unsigned layer = (ptr[1] >> 1) & 3;
+
+    if (layer != 0) {
+        return false;
+    }
+
+    unsigned ID = (ptr[1] >> 3) & 1;
+    unsigned profile_ObjectType = ptr[2] >> 6;
+
+    if (ID == 1 && profile_ObjectType == 3) {
+        // MPEG-2 profile 3 is reserved.
+        return false;
+    }
+
+    return true;
+}
+
+static bool IsSeeminglyValidMPEGAudioHeader(const uint8_t *ptr, size_t size) {
+    if (size < 3) {
+        // Not enough data to verify header.
+        return false;
+    }
+
+    if (ptr[0] != 0xff || (ptr[1] >> 5) != 0x07) {
+        return false;
+    }
+
+    unsigned ID = (ptr[1] >> 3) & 3;
+
+    if (ID == 1) {
+        return false;  // reserved
+    }
+
+    unsigned layer = (ptr[1] >> 1) & 3;
+
+    if (layer == 0) {
+        return false;  // reserved
+    }
+
+    unsigned bitrateIndex = (ptr[2] >> 4);
+
+    if (bitrateIndex == 0x0f) {
+        return false;  // reserved
+    }
+
+    unsigned samplingRateIndex = (ptr[2] >> 2) & 3;
+
+    if (samplingRateIndex == 3) {
+        return false;  // reserved
+    }
+
+    return true;
+}
+
+status_t ElementaryStreamQueue::appendData(
+        const void *data, size_t size, int64_t timeUs) {
+    if (mBuffer == NULL || mBuffer->size() == 0) {
+        switch (mMode) {
+            case H264:
+            case MPEG_VIDEO:
+            {
+#if 0
+                if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) {
+                    return ERROR_MALFORMED;
+                }
+#else
+                uint8_t *ptr = (uint8_t *)data;
+
+                ssize_t startOffset = -1;
+                for (size_t i = 0; i + 3 < size; ++i) {
+                    if (!memcmp("\x00\x00\x00\x01", &ptr[i], 4)) {
+                        startOffset = i;
+                        break;
+                    }
+                }
+
+                if (startOffset < 0) {
+                    return ERROR_MALFORMED;
+                }
+
+                if (startOffset > 0) {
+                    ALOGI("found something resembling an H.264/MPEG syncword at "
+                         "offset %ld",
+                         startOffset);
+                }
+
+                data = &ptr[startOffset];
+                size -= startOffset;
+#endif
+                break;
+            }
+
+            case MPEG4_VIDEO:
+            {
+#if 0
+                if (size < 3 || memcmp("\x00\x00\x01", data, 3)) {
+                    return ERROR_MALFORMED;
+                }
+#else
+                uint8_t *ptr = (uint8_t *)data;
+
+                ssize_t startOffset = -1;
+                for (size_t i = 0; i + 2 < size; ++i) {
+                    if (!memcmp("\x00\x00\x01", &ptr[i], 3)) {
+                        startOffset = i;
+                        break;
+                    }
+                }
+
+                if (startOffset < 0) {
+                    return ERROR_MALFORMED;
+                }
+
+                if (startOffset > 0) {
+                    ALOGI("found something resembling an H.264/MPEG syncword at "
+                         "offset %ld",
+                         startOffset);
+                }
+
+                data = &ptr[startOffset];
+                size -= startOffset;
+#endif
+                break;
+            }
+
+            case AAC:
+            {
+                uint8_t *ptr = (uint8_t *)data;
+
+#if 0
+                if (size < 2 || ptr[0] != 0xff || (ptr[1] >> 4) != 0x0f) {
+                    return ERROR_MALFORMED;
+                }
+#else
+                ssize_t startOffset = -1;
+                for (size_t i = 0; i < size; ++i) {
+                    if (IsSeeminglyValidADTSHeader(&ptr[i], size - i)) {
+                        startOffset = i;
+                        break;
+                    }
+                }
+
+                if (startOffset < 0) {
+                    return ERROR_MALFORMED;
+                }
+
+                if (startOffset > 0) {
+                    ALOGI("found something resembling an AAC syncword at offset %ld",
+                         startOffset);
+                }
+
+                data = &ptr[startOffset];
+                size -= startOffset;
+#endif
+                break;
+            }
+
+            case MPEG_AUDIO:
+            {
+                uint8_t *ptr = (uint8_t *)data;
+
+                ssize_t startOffset = -1;
+                for (size_t i = 0; i < size; ++i) {
+                    if (IsSeeminglyValidMPEGAudioHeader(&ptr[i], size - i)) {
+                        startOffset = i;
+                        break;
+                    }
+                }
+
+                if (startOffset < 0) {
+                    return ERROR_MALFORMED;
+                }
+
+                if (startOffset > 0) {
+                    ALOGI("found something resembling an MPEG audio "
+                         "syncword at offset %ld",
+                         startOffset);
+                }
+
+                data = &ptr[startOffset];
+                size -= startOffset;
+                break;
+            }
+
+            case PCM_AUDIO:
+            {
+                break;
+            }
+
+            default:
+                TRESPASS();
+                break;
+        }
+    }
+
+    size_t neededSize = (mBuffer == NULL ? 0 : mBuffer->size()) + size;
+    if (mBuffer == NULL || neededSize > mBuffer->capacity()) {
+        neededSize = (neededSize + 65535) & ~65535;
+
+        ALOGV("resizing buffer to size %d", neededSize);
+
+        sp<ABuffer> buffer = new ABuffer(neededSize);
+        if (mBuffer != NULL) {
+            memcpy(buffer->data(), mBuffer->data(), mBuffer->size());
+            buffer->setRange(0, mBuffer->size());
+        } else {
+            buffer->setRange(0, 0);
+        }
+
+        mBuffer = buffer;
+    }
+
+    memcpy(mBuffer->data() + mBuffer->size(), data, size);
+    mBuffer->setRange(0, mBuffer->size() + size);
+
+    RangeInfo info;
+    info.mLength = size;
+    info.mTimestampUs = timeUs;
+    mRangeInfos.push_back(info);
+
+#if 0
+    if (mMode == AAC) {
+        ALOGI("size = %d, timeUs = %.2f secs", size, timeUs / 1E6);
+        hexdump(data, size);
+    }
+#endif
+
+    return OK;
+}
+
+sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnit() {
+    if ((mFlags & kFlag_AlignedData) && mMode == H264) {
+        if (mRangeInfos.empty()) {
+            return NULL;
+        }
+
+        RangeInfo info = *mRangeInfos.begin();
+        mRangeInfos.erase(mRangeInfos.begin());
+
+        sp<ABuffer> accessUnit = new ABuffer(info.mLength);
+        memcpy(accessUnit->data(), mBuffer->data(), info.mLength);
+        accessUnit->meta()->setInt64("timeUs", info.mTimestampUs);
+
+        memmove(mBuffer->data(),
+                mBuffer->data() + info.mLength,
+                mBuffer->size() - info.mLength);
+
+        mBuffer->setRange(0, mBuffer->size() - info.mLength);
+
+        if (mFormat == NULL) {
+            mFormat = MakeAVCCodecSpecificData(accessUnit);
+        }
+
+        return accessUnit;
+    }
+
+    switch (mMode) {
+        case H264:
+            return dequeueAccessUnitH264();
+        case AAC:
+            return dequeueAccessUnitAAC();
+        case MPEG_VIDEO:
+            return dequeueAccessUnitMPEGVideo();
+        case MPEG4_VIDEO:
+            return dequeueAccessUnitMPEG4Video();
+        case PCM_AUDIO:
+            return dequeueAccessUnitPCMAudio();
+        default:
+            CHECK_EQ((unsigned)mMode, (unsigned)MPEG_AUDIO);
+            return dequeueAccessUnitMPEGAudio();
+    }
+}
+
+sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitPCMAudio() {
+    if (mBuffer->size() < 4) {
+        return NULL;
+    }
+
+    ABitReader bits(mBuffer->data(), 4);
+    CHECK_EQ(bits.getBits(8), 0xa0u);
+    unsigned numAUs = bits.getBits(8);
+    bits.skipBits(8);
+    // unsigned quantization_word_length = bits.getBits(2);
+    unsigned audio_sampling_frequency = bits.getBits(3);
+    unsigned num_channels = bits.getBits(3);
+
+    CHECK_EQ(audio_sampling_frequency, 2u);  // 48kHz
+    CHECK_EQ(num_channels, 1u);  // stereo!
+
+    if (mFormat == NULL) {
+        mFormat = new MetaData;
+        mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+        mFormat->setInt32(kKeyChannelCount, 2);
+        mFormat->setInt32(kKeySampleRate, 48000);
+    }
+
+    static const size_t kFramesPerAU = 80;
+    size_t frameSize = 2 /* numChannels */ * sizeof(int16_t);
+
+    size_t payloadSize = numAUs * frameSize * kFramesPerAU;
+
+    if (mBuffer->size() < 4 + payloadSize) {
+        return NULL;
+    }
+
+    sp<ABuffer> accessUnit = new ABuffer(payloadSize);
+    memcpy(accessUnit->data(), mBuffer->data() + 4, payloadSize);
+
+    int64_t timeUs = fetchTimestamp(payloadSize + 4);
+    CHECK_GE(timeUs, 0ll);
+    accessUnit->meta()->setInt64("timeUs", timeUs);
+
+    int16_t *ptr = (int16_t *)accessUnit->data();
+    for (size_t i = 0; i < payloadSize / sizeof(int16_t); ++i) {
+        ptr[i] = ntohs(ptr[i]);
+    }
+
+    memmove(
+            mBuffer->data(),
+            mBuffer->data() + 4 + payloadSize,
+            mBuffer->size() - 4 - payloadSize);
+
+    mBuffer->setRange(0, mBuffer->size() - 4 - payloadSize);
+
+    return accessUnit;
+}
+
+sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAAC() {
+    int64_t timeUs = 0ll;
+
+    size_t offset = 0;
+    while (offset + 7 <= mBuffer->size()) {
+        ABitReader bits(mBuffer->data() + offset, mBuffer->size() - offset);
+
+        // adts_fixed_header
+
+        CHECK_EQ(bits.getBits(12), 0xfffu);
+        bits.skipBits(4);  // ID, layer, protection_absent
+
+        if (mFormat == NULL) {
+            unsigned profile = bits.getBits(2);
+            CHECK_NE(profile, 3u);
+            unsigned sampling_freq_index = bits.getBits(4);
+            bits.getBits(1);  // private_bit
+            unsigned channel_configuration = bits.getBits(3);
+            CHECK_NE(channel_configuration, 0u);
+            bits.skipBits(2);  // original_copy, home
+
+            mFormat = MakeAACCodecSpecificData(
+                    profile, sampling_freq_index, channel_configuration);
+
+            mFormat->setInt32(kKeyIsADTS, true);
+
+            int32_t sampleRate;
+            int32_t numChannels;
+            CHECK(mFormat->findInt32(kKeySampleRate, &sampleRate));
+            CHECK(mFormat->findInt32(kKeyChannelCount, &numChannels));
+
+            ALOGI("found AAC codec config (%d Hz, %d channels)",
+                 sampleRate, numChannels);
+        } else {
+            // profile_ObjectType, sampling_frequency_index, private_bits,
+            // channel_configuration, original_copy, home
+            bits.skipBits(12);
+        }
+
+        // adts_variable_header
+
+        // copyright_identification_bit, copyright_identification_start
+        bits.skipBits(2);
+
+        unsigned aac_frame_length = bits.getBits(13);
+
+        bits.skipBits(11);  // adts_buffer_fullness
+
+        unsigned number_of_raw_data_blocks_in_frame = bits.getBits(2);
+
+        if (number_of_raw_data_blocks_in_frame != 0) {
+            // To be implemented.
+            TRESPASS();
+        }
+
+        if (offset + aac_frame_length > mBuffer->size()) {
+            break;
+        }
+
+        // size_t headerSize = protection_absent ? 7 : 9;
+
+        int64_t tmpUs = fetchTimestamp(aac_frame_length);
+        CHECK_GE(tmpUs, 0ll);
+
+        if (offset == 0) {
+            timeUs = tmpUs;
+        }
+
+        offset += aac_frame_length;
+    }
+
+    if (offset == 0) {
+        return NULL;
+    }
+
+    sp<ABuffer> accessUnit = new ABuffer(offset);
+    memcpy(accessUnit->data(), mBuffer->data(), offset);
+
+    memmove(mBuffer->data(), mBuffer->data() + offset,
+            mBuffer->size() - offset);
+    mBuffer->setRange(0, mBuffer->size() - offset);
+
+    accessUnit->meta()->setInt64("timeUs", timeUs);
+
+    return accessUnit;
+}
+
+int64_t ElementaryStreamQueue::fetchTimestamp(size_t size) {
+    int64_t timeUs = -1;
+    bool first = true;
+
+    while (size > 0) {
+        CHECK(!mRangeInfos.empty());
+
+        RangeInfo *info = &*mRangeInfos.begin();
+
+        if (first) {
+            timeUs = info->mTimestampUs;
+            first = false;
+        }
+
+        if (info->mLength > size) {
+            info->mLength -= size;
+
+            if (first) {
+                info->mTimestampUs = -1;
+            }
+
+            size = 0;
+        } else {
+            size -= info->mLength;
+
+            mRangeInfos.erase(mRangeInfos.begin());
+            info = NULL;
+        }
+    }
+
+    if (timeUs == 0ll) {
+        ALOGV("Returning 0 timestamp");
+    }
+
+    return timeUs;
+}
+
+struct NALPosition {
+    size_t nalOffset;
+    size_t nalSize;
+};
+
+sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitH264() {
+    const uint8_t *data = mBuffer->data();
+
+    size_t size = mBuffer->size();
+    std::vector<NALPosition> nals;
+
+    size_t totalSize = 0;
+
+    status_t err;
+    const uint8_t *nalStart;
+    size_t nalSize;
+    bool foundSlice = false;
+    while ((err = getNextNALUnit(&data, &size, &nalStart, &nalSize)) == OK) {
+        if (nalSize == 0) continue;
+
+        unsigned nalType = nalStart[0] & 0x1f;
+        bool flush = false;
+
+        if (nalType == 1 || nalType == 5) {
+            if (foundSlice) {
+                ABitReader br(nalStart + 1, nalSize);
+                unsigned first_mb_in_slice = parseUE(&br);
+
+                if (first_mb_in_slice == 0) {
+                    // This slice starts a new frame.
+
+                    flush = true;
+                }
+            }
+
+            foundSlice = true;
+        } else if ((nalType == 9 || nalType == 7) && foundSlice) {
+            // Access unit delimiter and SPS will be associated with the
+            // next frame.
+
+            flush = true;
+        }
+
+        if (flush) {
+            // The access unit will contain all nal units up to, but excluding
+            // the current one, separated by 0x00 0x00 0x00 0x01 startcodes.
+
+            size_t auSize = 4 * nals.size() + totalSize;
+            sp<ABuffer> accessUnit = new ABuffer(auSize);
+
+#if !LOG_NDEBUG
+            std::string out;
+#endif
+
+            size_t dstOffset = 0;
+            for (size_t i = 0; i < nals.size(); ++i) {
+                const NALPosition &pos = nals.at(i);
+
+                unsigned nalType = mBuffer->data()[pos.nalOffset] & 0x1f;
+
+#if !LOG_NDEBUG
+                char tmp[128];
+                sprintf(tmp, "0x%02x", nalType);
+                if (i > 0) {
+                    out.append(", ");
+                }
+                out.append(tmp);
+#endif
+
+                memcpy(accessUnit->data() + dstOffset, "\x00\x00\x00\x01", 4);
+
+                memcpy(accessUnit->data() + dstOffset + 4,
+                       mBuffer->data() + pos.nalOffset,
+                       pos.nalSize);
+
+                dstOffset += pos.nalSize + 4;
+            }
+
+            ALOGV("accessUnit contains nal types %s", out.c_str());
+
+            const NALPosition &pos = nals.at(nals.size() - 1);
+            size_t nextScan = pos.nalOffset + pos.nalSize;
+
+            memmove(mBuffer->data(),
+                    mBuffer->data() + nextScan,
+                    mBuffer->size() - nextScan);
+
+            mBuffer->setRange(0, mBuffer->size() - nextScan);
+
+            int64_t timeUs = fetchTimestamp(nextScan);
+            CHECK_GE(timeUs, 0ll);
+
+            accessUnit->meta()->setInt64("timeUs", timeUs);
+
+            if (mFormat == NULL) {
+                mFormat = MakeAVCCodecSpecificData(accessUnit);
+            }
+
+            return accessUnit;
+        }
+
+        NALPosition pos;
+        pos.nalOffset = nalStart - mBuffer->data();
+        pos.nalSize = nalSize;
+
+        nals.push_back(pos);
+
+        totalSize += nalSize;
+    }
+    CHECK_EQ(err, (status_t)-EAGAIN);
+
+    return NULL;
+}
+
+sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitMPEGAudio() {
+    const uint8_t *data = mBuffer->data();
+    size_t size = mBuffer->size();
+
+    if (size < 4) {
+        return NULL;
+    }
+
+    uint32_t header = U32_AT(data);
+
+    size_t frameSize;
+    int samplingRate, numChannels, bitrate, numSamples;
+    CHECK(GetMPEGAudioFrameSize(
+                header, &frameSize, &samplingRate, &numChannels,
+                &bitrate, &numSamples));
+
+    if (size < frameSize) {
+        return NULL;
+    }
+
+    unsigned layer = 4 - ((header >> 17) & 3);
+
+    sp<ABuffer> accessUnit = new ABuffer(frameSize);
+    memcpy(accessUnit->data(), data, frameSize);
+
+    memmove(mBuffer->data(),
+            mBuffer->data() + frameSize,
+            mBuffer->size() - frameSize);
+
+    mBuffer->setRange(0, mBuffer->size() - frameSize);
+
+    int64_t timeUs = fetchTimestamp(frameSize);
+    CHECK_GE(timeUs, 0ll);
+
+    accessUnit->meta()->setInt64("timeUs", timeUs);
+
+    if (mFormat == NULL) {
+        mFormat = new MetaData;
+
+        switch (layer) {
+            case 1:
+                mFormat->setCString(
+                        kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I);
+                break;
+            case 2:
+                mFormat->setCString(
+                        kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II);
+                break;
+            case 3:
+                mFormat->setCString(
+                        kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
+                break;
+            default:
+                TRESPASS();
+        }
+
+        mFormat->setInt32(kKeySampleRate, samplingRate);
+        mFormat->setInt32(kKeyChannelCount, numChannels);
+    }
+
+    return accessUnit;
+}
+
+static void EncodeSize14(uint8_t **_ptr, size_t size) {
+    CHECK_LE(size, 0x3fffu);
+
+    uint8_t *ptr = *_ptr;
+
+    *ptr++ = 0x80 | (size >> 7);
+    *ptr++ = size & 0x7f;
+
+    *_ptr = ptr;
+}
+
+static sp<ABuffer> MakeMPEGVideoESDS(const sp<ABuffer> &csd) {
+    sp<ABuffer> esds = new ABuffer(csd->size() + 25);
+
+    uint8_t *ptr = esds->data();
+    *ptr++ = 0x03;
+    EncodeSize14(&ptr, 22 + csd->size());
+
+    *ptr++ = 0x00;  // ES_ID
+    *ptr++ = 0x00;
+
+    *ptr++ = 0x00;  // streamDependenceFlag, URL_Flag, OCRstreamFlag
+
+    *ptr++ = 0x04;
+    EncodeSize14(&ptr, 16 + csd->size());
+
+    *ptr++ = 0x40;  // Audio ISO/IEC 14496-3
+
+    for (size_t i = 0; i < 12; ++i) {
+        *ptr++ = 0x00;
+    }
+
+    *ptr++ = 0x05;
+    EncodeSize14(&ptr, csd->size());
+
+    memcpy(ptr, csd->data(), csd->size());
+
+    return esds;
+}
+
+sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitMPEGVideo() {
+    const uint8_t *data = mBuffer->data();
+    size_t size = mBuffer->size();
+
+    bool sawPictureStart = false;
+    int pprevStartCode = -1;
+    int prevStartCode = -1;
+    int currentStartCode = -1;
+
+    size_t offset = 0;
+    while (offset + 3 < size) {
+        if (memcmp(&data[offset], "\x00\x00\x01", 3)) {
+            ++offset;
+            continue;
+        }
+
+        pprevStartCode = prevStartCode;
+        prevStartCode = currentStartCode;
+        currentStartCode = data[offset + 3];
+
+        if (currentStartCode == 0xb3 && mFormat == NULL) {
+            memmove(mBuffer->data(), mBuffer->data() + offset, size - offset);
+            size -= offset;
+            (void)fetchTimestamp(offset);
+            offset = 0;
+            mBuffer->setRange(0, size);
+        }
+
+        if ((prevStartCode == 0xb3 && currentStartCode != 0xb5)
+                || (pprevStartCode == 0xb3 && prevStartCode == 0xb5)) {
+            // seqHeader without/with extension
+
+            if (mFormat == NULL) {
+                CHECK_GE(size, 7u);
+
+                unsigned width =
+                    (data[4] << 4) | data[5] >> 4;
+
+                unsigned height =
+                    ((data[5] & 0x0f) << 8) | data[6];
+
+                mFormat = new MetaData;
+                mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG2);
+                mFormat->setInt32(kKeyWidth, width);
+                mFormat->setInt32(kKeyHeight, height);
+
+                ALOGI("found MPEG2 video codec config (%d x %d)", width, height);
+
+                sp<ABuffer> csd = new ABuffer(offset);
+                memcpy(csd->data(), data, offset);
+
+                memmove(mBuffer->data(),
+                        mBuffer->data() + offset,
+                        mBuffer->size() - offset);
+
+                mBuffer->setRange(0, mBuffer->size() - offset);
+                size -= offset;
+                (void)fetchTimestamp(offset);
+                offset = 0;
+
+                // hexdump(csd->data(), csd->size());
+
+                sp<ABuffer> esds = MakeMPEGVideoESDS(csd);
+                mFormat->setData(
+                        kKeyESDS, kTypeESDS, esds->data(), esds->size());
+
+                return NULL;
+            }
+        }
+
+        if (mFormat != NULL && currentStartCode == 0x00) {
+            // Picture start
+
+            if (!sawPictureStart) {
+                sawPictureStart = true;
+            } else {
+                sp<ABuffer> accessUnit = new ABuffer(offset);
+                memcpy(accessUnit->data(), data, offset);
+
+                memmove(mBuffer->data(),
+                        mBuffer->data() + offset,
+                        mBuffer->size() - offset);
+
+                mBuffer->setRange(0, mBuffer->size() - offset);
+
+                int64_t timeUs = fetchTimestamp(offset);
+                CHECK_GE(timeUs, 0ll);
+
+                offset = 0;
+
+                accessUnit->meta()->setInt64("timeUs", timeUs);
+
+                ALOGV("returning MPEG video access unit at time %lld us",
+                      timeUs);
+
+                // hexdump(accessUnit->data(), accessUnit->size());
+
+                return accessUnit;
+            }
+        }
+
+        ++offset;
+    }
+
+    return NULL;
+}
+
+static ssize_t getNextChunkSize(
+        const uint8_t *data, size_t size) {
+    static const char kStartCode[] = "\x00\x00\x01";
+
+    if (size < 3) {
+        return -EAGAIN;
+    }
+
+    if (memcmp(kStartCode, data, 3)) {
+        TRESPASS();
+    }
+
+    size_t offset = 3;
+    while (offset + 2 < size) {
+        if (!memcmp(&data[offset], kStartCode, 3)) {
+            return offset;
+        }
+
+        ++offset;
+    }
+
+    return -EAGAIN;
+}
+
+sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitMPEG4Video() {
+    uint8_t *data = mBuffer->data();
+    size_t size = mBuffer->size();
+
+    enum {
+        SKIP_TO_VISUAL_OBJECT_SEQ_START,
+        EXPECT_VISUAL_OBJECT_START,
+        EXPECT_VO_START,
+        EXPECT_VOL_START,
+        WAIT_FOR_VOP_START,
+        SKIP_TO_VOP_START,
+
+    } state;
+
+    if (mFormat == NULL) {
+        state = SKIP_TO_VISUAL_OBJECT_SEQ_START;
+    } else {
+        state = SKIP_TO_VOP_START;
+    }
+
+    int32_t width = -1, height = -1;
+
+    size_t offset = 0;
+    ssize_t chunkSize;
+    while ((chunkSize = getNextChunkSize(
+                    &data[offset], size - offset)) > 0) {
+        bool discard = false;
+
+        unsigned chunkType = data[offset + 3];
+
+        switch (state) {
+            case SKIP_TO_VISUAL_OBJECT_SEQ_START:
+            {
+                if (chunkType == 0xb0) {
+                    // Discard anything before this marker.
+
+                    state = EXPECT_VISUAL_OBJECT_START;
+                } else {
+                    discard = true;
+                }
+                break;
+            }
+
+            case EXPECT_VISUAL_OBJECT_START:
+            {
+                CHECK_EQ(chunkType, 0xb5u);
+                state = EXPECT_VO_START;
+                break;
+            }
+
+            case EXPECT_VO_START:
+            {
+                CHECK_LE(chunkType, 0x1fu);
+                state = EXPECT_VOL_START;
+                break;
+            }
+
+            case EXPECT_VOL_START:
+            {
+                CHECK((chunkType & 0xf0) == 0x20);
+
+                CHECK(ExtractDimensionsFromVOLHeader(
+                            &data[offset], chunkSize,
+                            &width, &height));
+
+                state = WAIT_FOR_VOP_START;
+                break;
+            }
+
+            case WAIT_FOR_VOP_START:
+            {
+                if (chunkType == 0xb3 || chunkType == 0xb6) {
+                    // group of VOP or VOP start.
+
+                    mFormat = new MetaData;
+                    mFormat->setCString(
+                            kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+
+                    mFormat->setInt32(kKeyWidth, width);
+                    mFormat->setInt32(kKeyHeight, height);
+
+                    ALOGI("found MPEG4 video codec config (%d x %d)",
+                         width, height);
+
+                    sp<ABuffer> csd = new ABuffer(offset);
+                    memcpy(csd->data(), data, offset);
+
+                    // hexdump(csd->data(), csd->size());
+
+                    sp<ABuffer> esds = MakeMPEGVideoESDS(csd);
+                    mFormat->setData(
+                            kKeyESDS, kTypeESDS,
+                            esds->data(), esds->size());
+
+                    discard = true;
+                    state = SKIP_TO_VOP_START;
+                }
+
+                break;
+            }
+
+            case SKIP_TO_VOP_START:
+            {
+                if (chunkType == 0xb6) {
+                    offset += chunkSize;
+
+                    sp<ABuffer> accessUnit = new ABuffer(offset);
+                    memcpy(accessUnit->data(), data, offset);
+
+                    memmove(data, &data[offset], size - offset);
+                    size -= offset;
+                    mBuffer->setRange(0, size);
+
+                    int64_t timeUs = fetchTimestamp(offset);
+                    CHECK_GE(timeUs, 0ll);
+
+                    offset = 0;
+
+                    accessUnit->meta()->setInt64("timeUs", timeUs);
+
+                    ALOGV("returning MPEG4 video access unit at time %lld us",
+                         timeUs);
+
+                    // hexdump(accessUnit->data(), accessUnit->size());
+
+                    return accessUnit;
+                } else if (chunkType != 0xb3) {
+                    offset += chunkSize;
+                    discard = true;
+                }
+
+                break;
+            }
+
+            default:
+                TRESPASS();
+        }
+
+        if (discard) {
+            (void)fetchTimestamp(offset);
+            memmove(data, &data[offset], size - offset);
+            size -= offset;
+            offset = 0;
+            mBuffer->setRange(0, size);
+        } else {
+            offset += chunkSize;
+        }
+    }
+
+    return NULL;
+}
+
+}  // namespace android
diff --git a/host/frontend/gcastv2/libandroid/ESQueue.h b/host/frontend/gcastv2/libandroid/ESQueue.h
new file mode 100644
index 0000000..9118c24
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/ESQueue.h
@@ -0,0 +1,85 @@
+/*
+ * 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 ES_QUEUE_H_
+
+#define ES_QUEUE_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include <list>
+
+namespace android {
+
+struct ABuffer;
+struct MetaData;
+
+struct ElementaryStreamQueue {
+    enum Mode {
+        H264,
+        AAC,
+        MPEG_AUDIO,
+        MPEG_VIDEO,
+        MPEG4_VIDEO,
+        PCM_AUDIO,
+    };
+
+    enum Flags {
+        // Data appended to the queue is always at access unit boundaries.
+        kFlag_AlignedData = 1,
+    };
+    ElementaryStreamQueue(Mode mode, uint32_t flags = 0);
+
+    status_t appendData(const void *data, size_t size, int64_t timeUs);
+    void clear(bool clearFormat);
+
+    sp<ABuffer> dequeueAccessUnit();
+
+    sp<MetaData> getFormat();
+
+private:
+    struct RangeInfo {
+        int64_t mTimestampUs;
+        size_t mLength;
+    };
+
+    Mode mMode;
+    uint32_t mFlags;
+
+    sp<ABuffer> mBuffer;
+    std::list<RangeInfo> mRangeInfos;
+
+    sp<MetaData> mFormat;
+
+    sp<ABuffer> dequeueAccessUnitH264();
+    sp<ABuffer> dequeueAccessUnitAAC();
+    sp<ABuffer> dequeueAccessUnitMPEGAudio();
+    sp<ABuffer> dequeueAccessUnitMPEGVideo();
+    sp<ABuffer> dequeueAccessUnitMPEG4Video();
+    sp<ABuffer> dequeueAccessUnitPCMAudio();
+
+    // consume a logical (compressed) access unit of size "size",
+    // returns its timestamp in us (or -1 if no time information).
+    int64_t fetchTimestamp(size_t size);
+
+    DISALLOW_EVIL_CONSTRUCTORS(ElementaryStreamQueue);
+};
+
+}  // namespace android
+
+#endif  // ES_QUEUE_H_
diff --git a/host/frontend/gcastv2/libandroid/FileSource.cpp b/host/frontend/gcastv2/libandroid/FileSource.cpp
new file mode 100644
index 0000000..986b4f5
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/FileSource.cpp
@@ -0,0 +1,76 @@
+#include <media/stagefright/FileSource.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#ifdef TARGET_IOS
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+namespace android {
+
+FileSource::FileSource(const char *path)
+    : mFd(-1),
+      mInitCheck(NO_INIT) {
+#ifdef TARGET_IOS
+    CFBundleRef mainBundle = CFBundleGetMainBundle();
+
+    CFStringRef pathRef =
+        CFStringCreateWithCString(kCFAllocatorDefault, path, kCFStringEncodingUTF8);
+
+    CFURLRef url = CFBundleCopyResourceURL(mainBundle, pathRef, NULL, NULL);
+
+    CFRelease(pathRef);
+    pathRef = nullptr;
+
+    pathRef = CFURLCopyPath(url);
+
+    CFRelease(url);
+    url = nullptr;
+
+    char fullPath[256];
+    CFStringGetCString(
+            pathRef, fullPath, sizeof(fullPath), kCFStringEncodingUTF8);
+
+    CFRelease(pathRef);
+    pathRef = nullptr;
+
+    path = fullPath;
+#endif
+
+    mFd = open(path, O_RDONLY);
+    mInitCheck = (mFd >= 0) ? OK : -errno;
+}
+
+FileSource::~FileSource() {
+    if (mFd >= 0) {
+        close(mFd);
+        mFd = -1;
+    }
+}
+
+status_t FileSource::initCheck() const {
+    return mInitCheck;
+}
+
+status_t FileSource::getSize(off_t *size) const {
+    *size = lseek(mFd, 0, SEEK_END);
+    if (*size == -1) {
+        return -errno;
+    }
+
+    return OK;
+}
+
+ssize_t FileSource::readAt(off_t offset, void *data, size_t size) {
+    ssize_t n = pread(mFd, data, size, offset);
+
+    if (n == -1) {
+        return -errno;
+    }
+
+    return n;
+}
+
+}  // namespace android
+
diff --git a/host/frontend/gcastv2/libandroid/JSONObject.cpp b/host/frontend/gcastv2/libandroid/JSONObject.cpp
new file mode 100644
index 0000000..db658ed
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/JSONObject.cpp
@@ -0,0 +1,691 @@
+/*
+ * Copyright (C) 2013 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 "JSONObject"
+#include <utils/Log.h>
+
+#include <media/stagefright/foundation/JSONObject.h>
+
+#include <ctype.h>
+#include <media/stagefright/Utils.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+// static
+ssize_t JSONValue::Parse(const char *data, size_t size, JSONValue *out) {
+    size_t offset = 0;
+    while (offset < size && isspace(data[offset])) {
+        ++offset;
+    }
+
+    if (offset == size) {
+        return ERROR_MALFORMED;
+    }
+
+    if (data[offset] == '[') {
+        sp<JSONArray> array = new JSONArray;
+        ++offset;
+
+        for (;;) {
+            while (offset < size && isspace(data[offset])) {
+                ++offset;
+            }
+
+            if (offset == size) {
+                return ERROR_MALFORMED;
+            }
+
+            if (data[offset] == ']') {
+                ++offset;
+                break;
+            }
+
+            JSONValue val;
+            ssize_t n = Parse(&data[offset], size - offset, &val);
+
+            if (n < 0) {
+                return n;
+            }
+
+            array->addValue(val);
+
+            offset += n;
+
+            while (offset < size && isspace(data[offset])) {
+                ++offset;
+            }
+
+            if (offset == size) {
+                return ERROR_MALFORMED;
+            }
+
+            if (data[offset] == ',') {
+                ++offset;
+            } else if (data[offset] != ']') {
+                return ERROR_MALFORMED;
+            }
+        };
+
+        out->setArray(array);
+
+        return offset;
+    } else if (data[offset] == '{') {
+        sp<JSONObject> obj = new JSONObject;
+        ++offset;
+
+        for (;;) {
+            while (offset < size && isspace(data[offset])) {
+                ++offset;
+            }
+
+            if (offset == size) {
+                return ERROR_MALFORMED;
+            }
+
+            if (data[offset] == '}') {
+                ++offset;
+                break;
+            }
+
+            JSONValue key;
+            ssize_t n = Parse(&data[offset], size - offset, &key);
+
+            if (n < 0) {
+                return n;
+            }
+
+            if (key.type() != TYPE_STRING) {
+                return ERROR_MALFORMED;
+            }
+
+            offset += n;
+
+            while (offset < size && isspace(data[offset])) {
+                ++offset;
+            }
+
+            if (offset == size || data[offset] != ':') {
+                return ERROR_MALFORMED;
+            }
+
+            ++offset;
+
+            JSONValue val;
+            n = Parse(&data[offset], size - offset, &val);
+
+            if (n < 0) {
+                return n;
+            }
+
+            std::string keyVal;
+            CHECK(key.getString(&keyVal));
+
+            obj->setValue(keyVal.c_str(), val);
+
+            offset += n;
+
+            while (offset < size && isspace(data[offset])) {
+                ++offset;
+            }
+
+            if (offset == size) {
+                return ERROR_MALFORMED;
+            }
+
+            if (data[offset] == ',') {
+                ++offset;
+            } else if (data[offset] != '}') {
+                return ERROR_MALFORMED;
+            }
+        };
+
+        out->setObject(obj);
+
+        return offset;
+    } else if (data[offset] == '"') {
+        ++offset;
+
+        std::string s;
+        bool escaped = false;
+        while (offset < size) {
+            if (escaped) {
+                char c;
+                switch (data[offset]) {
+                    case '\"':
+                    case '\\':
+                    case '/':
+                        c = data[offset];
+                        break;
+                    case 'b':
+                        c = '\x08';
+                        break;
+                    case 'f':
+                        c = '\x0c';
+                        break;
+                    case 'n':
+                        c = '\x0a';
+                        break;
+                    case 'r':
+                        c = '\x0d';
+                        break;
+                    case 't':
+                        c = '\x09';
+                        break;
+                    default:
+                        return ERROR_MALFORMED;
+                }
+
+                s.append(1, c);
+                ++offset;
+
+                escaped = false;
+                continue;
+            } else if (data[offset] == '\\') {
+                escaped = true;
+                ++offset;
+                continue;
+            } else if (data[offset] == '"') {
+                break;
+            }
+
+            s.append(1, data[offset++]);
+        }
+
+        if (offset == size) {
+            return ERROR_MALFORMED;
+        }
+
+        ++offset;
+        out->setString(s);
+
+        return offset;
+    } else if (isdigit(data[offset]) || data[offset] == '-') {
+        bool negate = false;
+        if (data[offset] == '-') {
+            negate = true;
+            ++offset;
+
+            if (offset == size) {
+                return ERROR_MALFORMED;
+            }
+        }
+
+        size_t firstDigitOffset = offset;
+        while (offset < size && isdigit(data[offset])) {
+            ++offset;
+        }
+
+        size_t numDigits = offset - firstDigitOffset;
+        if (numDigits > 1 && data[firstDigitOffset] == '0') {
+            // No leading zeros.
+            return ERROR_MALFORMED;
+        }
+
+        size_t firstFracDigitOffset = 0;
+        size_t numFracDigits = 0;
+
+        if (offset < size && data[offset] == '.') {
+            ++offset;
+
+            firstFracDigitOffset = offset;
+            while (offset < size && isdigit(data[offset])) {
+                ++offset;
+            }
+
+            numFracDigits = offset - firstFracDigitOffset;
+            if (numFracDigits == 0) {
+                return ERROR_MALFORMED;
+            }
+        }
+
+        bool negateExponent = false;
+        size_t firstExpDigitOffset = 0;
+        size_t numExpDigits = 0;
+
+        if (offset < size && (data[offset] == 'e' || data[offset] == 'E')) {
+            ++offset;
+
+            if (offset == size) {
+                return ERROR_MALFORMED;
+            }
+
+            if (data[offset] == '+' || data[offset] == '-') {
+                if (data[offset] == '-') {
+                    negateExponent = true;
+                }
+
+                ++offset;
+            }
+
+            firstExpDigitOffset = offset;
+            while (offset < size && isdigit(data[offset])) {
+                ++offset;
+            }
+
+            numExpDigits = offset - firstExpDigitOffset;
+            if (numExpDigits == 0) {
+                return ERROR_MALFORMED;
+            }
+        }
+
+        CHECK_EQ(numFracDigits, 0u);
+        CHECK_EQ(numExpDigits, 0u);
+
+        int32_t x = 0;
+        for (size_t i = 0; i < numDigits; ++i) {
+            x *= 10;
+            x += data[firstDigitOffset + i] - '0';
+
+            CHECK_GE(x, 0);
+        }
+
+        if (negate) {
+            x = -x;
+        }
+
+        out->setInt32(x);
+
+        return offset;
+    } else if (offset + 4 <= size && !strncmp("null", &data[offset], 4)) {
+        out->unset();
+        return offset + 4;
+    } else if (offset + 4 <= size && !strncmp("true", &data[offset], 4)) {
+        out->setBoolean(true);
+        return offset + 4;
+    } else if (offset + 5 <= size && !strncmp("false", &data[offset], 5)) {
+        out->setBoolean(false);
+        return offset + 5;
+    }
+
+    return ERROR_MALFORMED;
+}
+
+JSONValue::JSONValue()
+    : mType(TYPE_NULL) {
+}
+
+JSONValue::JSONValue(const JSONValue &other)
+    : mType(TYPE_NULL) {
+    *this = other;
+}
+
+JSONValue &JSONValue::operator=(const JSONValue &other) {
+    if (&other != this) {
+        unset();
+        mType = other.mType;
+        mValue = other.mValue;
+
+        switch (mType) {
+            case TYPE_STRING:
+                mValue.mString = new std::string(*other.mValue.mString);
+                break;
+            case TYPE_OBJECT:
+            case TYPE_ARRAY:
+                mValue.mObjectOrArray->incStrong(this);
+                break;
+
+            default:
+                break;
+        }
+    }
+
+    return *this;
+}
+
+JSONValue::~JSONValue() {
+    unset();
+}
+
+JSONValue::FieldType JSONValue::type() const {
+    return mType;
+}
+
+bool JSONValue::getInt32(int32_t *value) const {
+    if (mType != TYPE_NUMBER) {
+        return false;
+    }
+
+    *value = mValue.mInt32;
+    return true;
+}
+
+bool JSONValue::getString(std::string *value) const {
+    if (mType != TYPE_STRING) {
+        return false;
+    }
+
+    *value = *mValue.mString;
+    return true;
+}
+
+bool JSONValue::getBoolean(bool *value) const {
+    if (mType != TYPE_BOOLEAN) {
+        return false;
+    }
+
+    *value = mValue.mBoolean;
+    return true;
+}
+
+bool JSONValue::getObject(sp<JSONObject> *value) const {
+    if (mType != TYPE_OBJECT) {
+        return false;
+    }
+
+    *value = static_cast<JSONObject *>(mValue.mObjectOrArray);
+    return true;
+}
+
+bool JSONValue::getArray(sp<JSONArray> *value) const {
+    if (mType != TYPE_ARRAY) {
+        return false;
+    }
+
+    *value = static_cast<JSONArray *>(mValue.mObjectOrArray);
+    return true;
+}
+
+void JSONValue::setInt32(int32_t value) {
+    unset();
+
+    mValue.mInt32 = value;
+    mType = TYPE_NUMBER;
+}
+
+void JSONValue::setString(std::string_view value) {
+    unset();
+
+    mValue.mString = new std::string(value);
+    mType = TYPE_STRING;
+}
+
+void JSONValue::setBoolean(bool value) {
+    unset();
+
+    mValue.mBoolean = value;
+    mType = TYPE_BOOLEAN;
+}
+
+void JSONValue::setObject(const sp<JSONObject> &obj) {
+    unset();
+
+    mValue.mObjectOrArray = obj.get();
+    mValue.mObjectOrArray->incStrong(this);
+
+    mType = TYPE_OBJECT;
+}
+
+void JSONValue::setArray(const sp<JSONArray> &array) {
+    unset();
+
+    mValue.mObjectOrArray = array.get();
+    mValue.mObjectOrArray->incStrong(this);
+
+    mType = TYPE_ARRAY;
+}
+
+void JSONValue::unset() {
+    switch (mType) {
+        case TYPE_STRING:
+            delete mValue.mString;
+            break;
+        case TYPE_OBJECT:
+        case TYPE_ARRAY:
+            mValue.mObjectOrArray->decStrong(this);
+            break;
+
+        default:
+            break;
+    }
+
+    mType = TYPE_NULL;
+}
+
+static void EscapeString(const char *in, size_t inSize, std::string *out) {
+    CHECK(in != out->c_str());
+    out->clear();
+
+    for (size_t i = 0; i < inSize; ++i) {
+        char c = in[i];
+        switch (c) {
+            case '\"':
+                out->append("\\\"");
+                break;
+            case '\\':
+                out->append("\\\\");
+                break;
+            case '/':
+                out->append("\\/");
+                break;
+            case '\x08':
+                out->append("\\b");
+                break;
+            case '\x0c':
+                out->append("\\f");
+                break;
+            case '\x0a':
+                out->append("\\n");
+                break;
+            case '\x0d':
+                out->append("\\r");
+                break;
+            case '\x09':
+                out->append("\\t");
+                break;
+            default:
+                out->append(1, c);
+                break;
+        }
+    }
+}
+
+std::string JSONValue::toString(size_t depth, bool indentFirstLine) const {
+    static const char kIndent[] = "                                        ";
+
+    std::string out;
+
+    switch (mType) {
+        case TYPE_STRING:
+        {
+            std::string escaped;
+            EscapeString(
+                    mValue.mString->c_str(), mValue.mString->size(), &escaped);
+
+            out.append("\"");
+            out.append(escaped);
+            out.append("\"");
+            break;
+        }
+
+        case TYPE_NUMBER:
+        {
+            out = StringPrintf("%d", mValue.mInt32);
+            break;
+        }
+
+        case TYPE_BOOLEAN:
+        {
+            out = mValue.mBoolean ? "true" : "false";
+            break;
+        }
+
+        case TYPE_NULL:
+        {
+            out = "null";
+            break;
+        }
+
+        case TYPE_OBJECT:
+        case TYPE_ARRAY:
+        {
+            out = (mType == TYPE_OBJECT) ? "{\n" : "[\n";
+            out.append(mValue.mObjectOrArray->internalToString(depth + 1, true));
+            out.append("\n");
+            out.append(kIndent, 2 * depth);
+            out.append(mType == TYPE_OBJECT ? "}" : "]");
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+
+    if (indentFirstLine) {
+        out.insert(0, kIndent, 2 * depth);
+    }
+
+    return out;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+// static
+sp<JSONCompound> JSONCompound::Parse(const char *data, size_t size) {
+    JSONValue value;
+    ssize_t result = JSONValue::Parse(data, size, &value);
+
+    if (result < 0) {
+        return NULL;
+    }
+
+    sp<JSONObject> obj;
+    if (value.getObject(&obj)) {
+        return obj;
+    }
+
+    sp<JSONArray> array;
+    if (value.getArray(&array)) {
+        return array;
+    }
+
+    return NULL;
+}
+
+std::string JSONCompound::toString(size_t depth, bool indentFirstLine) const {
+    JSONValue val;
+    if (isObject()) {
+        val.setObject((JSONObject *)this);
+    } else {
+        val.setArray((JSONArray *)this);
+    }
+
+    return val.toString(depth, indentFirstLine);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+JSONObject::JSONObject() {}
+JSONObject::~JSONObject() {}
+
+bool JSONObject::isObject() const {
+    return true;
+}
+
+bool JSONObject::getValue(const char *key, JSONValue *value) const {
+    auto it = mValues.find(key);
+
+    if (it == mValues.end()) {
+        return false;
+    }
+
+    *value = it->second;
+
+    return true;
+}
+
+void JSONObject::setValue(const char *key, const JSONValue &value) {
+    mValues[std::string(key)] = value;
+}
+
+void JSONObject::remove(const char *key) {
+    mValues.erase(key);
+}
+
+std::string JSONObject::internalToString(
+        size_t depth, bool /* indentFirstLine */) const {
+    static const char kIndent[] = "                                        ";
+
+    std::string out;
+    for (auto it = mValues.begin(); it != mValues.end();) {
+        const std::string &key = it->first;
+        std::string escapedKey;
+        EscapeString(key.c_str(), key.size(), &escapedKey);
+
+        out.append(kIndent, 2 * depth);
+        out.append("\"");
+        out.append(escapedKey);
+        out.append("\": ");
+
+        out.append(it->second.toString(depth + 1, false));
+
+        ++it;
+        if (it != mValues.end()) {
+            out.append(",\n");
+        }
+    }
+
+    return out;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+JSONArray::JSONArray() {}
+
+JSONArray::~JSONArray() {}
+
+bool JSONArray::isObject() const {
+    return false;
+}
+
+size_t JSONArray::size() const {
+    return mValues.size();
+}
+
+bool JSONArray::getValue(size_t key, JSONValue *value) const {
+    if (key >= mValues.size()) {
+        return false;
+    }
+
+    *value = mValues[key];
+
+    return true;
+}
+
+void JSONArray::addValue(const JSONValue &value) {
+    mValues.push_back(value);
+}
+
+std::string JSONArray::internalToString(
+        size_t depth, bool /* indentFirstLine */) const {
+    std::string out;
+    for (size_t i = 0; i < mValues.size(); ++i) {
+        out.append(mValues[i].toString(depth));
+
+        if (i + 1 < mValues.size()) {
+            out.append(",\n");
+        }
+    }
+
+    return out;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+}  // namespace android
+
diff --git a/host/frontend/gcastv2/libandroid/JavaThread.cpp b/host/frontend/gcastv2/libandroid/JavaThread.cpp
new file mode 100644
index 0000000..e35846f
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/JavaThread.cpp
@@ -0,0 +1,30 @@
+#include <helpers/JavaThread.h>
+
+#include <helpers/MyAndroidRuntime.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+void javaAttachThread() {
+    JavaVMAttachArgs args;
+    args.version = JNI_VERSION_1_4;
+    args.name = (char *)"JavaThread";
+    args.group = nullptr;
+
+    JavaVM *vm = MyAndroidRuntime::getJavaVM();
+    CHECK(vm);
+
+    JNIEnv *env;
+    jint result = vm->AttachCurrentThread(&env, (void *)&args);
+    CHECK_EQ(result, JNI_OK);
+}
+
+void javaDetachThread() {
+    JavaVM *vm = MyAndroidRuntime::getJavaVM();
+    CHECK(vm);
+
+    CHECK_EQ(vm->DetachCurrentThread(), JNI_OK);
+}
+
+}  // namespace android
+
diff --git a/host/frontend/gcastv2/libandroid/KeyStore.cpp b/host/frontend/gcastv2/libandroid/KeyStore.cpp
new file mode 100644
index 0000000..83ec9cd
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/KeyStore.cpp
@@ -0,0 +1,30 @@
+#include <utils/KeyStore.h>
+
+#include <unordered_map>
+#include <mutex>
+
+static std::unordered_map<std::string, std::vector<uint8_t>> gCertStore;
+static std::mutex gCertStoreLock;
+
+void setCertificateOrKey(
+        const std::string &name, const void *_data, size_t size) {
+
+    const uint8_t *data = static_cast<const uint8_t *>(_data);
+
+    std::lock_guard autoLock(gCertStoreLock);
+    gCertStore[name] = std::vector<uint8_t>(data, &data[size]);
+}
+
+bool getCertificateOrKey(
+        const std::string &name, std::vector<uint8_t> *data) {
+    std::lock_guard autoLock(gCertStoreLock);
+
+    auto it = gCertStore.find(name);
+    if (it == gCertStore.end()) {
+        return false;
+    }
+
+    *data = it->second;
+
+    return true;
+}
diff --git a/host/frontend/gcastv2/libandroid/MyAndroidRuntime.cpp b/host/frontend/gcastv2/libandroid/MyAndroidRuntime.cpp
new file mode 100644
index 0000000..ae111b0
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/MyAndroidRuntime.cpp
@@ -0,0 +1,28 @@
+#include <helpers/MyAndroidRuntime.h>
+
+namespace android {
+
+static JavaVM *gVM;
+
+// static
+void MyAndroidRuntime::setJavaVM(JavaVM *vm) {
+    gVM = vm;
+}
+
+// static
+JavaVM *MyAndroidRuntime::getJavaVM() {
+    return gVM;
+}
+
+// static
+JNIEnv *MyAndroidRuntime::getJNIEnv() {
+    JNIEnv *env;
+    if (gVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+        return nullptr;
+    }
+
+    return env;
+}
+
+}  // namespace android
+
diff --git a/host/frontend/gcastv2/libandroid/MyJNIHelpers.cpp b/host/frontend/gcastv2/libandroid/MyJNIHelpers.cpp
new file mode 100644
index 0000000..f1ed0fe
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/MyJNIHelpers.cpp
@@ -0,0 +1,30 @@
+#include <helpers/MyJNIHelpers.h>
+
+#include <helpers/MyScopedLocalRef.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+void jniThrowException(
+        JNIEnv *env, const char *className, const char *msg) {
+    MyScopedLocalRef<jclass> clazz(env, env->FindClass(className));
+    CHECK(clazz.get() != nullptr);
+
+    CHECK_EQ(env->ThrowNew(clazz.get(), msg), JNI_OK);
+}
+
+int jniRegisterNativeMethods(
+        JNIEnv *env,
+        const char *className,
+        const JNINativeMethod *methods,
+        size_t numMethods) {
+    MyScopedLocalRef<jclass> clazz(env, env->FindClass(className));
+    CHECK(clazz.get() != nullptr);
+
+    CHECK_GE(env->RegisterNatives(clazz.get(), methods, numMethods), 0);
+
+    return 0;
+}
+
+}  // namespaced android
+
diff --git a/host/frontend/gcastv2/libandroid/MyScopedByteArray.cpp b/host/frontend/gcastv2/libandroid/MyScopedByteArray.cpp
new file mode 100644
index 0000000..6f31d38
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/MyScopedByteArray.cpp
@@ -0,0 +1,31 @@
+#include <helpers/MyScopedByteArray.h>
+
+namespace android {
+
+MyScopedByteArray::MyScopedByteArray(JNIEnv *env, jbyteArray arrayObj)
+    : mEnv(env),
+      mArrayObj(arrayObj),
+      mElements(nullptr),
+      mSize(0) {
+    if (mArrayObj) {
+        mElements = env->GetByteArrayElements(mArrayObj, nullptr /* isCopy */);
+        mSize = env->GetArrayLength(mArrayObj);
+    }
+}
+
+MyScopedByteArray::~MyScopedByteArray() {
+    if (mArrayObj) {
+        mEnv->ReleaseByteArrayElements(mArrayObj, mElements, 0 /* mode */);
+    }
+}
+
+const jbyte *MyScopedByteArray::data() const {
+    return mElements;
+}
+
+jsize MyScopedByteArray::size() const {
+    return mSize;
+}
+
+}  // namespace android
+
diff --git a/host/frontend/gcastv2/libandroid/MyScopedUTF8String.cpp b/host/frontend/gcastv2/libandroid/MyScopedUTF8String.cpp
new file mode 100644
index 0000000..2f7e3fe
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/MyScopedUTF8String.cpp
@@ -0,0 +1,22 @@
+#include <helpers/MyScopedUTF8String.h>
+
+namespace android {
+
+MyScopedUTF8String::MyScopedUTF8String(JNIEnv *env, jstring stringObj)
+    : mEnv(env),
+      mStringObj(stringObj),
+      mData(stringObj ? env->GetStringUTFChars(stringObj, nullptr) : nullptr) {
+}
+
+MyScopedUTF8String::~MyScopedUTF8String() {
+    if (mData) {
+        mEnv->ReleaseStringUTFChars(mStringObj, mData);
+        mData = nullptr;
+    }
+}
+
+const char *MyScopedUTF8String::c_str() const {
+    return mData;
+}
+
+}  // namespace android
diff --git a/host/frontend/gcastv2/libandroid/NuMediaExtractor.cpp b/host/frontend/gcastv2/libandroid/NuMediaExtractor.cpp
new file mode 100644
index 0000000..cce5696
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/NuMediaExtractor.cpp
@@ -0,0 +1,241 @@
+#include <media/stagefright/NuMediaExtractor.h>
+
+#include <media/stagefright/AnotherPacketSource.h>
+#include <media/stagefright/ATSParser.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+NuMediaExtractor::NuMediaExtractor()
+    : mFlags(0),
+      mParser(new ATSParser),
+      mFile(NULL),
+      mNumTracks(0),
+      mAudioTrackIndex(-1),
+      mVideoTrackIndex(-1),
+      mNextIndex(-1) {
+    mFinalResult[0] = mFinalResult[1] = OK;
+}
+
+NuMediaExtractor::~NuMediaExtractor() {
+    if (mFile) {
+        fclose(mFile);
+        mFile = NULL;
+    }
+}
+
+status_t NuMediaExtractor::setDataSource(const char *path) {
+    if (mFile) {
+        return UNKNOWN_ERROR;
+    }
+
+    mFile = fopen(path, "rb");
+
+    if (!mFile) {
+        return -ENOENT;
+    }
+
+    for (size_t i = 0; i < 1024; ++i) {
+        if (mVideoSource == NULL) {
+            mVideoSource = mParser->getSource(ATSParser::VIDEO);
+        }
+
+        if (mAudioSource == NULL) {
+            mAudioSource = mParser->getSource(ATSParser::AUDIO);
+        }
+
+        if (feedMoreData() != OK) {
+            break;
+        }
+    }
+
+    if (mAudioSource != NULL && mAudioSource->getFormat() == NULL) {
+        mAudioSource.clear();
+    }
+
+    if (mVideoSource != NULL && mVideoSource->getFormat() == NULL) {
+        mVideoSource.clear();
+    }
+
+    mNumTracks = 0;
+    if (mAudioSource != NULL) {
+        mAudioTrackIndex = mNumTracks;
+        ++mNumTracks;
+    }
+    if (mVideoSource != NULL) {
+        mVideoTrackIndex = mNumTracks;
+        ++mNumTracks;
+    }
+
+    return OK;
+}
+
+size_t NuMediaExtractor::countTracks() const {
+    return mNumTracks;
+}
+
+status_t NuMediaExtractor::getTrackFormat(
+        size_t index, sp<AMessage> *format) const {
+    CHECK_LT(index, mNumTracks);
+
+    sp<MetaData> meta;
+    if ((ssize_t)index == mAudioTrackIndex) {
+        meta = mAudioSource->getFormat();
+    } else {
+        meta = mVideoSource->getFormat();
+    }
+
+    return convertMetaDataToMessage(meta, format);
+}
+
+status_t NuMediaExtractor::selectTrack(size_t index) {
+    CHECK_LT(index, mNumTracks);
+
+    if ((ssize_t)index == mAudioTrackIndex) {
+        mFlags |= FLAG_AUDIO_SELECTED;
+    } else {
+        mFlags |= FLAG_VIDEO_SELECTED;
+    }
+
+    return OK;
+}
+
+status_t NuMediaExtractor::getSampleTime(int64_t *timeUs) {
+    fetchSamples();
+
+    if (mNextIndex < 0) {
+        return ERROR_END_OF_STREAM;
+    }
+
+    CHECK(mNextBuffer[mNextIndex]->meta()->findInt64("timeUs", timeUs));
+
+    return OK;
+}
+
+status_t NuMediaExtractor::getSampleTrackIndex(size_t *index) {
+    fetchSamples();
+
+    if (mNextIndex < 0) {
+        return ERROR_END_OF_STREAM;
+    }
+
+    *index = mNextIndex;
+
+    return OK;
+}
+
+status_t NuMediaExtractor::readSampleData(sp<ABuffer> accessUnit) {
+    fetchSamples();
+
+    if (mNextIndex < 0) {
+        return ERROR_END_OF_STREAM;
+    }
+
+    accessUnit->setRange(0, mNextBuffer[mNextIndex]->size());
+
+    memcpy(accessUnit->data(),
+           mNextBuffer[mNextIndex]->data(),
+           mNextBuffer[mNextIndex]->size());
+
+    return OK;
+}
+
+status_t NuMediaExtractor::advance() {
+    if (mNextIndex < 0) {
+        return ERROR_END_OF_STREAM;
+    }
+
+    mNextBuffer[mNextIndex].clear();
+    mNextIndex = -1;
+
+    return OK;
+}
+
+void NuMediaExtractor::fetchSamples() {
+    status_t err;
+
+    if ((mFlags & FLAG_AUDIO_SELECTED)
+            && mNextBuffer[mAudioTrackIndex] == NULL
+            && mFinalResult[mAudioTrackIndex] == OK) {
+        status_t finalResult;
+        while (!mAudioSource->hasBufferAvailable(&finalResult)
+                && finalResult == OK) {
+            feedMoreData();
+        }
+
+        err = mAudioSource->dequeueAccessUnit(&mNextBuffer[mAudioTrackIndex]);
+
+        if (err != OK) {
+            mFinalResult[mAudioTrackIndex] = err;
+        }
+    }
+
+    if ((mFlags & FLAG_VIDEO_SELECTED)
+            && mNextBuffer[mVideoTrackIndex] == NULL
+            && mFinalResult[mVideoTrackIndex] == OK) {
+        status_t finalResult;
+        while (!mVideoSource->hasBufferAvailable(&finalResult)
+                && finalResult == OK) {
+            feedMoreData();
+        }
+
+        err = mVideoSource->dequeueAccessUnit(&mNextBuffer[mVideoTrackIndex]);
+
+        if (err != OK) {
+            mFinalResult[mVideoTrackIndex] = err;
+        }
+    }
+
+    bool haveValidTime = false;
+    int64_t minTimeUs = -1ll;
+    size_t minIndex = 0;
+
+    if ((mFlags & FLAG_AUDIO_SELECTED)
+            && mNextBuffer[mAudioTrackIndex] != NULL) {
+        CHECK(mNextBuffer[mAudioTrackIndex]->meta()->findInt64("timeUs", &minTimeUs));
+        haveValidTime = true;
+        minIndex = mAudioTrackIndex;
+    }
+
+    if ((mFlags & FLAG_VIDEO_SELECTED)
+            && mNextBuffer[mVideoTrackIndex] != NULL) {
+        int64_t timeUs;
+        CHECK(mNextBuffer[mVideoTrackIndex]->meta()->findInt64("timeUs", &timeUs));
+
+        if (!haveValidTime || timeUs < minTimeUs) {
+            haveValidTime = true;
+            minTimeUs = timeUs;
+            minIndex = mVideoTrackIndex;
+        }
+    }
+
+    if (!haveValidTime) {
+        mNextIndex = -1;
+    } else {
+        mNextIndex = minIndex;
+    }
+}
+
+status_t NuMediaExtractor::feedMoreData() {
+    uint8_t packet[188];
+    ssize_t n = fread(packet, 1, sizeof(packet), mFile);
+
+    status_t err;
+
+    if (n < (ssize_t)sizeof(packet)) {
+        err = UNKNOWN_ERROR;
+    } else {
+        err = mParser->feedTSPacket(packet, sizeof(packet));
+    }
+
+    if (err != OK) {
+        mParser->signalEOS(err);
+        return err;
+    }
+
+    return OK;
+}
+
+}  // namespace android
diff --git a/host/frontend/gcastv2/libandroid/ParsedMessage.cpp b/host/frontend/gcastv2/libandroid/ParsedMessage.cpp
new file mode 100644
index 0000000..132ded9
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/ParsedMessage.cpp
@@ -0,0 +1,303 @@
+/*
+ * Copyright 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 <media/stagefright/foundation/ParsedMessage.h>
+
+#include <media/stagefright/Utils.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/hexdump.h>
+
+#include <ctype.h>
+
+namespace android {
+
+// static
+sp<ParsedMessage> ParsedMessage::Parse(
+        const char *data, size_t size, bool noMoreData, size_t *length) {
+    sp<ParsedMessage> msg = new ParsedMessage;
+    ssize_t res = msg->parse(data, size, noMoreData);
+
+    if (res < 0) {
+        *length = 0;
+        return NULL;
+    }
+
+    *length = res;
+    return msg;
+}
+
+ParsedMessage::ParsedMessage() {
+}
+
+ParsedMessage::~ParsedMessage() {
+}
+
+bool ParsedMessage::findString(const char *name, std::string *value) const {
+    std::string key = name;
+    toLower(&key);
+
+    auto it = mDict.find(key);
+
+    if (it == mDict.end()) {
+        value->clear();
+
+        return false;
+    }
+
+    *value = it->second;
+    return true;
+}
+
+bool ParsedMessage::findInt32(const char *name, int32_t *value) const {
+    std::string stringValue;
+
+    if (!findString(name, &stringValue)) {
+        return false;
+    }
+
+    char *end;
+    *value = (int32_t)strtol(stringValue.c_str(), &end, 10);
+
+    if (end == stringValue.c_str() || *end != '\0') {
+        *value = 0;
+        return false;
+    }
+
+    return true;
+}
+
+const char *ParsedMessage::getContent() const {
+    return mContent.c_str();
+}
+
+ssize_t ParsedMessage::parse(const char *data, size_t size, bool noMoreData) {
+    if (size == 0) {
+        return -1;
+    }
+
+    auto lastDictIter = mDict.end();
+
+    size_t offset = 0;
+    bool headersComplete = false;
+    while (offset < size) {
+        size_t lineEndOffset = offset;
+        while (lineEndOffset + 1 < size
+                && (data[lineEndOffset] != '\r'
+                        || data[lineEndOffset + 1] != '\n')) {
+            ++lineEndOffset;
+        }
+
+        if (lineEndOffset + 1 >= size) {
+            return -1;
+        }
+
+        std::string line(&data[offset], lineEndOffset - offset);
+
+        if (offset == 0) {
+            // Special handling for the request/status line.
+
+            mDict[std::string("_")] = line;
+            offset = lineEndOffset + 2;
+
+            continue;
+        }
+
+        if (lineEndOffset == offset) {
+            // An empty line separates headers from body.
+            headersComplete = true;
+            offset += 2;
+            break;
+        }
+
+        if (line.c_str()[0] == ' ' || line.c_str()[0] == '\t') {
+            // Support for folded header values.
+
+            if (lastDictIter != mDict.end()) {
+                // Otherwise it's malformed since the first header line
+                // cannot continue anything...
+
+                std::string &value = lastDictIter->second;
+                value.append(line);
+            }
+
+            offset = lineEndOffset + 2;
+            continue;
+        }
+
+        ssize_t colonPos = line.find(":");
+        if (colonPos >= 0) {
+            std::string key(line, 0, colonPos);
+            trim(&key);
+            toLower(&key);
+
+            line.erase(0, colonPos + 1);
+
+            lastDictIter = mDict.insert(std::make_pair(key, line)).first;
+        }
+
+        offset = lineEndOffset + 2;
+    }
+
+    if (!headersComplete && (!noMoreData || offset == 0)) {
+        // We either saw the empty line separating headers from body
+        // or we saw at least the status line and know that no more data
+        // is going to follow.
+        return -1;
+    }
+
+    for (auto &pair : mDict) {
+        trim(&pair.second);
+    }
+
+    int32_t contentLength;
+    if (!findInt32("content-length", &contentLength) || contentLength < 0) {
+        contentLength = 0;
+    }
+
+    size_t totalLength = offset + contentLength;
+
+    if (size < totalLength) {
+        return -1;
+    }
+
+    mContent.assign(&data[offset], contentLength);
+
+    return totalLength;
+}
+
+bool ParsedMessage::getRequestField(size_t index, std::string *field) const {
+    std::string line;
+    CHECK(findString("_", &line));
+
+    size_t prevOffset = 0;
+    size_t offset = 0;
+    for (size_t i = 0; i <= index; ++i) {
+        if (offset >= line.size()) {
+            return false;
+        }
+
+        ssize_t spacePos = line.find(" ", offset);
+
+        if (spacePos < 0) {
+            spacePos = line.size();
+        }
+
+        prevOffset = offset;
+        offset = spacePos + 1;
+    }
+
+    field->assign(line, prevOffset, offset - prevOffset - 1);
+
+    return true;
+}
+
+bool ParsedMessage::getStatusCode(int32_t *statusCode) const {
+    std::string statusCodeString;
+    if (!getRequestField(1, &statusCodeString)) {
+        return false;
+    }
+
+    char *end;
+    *statusCode = (int32_t)strtol(statusCodeString.c_str(), &end, 10);
+
+    if (*end != '\0' || end == statusCodeString.c_str()
+            || (*statusCode) < 100 || (*statusCode) > 999) {
+        *statusCode = 0;
+        return false;
+    }
+
+    return true;
+}
+
+std::string ParsedMessage::debugString() const {
+    std::string line;
+    CHECK(findString("_", &line));
+
+    line.append("\n");
+
+    for (const auto &pair : mDict) {
+        const std::string &key = pair.first;
+        const std::string &value = pair.second;
+
+        if (key == std::string("_")) {
+            continue;
+        }
+
+        line.append(key);
+        line.append(": ");
+        line.append(value);
+        line.append("\n");
+    }
+
+    line.append("\n");
+    line.append(mContent);
+
+    return line;
+}
+
+// static
+bool ParsedMessage::GetAttribute(
+        const char *s, const char *key, std::string *value) {
+    value->clear();
+
+    size_t keyLen = strlen(key);
+
+    for (;;) {
+        while (isspace(*s)) {
+            ++s;
+        }
+
+        const char *colonPos = strchr(s, ';');
+
+        size_t len =
+            (colonPos == NULL) ? strlen(s) : colonPos - s;
+
+        if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) {
+            value->assign(&s[keyLen + 1], len - keyLen - 1);
+            return true;
+        }
+
+        if (colonPos == NULL) {
+            return false;
+        }
+
+        s = colonPos + 1;
+    }
+}
+
+// static
+bool ParsedMessage::GetInt32Attribute(
+        const char *s, const char *key, int32_t *value) {
+    std::string stringValue;
+    if (!GetAttribute(s, key, &stringValue)) {
+        *value = 0;
+        return false;
+    }
+
+    char *end;
+    *value = (int32_t)strtol(stringValue.c_str(), &end, 10);
+
+    if (end == stringValue.c_str() || *end != '\0') {
+        *value = 0;
+        return false;
+    }
+
+    return true;
+}
+
+}  // namespace android
+
diff --git a/host/frontend/gcastv2/libandroid/RefBase.cpp b/host/frontend/gcastv2/libandroid/RefBase.cpp
new file mode 100644
index 0000000..920c561
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/RefBase.cpp
@@ -0,0 +1,15 @@
+#include <utils/RefBase.h>
+
+namespace android {
+
+RefBase *WeakList::promote() const {
+    if (mObject == NULL) {
+        return NULL;
+    }
+
+    mObject->incStrong(this);
+    return mObject;
+}
+
+}  // namespace android
+
diff --git a/host/frontend/gcastv2/libandroid/TSPacketizer.cpp b/host/frontend/gcastv2/libandroid/TSPacketizer.cpp
new file mode 100644
index 0000000..f6c4436
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/TSPacketizer.cpp
@@ -0,0 +1,697 @@
+/*
+ * Copyright 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 "TSPacketizer"
+#include <utils/Log.h>
+
+#include <media/stagefright/foundation/TSPacketizer.h>
+
+#include <media/stagefright/avc_utils.h>
+#include <media/stagefright/Utils.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 <arpa/inet.h>
+
+#include <vector>
+
+namespace android {
+
+struct TSPacketizer::Track : public RefBase {
+    Track(const sp<AMessage> &format,
+          unsigned PID, unsigned streamType, unsigned streamID);
+
+    unsigned PID() const;
+    unsigned streamType() const;
+    unsigned streamID() const;
+
+    // Returns the previous value.
+    unsigned incrementContinuityCounter();
+
+    bool isAudio() const;
+    bool isVideo() const;
+
+    bool isH264() const;
+    bool lacksADTSHeader() const;
+
+    sp<ABuffer> prependCSD(const sp<ABuffer> &accessUnit) const;
+    sp<ABuffer> prependADTSHeader(const sp<ABuffer> &accessUnit) const;
+
+protected:
+    virtual ~Track();
+
+private:
+    sp<AMessage> mFormat;
+
+    unsigned mPID;
+    unsigned mStreamType;
+    unsigned mStreamID;
+    unsigned mContinuityCounter;
+
+    std::string mMIME;
+    std::vector<sp<ABuffer>> mCSD;
+
+    DISALLOW_EVIL_CONSTRUCTORS(Track);
+};
+
+TSPacketizer::Track::Track(
+        const sp<AMessage> &format,
+        unsigned PID, unsigned streamType, unsigned streamID)
+    : mFormat(format),
+      mPID(PID),
+      mStreamType(streamType),
+      mStreamID(streamID),
+      mContinuityCounter(0) {
+    CHECK(format->findString("mime", &mMIME));
+
+    if (!strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_VIDEO_AVC)
+            || !strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) {
+        for (size_t i = 0;; ++i) {
+            sp<ABuffer> csd;
+            if (!format->findBuffer(StringPrintf("csd-%d", i).c_str(), &csd)) {
+                break;
+            }
+
+            mCSD.push_back(csd);
+        }
+    }
+}
+
+TSPacketizer::Track::~Track() {
+}
+
+unsigned TSPacketizer::Track::PID() const {
+    return mPID;
+}
+
+unsigned TSPacketizer::Track::streamType() const {
+    return mStreamType;
+}
+
+unsigned TSPacketizer::Track::streamID() const {
+    return mStreamID;
+}
+
+unsigned TSPacketizer::Track::incrementContinuityCounter() {
+    unsigned prevCounter = mContinuityCounter;
+
+    if (++mContinuityCounter == 16) {
+        mContinuityCounter = 0;
+    }
+
+    return prevCounter;
+}
+
+bool TSPacketizer::Track::isAudio() const {
+    return !strncasecmp("audio/", mMIME.c_str(), 6);
+}
+
+bool TSPacketizer::Track::isVideo() const {
+    return !strncasecmp("video/", mMIME.c_str(), 6);
+}
+
+bool TSPacketizer::Track::isH264() const {
+    return !strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_VIDEO_AVC);
+}
+
+bool TSPacketizer::Track::lacksADTSHeader() const {
+    if (strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) {
+        return false;
+    }
+
+    int32_t isADTS;
+    if (mFormat->findInt32("is-adts", &isADTS) && isADTS != 0) {
+        return false;
+    }
+
+    return true;
+}
+
+sp<ABuffer> TSPacketizer::Track::prependCSD(
+        const sp<ABuffer> &accessUnit) const {
+    size_t size = 0;
+    for (const auto &csd : mCSD) {
+        size += csd->size();
+    }
+
+    sp<ABuffer> dup = new ABuffer(accessUnit->size() + size);
+    size_t offset = 0;
+    for (const auto &csd : mCSD) {
+        memcpy(dup->data() + offset, csd->data(), csd->size());
+        offset += csd->size();
+    }
+
+    memcpy(dup->data() + offset, accessUnit->data(), accessUnit->size());
+
+    return dup;
+}
+
+sp<ABuffer> TSPacketizer::Track::prependADTSHeader(
+        const sp<ABuffer> &accessUnit) const {
+    CHECK_EQ(mCSD.size(), 1u);
+
+    const uint8_t *codec_specific_data = mCSD[0]->data();
+
+    const uint32_t aac_frame_length = static_cast<uint32_t>(accessUnit->size() + 7);
+
+    sp<ABuffer> dup = new ABuffer(aac_frame_length);
+
+    unsigned profile = (codec_specific_data[0] >> 3) - 1;
+
+    unsigned sampling_freq_index =
+        ((codec_specific_data[0] & 7) << 1)
+        | (codec_specific_data[1] >> 7);
+
+    unsigned channel_configuration =
+        (codec_specific_data[1] >> 3) & 0x0f;
+
+    uint8_t *ptr = dup->data();
+
+    *ptr++ = 0xff;
+    *ptr++ = 0xf1;  // b11110001, ID=0, layer=0, protection_absent=1
+
+    *ptr++ =
+        profile << 6
+        | sampling_freq_index << 2
+        | ((channel_configuration >> 2) & 1);  // private_bit=0
+
+    // original_copy=0, home=0, copyright_id_bit=0, copyright_id_start=0
+    *ptr++ =
+        (channel_configuration & 3) << 6
+        | aac_frame_length >> 11;
+    *ptr++ = (aac_frame_length >> 3) & 0xff;
+    *ptr++ = (aac_frame_length & 7) << 5;
+
+    // adts_buffer_fullness=0, number_of_raw_data_blocks_in_frame=0
+    *ptr++ = 0;
+
+    memcpy(ptr, accessUnit->data(), accessUnit->size());
+
+    return dup;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+TSPacketizer::TSPacketizer()
+    : mPATContinuityCounter(0),
+      mPMTContinuityCounter(0) {
+    initCrcTable();
+}
+
+TSPacketizer::~TSPacketizer() {
+}
+
+ssize_t TSPacketizer::addTrack(const sp<AMessage> &format) {
+    std::string mime;
+    CHECK(format->findString("mime", &mime));
+
+    unsigned PIDStart;
+    bool isVideo = !strncasecmp("video/", mime.c_str(), 6);
+    bool isAudio = !strncasecmp("audio/", mime.c_str(), 6);
+
+    if (isVideo) {
+        PIDStart = 0x1011;
+    } else if (isAudio) {
+        PIDStart = 0x1100;
+    } else {
+        return ERROR_UNSUPPORTED;
+    }
+
+    unsigned streamType;
+    unsigned streamIDStart;
+    unsigned streamIDStop;
+
+    if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_AVC)) {
+        streamType = 0x1b;
+        streamIDStart = 0xe0;
+        streamIDStop = 0xef;
+    } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) {
+        streamType = 0x0f;
+        streamIDStart = 0xc0;
+        streamIDStop = 0xdf;
+    } else {
+        return ERROR_UNSUPPORTED;
+    }
+
+    size_t numTracksOfThisType = 0;
+    unsigned PID = PIDStart;
+
+    for (const auto &track : mTracks) {
+        if (track->streamType() == streamType) {
+            ++numTracksOfThisType;
+        }
+
+        if ((isAudio && track->isAudio()) || (isVideo && track->isVideo())) {
+            ++PID;
+        }
+    }
+
+    unsigned streamID = static_cast<unsigned>(streamIDStart + numTracksOfThisType);
+    if (streamID > streamIDStop) {
+        return -ERANGE;
+    }
+
+    sp<Track> track = new Track(format, PID, streamType, streamID);
+    size_t index = mTracks.size();
+    mTracks.push_back(track);
+    return index;
+}
+
+status_t TSPacketizer::packetize(
+        size_t trackIndex,
+        const sp<ABuffer> &_accessUnit,
+        sp<ABuffer> *packets,
+        uint32_t flags) {
+    sp<ABuffer> accessUnit = _accessUnit;
+
+    packets->clear();
+
+    if (trackIndex >= mTracks.size()) {
+        return -ERANGE;
+    }
+
+    int64_t timeUs;
+    CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
+
+    const sp<Track> &track = mTracks[trackIndex];
+
+    if (track->isH264()) {
+        if (IsIDR(accessUnit)) {
+            // prepend codec specific data, i.e. SPS and PPS.
+            accessUnit = track->prependCSD(accessUnit);
+        }
+    } else if (track->lacksADTSHeader()) {
+        accessUnit = track->prependADTSHeader(accessUnit);
+    }
+
+    // 0x47
+    // transport_error_indicator = b0
+    // payload_unit_start_indicator = b1
+    // transport_priority = b0
+    // PID
+    // transport_scrambling_control = b00
+    // adaptation_field_control = b??
+    // continuity_counter = b????
+    // -- payload follows
+    // packet_startcode_prefix = 0x000001
+    // stream_id
+    // PES_packet_length = 0x????
+    // reserved = b10
+    // PES_scrambling_control = b00
+    // PES_priority = b0
+    // data_alignment_indicator = b1
+    // copyright = b0
+    // original_or_copy = b0
+    // PTS_DTS_flags = b10  (PTS only)
+    // ESCR_flag = b0
+    // ES_rate_flag = b0
+    // DSM_trick_mode_flag = b0
+    // additional_copy_info_flag = b0
+    // PES_CRC_flag = b0
+    // PES_extension_flag = b0
+    // PES_header_data_length = 0x05
+    // reserved = b0010 (PTS)
+    // PTS[32..30] = b???
+    // reserved = b1
+    // PTS[29..15] = b??? ???? ???? ???? (15 bits)
+    // reserved = b1
+    // PTS[14..0] = b??? ???? ???? ???? (15 bits)
+    // reserved = b1
+    // the first fragment of "buffer" follows
+
+    size_t numTSPackets;
+    if (accessUnit->size() <= 170) {
+        numTSPackets = 1;
+    } else {
+        numTSPackets = 1 + ((accessUnit->size() - 170) + 183) / 184;
+    }
+
+    if (flags & EMIT_PAT_AND_PMT) {
+        numTSPackets += 2;
+    }
+
+    if (flags & EMIT_PCR) {
+        ++numTSPackets;
+    }
+
+    sp<ABuffer> buffer = new ABuffer(numTSPackets * 188);
+    uint8_t *packetDataStart = buffer->data();
+
+    if (flags & EMIT_PAT_AND_PMT) {
+        // Program Association Table (PAT):
+        // 0x47
+        // transport_error_indicator = b0
+        // payload_unit_start_indicator = b1
+        // transport_priority = b0
+        // PID = b0000000000000 (13 bits)
+        // transport_scrambling_control = b00
+        // adaptation_field_control = b01 (no adaptation field, payload only)
+        // continuity_counter = b????
+        // skip = 0x00
+        // --- payload follows
+        // table_id = 0x00
+        // section_syntax_indicator = b1
+        // must_be_zero = b0
+        // reserved = b11
+        // section_length = 0x00d
+        // transport_stream_id = 0x0000
+        // reserved = b11
+        // version_number = b00001
+        // current_next_indicator = b1
+        // section_number = 0x00
+        // last_section_number = 0x00
+        //   one program follows:
+        //   program_number = 0x0001
+        //   reserved = b111
+        //   program_map_PID = kPID_PMT (13 bits!)
+        // CRC = 0x????????
+
+        if (++mPATContinuityCounter == 16) {
+            mPATContinuityCounter = 0;
+        }
+
+        uint8_t *ptr = packetDataStart;
+        *ptr++ = 0x47;
+        *ptr++ = 0x40;
+        *ptr++ = 0x00;
+        *ptr++ = 0x10 | mPATContinuityCounter;
+        *ptr++ = 0x00;
+
+        const uint8_t *crcDataStart = ptr;
+        *ptr++ = 0x00;
+        *ptr++ = 0xb0;
+        *ptr++ = 0x0d;
+        *ptr++ = 0x00;
+        *ptr++ = 0x00;
+        *ptr++ = 0xc3;
+        *ptr++ = 0x00;
+        *ptr++ = 0x00;
+        *ptr++ = 0x00;
+        *ptr++ = 0x01;
+        *ptr++ = 0xe0 | (kPID_PMT >> 8);
+        *ptr++ = kPID_PMT & 0xff;
+
+        CHECK_EQ(ptr - crcDataStart, 12);
+        uint32_t crc = htonl(crc32(crcDataStart, ptr - crcDataStart));
+        memcpy(ptr, &crc, 4);
+        ptr += 4;
+
+        size_t sizeLeft = packetDataStart + 188 - ptr;
+        memset(ptr, 0xff, sizeLeft);
+
+        packetDataStart += 188;
+
+        // Program Map (PMT):
+        // 0x47
+        // transport_error_indicator = b0
+        // payload_unit_start_indicator = b1
+        // transport_priority = b0
+        // PID = kPID_PMT (13 bits)
+        // transport_scrambling_control = b00
+        // adaptation_field_control = b01 (no adaptation field, payload only)
+        // continuity_counter = b????
+        // skip = 0x00
+        // -- payload follows
+        // table_id = 0x02
+        // section_syntax_indicator = b1
+        // must_be_zero = b0
+        // reserved = b11
+        // section_length = 0x???
+        // program_number = 0x0001
+        // reserved = b11
+        // version_number = b00001
+        // current_next_indicator = b1
+        // section_number = 0x00
+        // last_section_number = 0x00
+        // reserved = b111
+        // PCR_PID = kPCR_PID (13 bits)
+        // reserved = b1111
+        // program_info_length = 0x000
+        //   one or more elementary stream descriptions follow:
+        //   stream_type = 0x??
+        //   reserved = b111
+        //   elementary_PID = b? ???? ???? ???? (13 bits)
+        //   reserved = b1111
+        //   ES_info_length = 0x000
+        // CRC = 0x????????
+
+        if (++mPMTContinuityCounter == 16) {
+            mPMTContinuityCounter = 0;
+        }
+
+        size_t section_length = 5 * mTracks.size() + 4 + 9;
+
+        ptr = packetDataStart;
+        *ptr++ = 0x47;
+        *ptr++ = 0x40 | (kPID_PMT >> 8);
+        *ptr++ = kPID_PMT & 0xff;
+        *ptr++ = 0x10 | mPMTContinuityCounter;
+        *ptr++ = 0x00;
+
+        crcDataStart = ptr;
+        *ptr++ = 0x02;
+        *ptr++ = 0xb0 | (section_length >> 8);
+        *ptr++ = section_length & 0xff;
+        *ptr++ = 0x00;
+        *ptr++ = 0x01;
+        *ptr++ = 0xc3;
+        *ptr++ = 0x00;
+        *ptr++ = 0x00;
+        *ptr++ = 0xe0 | (kPID_PCR >> 8);
+        *ptr++ = kPID_PCR & 0xff;
+        *ptr++ = 0xf0;
+        *ptr++ = 0x00;
+
+        for (size_t i = 0; i < mTracks.size(); ++i) {
+            const sp<Track> &track = mTracks[i];
+
+            *ptr++ = track->streamType();
+            *ptr++ = 0xe0 | (track->PID() >> 8);
+            *ptr++ = track->PID() & 0xff;
+            *ptr++ = 0xf0;
+            *ptr++ = 0x00;
+        }
+
+        CHECK_EQ(static_cast<size_t>(ptr - crcDataStart),
+                 12 + mTracks.size() * 5);
+
+        crc = htonl(crc32(crcDataStart, ptr - crcDataStart));
+        memcpy(ptr, &crc, 4);
+        ptr += 4;
+
+        sizeLeft = packetDataStart + 188 - ptr;
+        memset(ptr, 0xff, sizeLeft);
+
+        packetDataStart += 188;
+    }
+
+    if (flags & EMIT_PCR) {
+        // PCR stream
+        // 0x47
+        // transport_error_indicator = b0
+        // payload_unit_start_indicator = b1
+        // transport_priority = b0
+        // PID = kPCR_PID (13 bits)
+        // transport_scrambling_control = b00
+        // adaptation_field_control = b10 (adaptation field only, no payload)
+        // continuity_counter = b0000 (does not increment)
+        // adaptation_field_length = 183
+        // discontinuity_indicator = b0
+        // random_access_indicator = b0
+        // elementary_stream_priority_indicator = b0
+        // PCR_flag = b1
+        // OPCR_flag = b0
+        // splicing_point_flag = b0
+        // transport_private_data_flag = b0
+        // adaptation_field_extension_flag = b0
+        // program_clock_reference_base = b?????????????????????????????????
+        // reserved = b111111
+        // program_clock_reference_extension = b?????????
+
+#if 0
+        int64_t nowUs = ALooper::GetNowUs();
+#else
+        int64_t nowUs = timeUs;
+#endif
+
+        uint64_t PCR = nowUs * 27;  // PCR based on a 27MHz clock
+        uint64_t PCR_base = PCR / 300;
+        uint32_t PCR_ext = PCR % 300;
+
+        uint8_t *ptr = packetDataStart;
+        *ptr++ = 0x47;
+        *ptr++ = 0x40 | (kPID_PCR >> 8);
+        *ptr++ = kPID_PCR & 0xff;
+        *ptr++ = 0x20;
+        *ptr++ = 0xb7;  // adaptation_field_length
+        *ptr++ = 0x10;
+        *ptr++ = (PCR_base >> 25) & 0xff;
+        *ptr++ = (PCR_base >> 17) & 0xff;
+        *ptr++ = (PCR_base >> 9) & 0xff;
+        *ptr++ = ((PCR_base & 1) << 7) | 0x7e | ((PCR_ext >> 8) & 1);
+        *ptr++ = (PCR_ext & 0xff);
+
+        size_t sizeLeft = packetDataStart + 188 - ptr;
+        memset(ptr, 0xff, sizeLeft);
+
+        packetDataStart += 188;
+    }
+
+    uint32_t PTS = static_cast<uint32_t>((timeUs * 9ll) / 100ll);
+
+    size_t PES_packet_length = accessUnit->size() + 8;
+    bool padding = (accessUnit->size() < (188 - 18));
+
+    if (PES_packet_length >= 65536) {
+        // This really should only happen for video.
+        CHECK(track->isVideo());
+
+        // It's valid to set this to 0 for video according to the specs.
+        PES_packet_length = 0;
+    }
+
+    uint8_t *ptr = packetDataStart;
+    *ptr++ = 0x47;
+    *ptr++ = 0x40 | (track->PID() >> 8);
+    *ptr++ = track->PID() & 0xff;
+    *ptr++ = (padding ? 0x30 : 0x10) | track->incrementContinuityCounter();
+
+    if (padding) {
+        size_t paddingSize = 188 - 18 - accessUnit->size();
+        *ptr++ = paddingSize - 1;
+        if (paddingSize >= 2) {
+            *ptr++ = 0x00;
+            memset(ptr, 0xff, paddingSize - 2);
+            ptr += paddingSize - 2;
+        }
+    }
+
+    *ptr++ = 0x00;
+    *ptr++ = 0x00;
+    *ptr++ = 0x01;
+    *ptr++ = track->streamID();
+    *ptr++ = PES_packet_length >> 8;
+    *ptr++ = PES_packet_length & 0xff;
+    *ptr++ = 0x84;
+    *ptr++ = 0x80;
+    *ptr++ = 0x05;
+    *ptr++ = 0x20 | (((PTS >> 30) & 7) << 1) | 1;
+    *ptr++ = (PTS >> 22) & 0xff;
+    *ptr++ = (((PTS >> 15) & 0x7f) << 1) | 1;
+    *ptr++ = (PTS >> 7) & 0xff;
+    *ptr++ = ((PTS & 0x7f) << 1) | 1;
+
+    // 18 bytes of TS/PES header leave 188 - 18 = 170 bytes for the payload
+
+    size_t sizeLeft = packetDataStart + 188 - ptr;
+    size_t copy = accessUnit->size();
+    if (copy > sizeLeft) {
+        copy = sizeLeft;
+    }
+
+    memcpy(ptr, accessUnit->data(), copy);
+    ptr += copy;
+    CHECK_EQ(sizeLeft, copy);
+    memset(ptr, 0xff, sizeLeft - copy);
+
+    packetDataStart += 188;
+
+    size_t offset = copy;
+    while (offset < accessUnit->size()) {
+        bool padding = (accessUnit->size() - offset) < (188 - 4);
+
+        // for subsequent fragments of "buffer":
+        // 0x47
+        // transport_error_indicator = b0
+        // payload_unit_start_indicator = b0
+        // transport_priority = b0
+        // PID = b0 0001 1110 ???? (13 bits) [0x1e0 + 1 + sourceIndex]
+        // transport_scrambling_control = b00
+        // adaptation_field_control = b??
+        // continuity_counter = b????
+        // the fragment of "buffer" follows.
+
+        uint8_t *ptr = packetDataStart;
+        *ptr++ = 0x47;
+        *ptr++ = 0x00 | (track->PID() >> 8);
+        *ptr++ = track->PID() & 0xff;
+
+        *ptr++ = (padding ? 0x30 : 0x10) | track->incrementContinuityCounter();
+
+        if (padding) {
+            size_t paddingSize = 188 - 4 - (accessUnit->size() - offset);
+            *ptr++ = paddingSize - 1;
+            if (paddingSize >= 2) {
+                *ptr++ = 0x00;
+                memset(ptr, 0xff, paddingSize - 2);
+                ptr += paddingSize - 2;
+            }
+        }
+
+        // 4 bytes of TS header leave 188 - 4 = 184 bytes for the payload
+
+        size_t sizeLeft = packetDataStart + 188 - ptr;
+        size_t copy = accessUnit->size() - offset;
+        if (copy > sizeLeft) {
+            copy = sizeLeft;
+        }
+
+        memcpy(ptr, accessUnit->data() + offset, copy);
+        ptr += copy;
+        CHECK_EQ(sizeLeft, copy);
+        memset(ptr, 0xff, sizeLeft - copy);
+
+        offset += copy;
+        packetDataStart += 188;
+    }
+
+    CHECK(packetDataStart == buffer->data() + buffer->capacity());
+
+    *packets = buffer;
+
+    return OK;
+}
+
+void TSPacketizer::initCrcTable() {
+    uint32_t poly = 0x04C11DB7;
+
+    for (int i = 0; i < 256; i++) {
+        uint32_t crc = i << 24;
+        for (int j = 0; j < 8; j++) {
+            crc = (crc << 1) ^ ((crc & 0x80000000) ? (poly) : 0);
+        }
+        mCrcTable[i] = crc;
+    }
+}
+
+uint32_t TSPacketizer::crc32(const uint8_t *start, size_t size) const {
+    uint32_t crc = 0xFFFFFFFF;
+    const uint8_t *p;
+
+    for (p = start; p < start + size; ++p) {
+        crc = (crc << 8) ^ mCrcTable[((crc >> 24) ^ *p) & 0xFF];
+    }
+
+    return crc;
+}
+
+}  // namespace android
+
diff --git a/host/frontend/gcastv2/libandroid/Utils.cpp b/host/frontend/gcastv2/libandroid/Utils.cpp
new file mode 100644
index 0000000..750b95f
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/Utils.cpp
@@ -0,0 +1,226 @@
+/*
+ * 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 <arpa/inet.h>
+
+#include <media/stagefright/Utils.h>
+
+#include <media/stagefright/MetaData.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);
+
+    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);
+    } 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 isADTS;
+        if (meta->findInt32(kKeyIsADTS, &isADTS)) {
+            msg->setInt32("is-adts", true);
+        }
+    }
+
+    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);
+    }
+
+    *format = msg;
+
+    return OK;
+}
+
+std::string MakeUserAgent() {
+    return "stagefright/1.2 (OS X)";
+}
+
+void toLower(std::string *s) {
+    std::transform(s->begin(), s->end(), s->begin(), ::tolower);
+}
+
+void trim(std::string *s) {
+    size_t i = 0;
+    while (i < s->size() && isspace(s->at(i))) {
+        ++i;
+    }
+
+    size_t j = s->size();
+    while (j > i && isspace(s->at(j - 1))) {
+        --j;
+    }
+
+    s->erase(0, i);
+    j -= i;
+    s->erase(j);
+}
+
+bool startsWith(std::string_view s1, std::string_view s2) {
+    if (s1.size() < s2.size()) {
+        return false;
+    }
+
+    return s1.substr(0, s2.size()) == s2;
+}
+
+std::string StringPrintf(const char *format, ...) {
+    va_list ap;
+    va_start(ap, format);
+
+    char *buffer;
+    (void)vasprintf(&buffer, format, ap);
+
+    va_end(ap);
+
+    std::string result(buffer);
+
+    free(buffer);
+    buffer = NULL;
+
+    return result;
+}
+
+}  // namespace android
+
diff --git a/host/frontend/gcastv2/libandroid/avc_utils.cpp b/host/frontend/gcastv2/libandroid/avc_utils.cpp
new file mode 100644
index 0000000..860d045
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/avc_utils.cpp
@@ -0,0 +1,608 @@
+/*
+ * 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 "avc_utils"
+#include <utils/Log.h>
+
+#include <media/stagefright/avc_utils.h>
+
+#include <media/stagefright/foundation/ABitReader.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+unsigned parseUE(ABitReader *br) {
+    unsigned numZeroes = 0;
+    while (br->getBits(1) == 0) {
+        ++numZeroes;
+    }
+
+    unsigned x = br->getBits(numZeroes);
+
+    return x + (1u << numZeroes) - 1;
+}
+
+// Determine video dimensions from the sequence parameterset.
+void FindAVCDimensions(
+        const sp<ABuffer> &seqParamSet, int32_t *width, int32_t *height) {
+    ABitReader br(seqParamSet->data() + 1, seqParamSet->size() - 1);
+
+    unsigned profile_idc = br.getBits(8);
+    br.skipBits(16);
+    parseUE(&br);  // seq_parameter_set_id
+
+    unsigned chroma_format_idc = 1;  // 4:2:0 chroma format
+
+    if (profile_idc == 100 || profile_idc == 110
+            || profile_idc == 122 || profile_idc == 244
+            || profile_idc == 44 || profile_idc == 83 || profile_idc == 86) {
+        chroma_format_idc = parseUE(&br);
+        if (chroma_format_idc == 3) {
+            br.skipBits(1);  // residual_colour_transform_flag
+        }
+        parseUE(&br);  // bit_depth_luma_minus8
+        parseUE(&br);  // bit_depth_chroma_minus8
+        br.skipBits(1);  // qpprime_y_zero_transform_bypass_flag
+        CHECK_EQ(br.getBits(1), 0u);  // seq_scaling_matrix_present_flag
+    }
+
+    parseUE(&br);  // log2_max_frame_num_minus4
+    unsigned pic_order_cnt_type = parseUE(&br);
+
+    if (pic_order_cnt_type == 0) {
+        parseUE(&br);  // log2_max_pic_order_cnt_lsb_minus4
+    } else if (pic_order_cnt_type == 1) {
+        // offset_for_non_ref_pic, offset_for_top_to_bottom_field and
+        // offset_for_ref_frame are technically se(v), but since we are
+        // just skipping over them the midpoint does not matter.
+
+        br.getBits(1);  // delta_pic_order_always_zero_flag
+        parseUE(&br);  // offset_for_non_ref_pic
+        parseUE(&br);  // offset_for_top_to_bottom_field
+
+        unsigned num_ref_frames_in_pic_order_cnt_cycle = parseUE(&br);
+        for (unsigned i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; ++i) {
+            parseUE(&br);  // offset_for_ref_frame
+        }
+    }
+
+    parseUE(&br);  // num_ref_frames
+    br.getBits(1);  // gaps_in_frame_num_value_allowed_flag
+
+    unsigned pic_width_in_mbs_minus1 = parseUE(&br);
+    unsigned pic_height_in_map_units_minus1 = parseUE(&br);
+    unsigned frame_mbs_only_flag = br.getBits(1);
+
+    *width = pic_width_in_mbs_minus1 * 16 + 16;
+
+    *height = (2 - frame_mbs_only_flag)
+        * (pic_height_in_map_units_minus1 * 16 + 16);
+
+    if (!frame_mbs_only_flag) {
+        br.getBits(1);  // mb_adaptive_frame_field_flag
+    }
+
+    br.getBits(1);  // direct_8x8_inference_flag
+
+    if (br.getBits(1)) {  // frame_cropping_flag
+        unsigned frame_crop_left_offset = parseUE(&br);
+        unsigned frame_crop_right_offset = parseUE(&br);
+        unsigned frame_crop_top_offset = parseUE(&br);
+        unsigned frame_crop_bottom_offset = parseUE(&br);
+
+        unsigned cropUnitX, cropUnitY;
+        if (chroma_format_idc == 0  /* monochrome */) {
+            cropUnitX = 1;
+            cropUnitY = 2 - frame_mbs_only_flag;
+        } else {
+            unsigned subWidthC = (chroma_format_idc == 3) ? 1 : 2;
+            unsigned subHeightC = (chroma_format_idc == 1) ? 2 : 1;
+
+            cropUnitX = subWidthC;
+            cropUnitY = subHeightC * (2 - frame_mbs_only_flag);
+        }
+
+        ALOGV("frame_crop = (%u, %u, %u, %u), cropUnitX = %u, cropUnitY = %u",
+             frame_crop_left_offset, frame_crop_right_offset,
+             frame_crop_top_offset, frame_crop_bottom_offset,
+             cropUnitX, cropUnitY);
+
+        *width -=
+            (frame_crop_left_offset + frame_crop_right_offset) * cropUnitX;
+        *height -=
+            (frame_crop_top_offset + frame_crop_bottom_offset) * cropUnitY;
+    }
+}
+
+status_t getNextNALUnit(
+        const uint8_t **_data, size_t *_size,
+        const uint8_t **nalStart, size_t *nalSize,
+        bool startCodeFollows) {
+    const uint8_t *data = *_data;
+    size_t size = *_size;
+
+    *nalStart = NULL;
+    *nalSize = 0;
+
+    if (size == 0) {
+        return -EAGAIN;
+    }
+
+    // Skip any number of leading 0x00.
+
+    size_t offset = 0;
+    while (offset < size && data[offset] == 0x00) {
+        ++offset;
+    }
+
+    if (offset == size) {
+        return -EAGAIN;
+    }
+
+    // A valid startcode consists of at least two 0x00 bytes followed by 0x01.
+
+    if (offset < 2 || data[offset] != 0x01) {
+        return ERROR_MALFORMED;
+    }
+
+    ++offset;
+
+    size_t startOffset = offset;
+
+    for (;;) {
+        while (offset < size && data[offset] != 0x01) {
+            ++offset;
+        }
+
+        if (offset == size) {
+            if (startCodeFollows) {
+                offset = size + 2;
+                break;
+            }
+
+            return -EAGAIN;
+        }
+
+        if (data[offset - 1] == 0x00 && data[offset - 2] == 0x00) {
+            break;
+        }
+
+        ++offset;
+    }
+
+    size_t endOffset = offset - 2;
+    while (data[endOffset - 1] == 0x00) {
+        --endOffset;
+    }
+
+    *nalStart = &data[startOffset];
+    *nalSize = endOffset - startOffset;
+
+    if (offset + 2 < size) {
+        *_data = &data[offset - 2];
+        *_size = size - offset + 2;
+    } else {
+        *_data = NULL;
+        *_size = 0;
+    }
+
+    return OK;
+}
+
+static sp<ABuffer> FindNAL(const uint8_t *data, size_t size, unsigned nalType) {
+    const uint8_t *nalStart;
+    size_t nalSize;
+    while (getNextNALUnit(&data, &size, &nalStart, &nalSize, true) == OK) {
+        if ((nalStart[0] & 0x1f) == nalType) {
+            sp<ABuffer> buffer = new ABuffer(nalSize);
+            memcpy(buffer->data(), nalStart, nalSize);
+            return buffer;
+        }
+    }
+
+    return NULL;
+}
+
+const char *AVCProfileToString(uint8_t profile) {
+    switch (profile) {
+        case kAVCProfileBaseline:
+            return "Baseline";
+        case kAVCProfileMain:
+            return "Main";
+        case kAVCProfileExtended:
+            return "Extended";
+        case kAVCProfileHigh:
+            return "High";
+        case kAVCProfileHigh10:
+            return "High 10";
+        case kAVCProfileHigh422:
+            return "High 422";
+        case kAVCProfileHigh444:
+            return "High 444";
+        case kAVCProfileCAVLC444Intra:
+            return "CAVLC 444 Intra";
+        default:   return "Unknown";
+    }
+}
+
+sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit) {
+    const uint8_t *data = accessUnit->data();
+    size_t size = accessUnit->size();
+
+    sp<ABuffer> seqParamSet = FindNAL(data, size, 7);
+    if (seqParamSet == NULL) {
+        return NULL;
+    }
+
+    int32_t width, height;
+    FindAVCDimensions(seqParamSet, &width, &height);
+
+    sp<ABuffer> picParamSet = FindNAL(data, size, 8);
+    CHECK(picParamSet != NULL);
+
+    size_t csdSize =
+        1 + 3 + 1 + 1
+        + 2 * 1 + seqParamSet->size()
+        + 1 + 2 * 1 + picParamSet->size();
+
+    sp<ABuffer> csd = new ABuffer(csdSize);
+    uint8_t *out = csd->data();
+
+    *out++ = 0x01;  // configurationVersion
+    memcpy(out, seqParamSet->data() + 1, 3);  // profile/level...
+
+    uint8_t profile = out[0];
+    uint8_t level = out[2];
+
+    out += 3;
+    *out++ = (0x3f << 2) | 1;  // lengthSize == 2 bytes
+    *out++ = 0xe0 | 1;
+
+    *out++ = seqParamSet->size() >> 8;
+    *out++ = seqParamSet->size() & 0xff;
+    memcpy(out, seqParamSet->data(), seqParamSet->size());
+    out += seqParamSet->size();
+
+    *out++ = 1;
+
+    *out++ = picParamSet->size() >> 8;
+    *out++ = picParamSet->size() & 0xff;
+    memcpy(out, picParamSet->data(), picParamSet->size());
+
+#if 0
+    ALOGI("AVC seq param set");
+    hexdump(seqParamSet->data(), seqParamSet->size());
+#endif
+
+    sp<MetaData> meta = new MetaData;
+    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+
+    meta->setData(kKeyAVCC, 0, csd->data(), csd->size());
+    meta->setInt32(kKeyWidth, width);
+    meta->setInt32(kKeyHeight, height);
+
+    ALOGI("found AVC codec config (%d x %d, %s-profile level %d.%d)",
+         width, height, AVCProfileToString(profile), level / 10, level % 10);
+
+    return meta;
+}
+
+bool IsIDR(const sp<ABuffer> &buffer) {
+    const uint8_t *data = buffer->data();
+    size_t size = buffer->size();
+
+    bool foundIDR = false;
+
+    const uint8_t *nalStart;
+    size_t nalSize;
+    while (getNextNALUnit(&data, &size, &nalStart, &nalSize, true) == OK) {
+        CHECK_GT(nalSize, 0u);
+
+        unsigned nalType = nalStart[0] & 0x1f;
+
+        if (nalType == 5) {
+            foundIDR = true;
+            break;
+        }
+    }
+
+    return foundIDR;
+}
+
+sp<MetaData> MakeAACCodecSpecificData(
+        unsigned profile, unsigned sampling_freq_index,
+        unsigned channel_configuration) {
+    sp<MetaData> meta = new MetaData;
+    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
+
+    CHECK_LE(sampling_freq_index, 11u);
+    static const int32_t kSamplingFreq[] = {
+        96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
+        16000, 12000, 11025, 8000
+    };
+    meta->setInt32(kKeySampleRate, kSamplingFreq[sampling_freq_index]);
+    meta->setInt32(kKeyChannelCount, channel_configuration);
+
+    static const uint8_t kStaticESDS[] = {
+        0x03, 22,
+        0x00, 0x00,     // ES_ID
+        0x00,           // streamDependenceFlag, URL_Flag, OCRstreamFlag
+
+        0x04, 17,
+        0x40,                       // Audio ISO/IEC 14496-3
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+
+        0x05, 2,
+        // AudioSpecificInfo follows
+
+        // oooo offf fccc c000
+        // o - audioObjectType
+        // f - samplingFreqIndex
+        // c - channelConfig
+    };
+    sp<ABuffer> csd = new ABuffer(sizeof(kStaticESDS) + 2);
+    memcpy(csd->data(), kStaticESDS, sizeof(kStaticESDS));
+
+    csd->data()[sizeof(kStaticESDS)] =
+        ((profile + 1) << 3) | (sampling_freq_index >> 1);
+
+    csd->data()[sizeof(kStaticESDS) + 1] =
+        ((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3);
+
+    meta->setData(kKeyESDS, 0, csd->data(), csd->size());
+
+    return meta;
+}
+
+bool ExtractDimensionsFromVOLHeader(
+        const uint8_t *data, size_t size, int32_t *width, int32_t *height) {
+    ABitReader br(&data[4], size - 4);
+    br.skipBits(1);  // random_accessible_vol
+    unsigned video_object_type_indication = br.getBits(8);
+
+    CHECK_NE(video_object_type_indication,
+             0x21u /* Fine Granularity Scalable */);
+
+    unsigned video_object_layer_verid;
+    unsigned video_object_layer_priority;
+    if (br.getBits(1)) {
+        video_object_layer_verid = br.getBits(4);
+        video_object_layer_priority = br.getBits(3);
+    }
+    unsigned aspect_ratio_info = br.getBits(4);
+    if (aspect_ratio_info == 0x0f /* extended PAR */) {
+        br.skipBits(8);  // par_width
+        br.skipBits(8);  // par_height
+    }
+    if (br.getBits(1)) {  // vol_control_parameters
+        br.skipBits(2);  // chroma_format
+        br.skipBits(1);  // low_delay
+        if (br.getBits(1)) {  // vbv_parameters
+            br.skipBits(15);  // first_half_bit_rate
+            CHECK(br.getBits(1));  // marker_bit
+            br.skipBits(15);  // latter_half_bit_rate
+            CHECK(br.getBits(1));  // marker_bit
+            br.skipBits(15);  // first_half_vbv_buffer_size
+            CHECK(br.getBits(1));  // marker_bit
+            br.skipBits(3);  // latter_half_vbv_buffer_size
+            br.skipBits(11);  // first_half_vbv_occupancy
+            CHECK(br.getBits(1));  // marker_bit
+            br.skipBits(15);  // latter_half_vbv_occupancy
+            CHECK(br.getBits(1));  // marker_bit
+        }
+    }
+    unsigned video_object_layer_shape = br.getBits(2);
+    CHECK_EQ(video_object_layer_shape, 0x00u /* rectangular */);
+
+    CHECK(br.getBits(1));  // marker_bit
+    unsigned vop_time_increment_resolution = br.getBits(16);
+    CHECK(br.getBits(1));  // marker_bit
+
+    if (br.getBits(1)) {  // fixed_vop_rate
+        // range [0..vop_time_increment_resolution)
+
+        // vop_time_increment_resolution
+        // 2 => 0..1, 1 bit
+        // 3 => 0..2, 2 bits
+        // 4 => 0..3, 2 bits
+        // 5 => 0..4, 3 bits
+        // ...
+
+        CHECK_GT(vop_time_increment_resolution, 0u);
+        --vop_time_increment_resolution;
+
+        unsigned numBits = 0;
+        while (vop_time_increment_resolution > 0) {
+            ++numBits;
+            vop_time_increment_resolution >>= 1;
+        }
+
+        br.skipBits(numBits);  // fixed_vop_time_increment
+    }
+
+    CHECK(br.getBits(1));  // marker_bit
+    unsigned video_object_layer_width = br.getBits(13);
+    CHECK(br.getBits(1));  // marker_bit
+    unsigned video_object_layer_height = br.getBits(13);
+    CHECK(br.getBits(1));  // marker_bit
+
+    // unsigned interlaced = br.getBits(1);
+
+    *width = video_object_layer_width;
+    *height = video_object_layer_height;
+
+    return true;
+}
+
+bool GetMPEGAudioFrameSize(
+        uint32_t header, size_t *frame_size,
+        int *out_sampling_rate, int *out_channels,
+        int *out_bitrate, int *out_num_samples) {
+    *frame_size = 0;
+
+    if (out_sampling_rate) {
+        *out_sampling_rate = 0;
+    }
+
+    if (out_channels) {
+        *out_channels = 0;
+    }
+
+    if (out_bitrate) {
+        *out_bitrate = 0;
+    }
+
+    if (out_num_samples) {
+        *out_num_samples = 1152;
+    }
+
+    if ((header & 0xffe00000) != 0xffe00000) {
+        return false;
+    }
+
+    unsigned version = (header >> 19) & 3;
+
+    if (version == 0x01) {
+        return false;
+    }
+
+    unsigned layer = (header >> 17) & 3;
+
+    if (layer == 0x00) {
+        return false;
+    }
+
+    // unsigned protection = (header >> 16) & 1;
+
+    unsigned bitrate_index = (header >> 12) & 0x0f;
+
+    if (bitrate_index == 0 || bitrate_index == 0x0f) {
+        // Disallow "free" bitrate.
+        return false;
+    }
+
+    unsigned sampling_rate_index = (header >> 10) & 3;
+
+    if (sampling_rate_index == 3) {
+        return false;
+    }
+
+    static const int kSamplingRateV1[] = { 44100, 48000, 32000 };
+    int sampling_rate = kSamplingRateV1[sampling_rate_index];
+    if (version == 2 /* V2 */) {
+        sampling_rate /= 2;
+    } else if (version == 0 /* V2.5 */) {
+        sampling_rate /= 4;
+    }
+
+    unsigned padding = (header >> 9) & 1;
+
+    if (layer == 3) {
+        // layer I
+
+        static const int kBitrateV1[] = {
+            32, 64, 96, 128, 160, 192, 224, 256,
+            288, 320, 352, 384, 416, 448
+        };
+
+        static const int kBitrateV2[] = {
+            32, 48, 56, 64, 80, 96, 112, 128,
+            144, 160, 176, 192, 224, 256
+        };
+
+        int bitrate =
+            (version == 3 /* V1 */)
+                ? kBitrateV1[bitrate_index - 1]
+                : kBitrateV2[bitrate_index - 1];
+
+        if (out_bitrate) {
+            *out_bitrate = bitrate;
+        }
+
+        *frame_size = (12000 * bitrate / sampling_rate + padding) * 4;
+
+        if (out_num_samples) {
+            *out_num_samples = 384;
+        }
+    } else {
+        // layer II or III
+
+        static const int kBitrateV1L2[] = {
+            32, 48, 56, 64, 80, 96, 112, 128,
+            160, 192, 224, 256, 320, 384
+        };
+
+        static const int kBitrateV1L3[] = {
+            32, 40, 48, 56, 64, 80, 96, 112,
+            128, 160, 192, 224, 256, 320
+        };
+
+        static const int kBitrateV2[] = {
+            8, 16, 24, 32, 40, 48, 56, 64,
+            80, 96, 112, 128, 144, 160
+        };
+
+        int bitrate;
+        if (version == 3 /* V1 */) {
+            bitrate = (layer == 2 /* L2 */)
+                ? kBitrateV1L2[bitrate_index - 1]
+                : kBitrateV1L3[bitrate_index - 1];
+
+            if (out_num_samples) {
+                *out_num_samples = 1152;
+            }
+        } else {
+            // V2 (or 2.5)
+
+            bitrate = kBitrateV2[bitrate_index - 1];
+            if (out_num_samples) {
+                *out_num_samples = 576;
+            }
+        }
+
+        if (out_bitrate) {
+            *out_bitrate = bitrate;
+        }
+
+        if (version == 3 /* V1 */) {
+            *frame_size = 144000 * bitrate / sampling_rate + padding;
+        } else {
+            // V2 or V2.5
+            *frame_size = 72000 * bitrate / sampling_rate + padding;
+        }
+    }
+
+    if (out_sampling_rate) {
+        *out_sampling_rate = sampling_rate;
+    }
+
+    if (out_channels) {
+        int channel_mode = (header >> 6) & 3;
+
+        *out_channels = (channel_mode == 3) ? 1 : 2;
+    }
+
+    return true;
+}
+
+}  // namespace android
+
diff --git a/host/frontend/gcastv2/libandroid/base64.cpp b/host/frontend/gcastv2/libandroid/base64.cpp
new file mode 100644
index 0000000..3ec5410
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/base64.cpp
@@ -0,0 +1,122 @@
+#include <media/stagefright/foundation/base64.h>
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+sp<ABuffer> decodeBase64(const std::string_view &s) {
+    if ((s.size() % 4) != 0) {
+        return NULL;
+    }
+
+    size_t n = s.size();
+    size_t padding = 0;
+    if (n >= 1 && s[n - 1] == '=') {
+        padding = 1;
+
+        if (n >= 2 && s[n - 2] == '=') {
+            padding = 2;
+        }
+    }
+
+    size_t outLen = 3 * s.size() / 4 - padding;
+
+    sp<ABuffer> buffer = new ABuffer(outLen);
+
+    uint8_t *out = buffer->data();
+    size_t j = 0;
+    uint32_t accum = 0;
+    for (size_t i = 0; i < n; ++i) {
+        char c = s[i];
+        unsigned value;
+        if (c >= 'A' && c <= 'Z') {
+            value = c - 'A';
+        } else if (c >= 'a' && c <= 'z') {
+            value = 26 + c - 'a';
+        } else if (c >= '0' && c <= '9') {
+            value = 52 + c - '0';
+        } else if (c == '+') {
+            value = 62;
+        } else if (c == '/') {
+            value = 63;
+        } else if (c != '=') {
+            return NULL;
+        } else {
+            if (i < n - padding) {
+                return NULL;
+            }
+
+            value = 0;
+        }
+
+        accum = (accum << 6) | value;
+
+        if (((i + 1) % 4) == 0) {
+            out[j++] = (accum >> 16);
+
+            if (j < outLen) { out[j++] = (accum >> 8) & 0xff; }
+            if (j < outLen) { out[j++] = accum & 0xff; }
+
+            accum = 0;
+        }
+    }
+
+    return buffer;
+}
+
+static char encode6Bit(unsigned x) {
+    if (x <= 25) {
+        return 'A' + x;
+    } else if (x <= 51) {
+        return 'a' + x - 26;
+    } else if (x <= 61) {
+        return '0' + x - 52;
+    } else if (x == 62) {
+        return '+';
+    } else {
+        return '/';
+    }
+}
+
+void encodeBase64(const void *_data, size_t size, std::string *out) {
+    out->clear();
+
+    const uint8_t *data = (const uint8_t *)_data;
+
+    size_t i;
+    for (i = 0; i < (size / 3) * 3; i += 3) {
+        uint8_t x1 = data[i];
+        uint8_t x2 = data[i + 1];
+        uint8_t x3 = data[i + 2];
+
+        out->append(1, encode6Bit(x1 >> 2));
+        out->append(1, encode6Bit((x1 << 4 | x2 >> 4) & 0x3f));
+        out->append(1, encode6Bit((x2 << 2 | x3 >> 6) & 0x3f));
+        out->append(1, encode6Bit(x3 & 0x3f));
+    }
+    switch (size % 3) {
+        case 0:
+            break;
+        case 2:
+        {
+            uint8_t x1 = data[i];
+            uint8_t x2 = data[i + 1];
+            out->append(1, encode6Bit(x1 >> 2));
+            out->append(1, encode6Bit((x1 << 4 | x2 >> 4) & 0x3f));
+            out->append(1, encode6Bit((x2 << 2) & 0x3f));
+            out->append(1, '=');
+            break;
+        }
+        default:
+        {
+            uint8_t x1 = data[i];
+            out->append(1, encode6Bit(x1 >> 2));
+            out->append(1, encode6Bit((x1 << 4) & 0x3f));
+            out->append("==");
+            break;
+        }
+    }
+}
+
+}  // namespace android
diff --git a/host/frontend/gcastv2/libandroid/hexdump.cpp b/host/frontend/gcastv2/libandroid/hexdump.cpp
new file mode 100644
index 0000000..85b57b2
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/hexdump.cpp
@@ -0,0 +1,73 @@
+#include <media/stagefright/foundation/hexdump.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+
+#include <ctype.h>
+#include <stdint.h>
+#include <stdio.h>
+
+namespace android {
+
+static void appendIndent(std::string *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, std::string *appendTo) {
+    const uint8_t *data = (const uint8_t *)_data;
+
+    size_t offset = 0;
+    while (offset < size) {
+        std::string line;
+
+        appendIndent(&line, indent);
+
+        char tmp[32];
+        snprintf(tmp, sizeof(tmp), "%08lx:  ", (unsigned long)offset);
+
+        line.append(tmp);
+
+        for (size_t i = 0; i < 16; ++i) {
+            if (i == 8) {
+                line.append(1, ' ');
+            }
+            if (offset + i >= size) {
+                line.append("   ");
+            } else {
+                snprintf(tmp, sizeof(tmp), "%02x ", data[offset + i]);
+                line.append(tmp);
+            }
+        }
+
+        line.append(1, ' ');
+
+        for (size_t i = 0; i < 16; ++i) {
+            if (offset + i >= size) {
+                break;
+            }
+
+            if (isprint(data[offset + i])) {
+                line.append(1, (char)data[offset + i]);
+            } else {
+                line.append(1, '.');
+            }
+        }
+
+        if (appendTo != NULL) {
+            appendTo->append(line);
+            appendTo->append("\n");
+        } else {
+            ALOGI("%s", line.c_str());
+        }
+
+        offset += 16;
+    }
+}
+
+}  // namespace android
+
diff --git a/host/frontend/gcastv2/libandroid/include/helpers/JavaThread.h b/host/frontend/gcastv2/libandroid/include/helpers/JavaThread.h
new file mode 100644
index 0000000..4694dd4
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/helpers/JavaThread.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <helpers/MyAndroidRuntime.h>
+
+#include <thread>
+
+namespace android {
+
+void javaAttachThread();
+void javaDetachThread();
+
+template<class Function, class... Args>
+std::thread createJavaThread(Function &&f, Args&&... args) {
+    return std::thread([f, args...]{
+        javaAttachThread();
+        f(args...);
+        javaDetachThread();
+    });
+}
+
+}  // namespace android
+
diff --git a/host/frontend/gcastv2/libandroid/include/helpers/MyAndroidRuntime.h b/host/frontend/gcastv2/libandroid/include/helpers/MyAndroidRuntime.h
new file mode 100644
index 0000000..e1aae88
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/helpers/MyAndroidRuntime.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <jni.h>
+
+namespace android {
+
+struct MyAndroidRuntime {
+    static void setJavaVM(JavaVM *vm);
+    static JavaVM *getJavaVM();
+
+    static JNIEnv *getJNIEnv();
+};
+
+}  // namespace android
+
diff --git a/host/frontend/gcastv2/libandroid/include/helpers/MyJNIHelpers.h b/host/frontend/gcastv2/libandroid/include/helpers/MyJNIHelpers.h
new file mode 100644
index 0000000..0d6c9c8
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/helpers/MyJNIHelpers.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <jni.h>
+#include <sys/types.h>
+
+namespace android {
+
+void jniThrowException(
+        JNIEnv *env, const char *className, const char *msg);
+
+int jniRegisterNativeMethods(
+        JNIEnv *env,
+        const char *className,
+        const JNINativeMethod *methods,
+        size_t numMethods);
+
+}  // namespace android
+
diff --git a/host/frontend/gcastv2/libandroid/include/helpers/MyScopedByteArray.h b/host/frontend/gcastv2/libandroid/include/helpers/MyScopedByteArray.h
new file mode 100644
index 0000000..62fc607
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/helpers/MyScopedByteArray.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <jni.h>
+
+namespace android {
+
+struct MyScopedByteArray {
+    explicit MyScopedByteArray(JNIEnv *env, jbyteArray arrayObj);
+
+    MyScopedByteArray(const MyScopedByteArray &) = delete;
+    MyScopedByteArray &operator=(const MyScopedByteArray &) = delete;
+
+    ~MyScopedByteArray();
+
+    const jbyte *data() const;
+    jsize size() const;
+
+private:
+    JNIEnv *mEnv;
+    jbyteArray mArrayObj;
+    jbyte *mElements;
+    jsize mSize;
+};
+
+}  // namespace android
+
diff --git a/host/frontend/gcastv2/libandroid/include/helpers/MyScopedLocalRef.h b/host/frontend/gcastv2/libandroid/include/helpers/MyScopedLocalRef.h
new file mode 100644
index 0000000..d683669
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/helpers/MyScopedLocalRef.h
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <jni.h>
+
+template<class T>
+struct MyScopedLocalRef {
+    explicit MyScopedLocalRef()
+        : mEnv(nullptr),
+          mObj(nullptr) {
+    }
+
+    explicit MyScopedLocalRef(JNIEnv *env, T obj)
+        : mEnv(env),
+          mObj(obj) {
+    }
+
+    MyScopedLocalRef(const MyScopedLocalRef<T> &other) = delete;
+    MyScopedLocalRef &operator=(const MyScopedLocalRef<T> &other) = delete;
+
+    void setTo(JNIEnv *env, T obj) {
+        if (obj != mObj) {
+            clear();
+
+            mEnv = env;
+            mObj = obj;
+        }
+    }
+
+    void clear() {
+        if (mObj) {
+            mEnv->DeleteLocalRef(mObj);
+            mObj = nullptr;
+        }
+
+        mEnv = nullptr;
+    }
+
+    ~MyScopedLocalRef() {
+        clear();
+    }
+
+    T get() const {
+        return mObj;
+    }
+
+private:
+    JNIEnv *mEnv;
+    T mObj;
+};
diff --git a/host/frontend/gcastv2/libandroid/include/helpers/MyScopedUTF8String.h b/host/frontend/gcastv2/libandroid/include/helpers/MyScopedUTF8String.h
new file mode 100644
index 0000000..49e74e6
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/helpers/MyScopedUTF8String.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <jni.h>
+
+namespace android {
+
+struct MyScopedUTF8String {
+    explicit MyScopedUTF8String(JNIEnv *env, jstring stringObj);
+
+    MyScopedUTF8String(const MyScopedUTF8String &) = delete;
+    MyScopedUTF8String &operator=(const MyScopedUTF8String &) = delete;
+
+    ~MyScopedUTF8String();
+
+    const char *c_str() const;
+
+private:
+    JNIEnv *mEnv;
+    jstring mStringObj;
+    const char *mData;
+};
+
+}  // namespace android
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/ATSParser.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/ATSParser.h
new file mode 100644
index 0000000..3d53fdb
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/ATSParser.h
@@ -0,0 +1,142 @@
+/*
+ * 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_TS_PARSER_H_
+
+#define A_TS_PARSER_H_
+
+#include <sys/types.h>
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <utils/RefBase.h>
+
+#include <map>
+#include <vector>
+
+namespace android {
+
+struct ABitReader;
+struct ABuffer;
+struct AnotherPacketSource;
+
+struct ATSParser : public RefBase {
+    enum DiscontinuityType {
+        DISCONTINUITY_NONE              = 0,
+        DISCONTINUITY_TIME              = 1,
+        DISCONTINUITY_AUDIO_FORMAT      = 2,
+        DISCONTINUITY_VIDEO_FORMAT      = 4,
+        DISCONTINUITY_ABSOLUTE_TIME     = 8,
+        DISCONTINUITY_TIME_OFFSET       = 16,
+
+        DISCONTINUITY_SEEK              = DISCONTINUITY_TIME,
+
+        // For legacy reasons this also implies a time discontinuity.
+        DISCONTINUITY_FORMATCHANGE      =
+            DISCONTINUITY_AUDIO_FORMAT
+                | DISCONTINUITY_VIDEO_FORMAT
+                | DISCONTINUITY_TIME,
+    };
+
+    enum Flags {
+        // The 90kHz clock (PTS/DTS) is absolute, i.e. PTS=0 corresponds to
+        // a media time of 0.
+        // If this flag is _not_ specified, the first PTS encountered in a
+        // program of this stream will be assumed to correspond to media time 0
+        // instead.
+        TS_TIMESTAMPS_ARE_ABSOLUTE = 1,
+        // Video PES packets contain exactly one (aligned) access unit.
+        ALIGNED_VIDEO_DATA         = 2,
+
+        DUMP_PTS                   = 4,
+    };
+
+    ATSParser(uint32_t flags = 0);
+
+    status_t feedTSPacket(const void *data, size_t size);
+
+    void signalDiscontinuity(
+            DiscontinuityType type, const sp<AMessage> &extra);
+
+    void signalEOS(status_t finalResult);
+
+    enum SourceType {
+        VIDEO,
+        AUDIO
+    };
+    sp<AnotherPacketSource> getSource(SourceType type);
+
+    bool PTSTimeDeltaEstablished();
+
+    enum {
+        // From ISO/IEC 13818-1: 2000 (E), Table 2-29
+        STREAMTYPE_RESERVED             = 0x00,
+        STREAMTYPE_MPEG1_VIDEO          = 0x01,
+        STREAMTYPE_MPEG2_VIDEO          = 0x02,
+        STREAMTYPE_MPEG1_AUDIO          = 0x03,
+        STREAMTYPE_MPEG2_AUDIO          = 0x04,
+        STREAMTYPE_MPEG2_AUDIO_ADTS     = 0x0f,
+        STREAMTYPE_MPEG4_VIDEO          = 0x10,
+        STREAMTYPE_H264                 = 0x1b,
+        STREAMTYPE_PCM_AUDIO            = 0x83,
+    };
+
+protected:
+    virtual ~ATSParser();
+
+private:
+    struct Program;
+    struct Stream;
+    struct PSISection;
+
+    uint32_t mFlags;
+    std::vector<sp<Program>> mPrograms;
+
+    // Keyed by PID
+    std::map<unsigned, sp<PSISection> > mPSISections;
+
+    int64_t mAbsoluteTimeAnchorUs;
+
+    bool mTimeOffsetValid;
+    int64_t mTimeOffsetUs;
+
+    size_t mNumTSPacketsParsed;
+
+    void parseProgramAssociationTable(ABitReader *br);
+    void parseProgramMap(ABitReader *br);
+    void parsePES(ABitReader *br);
+
+    status_t parsePID(
+        ABitReader *br, unsigned PID,
+        unsigned continuity_counter,
+        unsigned payload_unit_start_indicator);
+
+    void parseAdaptationField(ABitReader *br, unsigned PID);
+    status_t parseTS(ABitReader *br);
+
+    void updatePCR(unsigned PID, uint64_t PCR, size_t byteOffsetFromStart);
+
+    uint64_t mPCR[2];
+    size_t mPCRBytes[2];
+    int64_t mSystemTimeUs[2];
+    size_t mNumPCRs;
+
+    DISALLOW_EVIL_CONSTRUCTORS(ATSParser);
+};
+
+}  // namespace android
+
+#endif  // A_TS_PARSER_H_
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/AnotherPacketSource.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/AnotherPacketSource.h
new file mode 100644
index 0000000..5f9c1fc
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/AnotherPacketSource.h
@@ -0,0 +1,84 @@
+/*
+ * 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 ANOTHER_PACKET_SOURCE_H_
+
+#define ANOTHER_PACKET_SOURCE_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/ATSParser.h>
+#include <media/stagefright/MediaSource.h>
+#include <utils/threads.h>
+
+#include <list>
+
+namespace android {
+
+struct ABuffer;
+
+struct AnotherPacketSource : public MediaSource {
+    AnotherPacketSource(const sp<MetaData> &meta);
+
+    void setFormat(const sp<MetaData> &meta);
+
+    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);
+
+    bool hasBufferAvailable(status_t *finalResult);
+
+    // Returns the difference between the last and the first queued
+    // presentation timestamps since the last discontinuity (if any).
+    int64_t getBufferedDurationUs(status_t *finalResult);
+
+    status_t nextBufferTime(int64_t *timeUs);
+
+    void queueAccessUnit(const sp<ABuffer> &buffer);
+
+    void queueDiscontinuity(
+            ATSParser::DiscontinuityType type, const sp<AMessage> &extra);
+
+    void signalEOS(status_t result);
+
+    status_t dequeueAccessUnit(sp<ABuffer> *buffer);
+
+    bool isFinished(int64_t duration) const;
+
+protected:
+    virtual ~AnotherPacketSource();
+
+private:
+    Mutex mLock;
+    Condition mCondition;
+
+    bool mIsAudio;
+    sp<MetaData> mFormat;
+    int64_t mLastQueuedTimeUs;
+    std::list<sp<ABuffer>> mBuffers;
+    status_t mEOSResult;
+
+    bool wasFormatChange(int32_t discontinuityType) const;
+
+    DISALLOW_EVIL_CONSTRUCTORS(AnotherPacketSource);
+};
+
+
+}  // namespace android
+
+#endif  // ANOTHER_PACKET_SOURCE_H_
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/FileSource.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/FileSource.h
new file mode 100644
index 0000000..2f4a739
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/FileSource.h
@@ -0,0 +1,30 @@
+#ifndef FILE_SOURCE_H_
+
+#define FILE_SOURCE_H_
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct FileSource : public RefBase {
+    FileSource(const char *path);
+
+    status_t initCheck() const;
+
+    status_t getSize(off_t *size) const;
+    ssize_t readAt(off_t offset, void *data, size_t size);
+
+protected:
+    virtual ~FileSource();
+
+private:
+    int mFd;
+    status_t mInitCheck;
+
+    DISALLOW_EVIL_CONSTRUCTORS(FileSource);
+};
+
+}  // namespace android
+
+#endif  // FILE_SOURCE_H_
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/MediaBuffer.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/MediaBuffer.h
new file mode 100644
index 0000000..a81c8f4
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/MediaBuffer.h
@@ -0,0 +1,37 @@
+#ifndef ANDROID_MEDIA_BUFFER_H_
+
+#define ANDROID_MEDIA_BUFFER_H_
+
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+struct ABuffer;
+
+struct MediaBuffer {
+    MediaBuffer(size_t size)
+        : mBuffer(new ABuffer(size)) {
+    }
+
+    sp<MetaData> meta_data() {
+        if (mMeta == NULL) {
+            mMeta = new MetaData;
+        }
+
+        return mMeta;
+    }
+
+    void *data() {
+        return mBuffer->data();
+    }
+
+private:
+    sp<ABuffer> mBuffer;
+    sp<MetaData> mMeta;
+
+    DISALLOW_EVIL_CONSTRUCTORS(MediaBuffer);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_MEDIA_BUFFER_H_
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/MediaDefs.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/MediaDefs.h
new file mode 100644
index 0000000..9492752
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/MediaDefs.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_MEDIA_DEFS_H_
+
+#define ANDROID_MEDIA_DEFS_H_
+
+#define MEDIA_MIMETYPE_VIDEO_AVC        "video/avc"
+#define MEDIA_MIMETYPE_AUDIO_AAC        "audio/mp4a-latm"
+#define MEDIA_MIMETYPE_AUDIO_MPEG       "audio/mpeg"
+#define MEDIA_MIMETYPE_VIDEO_MPEG2      "video/mpeg2"
+#define MEDIA_MIMETYPE_VIDEO_MPEG4      "video/mp4v-es"
+#define MEDIA_MIMETYPE_VIDEO_VPX        "video/x-vnd.on2.vpx"
+#define MEDIA_MIMETYPE_VIDEO_VP8        "video/x-vnd.on2.vp8"
+#define MEDIA_MIMETYPE_AUDIO_RAW        "audio/raw"
+#define MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I       "audio/mpegL1"
+#define MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II       "audio/mpegL2"
+#define MEDIA_MIMETYPE_AUDIO_OPUS       "audio/opus"
+
+#endif  // ANDROID_MEDIA_DEFS_H_
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/MediaErrors.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/MediaErrors.h
new file mode 100644
index 0000000..37d7dc3
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/MediaErrors.h
@@ -0,0 +1,15 @@
+#ifndef ANDROID_MEDIA_ERRORS_H_
+
+#define ANDROID_MEDIA_ERRORS_H_
+
+namespace android {
+
+enum {
+    ERROR_END_OF_STREAM = -10000,
+    ERROR_MALFORMED,
+    INFO_DISCONTINUITY,
+};
+
+}  // namespace android
+
+#endif  // ANDROID_MEDIA_ERRORS_H_
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/MediaSource.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/MediaSource.h
new file mode 100644
index 0000000..28f9329
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/MediaSource.h
@@ -0,0 +1,35 @@
+#ifndef ANDROID_MEDIASOURCE_H_
+
+#define ANDROID_MEDIASOURCE_H_
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct MediaBuffer;
+struct MetaData;
+
+struct MediaSource : public RefBase {
+    struct ReadOptions {
+    };
+
+    MediaSource() {}
+
+    virtual status_t start(MetaData *params = NULL) = 0;
+    virtual status_t stop() = 0;
+    virtual sp<MetaData> getFormat() = 0;
+
+    virtual status_t read(
+            MediaBuffer **out, const ReadOptions *params = NULL) = 0;
+
+protected:
+    virtual ~MediaSource() {}
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(MediaSource);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_MEDIASOURCE_H_
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/MetaData.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/MetaData.h
new file mode 100644
index 0000000..fde2308
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/MetaData.h
@@ -0,0 +1,115 @@
+#ifndef ANDROID_METADATA_H_
+
+#define ANDROID_METADATA_H_
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+enum {
+    kKeyMIMEType        = 'mime',
+    kKeyWidth           = 'widt',
+    kKeyHeight          = 'heig',
+    kKeyDuration        = 'dura',
+    kKeyAVCC            = 'avcc',
+    kKeyESDS            = 'esds',
+    kKeyTime            = 'time',
+    kKeySampleRate      = 'srat',
+    kKeyChannelCount    = '#chn',
+    kKeyIsADTS          = 'adts',
+};
+
+enum {
+  kTypeESDS = 'esds'
+};
+
+struct MetaData : public RefBase {
+    MetaData()
+        : mMessage(new AMessage) {
+    }
+
+    void setInt32(uint32_t key, int32_t x) {
+        std::string tmp;
+        tmp.append(std::to_string(key));
+
+        mMessage->setInt32(tmp.c_str(), x);
+    }
+
+    void setInt64(uint32_t key, int64_t x) {
+        std::string tmp;
+        tmp.append(std::to_string(key));
+
+        mMessage->setInt64(tmp.c_str(), x);
+    }
+
+    bool findInt32(uint32_t key, int32_t *x) const {
+        std::string tmp;
+      tmp.append(std::to_string(key));
+
+      return mMessage->findInt32(tmp.c_str(), x);
+    }
+
+    bool findCString(uint32_t key, const char **x) const {
+        std::string tmp;
+      tmp.append(std::to_string(key));
+
+      static std::string value;
+      if (!mMessage->findString(tmp.c_str(), &value)) {
+          *x = NULL;
+          return false;
+      }
+
+      *x = value.c_str();
+      return true;
+    }
+
+    void setCString(uint32_t key, const char *s) {
+        std::string tmp;
+        tmp.append(std::to_string(key));
+
+        mMessage->setString(tmp.c_str(), s);
+    }
+
+    void setData(uint32_t key, uint32_t type, const void *data, size_t size) {
+        std::string tmp;
+        tmp.append(std::to_string(key));
+
+        sp<ABuffer> buffer = new ABuffer(size);
+        buffer->meta()->setInt32("type", type);
+        memcpy(buffer->data(), data, size);
+
+        mMessage->setObject(tmp.c_str(), buffer);
+    }
+
+    bool findData(
+            uint32_t key, uint32_t *type, const void **data, size_t *size) const {
+        std::string tmp;
+        tmp.append(std::to_string(key));
+
+        sp<RefBase> obj;
+        if (!mMessage->findObject(tmp.c_str(), &obj)) {
+            return false;
+        }
+
+        sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+        CHECK(buffer->meta()->findInt32("type", (int32_t *)type));
+        *data = buffer->data();
+        *size = buffer->size();
+
+        return true;
+    }
+
+protected:
+    virtual ~MetaData() {}
+
+private:
+    sp<AMessage> mMessage;
+
+    DISALLOW_EVIL_CONSTRUCTORS(MetaData);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_METADATA_H_
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/NuMediaExtractor.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/NuMediaExtractor.h
new file mode 100644
index 0000000..97ff1f5
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/NuMediaExtractor.h
@@ -0,0 +1,64 @@
+#ifndef NU_MEDIA_EXTRACTOR_H_
+
+#define NU_MEDIA_EXTRACTOR_H_
+
+#include <stdio.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ABuffer;
+struct AMessage;
+struct AnotherPacketSource;
+struct ATSParser;
+
+struct NuMediaExtractor : public RefBase {
+    NuMediaExtractor();
+
+    status_t setDataSource(const char *path);
+
+    size_t countTracks() const;
+    status_t getTrackFormat(size_t index, sp<AMessage> *format) const;
+
+    status_t selectTrack(size_t index);
+
+    status_t getSampleTime(int64_t *timeUs);
+    status_t getSampleTrackIndex(size_t *index);
+    status_t readSampleData(sp<ABuffer> accessUnit);
+
+    status_t advance();
+
+protected:
+    virtual ~NuMediaExtractor();
+
+private:
+    enum Flags {
+        FLAG_AUDIO_SELECTED = 1,
+        FLAG_VIDEO_SELECTED = 2,
+    };
+
+    uint32_t mFlags;
+    sp<ATSParser> mParser;
+    FILE *mFile;
+
+    sp<AnotherPacketSource> mAudioSource;
+    sp<AnotherPacketSource> mVideoSource;
+    size_t mNumTracks;
+    ssize_t mAudioTrackIndex;
+    ssize_t mVideoTrackIndex;
+
+    sp<ABuffer> mNextBuffer[2];
+    status_t mFinalResult[2];
+    ssize_t mNextIndex;
+
+    status_t feedMoreData();
+    void fetchSamples();
+
+    DISALLOW_EVIL_CONSTRUCTORS(NuMediaExtractor);
+};
+
+}  // namespace android
+
+#endif // NU_MEDIA_EXTRACTOR_H_
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/Utils.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/Utils.h
new file mode 100644
index 0000000..4ae04b1
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/Utils.h
@@ -0,0 +1,61 @@
+/*
+ * 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 <stdint.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include <string>
+#include <string_view>
+
+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);
+
+std::string MakeUserAgent();
+
+void toLower(std::string *s);
+void trim(std::string *s);
+bool startsWith(std::string_view s1, std::string_view s2);
+
+std::string StringPrintf(const char *format, ...);
+
+}  // namespace android
+
+#endif  // UTILS_H_
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/avc_utils.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/avc_utils.h
new file mode 100644
index 0000000..15cd4d4
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/avc_utils.h
@@ -0,0 +1,72 @@
+/*
+ * 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 AVC_UTILS_H_
+
+#define AVC_UTILS_H_
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+struct ABitReader;
+
+enum {
+    kAVCProfileBaseline      = 0x42,
+    kAVCProfileMain          = 0x4d,
+    kAVCProfileExtended      = 0x58,
+    kAVCProfileHigh          = 0x64,
+    kAVCProfileHigh10        = 0x6e,
+    kAVCProfileHigh422       = 0x7a,
+    kAVCProfileHigh444       = 0xf4,
+    kAVCProfileCAVLC444Intra = 0x2c
+};
+
+void FindAVCDimensions(
+        const sp<ABuffer> &seqParamSet, int32_t *width, int32_t *height);
+
+unsigned parseUE(ABitReader *br);
+
+status_t getNextNALUnit(
+        const uint8_t **_data, size_t *_size,
+        const uint8_t **nalStart, size_t *nalSize,
+        bool startCodeFollows = false);
+
+struct MetaData;
+sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit);
+
+bool IsIDR(const sp<ABuffer> &accessUnit);
+
+const char *AVCProfileToString(uint8_t profile);
+
+sp<MetaData> MakeAACCodecSpecificData(
+        unsigned profile, unsigned sampling_freq_index,
+        unsigned channel_configuration);
+
+// Given an MPEG4 video VOL-header chunk (starting with 0x00 0x00 0x01 0x2?)
+// parse it and fill in dimensions, returns true iff successful.
+bool ExtractDimensionsFromVOLHeader(
+        const uint8_t *data, size_t size, int32_t *width, int32_t *height);
+
+bool GetMPEGAudioFrameSize(
+        uint32_t header, size_t *frame_size,
+        int *out_sampling_rate = NULL, int *out_channels = NULL,
+        int *out_bitrate = NULL, int *out_num_samples = NULL);
+
+}  // namespace android
+
+#endif  // AVC_UTILS_H_
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/AAtomizer.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/AAtomizer.h
new file mode 100644
index 0000000..c1cafd7
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/AAtomizer.h
@@ -0,0 +1,53 @@
+/*
+ * 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 "ABase.h"
+
+#include <utils/threads.h>
+
+#include <list>
+#include <string>
+#include <vector>
+
+namespace android {
+
+struct AAtomizer {
+    static const char *Atomize(const char *name);
+
+private:
+    static AAtomizer gAtomizer;
+
+    Mutex mLock;
+    std::vector<std::list<std::string>> 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_
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ABase.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ABase.h
new file mode 100644
index 0000000..9eceea3
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/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_
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ABitReader.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ABitReader.h
new file mode 100644
index 0000000..5135211
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ABitReader.h
@@ -0,0 +1,53 @@
+/*
+ * 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);
+
+    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();
+    void putBits(uint32_t x, size_t n);
+
+    DISALLOW_EVIL_CONSTRUCTORS(ABitReader);
+};
+
+}  // namespace android
+
+#endif  // A_BIT_READER_H_
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ABuffer.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ABuffer.h
new file mode 100644
index 0000000..7467e41
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ABuffer.h
@@ -0,0 +1,74 @@
+/*
+ * 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 "ABase.h"
+
+#include <sys/types.h>
+#include <stdint.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();
+
+    void reserve(size_t size);
+
+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_
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ADebug.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ADebug.h
new file mode 100644
index 0000000..d415718
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ADebug.h
@@ -0,0 +1,150 @@
+#ifndef A_DEBUG_H_
+
+#define A_DEBUG_H_
+
+#ifdef TARGET_ANDROID
+#include <android-base/logging.h>
+#else
+
+#include "ABase.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <string>
+#include <string_view>
+
+enum LogType {
+    VERBOSE,
+    INFO,
+    WARNING,
+    ERROR,
+    FATAL,
+};
+
+namespace android {
+
+struct Logger {
+    Logger(LogType type);
+    virtual ~Logger();
+
+    Logger &operator<<(std::string_view x) {
+        mMessage.append(x);
+        return *this;
+    }
+
+    Logger &operator<<(const std::string &x) {
+        mMessage.append(x);
+        return *this;
+    }
+
+    Logger &operator<<(const char *x) {
+        mMessage.append(x);
+        return *this;
+    }
+
+    Logger &operator<<(char *x) {
+        mMessage.append(x);
+        return *this;
+    }
+
+    template<class T> Logger &operator<<(const T &x) {
+        mMessage.append(std::to_string(x));
+        return *this;
+    }
+
+private:
+    std::string mMessage;
+    LogType mLogType;
+
+    DISALLOW_EVIL_CONSTRUCTORS(Logger);
+};
+
+const char *LeafName(const char *s);
+
+#undef LOG
+#define LOG(type)                                                       \
+    android::Logger(type)                                               \
+        << android::LeafName(__FILE__) << ":" << __LINE__ << " "
+
+#define CHECK(condition)                                \
+    do {                                                \
+        if (!(condition)) {                             \
+            LOG(FATAL) << "CHECK(" #condition ") failed.";    \
+        }                                               \
+    } while (false)
+
+using std::to_string;
+
+inline std::string to_string(std::string_view s) {
+    return std::string(s);
+}
+
+#define MAKE_COMPARATOR(suffix,op)                          \
+    template<class A, class B>                              \
+    std::string Compare_##suffix(const A &a, const B &b) {  \
+        std::string res;                                    \
+        if (!(a op b)) {                                    \
+            res.append(to_string(a));                       \
+            res.append(" vs. ");                            \
+            res.append(to_string(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 {                                                                \
+        std::string ___res = android::Compare_##suffix(x, y);           \
+        if (!___res.empty()) {                                          \
+            LOG(FATAL) << "CHECK_" #suffix "(" #x "," #y ") failed: "   \
+                       << ___res;                                       \
+        }                                                               \
+    } 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,>)
+
+}  // namespace android
+
+#endif  // defined(TARGET_ANDROID)
+
+namespace android {
+
+#define TRESPASS()      LOG(FATAL) << "Should not be here."
+
+template<char prefix>
+void Log(const char *fmt, ...) {
+    va_list ap;
+    va_start(ap, fmt);
+    printf("%c ", prefix);
+    vprintf(fmt, ap);
+    printf("\n");
+    va_end(ap);
+}
+
+#ifdef LOG_NDEBUG
+#define ALOGV   Log<'V'>
+#else
+#define ALOGV(...)
+#endif
+
+#define ALOGE   Log<'E'>
+#define ALOGI   Log<'I'>
+#define ALOGW   Log<'W'>
+
+}  // namespace android
+
+#endif  // A_DEBUG_H_
+
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/AHandler.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/AHandler.h
new file mode 100644
index 0000000..d8bed5a
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/AHandler.h
@@ -0,0 +1,60 @@
+/*
+ * 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 "ALooper.h"
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct AMessage;
+
+struct AHandler : public RefBase {
+    AHandler()
+        : mID(0) {
+    }
+
+    ALooper::handler_id id() const {
+        return mID;
+    }
+
+    ALooper *looper() const {
+        return mLooper;
+    }
+
+protected:
+    virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
+
+private:
+    friend struct ALooperRoster;
+
+    ALooper::handler_id mID;
+    ALooper *mLooper;
+
+    void setID(ALooper::handler_id id) {
+        mID = id;
+    }
+
+    DISALLOW_EVIL_CONSTRUCTORS(AHandler);
+};
+
+}  // namespace android
+
+#endif  // A_HANDLER_H_
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ALooper.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ALooper.h
new file mode 100644
index 0000000..a8ed67e
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ALooper.h
@@ -0,0 +1,78 @@
+/*
+ * 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_LOOPER_H_
+
+#define A_LOOPER_H_
+
+#include "ABase.h"
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+#include <deque>
+
+namespace android {
+
+struct AHandler;
+struct AMessage;
+
+struct ALooper : public RefBase {
+    typedef int32_t event_id;
+    typedef int32_t handler_id;
+
+    ALooper();
+
+    handler_id registerHandler(const sp<AHandler> &handler);
+    void unregisterHandler(handler_id handlerID);
+
+    status_t start(bool runOnCallingThread = false);
+    status_t stop();
+
+    void setName(const char * /* name */) {}
+
+    static int64_t GetNowUs();
+
+protected:
+    virtual ~ALooper();
+
+private:
+    friend struct ALooperRoster;
+
+    struct Event {
+        int64_t mWhenUs;
+        sp<AMessage> mMessage;
+    };
+
+    Mutex mLock;
+    Condition mQueueChangedCondition;
+
+    std::deque<Event> mEventQueue;
+
+    struct LooperThread;
+    sp<LooperThread> mThread;
+    bool mRunningLocally;
+
+    void post(const sp<AMessage> &msg, int64_t delayUs);
+    bool loop();
+
+    DISALLOW_EVIL_CONSTRUCTORS(ALooper);
+};
+
+}  // namespace android
+
+#endif  // A_LOOPER_H_
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ALooperRoster.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ALooperRoster.h
new file mode 100644
index 0000000..3e80088
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ALooperRoster.h
@@ -0,0 +1,53 @@
+/*
+ * 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_LOOPER_ROSTER_H_
+
+#define A_LOOPER_ROSTER_H_
+
+#include "ALooper.h"
+
+#include <map>
+
+namespace android {
+
+struct ALooperRoster {
+    ALooperRoster();
+
+    ALooper::handler_id registerHandler(
+            const sp<ALooper> looper, const sp<AHandler> &handler);
+
+    void unregisterHandler(ALooper::handler_id handlerID);
+
+    void postMessage(const sp<AMessage> &msg, int64_t delayUs = 0);
+    void deliverMessage(const sp<AMessage> &msg);
+
+private:
+    struct HandlerInfo {
+        sp<ALooper> mLooper;
+        wp<AHandler> mHandler;
+    };
+
+    Mutex mLock;
+    std::map<ALooper::handler_id, HandlerInfo> mHandlers;
+    ALooper::handler_id mNextHandlerID;
+
+    DISALLOW_EVIL_CONSTRUCTORS(ALooperRoster);
+};
+
+}  // namespace android
+
+#endif  // A_LOOPER_ROSTER_H_
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/AMessage.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/AMessage.h
new file mode 100644
index 0000000..0b24f75
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/AMessage.h
@@ -0,0 +1,124 @@
+/*
+ * 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_MESSAGE_H_
+
+#define A_MESSAGE_H_
+
+#include <utils/RefBase.h>
+
+#include "ABase.h"
+#include "ALooper.h"
+
+#include <string>
+
+namespace android {
+
+struct ABuffer;
+
+struct AMessage : public RefBase {
+    explicit AMessage(uint32_t what = 0, ALooper::handler_id target = 0);
+
+    AMessage(const AMessage &) = delete;
+    AMessage &operator=(const AMessage &) = delete;
+
+    void setWhat(uint32_t what);
+    uint32_t what() const;
+
+    void setTarget(ALooper::handler_id target);
+    ALooper::handler_id target() const;
+
+    void setInt32(const char *name, int32_t value);
+    void setInt64(const char *name, int64_t value);
+    void setSize(const char *name, size_t value);
+    void setFloat(const char *name, float value);
+    void setDouble(const char *name, double value);
+    void setPointer(const char *name, void *value);
+    void setString(const char *name, const char *s, ssize_t len = -1);
+    void setObject(const char *name, const sp<RefBase> &obj);
+    void setMessage(const char *name, const sp<AMessage> &obj);
+    void setBuffer(const char *name, const sp<ABuffer> &obj);
+
+    bool findInt32(const char *name, int32_t *value) const;
+    bool findInt64(const char *name, int64_t *value) const;
+    bool findSize(const char *name, size_t *value) const;
+    bool findFloat(const char *name, float *value) const;
+    bool findDouble(const char *name, double *value) const;
+    bool findPointer(const char *name, void **value) const;
+    bool findString(const char *name, std::string *value) const;
+    bool findObject(const char *name, sp<RefBase> *obj) const;
+    bool findMessage(const char *name, sp<AMessage> *obj) const;
+    bool findBuffer(const char *name, sp<ABuffer> *obj) const;
+
+    void post(int64_t delayUs = 0);
+
+    sp<AMessage> dup() const;
+
+    std::string debugString(size_t indent = 0) const;
+
+    size_t countEntries() const;
+
+    enum Type {
+        kTypeInt32,
+        kTypeInt64,
+        kTypeSize,
+        kTypeFloat,
+        kTypeDouble,
+        kTypePointer,
+        kTypeString,
+        kTypeObject,
+        kTypeMessage,
+        kTypeBuffer,
+    };
+    const char *getEntryNameAt(size_t i, Type *type) const;
+
+protected:
+    virtual ~AMessage();
+
+private:
+    uint32_t mWhat;
+    ALooper::handler_id mTarget;
+
+    struct Item {
+        union {
+            int32_t int32Value;
+            int64_t int64Value;
+            size_t sizeValue;
+            float floatValue;
+            double doubleValue;
+            void *ptrValue;
+            RefBase *refValue;
+            std::string *stringValue;
+        } u;
+        const char *mName;
+        Type mType;
+    };
+
+    enum {
+        kMaxNumItems = 16
+    };
+    Item mItems[kMaxNumItems];
+    size_t mNumItems;
+
+    void clear();
+    Item *allocateItem(const char *name);
+    void freeItem(Item *item);
+    const Item *findItem(const char *name, Type type) const;
+};
+
+}  // namespace android
+
+#endif  // A_MESSAGE_H_
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ANetworkSession.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ANetworkSession.h
new file mode 100644
index 0000000..3fa26ba
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ANetworkSession.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright 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 A_NETWORK_SESSION_H_
+
+#define A_NETWORK_SESSION_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+#include <netinet/in.h>
+
+#include <map>
+
+namespace android {
+
+struct AMessage;
+
+// Helper class to manage a number of live sockets (datagram and stream-based)
+// on a single thread. Clients are notified about activity through AMessages.
+struct ANetworkSession : public RefBase {
+    ANetworkSession();
+
+    status_t start();
+    status_t stop();
+
+    status_t createRTSPClient(
+            const char *host, unsigned port, const sp<AMessage> &notify,
+            int32_t *sessionID);
+
+    status_t createRTSPServer(
+            const struct in_addr &addr, unsigned port,
+            const sp<AMessage> &notify, int32_t *sessionID);
+
+    status_t createUDPSession(
+            unsigned localPort, const sp<AMessage> &notify, int32_t *sessionID);
+
+    status_t createUDPSession(
+            unsigned localPort,
+            const char *remoteHost,
+            unsigned remotePort,
+            const sp<AMessage> &notify,
+            int32_t *sessionID);
+
+    status_t connectUDPSession(
+            int32_t sessionID, const char *remoteHost, unsigned remotePort);
+
+    // passive
+    status_t createTCPDatagramSession(
+            const struct in_addr &addr, unsigned port,
+            const sp<AMessage> &notify, int32_t *sessionID);
+
+    // active
+    status_t createTCPDatagramSession(
+            unsigned localPort,
+            const char *remoteHost,
+            unsigned remotePort,
+            const sp<AMessage> &notify,
+            int32_t *sessionID);
+
+    status_t destroySession(int32_t sessionID);
+
+    status_t sendRequest(
+            int32_t sessionID, const void *data, ssize_t size = -1,
+            bool timeValid = false, int64_t timeUs = -1ll);
+
+    status_t switchToWebSocketMode(int32_t sessionID);
+
+    enum NotificationReason {
+        kWhatError,
+        kWhatConnected,
+        kWhatClientConnected,
+        kWhatData,
+        kWhatDatagram,
+        kWhatBinaryData,
+        kWhatWebSocketMessage,
+        kWhatNetworkStall,
+    };
+
+protected:
+    virtual ~ANetworkSession();
+
+private:
+    struct NetworkThread;
+    struct Session;
+
+    Mutex mLock;
+    sp<Thread> mThread;
+
+    int32_t mNextSessionID;
+
+    int mPipeFd[2];
+
+    std::map<int32_t, sp<Session> > mSessions;
+
+    enum Mode {
+        kModeCreateUDPSession,
+        kModeCreateTCPDatagramSessionPassive,
+        kModeCreateTCPDatagramSessionActive,
+        kModeCreateRTSPServer,
+        kModeCreateRTSPClient,
+    };
+    status_t createClientOrServer(
+            Mode mode,
+            const struct in_addr *addr,
+            unsigned port,
+            const char *remoteHost,
+            unsigned remotePort,
+            const sp<AMessage> &notify,
+            int32_t *sessionID);
+
+    void threadLoop();
+    void interrupt();
+
+    static status_t MakeSocketNonBlocking(int s);
+
+    DISALLOW_EVIL_CONSTRUCTORS(ANetworkSession);
+};
+
+}  // namespace android
+
+#endif  // A_NETWORK_SESSION_H_
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/JSONObject.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/JSONObject.h
new file mode 100644
index 0000000..7914282
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/JSONObject.h
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2013 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 JSON_OBJECT_H_
+
+#define JSON_OBJECT_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include <map>
+#include <string>
+#include <string_view>
+#include <vector>
+
+namespace android {
+
+struct JSONArray;
+struct JSONCompound;
+struct JSONObject;
+
+struct JSONValue {
+    enum FieldType {
+        TYPE_STRING,
+        TYPE_NUMBER,
+        TYPE_BOOLEAN,
+        TYPE_NULL,
+        TYPE_OBJECT,
+        TYPE_ARRAY,
+    };
+
+    // Returns the number of bytes consumed or an error.
+    static ssize_t Parse(const char *data, size_t size, JSONValue *out);
+
+    JSONValue();
+    JSONValue(const JSONValue &);
+    JSONValue &operator=(const JSONValue &);
+    ~JSONValue();
+
+    FieldType type() const;
+    bool getInt32(int32_t *value) const;
+    bool getString(std::string *value) const;
+    bool getBoolean(bool *value) const;
+    bool getObject(sp<JSONObject> *value) const;
+    bool getArray(sp<JSONArray> *value) const;
+
+    void setInt32(int32_t value);
+    void setString(std::string_view value);
+    void setBoolean(bool value);
+    void setObject(const sp<JSONObject> &obj);
+    void setArray(const sp<JSONArray> &array);
+    void unset();  // i.e. setNull()
+
+    std::string toString(size_t depth = 0, bool indentFirstLine = true) const;
+
+private:
+    FieldType mType;
+
+    union {
+        int32_t mInt32;
+        std::string *mString;
+        bool mBoolean;
+        JSONCompound *mObjectOrArray;
+    } mValue;
+};
+
+struct JSONCompound : public RefBase {
+    JSONCompound(const JSONCompound &) = delete;
+    JSONCompound &operator=(const JSONCompound &) = delete;
+
+    static sp<JSONCompound> Parse(const char *data, size_t size);
+
+    std::string toString(size_t depth = 0, bool indentFirstLine = true) const;
+
+    virtual bool isObject() const = 0;
+
+protected:
+    virtual ~JSONCompound() {}
+
+    virtual std::string internalToString(
+            size_t depth, bool indentFirstLine) const = 0;
+
+    JSONCompound() {}
+
+private:
+    friend struct JSONValue;
+};
+
+template<class KEY>
+struct JSONBase : public JSONCompound {
+    explicit JSONBase() {}
+
+    JSONBase(const JSONBase &) = delete;
+    JSONBase &operator=(const JSONBase &) = delete;
+
+#define PREAMBLE()                              \
+    JSONValue value;                            \
+    if (!getValue(key, &value)) {               \
+        return false;                           \
+    }
+
+    bool getFieldType(KEY key, JSONValue::FieldType *type) const {
+        PREAMBLE()
+        *type = value.type();
+        return true;
+    }
+
+    bool getInt32(KEY key, int32_t *out) const {
+        PREAMBLE()
+        return value.getInt32(out);
+    }
+
+    bool getString(KEY key, std::string *out) const {
+        PREAMBLE()
+        return value.getString(out);
+    }
+
+    bool getBoolean(KEY key, bool *out) const {
+        PREAMBLE()
+        return value.getBoolean(out);
+    }
+
+    bool getObject(KEY key, sp<JSONObject> *obj) const {
+        PREAMBLE()
+        return value.getObject(obj);
+    }
+
+    bool getArray(KEY key, sp<JSONArray> *obj) const {
+        PREAMBLE()
+        return value.getArray(obj);
+    }
+
+#undef PREAMBLE
+
+protected:
+    virtual ~JSONBase() {}
+
+    virtual bool getValue(KEY key, JSONValue *value) const = 0;
+};
+
+struct JSONObject : public JSONBase<const char *> {
+    explicit JSONObject();
+
+    JSONObject(const JSONObject &) = delete;
+    JSONObject &operator=(const JSONObject &) = delete;
+
+    virtual bool isObject() const;
+    void setValue(const char *key, const JSONValue &value);
+
+    void setInt32(const char *key, int32_t in) {
+        JSONValue val;
+        val.setInt32(in);
+        setValue(key, val);
+    }
+
+    void setString(const char *key, std::string_view in) {
+        JSONValue val;
+        val.setString(in);
+        setValue(key, val);
+    }
+
+    void setBoolean(const char *key, bool in) {
+        JSONValue val;
+        val.setBoolean(in);
+        setValue(key, val);
+    }
+
+    void setObject(const char *key, const sp<JSONObject> &obj) {
+        JSONValue val;
+        val.setObject(obj);
+        setValue(key, val);
+    }
+
+    void setArray(const char *key, const sp<JSONArray> &obj) {
+        JSONValue val;
+        val.setArray(obj);
+        setValue(key, val);
+    }
+
+    void remove(const char *key);
+
+protected:
+    virtual ~JSONObject();
+
+    virtual bool getValue(const char *key, JSONValue *value) const;
+    virtual std::string internalToString(size_t depth, bool indentFirstLine) const;
+
+private:
+    std::map<std::string, JSONValue> mValues;
+};
+
+struct JSONArray : public JSONBase<size_t> {
+    explicit JSONArray();
+
+    JSONArray(const JSONArray &) = delete;
+    JSONArray &operator=(const JSONArray &) = delete;
+
+    virtual bool isObject() const;
+    size_t size() const;
+    void addValue(const JSONValue &value);
+
+    void addInt32(int32_t in) {
+        JSONValue val;
+        val.setInt32(in);
+        addValue(val);
+    }
+
+    void addString(std::string_view in) {
+        JSONValue val;
+        val.setString(in);
+        addValue(val);
+    }
+
+    void addBoolean(bool in) {
+        JSONValue val;
+        val.setBoolean(in);
+        addValue(val);
+    }
+
+    void addObject(const sp<JSONObject> &obj) {
+        JSONValue val;
+        val.setObject(obj);
+        addValue(val);
+    }
+
+    void addArray(const sp<JSONArray> &obj) {
+        JSONValue val;
+        val.setArray(obj);
+        addValue(val);
+    }
+
+protected:
+    virtual ~JSONArray();
+
+    virtual bool getValue(size_t key, JSONValue *value) const;
+    virtual std::string internalToString(size_t depth, bool indentFirstLine) const;
+
+
+private:
+    std::vector<JSONValue> mValues;
+};
+
+}  // namespace android
+
+#endif  // JSON_OBJECT_H_
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ParsedMessage.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ParsedMessage.h
new file mode 100644
index 0000000..504e17b
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ParsedMessage.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 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 <media/stagefright/foundation/ABase.h>
+#include <utils/RefBase.h>
+
+#include <map>
+#include <string>
+
+namespace android {
+
+// Encapsulates an "HTTP/RTSP style" response, i.e. a status line,
+// key/value pairs making up the headers and an optional body/content.
+struct ParsedMessage : public RefBase {
+    static sp<ParsedMessage> Parse(
+            const char *data, size_t size, bool noMoreData, size_t *length);
+
+    bool findString(const char *name, std::string *value) const;
+    bool findInt32(const char *name, int32_t *value) const;
+
+    const char *getContent() const;
+
+    bool getRequestField(size_t index, std::string *field) const;
+    bool getStatusCode(int32_t *statusCode) const;
+
+    std::string debugString() const;
+
+    static bool GetAttribute(
+            const char *s, const char *key, std::string *value);
+
+    static bool GetInt32Attribute(
+            const char *s, const char *key, int32_t *value);
+
+
+protected:
+    virtual ~ParsedMessage();
+
+private:
+    std::map<std::string, std::string> mDict;
+    std::string mContent;
+
+    ParsedMessage();
+
+    ssize_t parse(const char *data, size_t size, bool noMoreData);
+
+    DISALLOW_EVIL_CONSTRUCTORS(ParsedMessage);
+};
+
+}  // namespace android
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ReflectorHandler.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ReflectorHandler.h
new file mode 100644
index 0000000..cc1cadc
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/ReflectorHandler.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+template<class T>
+struct ReflectorHandler : public AHandler {
+    explicit ReflectorHandler(T *target)
+        : mTarget(target) {
+    }
+
+    void onMessageReceived(const sp<AMessage> &msg) {
+        mTarget->onMessageReceived(msg);
+    }
+
+private:
+    T *mTarget;
+};
+
+}  // namespace android
+
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/TSPacketizer.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/TSPacketizer.h
new file mode 100644
index 0000000..ecfc41d
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/TSPacketizer.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 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 TS_PACKETIZER_H_
+
+#define TS_PACKETIZER_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include <vector>
+
+namespace android {
+
+struct ABuffer;
+struct AMessage;
+
+struct TSPacketizer : public RefBase {
+    TSPacketizer();
+
+    // Returns trackIndex or error.
+    ssize_t addTrack(const sp<AMessage> &format);
+
+    enum {
+        EMIT_PAT_AND_PMT = 1,
+        EMIT_PCR         = 2,
+    };
+    status_t packetize(
+            size_t trackIndex, const sp<ABuffer> &accessUnit,
+            sp<ABuffer> *packets,
+            uint32_t flags);
+
+protected:
+    virtual ~TSPacketizer();
+
+private:
+    enum {
+        kPID_PMT = 0x100,
+        kPID_PCR = 0x1000,
+    };
+
+    struct Track;
+
+    std::vector<sp<Track>> mTracks;
+
+    unsigned mPATContinuityCounter;
+    unsigned mPMTContinuityCounter;
+
+    uint32_t mCrcTable[256];
+
+    void initCrcTable();
+    uint32_t crc32(const uint8_t *start, size_t size) const;
+
+    DISALLOW_EVIL_CONSTRUCTORS(TSPacketizer);
+};
+
+}  // namespace android
+
+#endif  // TS_PACKETIZER_H_
+
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/base64.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/base64.h
new file mode 100644
index 0000000..e4f627b
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/base64.h
@@ -0,0 +1,19 @@
+#ifndef BASE_64_H_
+
+#define BASE_64_H_
+
+#include <utils/RefBase.h>
+
+#include <string>
+#include <string_view>
+
+namespace android {
+
+struct ABuffer;
+
+sp<ABuffer> decodeBase64(const std::string_view &s);
+void encodeBase64(const void *data, size_t size, std::string *out);
+
+}  // namespace android
+
+#endif  // BASE_64_H_
diff --git a/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/hexdump.h b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/hexdump.h
new file mode 100644
index 0000000..99b8fc1
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/media/stagefright/foundation/hexdump.h
@@ -0,0 +1,19 @@
+#ifndef HEXDUMP_H_
+
+#define HEXDUMP_H_
+
+#include <sys/types.h>
+
+#include <string>
+
+namespace android {
+
+void hexdump(
+        const void *_data,
+        size_t size,
+        size_t indent = 0,
+        std::string *appendTo = nullptr);
+
+}  // namespace android
+
+#endif  // HEXDUMP_H_
diff --git a/host/frontend/gcastv2/libandroid/include/utils/Errors.h b/host/frontend/gcastv2/libandroid/include/utils/Errors.h
new file mode 100644
index 0000000..6827e17
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/utils/Errors.h
@@ -0,0 +1,23 @@
+#ifndef ANDROID_ERRORS_H_
+
+#define ANDROID_ERRORS_H_
+
+#include <errno.h>
+#include <stdint.h>
+
+namespace android {
+
+typedef int32_t status_t;
+
+enum {
+    OK                = 0,
+    UNKNOWN_ERROR     = -1,
+    INVALID_OPERATION = -EINVAL,
+    NO_INIT           = -ENODEV,
+    ERROR_IO          = -EIO,
+    ERROR_UNSUPPORTED = INVALID_OPERATION,
+};
+
+}  // namespace android
+
+#endif  // ANDROID_ERRORS_H_
diff --git a/host/frontend/gcastv2/libandroid/include/utils/KeyStore.h b/host/frontend/gcastv2/libandroid/include/utils/KeyStore.h
new file mode 100644
index 0000000..1cbfd55
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/utils/KeyStore.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+void setCertificateOrKey(
+        const std::string &name, const void *_data, size_t size);
+
+bool getCertificateOrKey(
+        const std::string &name, std::vector<uint8_t> *data);
+
diff --git a/host/frontend/gcastv2/libandroid/include/utils/Log.h b/host/frontend/gcastv2/libandroid/include/utils/Log.h
new file mode 100644
index 0000000..5c73c18
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/utils/Log.h
@@ -0,0 +1,7 @@
+#ifndef ANDROID_LOG_H_
+
+#define ANDROID_LOG_H_
+
+#include <media/stagefright/foundation/ADebug.h>
+
+#endif  // ANDROID_LOG_H_
diff --git a/host/frontend/gcastv2/libandroid/include/utils/RefBase.h b/host/frontend/gcastv2/libandroid/include/utils/RefBase.h
new file mode 100644
index 0000000..de37c51
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/utils/RefBase.h
@@ -0,0 +1,234 @@
+#ifndef ANDROID_REFBASE_H_
+
+#define ANDROID_REFBASE_H_
+
+#include <stdlib.h>
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+#include <atomic>
+#include <list>
+
+namespace android {
+
+struct RefBase;
+
+// XXX Warning: The weak ref handling is NOT threadsafe yet.
+struct WeakList {
+    WeakList(RefBase *obj)
+        : mObject(obj) {
+    }
+
+    void add(void *id) {
+        mRefs.push_back(id);
+    }
+
+    void remove(void *id) {
+        auto it = mRefs.begin();
+        while (it != mRefs.end() && *it != id) {
+            ++it;
+        }
+        CHECK(it != mRefs.end());
+        mRefs.erase(it);
+
+        if (mRefs.empty() && mObject == NULL) {
+            delete this;
+        }
+    }
+
+    RefBase *promote() const;
+
+private:
+    friend struct RefBase;
+
+    RefBase *mObject;
+    std::list<void *> mRefs;
+
+    ~WeakList() {
+        CHECK(mRefs.empty());
+    }
+
+    void objectDied() {
+        mObject = NULL;
+        if (mRefs.empty()) {
+            delete this;
+        }
+    }
+
+    DISALLOW_EVIL_CONSTRUCTORS(WeakList);
+};
+
+struct RefBase {
+    RefBase()
+        : mNumRefs(0),
+          mWeakList(NULL) {
+    }
+
+    void incStrong(const void *) {
+        ++mNumRefs;
+    }
+
+    void decStrong(const void *) {
+        if (--mNumRefs == 0) {
+            if (mWeakList != NULL) {
+                mWeakList->objectDied();
+                mWeakList = NULL;
+            }
+
+            delete this;
+        }
+    }
+
+    WeakList *getWeakRefs() {
+        if (mWeakList == NULL) {
+            mWeakList = new WeakList(this);
+        }
+
+        return mWeakList;
+    }
+
+protected:
+    virtual ~RefBase() {}
+
+private:
+    std::atomic<int32_t> mNumRefs;
+    WeakList *mWeakList;
+
+    DISALLOW_EVIL_CONSTRUCTORS(RefBase);
+};
+
+template<class T>
+struct sp {
+    sp() : mObj(NULL) {}
+
+    sp(T *obj)
+        : mObj(obj) {
+        if (mObj) { mObj->incStrong(this); }
+    }
+
+    sp(const sp<T> &other)
+        : mObj(other.mObj) {
+        if (mObj) { mObj->incStrong(this); }
+    }
+
+    template<class U>
+    sp(const sp<U> &other)
+        : mObj(other.mObj) {
+        if (mObj) { mObj->incStrong(this); }
+    }
+
+    ~sp() {
+        if (mObj) { mObj->decStrong(this); }
+    }
+
+    T &operator*() const { return *mObj; }
+    T *operator->() const { return mObj; }
+    T *get() const { return mObj; }
+
+    sp<T> &operator=(T *obj) {
+        if (obj) { obj->incStrong(this); }
+        if (mObj) { mObj->decStrong(this); }
+        mObj = obj;
+
+        return *this;
+    }
+
+    sp<T> &operator=(const sp<T> &other) {
+        return (*this) = other.mObj;
+    }
+
+    template<class U>
+    sp<T> &operator=(const sp<U> &other) {
+        return (*this) = other.mObj;
+    }
+
+    void clear() {
+        if (mObj) { mObj->decStrong(this); mObj = NULL; }
+    }
+
+    bool operator==(const sp<T> &other) const {
+        return mObj == other.mObj;
+    }
+
+    bool operator!=(const sp<T> &other) const {
+        return mObj != other.mObj;
+    }
+
+    explicit operator bool() const {
+        return mObj != nullptr;
+    }
+
+private:
+    template<typename Y> friend struct sp;
+
+    T *mObj;
+};
+
+template<class T>
+struct wp {
+    wp() : mWeakList(NULL) {
+    }
+
+    wp(T *obj) {
+        if (obj != NULL) {
+          mWeakList = obj->getWeakRefs();
+          mWeakList->add(this);
+        } else {
+          mWeakList = NULL;
+        }
+    }
+
+    wp(const wp<T> &other)
+        : mWeakList(other.mWeakList) {
+        if (mWeakList != NULL) {
+            mWeakList->add(this);
+        }
+    }
+
+    wp<T> &operator=(const wp<T> &other) {
+        if (mWeakList != other.mWeakList) {
+            if (other.mWeakList != NULL) {
+                other.mWeakList->add(this);
+            }
+            if (mWeakList != NULL) {
+                mWeakList->remove(this);
+            }
+            mWeakList = other.mWeakList;
+        }
+
+        return *this;
+    }
+
+    ~wp() {
+        clear();
+    }
+
+    void clear() {
+        if (mWeakList != NULL) {
+            mWeakList->remove(this);
+            mWeakList = NULL;
+        }
+    }
+
+    sp<T> promote() const {
+        if (mWeakList == NULL) {
+            return NULL;
+        }
+
+        sp<T> result = (T *)mWeakList->promote();
+
+        if (result != NULL) {
+            result->decStrong(this);
+        }
+
+        return result;
+    }
+
+private:
+    WeakList *mWeakList;
+};
+
+}  // namespace android
+
+#endif  // ANDROID_REFBASE_H_
diff --git a/host/frontend/gcastv2/libandroid/include/utils/threads.h b/host/frontend/gcastv2/libandroid/include/utils/threads.h
new file mode 100644
index 0000000..f6e0cb1
--- /dev/null
+++ b/host/frontend/gcastv2/libandroid/include/utils/threads.h
@@ -0,0 +1,200 @@
+#ifndef ANDROID_THREADS_H_
+
+#define ANDROID_THREADS_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+#include <sys/time.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#ifdef TARGET_ANDROID_DEVICE
+#include <helpers/JavaThread.h>
+#include <memory>
+#include <thread>
+#else
+#include <pthread.h>
+#endif
+
+namespace android {
+
+struct Thread : public RefBase {
+    Thread()
+#ifndef TARGET_ANDROID_DEVICE
+        : mThread(0)
+#endif
+    {
+    }
+
+    status_t run(const char *name) {
+        if (mThread != 0) {
+            return INVALID_OPERATION;
+        }
+
+        mName = name;
+
+        mExitRequested = false;
+#ifdef TARGET_ANDROID_DEVICE
+        mThread = std::make_unique<std::thread>(
+                createJavaThread([this] {
+                    (void)ThreadWrapper(this);
+                }));
+#else
+        int res = pthread_create(&mThread, NULL, &Thread::ThreadWrapper, this);
+
+        if (res != 0) {
+            mThread = 0;
+
+            return -errno;
+        }
+#endif
+
+        return OK;
+    }
+
+    void requestExit() { mExitRequested = true; }
+
+    void requestExitAndWait() {
+        requestExit();
+
+#ifdef TARGET_ANDROID_DEVICE
+        if (mThread) {
+            mThread->join();
+            mThread.reset();
+        }
+#else
+        void *dummy;
+        pthread_join(mThread, &dummy);
+        mThread = 0;
+#endif
+    }
+
+protected:
+    virtual bool threadLoop() = 0;
+
+    ~Thread() {
+        if (mThread) {
+            requestExitAndWait();
+
+            CHECK(!mThread);
+        }
+    }
+
+private:
+#ifdef TARGET_ANDROID_DEVICE
+    std::unique_ptr<std::thread> mThread;
+#else
+    pthread_t mThread;
+#endif
+    volatile bool mExitRequested;
+    std::string mName;
+
+    static void *ThreadWrapper(void *param) {
+        Thread *me = static_cast<Thread *>(param);
+
+        while (!me->mExitRequested) {
+            if (!me->threadLoop()) {
+                break;
+            }
+        }
+
+        return NULL;
+    }
+
+    DISALLOW_EVIL_CONSTRUCTORS(Thread);
+};
+
+struct Mutex {
+    Mutex() {
+        CHECK_EQ(pthread_mutex_init(&mMutex, NULL), 0);
+    }
+
+    ~Mutex() {
+        CHECK_EQ(pthread_mutex_destroy(&mMutex), 0);
+    }
+
+    void lock() {
+        CHECK_EQ(pthread_mutex_lock(&mMutex), 0);
+    }
+
+    void unlock() {
+        CHECK_EQ(pthread_mutex_unlock(&mMutex), 0);
+    }
+
+    struct Autolock;
+
+private:
+    friend struct Condition;
+
+    pthread_mutex_t mMutex;
+
+    DISALLOW_EVIL_CONSTRUCTORS(Mutex);
+};
+
+struct Mutex::Autolock {
+    Autolock(Mutex &mutex)
+        : mMutex(mutex) {
+        mMutex.lock();
+    }
+
+    ~Autolock() {
+        mMutex.unlock();
+    }
+
+private:
+    Mutex &mMutex;
+
+    DISALLOW_EVIL_CONSTRUCTORS(Autolock);
+};
+
+struct Condition {
+    Condition() {
+        CHECK_EQ(pthread_cond_init(&mCond, NULL), 0);
+    }
+
+    ~Condition() {
+        CHECK_EQ(pthread_cond_destroy(&mCond), 0);
+    }
+
+    void signal() {
+        CHECK_EQ(pthread_cond_signal(&mCond), 0);
+    }
+
+    void broadcast() {
+        CHECK_EQ(pthread_cond_broadcast(&mCond), 0);
+    }
+
+    status_t wait(Mutex &mutex) {
+        int res = pthread_cond_wait(&mCond, &mutex.mMutex);
+
+        return res == 0 ? OK : -errno;
+    }
+
+    status_t waitRelative(Mutex &mutex, int64_t nsecs) {
+        struct timeval tv;
+        gettimeofday(&tv, NULL);
+
+        struct timespec abstime;
+        abstime.tv_sec = tv.tv_sec;
+        abstime.tv_nsec = tv.tv_usec * 1000ll + nsecs;
+
+        int res = pthread_cond_timedwait(&mCond, &mutex.mMutex, &abstime);
+
+        if (res == 0) {
+            return OK;
+        }
+
+        return -errno;
+    }
+
+private:
+    pthread_cond_t mCond;
+
+    DISALLOW_EVIL_CONSTRUCTORS(Condition);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_THREADS_H_