Merge "Add template class SingleStateQueue" into jb-mr2-dev
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index be1b2fc..0b1d1e4 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -19,6 +19,7 @@
 #define ANDROID_IOMX_H_
 
 #include <binder/IInterface.h>
+#include <gui/IGraphicBufferProducer.h>
 #include <ui/GraphicBuffer.h>
 #include <utils/List.h>
 #include <utils/String8.h>
@@ -96,6 +97,12 @@
             node_id node, OMX_U32 port_index,
             const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer) = 0;
 
+    virtual status_t createInputSurface(
+            node_id node, OMX_U32 port_index,
+            sp<IGraphicBufferProducer> *bufferProducer) = 0;
+
+    virtual status_t signalEndOfInputStream(node_id node) = 0;
+
     // This API clearly only makes sense if the caller lives in the
     // same process as the callee, i.e. is the media_server, as the
     // returned "buffer_data" pointer is just that, a pointer into local
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 317b6f0..097ec5f 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -43,6 +43,8 @@
         kWhatError               = 'erro',
         kWhatComponentAllocated  = 'cAll',
         kWhatComponentConfigured = 'cCon',
+        kWhatInputSurfaceCreated = 'isfc',
+        kWhatSignaledInputEOS    = 'seos',
         kWhatBuffersAllocated    = 'allc',
     };
 
@@ -55,9 +57,11 @@
     void initiateShutdown(bool keepComponentAllocated = false);
 
     void signalSetParameters(const sp<AMessage> &msg);
+    void signalEndOfInputStream();
 
     void initiateAllocateComponent(const sp<AMessage> &msg);
     void initiateConfigureComponent(const sp<AMessage> &msg);
+    void initiateCreateInputSurface();
     void initiateStart();
 
     void signalRequestIDRFrame();
@@ -105,6 +109,8 @@
         kWhatDrainDeferredMessages   = 'drai',
         kWhatAllocateComponent       = 'allo',
         kWhatConfigureComponent      = 'conf',
+        kWhatCreateInputSurface      = 'cisf',
+        kWhatSignalEndOfInputStream  = 'eois',
         kWhatStart                   = 'star',
         kWhatRequestIDRFrame         = 'ridr',
         kWhatSetParameters           = 'setP',
diff --git a/include/media/stagefright/BufferProducerWrapper.h b/include/media/stagefright/BufferProducerWrapper.h
new file mode 100644
index 0000000..d8acf30
--- /dev/null
+++ b/include/media/stagefright/BufferProducerWrapper.h
@@ -0,0 +1,46 @@
+/*
+ * 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 BUFFER_PRODUCER_WRAPPER_H_
+
+#define BUFFER_PRODUCER_WRAPPER_H_
+
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+
+// Can't use static_cast to cast a RefBase back to an IGraphicBufferProducer,
+// because IGBP's parent (IInterface) uses virtual inheritance.  This class
+// wraps IGBP while we pass it through AMessage.
+
+struct BufferProducerWrapper : RefBase {
+    BufferProducerWrapper(
+            const sp<IGraphicBufferProducer>& bufferProducer) :
+        mBufferProducer(bufferProducer) { }
+
+    sp<IGraphicBufferProducer> getBufferProducer() const {
+        return mBufferProducer;
+    }
+
+private:
+    const sp<IGraphicBufferProducer> mBufferProducer;
+
+    DISALLOW_EVIL_CONSTRUCTORS(BufferProducerWrapper);
+};
+
+}  // namespace android
+
+#endif  // BUFFER_PRODUCER_WRAPPER_H_
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 1002663..ef695a7 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -56,6 +56,8 @@
             const sp<ICrypto> &crypto,
             uint32_t flags);
 
+    status_t createInputSurface(sp<IGraphicBufferProducer>* bufferProducer);
+
     status_t start();
 
     // Returns to a state in which the component remains allocated but
@@ -101,6 +103,8 @@
     status_t renderOutputBufferAndRelease(size_t index);
     status_t releaseOutputBuffer(size_t index);
 
+    status_t signalEndOfInputStream();
+
     status_t getOutputFormat(sp<AMessage> *format) const;
 
     status_t getInputBuffers(Vector<sp<ABuffer> > *buffers) const;
@@ -143,6 +147,7 @@
     enum {
         kWhatInit                           = 'init',
         kWhatConfigure                      = 'conf',
+        kWhatCreateInputSurface             = 'cisf',
         kWhatStart                          = 'strt',
         kWhatStop                           = 'stop',
         kWhatRelease                        = 'rele',
@@ -150,6 +155,7 @@
         kWhatQueueInputBuffer               = 'queI',
         kWhatDequeueOutputBuffer            = 'deqO',
         kWhatReleaseOutputBuffer            = 'relO',
+        kWhatSignalEndOfInputStream         = 'eois',
         kWhatGetBuffers                     = 'getB',
         kWhatFlush                          = 'flus',
         kWhatGetOutputFormat                = 'getO',
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index 48e427a..d6cd43a 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -40,6 +40,8 @@
     ENABLE_GRAPHIC_BUFFERS,
     USE_BUFFER,
     USE_GRAPHIC_BUFFER,
+    CREATE_INPUT_SURFACE,
+    SIGNAL_END_OF_INPUT_STREAM,
     STORE_META_DATA_IN_BUFFERS,
     ALLOC_BUFFER,
     ALLOC_BUFFER_WITH_BACKUP,
@@ -280,6 +282,45 @@
         return err;
     }
 
+    virtual status_t createInputSurface(
+            node_id node, OMX_U32 port_index,
+            sp<IGraphicBufferProducer> *bufferProducer) {
+        Parcel data, reply;
+        status_t err;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        data.writeIntPtr((intptr_t)node);
+        data.writeInt32(port_index);
+        err = remote()->transact(CREATE_INPUT_SURFACE, data, &reply);
+        if (err != OK) {
+            ALOGW("binder transaction failed: %d", err);
+            return err;
+        }
+
+        err = reply.readInt32();
+        if (err != OK) {
+            return err;
+        }
+
+        *bufferProducer = IGraphicBufferProducer::asInterface(
+                reply.readStrongBinder());
+
+        return err;
+    }
+
+    virtual status_t signalEndOfInputStream(node_id node) {
+        Parcel data, reply;
+        status_t err;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        data.writeIntPtr((intptr_t)node);
+        err = remote()->transact(SIGNAL_END_OF_INPUT_STREAM, data, &reply);
+        if (err != OK) {
+            ALOGW("binder transaction failed: %d", err);
+            return err;
+        }
+
+        return reply.readInt32();
+    }
+
     virtual status_t storeMetaDataInBuffers(
             node_id node, OMX_U32 port_index, OMX_BOOL enable) {
         Parcel data, reply;
@@ -404,7 +445,7 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-#define CHECK_INTERFACE(interface, data, reply) \
+#define CHECK_OMX_INTERFACE(interface, data, reply) \
         do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
             ALOGW("Call incorrectly routed to " #interface); \
             return PERMISSION_DENIED; \
@@ -415,7 +456,7 @@
     switch (code) {
         case LIVES_LOCALLY:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
             node_id node = (void *)data.readIntPtr();
             pid_t pid = (pid_t)data.readInt32();
             reply->writeInt32(livesLocally(node, pid));
@@ -425,7 +466,7 @@
 
         case LIST_NODES:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             List<ComponentInfo> list;
             listNodes(&list);
@@ -448,7 +489,7 @@
 
         case ALLOCATE_NODE:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             const char *name = data.readCString();
 
@@ -468,7 +509,7 @@
 
         case FREE_NODE:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
 
@@ -479,7 +520,7 @@
 
         case SEND_COMMAND:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
 
@@ -497,7 +538,7 @@
         case GET_CONFIG:
         case SET_CONFIG:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             OMX_INDEXTYPE index = static_cast<OMX_INDEXTYPE>(data.readInt32());
@@ -539,7 +580,7 @@
 
         case GET_STATE:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             OMX_STATETYPE state = OMX_StateInvalid;
@@ -553,7 +594,7 @@
 
         case ENABLE_GRAPHIC_BUFFERS:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             OMX_U32 port_index = data.readInt32();
@@ -567,7 +608,7 @@
 
         case GET_GRAPHIC_BUFFER_USAGE:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             OMX_U32 port_index = data.readInt32();
@@ -582,7 +623,7 @@
 
         case USE_BUFFER:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             OMX_U32 port_index = data.readInt32();
@@ -602,7 +643,7 @@
 
         case USE_GRAPHIC_BUFFER:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             OMX_U32 port_index = data.readInt32();
@@ -621,9 +662,41 @@
             return NO_ERROR;
         }
 
+        case CREATE_INPUT_SURFACE:
+        {
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
+
+            node_id node = (void*)data.readIntPtr();
+            OMX_U32 port_index = data.readInt32();
+
+            sp<IGraphicBufferProducer> bufferProducer;
+            status_t err = createInputSurface(node, port_index,
+                    &bufferProducer);
+
+            reply->writeInt32(err);
+
+            if (err == OK) {
+                reply->writeStrongBinder(bufferProducer->asBinder());
+            }
+
+            return NO_ERROR;
+        }
+
+        case SIGNAL_END_OF_INPUT_STREAM:
+        {
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
+
+            node_id node = (void*)data.readIntPtr();
+
+            status_t err = signalEndOfInputStream(node);
+            reply->writeInt32(err);
+
+            return NO_ERROR;
+        }
+
         case STORE_META_DATA_IN_BUFFERS:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             OMX_U32 port_index = data.readInt32();
@@ -637,7 +710,7 @@
 
         case ALLOC_BUFFER:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             OMX_U32 port_index = data.readInt32();
@@ -659,7 +732,7 @@
 
         case ALLOC_BUFFER_WITH_BACKUP:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             OMX_U32 port_index = data.readInt32();
@@ -681,7 +754,7 @@
 
         case FREE_BUFFER:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             OMX_U32 port_index = data.readInt32();
@@ -693,7 +766,7 @@
 
         case FILL_BUFFER:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             buffer_id buffer = (void*)data.readIntPtr();
@@ -704,7 +777,7 @@
 
         case EMPTY_BUFFER:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             buffer_id buffer = (void*)data.readIntPtr();
@@ -723,7 +796,7 @@
 
         case GET_EXTENSION_INDEX:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             const char *parameter_name = data.readCString();
@@ -769,7 +842,7 @@
     switch (code) {
         case OBSERVER_ON_MSG:
         {
-            CHECK_INTERFACE(IOMXObserver, data, reply);
+            CHECK_OMX_INTERFACE(IOMXObserver, data, reply);
 
             omx_message msg;
             data.read(&msg, sizeof(msg));
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index a6cc4eb..59fc45e 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -26,6 +26,7 @@
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
 
+#include <media/stagefright/BufferProducerWrapper.h>
 #include <media/stagefright/MediaCodecList.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/NativeWindowWrapper.h>
@@ -192,6 +193,7 @@
     friend struct ACodec::UninitializedState;
 
     bool onConfigureComponent(const sp<AMessage> &msg);
+    void onCreateInputSurface(const sp<AMessage> &msg);
     void onStart();
     void onShutdown(bool keepComponentAllocated);
 
@@ -239,6 +241,9 @@
     // to fill with data.
     void resume();
 
+    // Send EOS on input stream.
+    void onSignalEndOfInputStream();
+
     // Returns true iff input and output buffers are in play.
     bool active() const { return mActive; }
 
@@ -392,6 +397,14 @@
     msg->post();
 }
 
+void ACodec::initiateCreateInputSurface() {
+    (new AMessage(kWhatCreateInputSurface, id()))->post();
+}
+
+void ACodec::signalEndOfInputStream() {
+    (new AMessage(kWhatSignalEndOfInputStream, id()))->post();
+}
+
 void ACodec::initiateStart() {
     (new AMessage(kWhatStart, id()))->post();
 }
@@ -2469,6 +2482,14 @@
             return onOMXMessage(msg);
         }
 
+        case ACodec::kWhatCreateInputSurface:
+        case ACodec::kWhatSignalEndOfInputStream:
+        {
+            ALOGE("Message 0x%x was not handled", msg->what());
+            mCodec->signalError(OMX_ErrorUndefined, INVALID_OPERATION);
+            return true;
+        }
+
         default:
             return false;
     }
@@ -3232,6 +3253,13 @@
             break;
         }
 
+        case ACodec::kWhatCreateInputSurface:
+        {
+            onCreateInputSurface(msg);
+            handled = true;
+            break;
+        }
+
         case ACodec::kWhatStart:
         {
             onStart();
@@ -3310,6 +3338,32 @@
     return true;
 }
 
+void ACodec::LoadedState::onCreateInputSurface(
+        const sp<AMessage> &msg) {
+    ALOGV("onCreateInputSurface");
+
+    sp<AMessage> notify = mCodec->mNotify->dup();
+    notify->setInt32("what", ACodec::kWhatInputSurfaceCreated);
+
+    sp<IGraphicBufferProducer> bufferProducer;
+    status_t err;
+
+    err = mCodec->mOMX->createInputSurface(mCodec->mNode, kPortIndexInput,
+            &bufferProducer);
+    if (err == OK) {
+        notify->setObject("input-surface",
+                new BufferProducerWrapper(bufferProducer));
+    } else {
+        // Can't use mCodec->signalError() here -- MediaCodec won't forward
+        // the error through because it's in the "configured" state.  We
+        // send a kWhatInputSurfaceCreated with an error value instead.
+        ALOGE("[%s] onCreateInputSurface returning error %d",
+                mCodec->mComponentName.c_str(), err);
+        notify->setInt32("err", err);
+    }
+    notify->post();
+}
+
 void ACodec::LoadedState::onStart() {
     ALOGV("onStart");
 
@@ -3484,6 +3538,17 @@
     mActive = true;
 }
 
+void ACodec::ExecutingState::onSignalEndOfInputStream() {
+    sp<AMessage> notify = mCodec->mNotify->dup();
+    notify->setInt32("what", ACodec::kWhatSignaledInputEOS);
+
+    status_t err = mCodec->mOMX->signalEndOfInputStream(mCodec->mNode);
+    if (err != OK) {
+        notify->setInt32("err", err);
+    }
+    notify->post();
+}
+
 void ACodec::ExecutingState::stateEntered() {
     ALOGV("[%s] Now Executing", mCodec->mComponentName.c_str());
 
@@ -3573,6 +3638,13 @@
             break;
         }
 
+        case ACodec::kWhatSignalEndOfInputStream:
+        {
+            onSignalEndOfInputStream();
+            handled = true;
+            break;
+        }
+
         default:
             handled = BaseState::onMessageReceived(msg);
             break;
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 83be0fd..79ea04c 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -30,6 +30,7 @@
 #include <media/stagefright/foundation/AString.h>
 #include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/ACodec.h>
+#include <media/stagefright/BufferProducerWrapper.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/NativeWindowWrapper.h>
@@ -62,6 +63,7 @@
     : mState(UNINITIALIZED),
       mLooper(looper),
       mCodec(new ACodec),
+      mReplyID(0),
       mFlags(0),
       mSoftRenderer(NULL),
       mDequeueInputTimeoutGeneration(0),
@@ -154,6 +156,28 @@
     return PostAndAwaitResponse(msg, &response);
 }
 
+status_t MediaCodec::createInputSurface(
+        sp<IGraphicBufferProducer>* bufferProducer) {
+    sp<AMessage> msg = new AMessage(kWhatCreateInputSurface, id());
+
+    // TODO(fadden): require MediaFormat colorFormat == AndroidOpaque
+
+    sp<AMessage> response;
+    status_t err = PostAndAwaitResponse(msg, &response);
+    if (err == NO_ERROR) {
+        // unwrap the sp<IGraphicBufferProducer>
+        sp<RefBase> obj;
+        bool found = response->findObject("input-surface", &obj);
+        CHECK(found);
+        sp<BufferProducerWrapper> wrapper(
+                static_cast<BufferProducerWrapper*>(obj.get()));
+        *bufferProducer = wrapper->getBufferProducer();
+    } else {
+        ALOGW("createInputSurface failed, err=%d", err);
+    }
+    return err;
+}
+
 status_t MediaCodec::start() {
     sp<AMessage> msg = new AMessage(kWhatStart, id());
 
@@ -232,6 +256,8 @@
 }
 
 status_t MediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
+    // TODO(fadden): fail if an input Surface has been configured
+
     sp<AMessage> msg = new AMessage(kWhatDequeueInputBuffer, id());
     msg->setInt64("timeoutUs", timeoutUs);
 
@@ -288,6 +314,13 @@
     return PostAndAwaitResponse(msg, &response);
 }
 
+status_t MediaCodec::signalEndOfInputStream() {
+    sp<AMessage> msg = new AMessage(kWhatSignalEndOfInputStream, id());
+
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
 status_t MediaCodec::getOutputFormat(sp<AMessage> *format) const {
     sp<AMessage> msg = new AMessage(kWhatGetOutputFormat, id());
 
@@ -575,6 +608,36 @@
                     break;
                 }
 
+                case ACodec::kWhatInputSurfaceCreated:
+                {
+                    // response to ACodec::kWhatCreateInputSurface
+                    status_t err = NO_ERROR;
+                    sp<AMessage> response = new AMessage();
+                    if (!msg->findInt32("err", &err)) {
+                        sp<RefBase> obj;
+                        msg->findObject("input-surface", &obj);
+                        CHECK(obj != NULL);
+                        response->setObject("input-surface", obj);
+                    } else {
+                        response->setInt32("err", err);
+                    }
+                    response->postReply(mReplyID);
+                    break;
+                }
+
+                case ACodec::kWhatSignaledInputEOS:
+                {
+                    // response to ACodec::kWhatSignalEndOfInputStream
+                    sp<AMessage> response = new AMessage();
+                    status_t err;
+                    if (msg->findInt32("err", &err)) {
+                        response->setInt32("err", err);
+                    }
+                    response->postReply(mReplyID);
+                    break;
+                }
+
+
                 case ACodec::kWhatBuffersAllocated:
                 {
                     int32_t portIndex;
@@ -881,6 +944,25 @@
             break;
         }
 
+        case kWhatCreateInputSurface:
+        {
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            // Must be configured, but can't have been started yet.
+            if (mState != CONFIGURED) {
+                sp<AMessage> response = new AMessage;
+                response->setInt32("err", INVALID_OPERATION);
+
+                response->postReply(replyID);
+                break;
+            }
+
+            mReplyID = replyID;
+            mCodec->initiateCreateInputSurface();
+            break;
+        }
+
         case kWhatStart:
         {
             uint32_t replyID;
@@ -947,6 +1029,7 @@
 
         case kWhatDequeueInputBuffer:
         {
+            // TODO(fadden): make this fail if we're using an input Surface
             uint32_t replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
@@ -1093,6 +1176,24 @@
             break;
         }
 
+        case kWhatSignalEndOfInputStream:
+        {
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            if (mState != STARTED || (mFlags & kFlagStickyError)) {
+                sp<AMessage> response = new AMessage;
+                response->setInt32("err", INVALID_OPERATION);
+
+                response->postReply(replyID);
+                break;
+            }
+
+            mReplyID = replyID;
+            mCodec->signalEndOfInputStream();
+            break;
+        }
+
         case kWhatGetBuffers:
         {
             uint32_t replyID;
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index 7cdb793..ff72e0e 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -83,6 +83,12 @@
             node_id node, OMX_U32 port_index,
             const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer);
 
+    virtual status_t createInputSurface(
+            node_id node, OMX_U32 port_index,
+            sp<IGraphicBufferProducer> *bufferProducer);
+
+    virtual status_t signalEndOfInputStream(node_id node);
+
     virtual status_t allocateBuffer(
             node_id node, OMX_U32 port_index, size_t size,
             buffer_id *buffer, void **buffer_data);
@@ -274,6 +280,18 @@
             node, port_index, graphicBuffer, buffer);
 }
 
+status_t MuxOMX::createInputSurface(
+        node_id node, OMX_U32 port_index,
+        sp<IGraphicBufferProducer> *bufferProducer) {
+    status_t err = getOMX(node)->createInputSurface(
+            node, port_index, bufferProducer);
+    return err;
+}
+
+status_t MuxOMX::signalEndOfInputStream(node_id node) {
+    return getOMX(node)->signalEndOfInputStream(node);
+}
+
 status_t MuxOMX::allocateBuffer(
         node_id node, OMX_U32 port_index, size_t size,
         buffer_id *buffer, void **buffer_data) {
diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h
index 2c87b34..24b8d98 100644
--- a/media/libstagefright/include/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -79,6 +79,12 @@
             node_id node, OMX_U32 port_index,
             const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer);
 
+    virtual status_t createInputSurface(
+            node_id node, OMX_U32 port_index,
+            sp<IGraphicBufferProducer> *bufferProducer);
+
+    virtual status_t signalEndOfInputStream(node_id node);
+
     virtual status_t allocateBuffer(
             node_id node, OMX_U32 port_index, size_t size,
             buffer_id *buffer, void **buffer_data);
diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h
index 47ca579..67aba6b 100644
--- a/media/libstagefright/include/OMXNodeInstance.h
+++ b/media/libstagefright/include/OMXNodeInstance.h
@@ -27,6 +27,7 @@
 
 class IOMXObserver;
 struct OMXMaster;
+struct GraphicBufferSource;
 
 struct OMXNodeInstance {
     OMXNodeInstance(
@@ -65,6 +66,11 @@
             OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
             OMX::buffer_id *buffer);
 
+    status_t createInputSurface(
+            OMX_U32 portIndex, sp<IGraphicBufferProducer> *bufferProducer);
+
+    status_t signalEndOfInputStream();
+
     status_t allocateBuffer(
             OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer,
             void **buffer_data);
@@ -82,12 +88,18 @@
             OMX_U32 rangeOffset, OMX_U32 rangeLength,
             OMX_U32 flags, OMX_TICKS timestamp);
 
+    status_t emptyDirectBuffer(
+            OMX_BUFFERHEADERTYPE *header,
+            OMX_U32 rangeOffset, OMX_U32 rangeLength,
+            OMX_U32 flags, OMX_TICKS timestamp);
+
     status_t getExtensionIndex(
             const char *parameterName, OMX_INDEXTYPE *index);
 
     void onMessage(const omx_message &msg);
     void onObserverDied(OMXMaster *master);
     void onGetHandleFailed();
+    void onEvent(OMX_EVENTTYPE event, OMX_U32 arg1, OMX_U32 arg2);
 
     static OMX_CALLBACKTYPE kCallbacks;
 
@@ -100,6 +112,13 @@
     sp<IOMXObserver> mObserver;
     bool mDying;
 
+    // Lock only covers mGraphicBufferSource.  We can't always use mLock
+    // because of rare instances where we'd end up locking it recursively.
+    Mutex mGraphicBufferSourceLock;
+    // Access this through getGraphicBufferSource().
+    sp<GraphicBufferSource> mGraphicBufferSource;
+
+
     struct ActiveBuffer {
         OMX_U32 mPortIndex;
         OMX::buffer_id mID;
@@ -132,6 +151,11 @@
             OMX_IN OMX_PTR pAppData,
             OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
 
+    status_t storeMetaDataInBuffers_l(OMX_U32 portIndex, OMX_BOOL enable);
+
+    sp<GraphicBufferSource> getGraphicBufferSource();
+    void setGraphicBufferSource(const sp<GraphicBufferSource>& bufferSource);
+
     OMXNodeInstance(const OMXNodeInstance &);
     OMXNodeInstance &operator=(const OMXNodeInstance &);
 };
diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk
index d7fbbbe..9129f08 100644
--- a/media/libstagefright/omx/Android.mk
+++ b/media/libstagefright/omx/Android.mk
@@ -2,6 +2,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=                     \
+        GraphicBufferSource.cpp       \
         OMX.cpp                       \
         OMXMaster.cpp                 \
         OMXNodeInstance.cpp           \
@@ -19,6 +20,7 @@
         libmedia                        \
         libutils                        \
         libui                           \
+        libgui                          \
         libcutils                       \
         libstagefright_foundation       \
         libdl
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
new file mode 100644
index 0000000..f207954
--- /dev/null
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -0,0 +1,441 @@
+/*
+ * 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_TAG "GraphicBufferSource"
+#include <utils/Log.h>
+
+#include <GraphicBufferSource.h>
+
+#include <OMX_Core.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+#include <MetadataBufferType.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+
+static const bool EXTRA_CHECK = true;
+
+
+GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance,
+        uint32_t bufferWidth, uint32_t bufferHeight) :
+    mInitCheck(UNKNOWN_ERROR),
+    mNodeInstance(nodeInstance),
+    mExecuting(false),
+    mNumFramesAvailable(0),
+    mEndOfStream(false),
+    mEndOfStreamSent(false) {
+
+    ALOGV("GraphicBufferSource w=%u h=%u", bufferWidth, bufferHeight);
+
+    if (bufferWidth == 0 || bufferHeight == 0) {
+        ALOGE("Invalid dimensions %dx%d", bufferWidth, bufferHeight);
+        mInitCheck = BAD_VALUE;
+        return;
+    }
+
+    mBufferQueue = new BufferQueue(true);
+    mBufferQueue->setDefaultBufferSize(bufferWidth, bufferHeight);
+    mBufferQueue->setSynchronousMode(true);
+    mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER |
+            GRALLOC_USAGE_HW_TEXTURE);
+
+    // Note that we can't create an sp<...>(this) in a ctor that will not keep a
+    // reference once the ctor ends, as that would cause the refcount of 'this'
+    // dropping to 0 at the end of the ctor.  Since all we need is a wp<...>
+    // that's what we create.
+    wp<BufferQueue::ConsumerListener> listener;
+    listener = static_cast<BufferQueue::ConsumerListener*>(this);
+
+    sp<BufferQueue::ConsumerListener> proxy;
+    proxy = new BufferQueue::ProxyConsumerListener(listener);
+
+    status_t err = mBufferQueue->consumerConnect(proxy);
+    if (err != NO_ERROR) {
+        ALOGE("Error connecting to BufferQueue: %s (%d)",
+                strerror(-err), err);
+        return;
+    }
+
+    mInitCheck = OK;
+}
+
+GraphicBufferSource::~GraphicBufferSource() {
+    ALOGV("~GraphicBufferSource");
+    status_t err = mBufferQueue->consumerDisconnect();
+    if (err != NO_ERROR) {
+        ALOGW("consumerDisconnect failed: %d", err);
+    }
+}
+
+void GraphicBufferSource::omxExecuting() {
+    Mutex::Autolock autoLock(mMutex);
+    ALOGV("--> executing; avail=%d, codec vec size=%zd",
+            mNumFramesAvailable, mCodecBuffers.size());
+    CHECK(!mExecuting);
+    mExecuting = true;
+
+    // Start by loading up as many buffers as possible.  We want to do this,
+    // rather than just submit the first buffer, to avoid a degenerate case:
+    // if all BQ buffers arrive before we start executing, and we only submit
+    // one here, the other BQ buffers will just sit until we get notified
+    // that the codec buffer has been released.  We'd then acquire and
+    // submit a single additional buffer, repeatedly, never using more than
+    // one codec buffer simultaneously.  (We could instead try to submit
+    // all BQ buffers whenever any codec buffer is freed, but if we get the
+    // initial conditions right that will never be useful.)
+    while (mNumFramesAvailable && isCodecBufferAvailable_l()) {
+        fillCodecBuffer_l();
+    }
+
+    ALOGV("done loading initial frames, avail=%d", mNumFramesAvailable);
+
+    // If EOS has already been signaled, and there are no more frames to
+    // submit, try to send EOS now as well.
+    if (mEndOfStream && mNumFramesAvailable == 0) {
+        submitEndOfInputStream_l();
+    }
+}
+
+void GraphicBufferSource::omxIdling(){
+    Mutex::Autolock autoLock(mMutex);
+    ALOGV("--> idling");
+    if (!mExecuting) {
+        // Transition from "loading" to "idling".  Nothing to do.
+        return;
+    }
+
+    ALOGV("Dropped down to idle, avail=%d eos=%d eosSent=%d",
+            mNumFramesAvailable, mEndOfStream, mEndOfStreamSent);
+
+    // Codec is no longer executing.  Discard all codec-related state.
+    mCodecBuffers.clear();
+    // TODO: scan mCodecBuffers to verify that all mGraphicBuffer entries
+    //       are null; complain if not
+
+    mExecuting = false;
+}
+
+void GraphicBufferSource::addCodecBuffer(OMX_BUFFERHEADERTYPE* header) {
+    Mutex::Autolock autoLock(mMutex);
+
+    if (mExecuting) {
+        // This should never happen -- buffers can only be allocated when
+        // transitioning from "loaded" to "idle".
+        ALOGE("addCodecBuffer: buffer added while executing");
+        return;
+    }
+
+    ALOGV("addCodecBuffer h=%p size=%lu p=%p",
+            header, header->nAllocLen, header->pBuffer);
+    CodecBuffer codecBuffer;
+    codecBuffer.mHeader = header;
+    mCodecBuffers.add(codecBuffer);
+}
+
+void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) {
+    Mutex::Autolock autoLock(mMutex);
+
+    CHECK(mExecuting);  // could this happen if app stop()s early?
+
+    int cbi = findMatchingCodecBuffer_l(header);
+    if (cbi < 0) {
+        // This should never happen.
+        ALOGE("codecBufferEmptied: buffer not recognized (h=%p)", header);
+        return;
+    }
+
+    ALOGV("codecBufferEmptied h=%p size=%lu filled=%lu p=%p",
+            header, header->nAllocLen, header->nFilledLen,
+            header->pBuffer);
+    CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi));
+
+    // header->nFilledLen may not be the original value, so we can't compare
+    // that to zero to see of this was the EOS buffer.  Instead we just
+    // see if the GraphicBuffer reference was null, which should only ever
+    // happen for EOS.
+    if (codecBuffer.mGraphicBuffer == NULL) {
+        CHECK(mEndOfStream);
+        // No GraphicBuffer to deal with, no additional input or output is
+        // expected, so just return.
+        return;
+    }
+
+    if (EXTRA_CHECK) {
+        // Pull the graphic buffer handle back out of the buffer, and confirm
+        // that it matches expectations.
+        OMX_U8* data = header->pBuffer;
+        buffer_handle_t bufferHandle;
+        memcpy(&bufferHandle, data + 4, sizeof(buffer_handle_t));
+        if (bufferHandle != codecBuffer.mGraphicBuffer->handle) {
+            // should never happen
+            ALOGE("codecBufferEmptied: buffer's handle is %p, expected %p",
+                    bufferHandle, codecBuffer.mGraphicBuffer->handle);
+            CHECK(!"codecBufferEmptied: mismatched buffer");
+        }
+    }
+
+    // Find matching entry in our cached copy of the BufferQueue slots.
+    // If we find a match, release that slot.  If we don't, the BufferQueue
+    // has dropped that GraphicBuffer, and there's nothing for us to release.
+    //
+    // (We could store "id" in CodecBuffer and avoid the slot search.)
+    int id;
+    for (id = 0; id < BufferQueue::NUM_BUFFER_SLOTS; id++) {
+        if (mBufferSlot[id] == NULL) {
+            continue;
+        }
+
+        if (mBufferSlot[id]->handle == codecBuffer.mGraphicBuffer->handle) {
+            ALOGV("cbi %d matches bq slot %d, handle=%p",
+                    cbi, id, mBufferSlot[id]->handle);
+
+            mBufferQueue->releaseBuffer(id, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR,
+                    Fence::NO_FENCE);
+            break;
+        }
+    }
+    if (id == BufferQueue::NUM_BUFFER_SLOTS) {
+        ALOGV("codecBufferEmptied: no match for emptied buffer in cbi %d",
+                cbi);
+    }
+
+    // Mark the codec buffer as available by clearing the GraphicBuffer ref.
+    codecBuffer.mGraphicBuffer = NULL;
+
+    if (mNumFramesAvailable) {
+        // Fill this codec buffer.
+        CHECK(!mEndOfStream);
+        ALOGV("buffer freed, %d frames avail", mNumFramesAvailable);
+        fillCodecBuffer_l();
+    } else if (mEndOfStream) {
+        // No frames available, but EOS is pending, so use this buffer to
+        // send that.
+        ALOGV("buffer freed, EOS pending");
+        submitEndOfInputStream_l();
+    }
+    return;
+}
+
+status_t GraphicBufferSource::fillCodecBuffer_l() {
+    CHECK(mExecuting && mNumFramesAvailable > 0);
+    int cbi = findAvailableCodecBuffer_l();
+    if (cbi < 0) {
+        // No buffers available, bail.
+        ALOGV("fillCodecBuffer_l: no codec buffers, avail now %d",
+                mNumFramesAvailable);
+    } else {
+        ALOGV("fillCodecBuffer_l: acquiring buffer, avail=%d",
+                mNumFramesAvailable);
+        BufferQueue::BufferItem item;
+        status_t err = mBufferQueue->acquireBuffer(&item);
+        if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
+            // shouldn't happen
+            ALOGW("fillCodecBuffer_l: frame was not available");
+            return err;
+        } else if (err != OK) {
+            // now what? fake end-of-stream?
+            ALOGW("fillCodecBuffer_l: acquireBuffer returned err=%d", err);
+            return err;
+        }
+
+        mNumFramesAvailable--;
+
+        // Wait for it to become available.
+        err = item.mFence->waitForever(1000,
+                "GraphicBufferSource::fillCodecBuffer_l");
+        if (err != OK) {
+            ALOGW("failed to wait for buffer fence: %d", err);
+            // keep going
+        }
+
+        // If this is the first time we're seeing this buffer, add it to our
+        // slot table.
+        if (item.mGraphicBuffer != NULL) {
+            ALOGV("fillCodecBuffer_l: setting mBufferSlot %d", item.mBuf);
+            mBufferSlot[item.mBuf] = item.mGraphicBuffer;
+        }
+
+        err = submitBuffer_l(mBufferSlot[item.mBuf], item.mTimestamp, cbi);
+        if (err != OK) {
+            ALOGV("submitBuffer_l failed, releasing bq buf %d", item.mBuf);
+            mBufferQueue->releaseBuffer(item.mBuf, EGL_NO_DISPLAY,
+                EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+        } else {
+            ALOGV("buffer submitted (bq %d, cbi %d)", item.mBuf, cbi);
+        }
+    }
+
+    return OK;
+}
+
+void GraphicBufferSource::signalEndOfInputStream() {
+    Mutex::Autolock autoLock(mMutex);
+    ALOGV("signalEndOfInputStream: exec=%d avail=%d",
+            mExecuting, mNumFramesAvailable);
+
+    // Set the end-of-stream flag.  If no frames are pending from the
+    // BufferQueue, and a codec buffer is available, and we're executing,
+    // we initiate the EOS from here.  Otherwise, we'll let
+    // codecBufferEmptied() (or omxExecuting) do it.
+    //
+    // Note: if there are no pending frames and all codec buffers are
+    // available, we *must* submit the EOS from here or we'll just
+    // stall since no future events are expected.
+    mEndOfStream = true;
+
+    if (mExecuting && mNumFramesAvailable == 0) {
+        submitEndOfInputStream_l();
+    }
+}
+
+status_t GraphicBufferSource::submitBuffer_l(sp<GraphicBuffer>& graphicBuffer,
+        int64_t timestamp, int cbi) {
+    ALOGV("submitBuffer_l cbi=%d", cbi);
+    CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi));
+    codecBuffer.mGraphicBuffer = graphicBuffer;
+
+    OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader;
+    CHECK(header->nAllocLen >= 4 + sizeof(buffer_handle_t));
+    OMX_U8* data = header->pBuffer;
+    const OMX_U32 type = kMetadataBufferTypeGrallocSource;
+    buffer_handle_t handle = codecBuffer.mGraphicBuffer->handle;
+    memcpy(data, &type, 4);
+    memcpy(data + 4, &handle, sizeof(buffer_handle_t));
+
+    status_t err = mNodeInstance->emptyDirectBuffer(header, 0,
+            4 + sizeof(buffer_handle_t), OMX_BUFFERFLAG_ENDOFFRAME,
+            timestamp);
+    if (err != OK) {
+        ALOGW("WARNING: emptyDirectBuffer failed: 0x%x", err);
+        codecBuffer.mGraphicBuffer = NULL;
+        return err;
+    }
+
+    ALOGV("emptyDirectBuffer succeeded, h=%p p=%p bufhandle=%p",
+            header, header->pBuffer, handle);
+    return OK;
+}
+
+void GraphicBufferSource::submitEndOfInputStream_l() {
+    CHECK(mEndOfStream);
+    if (mEndOfStreamSent) {
+        ALOGV("EOS already sent");
+        return;
+    }
+
+    int cbi = findAvailableCodecBuffer_l();
+    if (cbi < 0) {
+        ALOGV("submitEndOfInputStream_l: no codec buffers available");
+        return;
+    }
+
+    // We reject any additional incoming graphic buffers, so there's no need
+    // to stick a placeholder into codecBuffer.mGraphicBuffer to mark it as
+    // in-use.
+    CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi));
+
+    OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader;
+    if (EXTRA_CHECK) {
+        // Guard against implementations that don't check nFilledLen.
+        size_t fillLen = 4 + sizeof(buffer_handle_t);
+        CHECK(header->nAllocLen >= fillLen);
+        OMX_U8* data = header->pBuffer;
+        memset(data, 0xcd, fillLen);
+    }
+
+    uint64_t timestamp = 0; // does this matter?
+
+    status_t err = mNodeInstance->emptyDirectBuffer(header, /*offset*/ 0,
+            /*length*/ 0, OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS,
+            timestamp);
+    if (err != OK) {
+        ALOGW("emptyDirectBuffer EOS failed: 0x%x", err);
+    } else {
+        ALOGV("submitEndOfInputStream_l: buffer submitted, header=%p cbi=%d",
+                header, cbi);
+    }
+}
+
+int GraphicBufferSource::findAvailableCodecBuffer_l() {
+    CHECK(mCodecBuffers.size() > 0);
+
+    for (int i = (int)mCodecBuffers.size() - 1; i>= 0; --i) {
+        if (mCodecBuffers[i].mGraphicBuffer == NULL) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+int GraphicBufferSource::findMatchingCodecBuffer_l(
+        const OMX_BUFFERHEADERTYPE* header) {
+    for (int i = (int)mCodecBuffers.size() - 1; i>= 0; --i) {
+        if (mCodecBuffers[i].mHeader == header) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+// BufferQueue::ConsumerListener callback
+void GraphicBufferSource::onFrameAvailable() {
+    Mutex::Autolock autoLock(mMutex);
+
+    ALOGV("onFrameAvailable exec=%d avail=%d", mExecuting, mNumFramesAvailable);
+
+    if (mEndOfStream) {
+        // This should only be possible if a new buffer was queued after
+        // EOS was signaled, i.e. the app is misbehaving.
+        ALOGW("onFrameAvailable: EOS is set, ignoring frame");
+
+        BufferQueue::BufferItem item;
+        status_t err = mBufferQueue->acquireBuffer(&item);
+        if (err == OK) {
+            mBufferQueue->releaseBuffer(item.mBuf, EGL_NO_DISPLAY,
+                EGL_NO_SYNC_KHR, item.mFence);
+        }
+        return;
+    }
+
+    mNumFramesAvailable++;
+
+    if (mExecuting) {
+        fillCodecBuffer_l();
+    }
+}
+
+// BufferQueue::ConsumerListener callback
+void GraphicBufferSource::onBuffersReleased() {
+    Mutex::Autolock lock(mMutex);
+
+    uint32_t slotMask;
+    if (mBufferQueue->getReleasedBuffers(&slotMask) != NO_ERROR) {
+        ALOGW("onBuffersReleased: unable to get released buffer set");
+        slotMask = 0xffffffff;
+    }
+
+    ALOGV("onBuffersReleased: 0x%08x", slotMask);
+
+    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+        if ((slotMask & 0x01) != 0) {
+            mBufferSlot[i] = NULL;
+        }
+        slotMask >>= 1;
+    }
+}
+
+}  // namespace android
diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h
new file mode 100644
index 0000000..6d49f96
--- /dev/null
+++ b/media/libstagefright/omx/GraphicBufferSource.h
@@ -0,0 +1,176 @@
+/*
+ * 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 GRAPHIC_BUFFER_SOURCE_H_
+
+#define GRAPHIC_BUFFER_SOURCE_H_
+
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/BufferQueue.h>
+#include <utils/RefBase.h>
+
+#include <OMX_Core.h>
+#include "../include/OMXNodeInstance.h"
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+/*
+ * This class is used to feed OMX codecs from a Surface via BufferQueue.
+ *
+ * Instances of the class don't run on a dedicated thread.  Instead,
+ * various events trigger data movement:
+ *
+ *  - Availability of a new frame of data from the BufferQueue (notified
+ *    via the onFrameAvailable callback).
+ *  - The return of a codec buffer (via OnEmptyBufferDone).
+ *  - Application signaling end-of-stream.
+ *  - Transition to or from "executing" state.
+ *
+ * Frames of data (and, perhaps, the end-of-stream indication) can arrive
+ * before the codec is in the "executing" state, so we need to queue
+ * things up until we're ready to go.
+ */
+class GraphicBufferSource : public BufferQueue::ConsumerListener {
+public:
+    GraphicBufferSource(OMXNodeInstance* nodeInstance,
+            uint32_t bufferWidth, uint32_t bufferHeight);
+    virtual ~GraphicBufferSource();
+
+    // We can't throw an exception if the constructor fails, so we just set
+    // this and require that the caller test the value.
+    status_t initCheck() const {
+        return mInitCheck;
+    }
+
+    // Returns the handle to the producer side of the BufferQueue.  Buffers
+    // queued on this will be received by GraphicBufferSource.
+    sp<IGraphicBufferProducer> getIGraphicBufferProducer() const {
+        return mBufferQueue;
+    }
+
+    // This is called when OMX transitions to OMX_StateExecuting, which means
+    // we can start handing it buffers.  If we already have buffers of data
+    // sitting in the BufferQueue, this will send them to the codec.
+    void omxExecuting();
+
+    // This is called when OMX transitions to OMX_StateIdle.  If we were
+    // previously executing, this means we're about to be shut down.  (We
+    // also enter Idle on the way up.)
+    void omxIdling();
+
+    // A "codec buffer", i.e. a buffer that can be used to pass data into
+    // the encoder, has been allocated.  (This call does not call back into
+    // OMXNodeInstance.)
+    void addCodecBuffer(OMX_BUFFERHEADERTYPE* header);
+
+    // Called from OnEmptyBufferDone.  If we have a BQ buffer available,
+    // fill it with a new frame of data; otherwise, just mark it as available.
+    void codecBufferEmptied(OMX_BUFFERHEADERTYPE* header);
+
+    // This is called after the last input frame has been submitted.  We
+    // need to submit an empty buffer with the EOS flag set.  If we don't
+    // have a codec buffer ready, we just set the mEndOfStream flag.
+    void signalEndOfInputStream();
+
+protected:
+    // BufferQueue::ConsumerListener interface, called when a new frame of
+    // data is available.  If we're executing and a codec buffer is
+    // available, we acquire the buffer, copy the GraphicBuffer reference
+    // into the codec buffer, and call Empty[This]Buffer.  If we're not yet
+    // executing or there's no codec buffer available, we just increment
+    // mNumFramesAvailable and return.
+    virtual void onFrameAvailable();
+
+    // BufferQueue::ConsumerListener interface, called when the client has
+    // released one or more GraphicBuffers.  We clear out the appropriate
+    // set of mBufferSlot entries.
+    virtual void onBuffersReleased();
+
+private:
+    // Keep track of codec input buffers.  They may either be available
+    // (mGraphicBuffer == NULL) or in use by the codec.
+    struct CodecBuffer {
+        OMX_BUFFERHEADERTYPE* mHeader;
+        sp<GraphicBuffer> mGraphicBuffer;
+    };
+
+    // Returns the index of an available codec buffer.  If none are
+    // available, returns -1.  Mutex must be held by caller.
+    int findAvailableCodecBuffer_l();
+
+    // Returns true if a codec buffer is available.
+    bool isCodecBufferAvailable_l() {
+        return findAvailableCodecBuffer_l() >= 0;
+    }
+
+    // Finds the mCodecBuffers entry that matches.  Returns -1 if not found.
+    int findMatchingCodecBuffer_l(const OMX_BUFFERHEADERTYPE* header);
+
+    // Fills a codec buffer with a frame from the BufferQueue.  This must
+    // only be called when we know that a frame of data is ready (i.e. we're
+    // in the onFrameAvailable callback, or if we're in codecBufferEmptied
+    // and mNumFramesAvailable is nonzero).  Returns without doing anything if
+    // we don't have a codec buffer available.
+    status_t fillCodecBuffer_l();
+
+    // Marks the mCodecBuffers entry as in-use, copies the GraphicBuffer
+    // reference into the codec buffer, and submits the data to the codec.
+    status_t submitBuffer_l(sp<GraphicBuffer>& graphicBuffer,
+            int64_t timestamp, int cbi);
+
+    // Submits an empty buffer, with the EOS flag set.   Returns without
+    // doing anything if we don't have a codec buffer available.
+    void submitEndOfInputStream_l();
+
+    // Lock, covers all member variables.
+    mutable Mutex mMutex;
+
+    // Used to report constructor failure.
+    status_t mInitCheck;
+
+    // Pointer back to the object that contains us.  We send buffers here.
+    OMXNodeInstance* mNodeInstance;
+
+    // Set by omxExecuting() / omxIdling().
+    bool mExecuting;
+
+    // We consume graphic buffers from this.
+    sp<BufferQueue> mBufferQueue;
+
+    // Number of frames pending in BufferQueue that haven't yet been
+    // forwarded to the codec.
+    size_t mNumFramesAvailable;
+
+    // Set to true if we want to send end-of-stream after we run out of
+    // frames in BufferQueue.
+    bool mEndOfStream;
+    bool mEndOfStreamSent;
+
+    // Cache of GraphicBuffers from the buffer queue.  When the codec
+    // is done processing a GraphicBuffer, we can use this to map back
+    // to a slot number.
+    sp<GraphicBuffer> mBufferSlot[BufferQueue::NUM_BUFFER_SLOTS];
+
+    // Tracks codec buffers.
+    Vector<CodecBuffer> mCodecBuffers;
+
+    DISALLOW_EVIL_CONSTRUCTORS(GraphicBufferSource);
+};
+
+}  // namespace android
+
+#endif  // GRAPHIC_BUFFER_SOURCE_H_
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 29bc733..3987ead 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -345,6 +345,17 @@
             port_index, graphicBuffer, buffer);
 }
 
+status_t OMX::createInputSurface(
+        node_id node, OMX_U32 port_index,
+        sp<IGraphicBufferProducer> *bufferProducer) {
+    return findInstance(node)->createInputSurface(
+            port_index, bufferProducer);
+}
+
+status_t OMX::signalEndOfInputStream(node_id node) {
+    return findInstance(node)->signalEndOfInputStream();
+}
+
 status_t OMX::allocateBuffer(
         node_id node, OMX_U32 port_index, size_t size,
         buffer_id *buffer, void **buffer_data) {
@@ -393,6 +404,9 @@
         OMX_IN OMX_PTR pEventData) {
     ALOGV("OnEvent(%d, %ld, %ld)", eEvent, nData1, nData2);
 
+    // Forward to OMXNodeInstance.
+    findInstance(node)->onEvent(eEvent, nData1, nData2);
+
     omx_message msg;
     msg.type = omx_message::EVENT;
     msg.node = node;
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index bff3def..6c2c33b 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -20,14 +20,18 @@
 
 #include "../include/OMXNodeInstance.h"
 #include "OMXMaster.h"
+#include "GraphicBufferSource.h"
 
 #include <OMX_Component.h>
 
 #include <binder/IMemory.h>
+#include <gui/BufferQueue.h>
 #include <HardwareAPI.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaErrors.h>
 
+static const OMX_U32 kPortIndexInput = 0;
+
 namespace android {
 
 struct BufferMeta {
@@ -100,6 +104,17 @@
     mHandle = handle;
 }
 
+sp<GraphicBufferSource> OMXNodeInstance::getGraphicBufferSource() {
+    Mutex::Autolock autoLock(mGraphicBufferSourceLock);
+    return mGraphicBufferSource;
+}
+
+void OMXNodeInstance::setGraphicBufferSource(
+        const sp<GraphicBufferSource>& bufferSource) {
+    Mutex::Autolock autoLock(mGraphicBufferSourceLock);
+    mGraphicBufferSource = bufferSource;
+}
+
 OMX *OMXNodeInstance::owner() {
     return mOwner;
 }
@@ -354,7 +369,12 @@
         OMX_U32 portIndex,
         OMX_BOOL enable) {
     Mutex::Autolock autolock(mLock);
+    return storeMetaDataInBuffers_l(portIndex, enable);
+}
 
+status_t OMXNodeInstance::storeMetaDataInBuffers_l(
+        OMX_U32 portIndex,
+        OMX_BOOL enable) {
     OMX_INDEXTYPE index;
     OMX_STRING name = const_cast<OMX_STRING>(
             "OMX.google.android.index.storeMetaDataInBuffers");
@@ -411,6 +431,11 @@
 
     addActiveBuffer(portIndex, *buffer);
 
+    sp<GraphicBufferSource> bufferSource(getGraphicBufferSource());
+    if (bufferSource != NULL && portIndex == kPortIndexInput) {
+        bufferSource->addCodecBuffer(header);
+    }
+
     return OK;
 }
 
@@ -530,6 +555,60 @@
     return OK;
 }
 
+status_t OMXNodeInstance::createInputSurface(
+        OMX_U32 portIndex, sp<IGraphicBufferProducer> *bufferProducer) {
+    Mutex::Autolock autolock(mLock);
+    status_t err;
+
+    const sp<GraphicBufferSource>& surfaceCheck = getGraphicBufferSource();
+    if (surfaceCheck != NULL) {
+        return ALREADY_EXISTS;
+    }
+
+    // Input buffers will hold meta-data (gralloc references).
+    err = storeMetaDataInBuffers_l(portIndex, OMX_TRUE);
+    if (err != OK) {
+        return err;
+    }
+
+    // Retrieve the width and height of the graphic buffer, set when the
+    // codec was configured.
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    def.nSize = sizeof(def);
+    def.nVersion.s.nVersionMajor = 1;
+    def.nVersion.s.nVersionMinor = 0;
+    def.nVersion.s.nRevision = 0;
+    def.nVersion.s.nStep = 0;
+    def.nPortIndex = portIndex;
+    OMX_ERRORTYPE oerr = OMX_GetParameter(
+            mHandle, OMX_IndexParamPortDefinition, &def);
+    CHECK(oerr == OMX_ErrorNone);
+
+    GraphicBufferSource* bufferSource = new GraphicBufferSource(
+            this, def.format.video.nFrameWidth, def.format.video.nFrameHeight);
+    if ((err = bufferSource->initCheck()) != OK) {
+        delete bufferSource;
+        return err;
+    }
+    setGraphicBufferSource(bufferSource);
+
+    *bufferProducer = bufferSource->getIGraphicBufferProducer();
+    return OK;
+}
+
+status_t OMXNodeInstance::signalEndOfInputStream() {
+    // For non-Surface input, the MediaCodec should convert the call to a
+    // pair of requests (dequeue input buffer, queue input buffer with EOS
+    // flag set).  Seems easier than doing the equivalent from here.
+    sp<GraphicBufferSource> bufferSource(getGraphicBufferSource());
+    if (bufferSource == NULL) {
+        ALOGW("signalEndOfInputStream should only be used with Surface input");
+        return INVALID_OPERATION;
+    };
+    bufferSource->signalEndOfInputStream();
+    return OK;
+}
+
 status_t OMXNodeInstance::allocateBuffer(
         OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer,
         void **buffer_data) {
@@ -560,6 +639,11 @@
 
     addActiveBuffer(portIndex, *buffer);
 
+    sp<GraphicBufferSource> bufferSource(getGraphicBufferSource());
+    if (bufferSource != NULL && portIndex == kPortIndexInput) {
+        bufferSource->addCodecBuffer(header);
+    }
+
     return OK;
 }
 
@@ -592,6 +676,11 @@
 
     addActiveBuffer(portIndex, *buffer);
 
+    sp<GraphicBufferSource> bufferSource(getGraphicBufferSource());
+    if (bufferSource != NULL && portIndex == kPortIndexInput) {
+        bufferSource->addCodecBuffer(header);
+    }
+
     return OK;
 }
 
@@ -646,6 +735,26 @@
     return StatusFromOMXError(err);
 }
 
+// like emptyBuffer, but the data is already in header->pBuffer
+status_t OMXNodeInstance::emptyDirectBuffer(
+        OMX_BUFFERHEADERTYPE *header,
+        OMX_U32 rangeOffset, OMX_U32 rangeLength,
+        OMX_U32 flags, OMX_TICKS timestamp) {
+    Mutex::Autolock autoLock(mLock);
+
+    header->nFilledLen = rangeLength;
+    header->nOffset = rangeOffset;
+    header->nFlags = flags;
+    header->nTimeStamp = timestamp;
+
+    OMX_ERRORTYPE err = OMX_EmptyThisBuffer(mHandle, header);
+    if (err != OMX_ErrorNone) {
+        ALOGW("emptyDirectBuffer failed, OMX err=0x%x", err);
+    }
+
+    return StatusFromOMXError(err);
+}
+
 status_t OMXNodeInstance::getExtensionIndex(
         const char *parameterName, OMX_INDEXTYPE *index) {
     Mutex::Autolock autoLock(mLock);
@@ -682,6 +791,22 @@
     delete this;
 }
 
+// OMXNodeInstance::OnEvent calls OMX::OnEvent, which then calls here.
+// Don't try to acquire mLock here -- in rare circumstances this will hang.
+void OMXNodeInstance::onEvent(
+        OMX_EVENTTYPE event, OMX_U32 arg1, OMX_U32 arg2) {
+    const sp<GraphicBufferSource>& bufferSource(getGraphicBufferSource());
+
+    if (bufferSource != NULL && event == OMX_EventCmdComplete &&
+            arg1 == OMX_CommandStateSet) {
+        if (arg2 == OMX_StateExecuting) {
+            bufferSource->omxExecuting();
+        } else if (arg2 == OMX_StateIdle) {
+            bufferSource->omxIdling();
+        }
+    }
+}
+
 // static
 OMX_ERRORTYPE OMXNodeInstance::OnEvent(
         OMX_IN OMX_HANDLETYPE hComponent,
@@ -707,6 +832,17 @@
     if (instance->mDying) {
         return OMX_ErrorNone;
     }
+    const sp<GraphicBufferSource>& bufferSource(
+            instance->getGraphicBufferSource());
+    if (bufferSource != NULL) {
+        bufferSource->codecBufferEmptied(pBuffer);
+
+        // This is one of the buffers used exclusively by GraphicBufferSource.
+        // Don't dispatch a message back to ACodec, since it doesn't
+        // know that anyone asked to have the buffer emptied and will
+        // be very confused.
+        return OMX_ErrorNone;
+    }
     return instance->owner()->OnEmptyBufferDone(instance->nodeID(), pBuffer);
 }
 
diff --git a/media/libstagefright/wifi-display/ANetworkSession.cpp b/media/libstagefright/wifi-display/ANetworkSession.cpp
index 06f71f4..cb6011c 100644
--- a/media/libstagefright/wifi-display/ANetworkSession.cpp
+++ b/media/libstagefright/wifi-display/ANetworkSession.cpp
@@ -23,6 +23,7 @@
 
 #include <arpa/inet.h>
 #include <fcntl.h>
+#include <linux/tcp.h>
 #include <net/if.h>
 #include <netdb.h>
 #include <netinet/in.h>
@@ -314,6 +315,9 @@
             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);
@@ -770,6 +774,14 @@
             err = -errno;
             goto bail2;
         }
+    } else if (mode == kModeCreateTCPDatagramSessionActive) {
+        int flag = 1;
+        res = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
+
+        if (res < 0) {
+            err = -errno;
+            goto bail2;
+        }
     }
 
     err = MakeSocketNonBlocking(s);
diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk
index 5095e82..19f560c 100644
--- a/media/libstagefright/wifi-display/Android.mk
+++ b/media/libstagefright/wifi-display/Android.mk
@@ -4,21 +4,23 @@
 
 LOCAL_SRC_FILES:= \
         ANetworkSession.cpp             \
+        MediaReceiver.cpp               \
+        MediaSender.cpp                 \
         Parameters.cpp                  \
         ParsedMessage.cpp               \
+        rtp/RTPAssembler.cpp            \
+        rtp/RTPReceiver.cpp             \
+        rtp/RTPSender.cpp               \
         sink/DirectRenderer.cpp         \
-        sink/LinearRegression.cpp       \
-        sink/RTPSink.cpp                \
         sink/TunnelRenderer.cpp         \
         sink/WifiDisplaySink.cpp        \
+        SNTPClient.cpp                  \
         source/Converter.cpp            \
         source/MediaPuller.cpp          \
         source/PlaybackSession.cpp      \
         source/RepeaterSource.cpp       \
-        source/Sender.cpp               \
         source/TSPacketizer.cpp         \
         source/WifiDisplaySource.cpp    \
-        TimeSeries.cpp                  \
         VideoFormats.cpp                \
 
 LOCAL_C_INCLUDES:= \
@@ -85,3 +87,25 @@
 LOCAL_MODULE_TAGS := debug
 
 include $(BUILD_EXECUTABLE)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+        rtptest.cpp                 \
+
+LOCAL_SHARED_LIBRARIES:= \
+        libbinder                       \
+        libgui                          \
+        libmedia                        \
+        libstagefright                  \
+        libstagefright_foundation       \
+        libstagefright_wfd              \
+        libutils                        \
+
+LOCAL_MODULE:= rtptest
+
+LOCAL_MODULE_TAGS := debug
+
+include $(BUILD_EXECUTABLE)
diff --git a/media/libstagefright/wifi-display/MediaReceiver.cpp b/media/libstagefright/wifi-display/MediaReceiver.cpp
new file mode 100644
index 0000000..3c92d41
--- /dev/null
+++ b/media/libstagefright/wifi-display/MediaReceiver.cpp
@@ -0,0 +1,311 @@
+/*
+ * Copyright 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 "MediaReceiver"
+#include <utils/Log.h>
+
+#include "MediaReceiver.h"
+
+#include "ANetworkSession.h"
+#include "AnotherPacketSource.h"
+#include "rtp/RTPReceiver.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+MediaReceiver::MediaReceiver(
+        const sp<ANetworkSession> &netSession,
+        const sp<AMessage> &notify)
+    : mNetSession(netSession),
+      mNotify(notify),
+      mMode(MODE_UNDEFINED),
+      mGeneration(0),
+      mInitStatus(OK),
+      mInitDoneCount(0) {
+}
+
+MediaReceiver::~MediaReceiver() {
+}
+
+ssize_t MediaReceiver::addTrack(
+        RTPReceiver::TransportMode transportMode,
+        int32_t *localRTPPort) {
+    if (mMode != MODE_UNDEFINED) {
+        return INVALID_OPERATION;
+    }
+
+    size_t trackIndex = mTrackInfos.size();
+
+    TrackInfo info;
+
+    sp<AMessage> notify = new AMessage(kWhatReceiverNotify, id());
+    notify->setInt32("generation", mGeneration);
+    notify->setSize("trackIndex", trackIndex);
+
+    info.mReceiver = new RTPReceiver(mNetSession, notify);
+    looper()->registerHandler(info.mReceiver);
+
+    info.mReceiver->registerPacketType(
+            33, RTPReceiver::PACKETIZATION_TRANSPORT_STREAM);
+
+    info.mReceiver->registerPacketType(
+            96, RTPReceiver::PACKETIZATION_AAC);
+
+    info.mReceiver->registerPacketType(
+            97, RTPReceiver::PACKETIZATION_H264);
+
+    status_t err = info.mReceiver->initAsync(transportMode, localRTPPort);
+
+    if (err != OK) {
+        looper()->unregisterHandler(info.mReceiver->id());
+        info.mReceiver.clear();
+
+        return err;
+    }
+
+    mTrackInfos.push_back(info);
+
+    return trackIndex;
+}
+
+status_t MediaReceiver::connectTrack(
+        size_t trackIndex,
+        const char *remoteHost,
+        int32_t remoteRTPPort,
+        int32_t remoteRTCPPort) {
+    if (trackIndex >= mTrackInfos.size()) {
+        return -ERANGE;
+    }
+
+    TrackInfo *info = &mTrackInfos.editItemAt(trackIndex);
+    return info->mReceiver->connect(remoteHost, remoteRTPPort, remoteRTCPPort);
+}
+
+status_t MediaReceiver::initAsync(Mode mode) {
+    if ((mode == MODE_TRANSPORT_STREAM || mode == MODE_TRANSPORT_STREAM_RAW)
+            && mTrackInfos.size() > 1) {
+        return INVALID_OPERATION;
+    }
+
+    sp<AMessage> msg = new AMessage(kWhatInit, id());
+    msg->setInt32("mode", mode);
+    msg->post();
+
+    return OK;
+}
+
+void MediaReceiver::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatInit:
+        {
+            int32_t mode;
+            CHECK(msg->findInt32("mode", &mode));
+
+            CHECK_EQ(mMode, MODE_UNDEFINED);
+            mMode = (Mode)mode;
+
+            if (mInitStatus != OK || mInitDoneCount == mTrackInfos.size()) {
+                notifyInitDone(mInitStatus);
+            }
+
+            mTSParser = new ATSParser(ATSParser::ALIGNED_VIDEO_DATA);
+            mFormatKnownMask = 0;
+            break;
+        }
+
+        case kWhatReceiverNotify:
+        {
+            int32_t generation;
+            CHECK(msg->findInt32("generation", &generation));
+            if (generation != mGeneration) {
+                break;
+            }
+
+            onReceiverNotify(msg);
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+void MediaReceiver::onReceiverNotify(const sp<AMessage> &msg) {
+    int32_t what;
+    CHECK(msg->findInt32("what", &what));
+
+    switch (what) {
+        case RTPReceiver::kWhatInitDone:
+        {
+            ++mInitDoneCount;
+
+            int32_t err;
+            CHECK(msg->findInt32("err", &err));
+
+            if (err != OK) {
+                mInitStatus = err;
+                ++mGeneration;
+            }
+
+            if (mMode != MODE_UNDEFINED) {
+                if (mInitStatus != OK || mInitDoneCount == mTrackInfos.size()) {
+                    notifyInitDone(mInitStatus);
+                }
+            }
+            break;
+        }
+
+        case RTPReceiver::kWhatError:
+        {
+            int32_t err;
+            CHECK(msg->findInt32("err", &err));
+
+            notifyError(err);
+            break;
+        }
+
+        case RTPReceiver::kWhatAccessUnit:
+        {
+            size_t trackIndex;
+            CHECK(msg->findSize("trackIndex", &trackIndex));
+
+            sp<ABuffer> accessUnit;
+            CHECK(msg->findBuffer("accessUnit", &accessUnit));
+
+            int32_t followsDiscontinuity;
+            if (!msg->findInt32(
+                        "followsDiscontinuity", &followsDiscontinuity)) {
+                followsDiscontinuity = 0;
+            }
+
+            if (mMode == MODE_TRANSPORT_STREAM) {
+                if (followsDiscontinuity) {
+                    mTSParser->signalDiscontinuity(
+                            ATSParser::DISCONTINUITY_TIME, NULL /* extra */);
+                }
+
+                for (size_t offset = 0;
+                        offset < accessUnit->size(); offset += 188) {
+                    status_t err = mTSParser->feedTSPacket(
+                             accessUnit->data() + offset, 188);
+
+                    if (err != OK) {
+                        notifyError(err);
+                        break;
+                    }
+                }
+
+                drainPackets(0 /* trackIndex */, ATSParser::VIDEO);
+                drainPackets(1 /* trackIndex */, ATSParser::AUDIO);
+            } else {
+                postAccessUnit(trackIndex, accessUnit, NULL);
+            }
+            break;
+        }
+
+        case RTPReceiver::kWhatPacketLost:
+        {
+            notifyPacketLost();
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+void MediaReceiver::drainPackets(
+        size_t trackIndex, ATSParser::SourceType type) {
+    sp<AnotherPacketSource> source =
+        static_cast<AnotherPacketSource *>(
+                mTSParser->getSource(type).get());
+
+    if (source == NULL) {
+        return;
+    }
+
+    sp<AMessage> format;
+    if (!(mFormatKnownMask & (1ul << trackIndex))) {
+        sp<MetaData> meta = source->getFormat();
+        CHECK(meta != NULL);
+
+        CHECK_EQ((status_t)OK, convertMetaDataToMessage(meta, &format));
+
+        mFormatKnownMask |= 1ul << trackIndex;
+    }
+
+    status_t finalResult;
+    while (source->hasBufferAvailable(&finalResult)) {
+        sp<ABuffer> accessUnit;
+        status_t err = source->dequeueAccessUnit(&accessUnit);
+        if (err == OK) {
+            postAccessUnit(trackIndex, accessUnit, format);
+            format.clear();
+        } else if (err != INFO_DISCONTINUITY) {
+            notifyError(err);
+        }
+    }
+
+    if (finalResult != OK) {
+        notifyError(finalResult);
+    }
+}
+
+void MediaReceiver::notifyInitDone(status_t err) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatInitDone);
+    notify->setInt32("err", err);
+    notify->post();
+}
+
+void MediaReceiver::notifyError(status_t err) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatError);
+    notify->setInt32("err", err);
+    notify->post();
+}
+
+void MediaReceiver::notifyPacketLost() {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatPacketLost);
+    notify->post();
+}
+
+void MediaReceiver::postAccessUnit(
+        size_t trackIndex,
+        const sp<ABuffer> &accessUnit,
+        const sp<AMessage> &format) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatAccessUnit);
+    notify->setSize("trackIndex", trackIndex);
+    notify->setBuffer("accessUnit", accessUnit);
+
+    if (format != NULL) {
+        notify->setMessage("format", format);
+    }
+
+    notify->post();
+}
+
+}  // namespace android
+
+
diff --git a/media/libstagefright/wifi-display/MediaReceiver.h b/media/libstagefright/wifi-display/MediaReceiver.h
new file mode 100644
index 0000000..7adc3c4
--- /dev/null
+++ b/media/libstagefright/wifi-display/MediaReceiver.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright 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.
+ */
+
+#include <media/stagefright/foundation/AHandler.h>
+
+#include "ATSParser.h"
+#include "rtp/RTPReceiver.h"
+
+namespace android {
+
+struct ABuffer;
+struct ANetworkSession;
+struct AMessage;
+struct ATSParser;
+
+// This class facilitates receiving of media data for one or more tracks
+// over RTP. Either a 1:1 track to RTP channel mapping is used or a single
+// RTP channel provides the data for a transport stream that is consequently
+// demuxed and its track's data provided to the observer.
+struct MediaReceiver : public AHandler {
+    enum {
+        kWhatInitDone,
+        kWhatError,
+        kWhatAccessUnit,
+        kWhatPacketLost,
+    };
+
+    MediaReceiver(
+            const sp<ANetworkSession> &netSession,
+            const sp<AMessage> &notify);
+
+    ssize_t addTrack(
+            RTPReceiver::TransportMode transportMode,
+            int32_t *localRTPPort);
+
+    status_t connectTrack(
+            size_t trackIndex,
+            const char *remoteHost,
+            int32_t remoteRTPPort,
+            int32_t remoteRTCPPort);
+
+    enum Mode {
+        MODE_UNDEFINED,
+        MODE_TRANSPORT_STREAM,
+        MODE_TRANSPORT_STREAM_RAW,
+        MODE_ELEMENTARY_STREAMS,
+    };
+    status_t initAsync(Mode mode);
+
+protected:
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+    virtual ~MediaReceiver();
+
+private:
+    enum {
+        kWhatInit,
+        kWhatReceiverNotify,
+    };
+
+    struct TrackInfo {
+        sp<RTPReceiver> mReceiver;
+    };
+
+    sp<ANetworkSession> mNetSession;
+    sp<AMessage> mNotify;
+
+    Mode mMode;
+    int32_t mGeneration;
+
+    Vector<TrackInfo> mTrackInfos;
+
+    status_t mInitStatus;
+    size_t mInitDoneCount;
+
+    sp<ATSParser> mTSParser;
+    uint32_t mFormatKnownMask;
+
+    void onReceiverNotify(const sp<AMessage> &msg);
+
+    void drainPackets(size_t trackIndex, ATSParser::SourceType type);
+
+    void notifyInitDone(status_t err);
+    void notifyError(status_t err);
+    void notifyPacketLost();
+
+    void postAccessUnit(
+            size_t trackIndex,
+            const sp<ABuffer> &accessUnit,
+            const sp<AMessage> &format);
+
+    DISALLOW_EVIL_CONSTRUCTORS(MediaReceiver);
+};
+
+}  // namespace android
+
diff --git a/media/libstagefright/wifi-display/MediaSender.cpp b/media/libstagefright/wifi-display/MediaSender.cpp
new file mode 100644
index 0000000..900aa82
--- /dev/null
+++ b/media/libstagefright/wifi-display/MediaSender.cpp
@@ -0,0 +1,443 @@
+/*
+ * Copyright 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 "MediaSender"
+#include <utils/Log.h>
+
+#include "MediaSender.h"
+
+#include "ANetworkSession.h"
+#include "rtp/RTPSender.h"
+#include "source/TSPacketizer.h"
+
+#include "include/avc_utils.h"
+
+#include <media/IHDCP.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+namespace android {
+
+MediaSender::MediaSender(
+        const sp<ANetworkSession> &netSession,
+        const sp<AMessage> &notify)
+    : mNetSession(netSession),
+      mNotify(notify),
+      mMode(MODE_UNDEFINED),
+      mGeneration(0),
+      mPrevTimeUs(-1ll),
+      mInitDoneCount(0) {
+}
+
+MediaSender::~MediaSender() {
+}
+
+status_t MediaSender::setHDCP(const sp<IHDCP> &hdcp) {
+    if (mMode != MODE_UNDEFINED) {
+        return INVALID_OPERATION;
+    }
+
+    mHDCP = hdcp;
+
+    return OK;
+}
+
+ssize_t MediaSender::addTrack(const sp<AMessage> &format, uint32_t flags) {
+    if (mMode != MODE_UNDEFINED) {
+        return INVALID_OPERATION;
+    }
+
+    TrackInfo info;
+    info.mFormat = format;
+    info.mFlags = flags;
+    info.mPacketizerTrackIndex = -1;
+
+    AString mime;
+    CHECK(format->findString("mime", &mime));
+    info.mIsAudio = !strncasecmp("audio/", mime.c_str(), 6);
+
+    size_t index = mTrackInfos.size();
+    mTrackInfos.push_back(info);
+
+    return index;
+}
+
+status_t MediaSender::initAsync(
+        ssize_t trackIndex,
+        RTPSender::TransportMode transportMode,
+        const char *remoteHost,
+        int32_t remoteRTPPort,
+        int32_t remoteRTCPPort,
+        int32_t *localRTPPort) {
+    if (trackIndex < 0) {
+        if (mMode != MODE_UNDEFINED) {
+            return INVALID_OPERATION;
+        }
+
+        mTSPacketizer = new TSPacketizer;
+
+        status_t err = OK;
+        for (size_t i = 0; i < mTrackInfos.size(); ++i) {
+            TrackInfo *info = &mTrackInfos.editItemAt(i);
+
+            sp<AMessage> trackFormat = info->mFormat;
+            if (mHDCP != NULL && !info->mIsAudio) {
+                // HDCP2.0 _and_ HDCP 2.1 specs say to set the version
+                // inside the HDCP descriptor to 0x20!!!
+                trackFormat->setInt32("hdcp-version", 0x20);
+            }
+
+            ssize_t packetizerTrackIndex =
+                mTSPacketizer->addTrack(trackFormat);
+
+            if (packetizerTrackIndex < 0) {
+                err = packetizerTrackIndex;
+                break;
+            }
+
+            info->mPacketizerTrackIndex = packetizerTrackIndex;
+        }
+
+        if (err == OK) {
+            sp<AMessage> notify = new AMessage(kWhatSenderNotify, id());
+            notify->setInt32("generation", mGeneration);
+            mTSSender = new RTPSender(mNetSession, notify);
+            looper()->registerHandler(mTSSender);
+
+            err = mTSSender->initAsync(
+                    transportMode,
+                    remoteHost,
+                    remoteRTPPort,
+                    remoteRTCPPort,
+                    localRTPPort);
+
+            if (err != OK) {
+                looper()->unregisterHandler(mTSSender->id());
+                mTSSender.clear();
+            }
+        }
+
+        if (err != OK) {
+            for (size_t i = 0; i < mTrackInfos.size(); ++i) {
+                TrackInfo *info = &mTrackInfos.editItemAt(i);
+                info->mPacketizerTrackIndex = -1;
+            }
+
+            mTSPacketizer.clear();
+            return err;
+        }
+
+        mMode = MODE_TRANSPORT_STREAM;
+        mInitDoneCount = 1;
+
+        return OK;
+    }
+
+    if (mMode == MODE_TRANSPORT_STREAM) {
+        return INVALID_OPERATION;
+    }
+
+    if ((size_t)trackIndex >= mTrackInfos.size()) {
+        return -ERANGE;
+    }
+
+    TrackInfo *info = &mTrackInfos.editItemAt(trackIndex);
+
+    if (info->mSender != NULL) {
+        return INVALID_OPERATION;
+    }
+
+    sp<AMessage> notify = new AMessage(kWhatSenderNotify, id());
+    notify->setInt32("generation", mGeneration);
+    notify->setSize("trackIndex", trackIndex);
+
+    info->mSender = new RTPSender(mNetSession, notify);
+    looper()->registerHandler(info->mSender);
+
+    status_t err = info->mSender->initAsync(
+            transportMode,
+            remoteHost,
+            remoteRTPPort,
+            remoteRTCPPort,
+            localRTPPort);
+
+    if (err != OK) {
+        looper()->unregisterHandler(info->mSender->id());
+        info->mSender.clear();
+
+        return err;
+    }
+
+    if (mMode == MODE_UNDEFINED) {
+        mInitDoneCount = mTrackInfos.size();
+    }
+
+    mMode = MODE_ELEMENTARY_STREAMS;
+
+    return OK;
+}
+
+status_t MediaSender::queueAccessUnit(
+        size_t trackIndex, const sp<ABuffer> &accessUnit) {
+    if (mMode == MODE_UNDEFINED) {
+        return INVALID_OPERATION;
+    }
+
+    if (trackIndex >= mTrackInfos.size()) {
+        return -ERANGE;
+    }
+
+    if (mMode == MODE_TRANSPORT_STREAM) {
+        TrackInfo *info = &mTrackInfos.editItemAt(trackIndex);
+        info->mAccessUnits.push_back(accessUnit);
+
+        mTSPacketizer->extractCSDIfNecessary(info->mPacketizerTrackIndex);
+
+        for (;;) {
+            ssize_t minTrackIndex = -1;
+            int64_t minTimeUs = -1ll;
+
+            for (size_t i = 0; i < mTrackInfos.size(); ++i) {
+                const TrackInfo &info = mTrackInfos.itemAt(i);
+
+                if (info.mAccessUnits.empty()) {
+                    minTrackIndex = -1;
+                    minTimeUs = -1ll;
+                    break;
+                }
+
+                int64_t timeUs;
+                const sp<ABuffer> &accessUnit = *info.mAccessUnits.begin();
+                CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
+
+                if (minTrackIndex < 0 || timeUs < minTimeUs) {
+                    minTrackIndex = i;
+                    minTimeUs = timeUs;
+                }
+            }
+
+            if (minTrackIndex < 0) {
+                return OK;
+            }
+
+            TrackInfo *info = &mTrackInfos.editItemAt(minTrackIndex);
+            sp<ABuffer> accessUnit = *info->mAccessUnits.begin();
+            info->mAccessUnits.erase(info->mAccessUnits.begin());
+
+            sp<ABuffer> tsPackets;
+            status_t err = packetizeAccessUnit(
+                    minTrackIndex, accessUnit, &tsPackets);
+
+            if (err == OK) {
+                err = mTSSender->queueBuffer(
+                        tsPackets,
+                        33 /* packetType */,
+                        RTPSender::PACKETIZATION_TRANSPORT_STREAM);
+            }
+
+            if (err != OK) {
+                return err;
+            }
+        }
+    }
+
+    TrackInfo *info = &mTrackInfos.editItemAt(trackIndex);
+
+    return info->mSender->queueBuffer(
+            accessUnit,
+            info->mIsAudio ? 96 : 97 /* packetType */,
+            info->mIsAudio
+                ? RTPSender::PACKETIZATION_AAC : RTPSender::PACKETIZATION_H264);
+}
+
+void MediaSender::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatSenderNotify:
+        {
+            int32_t generation;
+            CHECK(msg->findInt32("generation", &generation));
+            if (generation != mGeneration) {
+                break;
+            }
+
+            onSenderNotify(msg);
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+void MediaSender::onSenderNotify(const sp<AMessage> &msg) {
+    int32_t what;
+    CHECK(msg->findInt32("what", &what));
+
+    switch (what) {
+        case RTPSender::kWhatInitDone:
+        {
+            --mInitDoneCount;
+
+            int32_t err;
+            CHECK(msg->findInt32("err", &err));
+
+            if (err != OK) {
+                notifyInitDone(err);
+                ++mGeneration;
+                break;
+            }
+
+            if (mInitDoneCount == 0) {
+                notifyInitDone(OK);
+            }
+            break;
+        }
+
+        case RTPSender::kWhatError:
+        {
+            int32_t err;
+            CHECK(msg->findInt32("err", &err));
+
+            notifyError(err);
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+void MediaSender::notifyInitDone(status_t err) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatInitDone);
+    notify->setInt32("err", err);
+    notify->post();
+}
+
+void MediaSender::notifyError(status_t err) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatError);
+    notify->setInt32("err", err);
+    notify->post();
+}
+
+status_t MediaSender::packetizeAccessUnit(
+        size_t trackIndex,
+        sp<ABuffer> accessUnit,
+        sp<ABuffer> *tsPackets) {
+    const TrackInfo &info = mTrackInfos.itemAt(trackIndex);
+
+    uint32_t flags = 0;
+
+    bool isHDCPEncrypted = false;
+    uint64_t inputCTR;
+    uint8_t HDCP_private_data[16];
+
+    bool manuallyPrependSPSPPS =
+        !info.mIsAudio
+        && (info.mFlags & FLAG_MANUALLY_PREPEND_SPS_PPS)
+        && IsIDR(accessUnit);
+
+    if (mHDCP != NULL && !info.mIsAudio) {
+        isHDCPEncrypted = true;
+
+        if (manuallyPrependSPSPPS) {
+            accessUnit = mTSPacketizer->prependCSD(
+                    info.mPacketizerTrackIndex, accessUnit);
+        }
+
+        status_t err = mHDCP->encrypt(
+                accessUnit->data(), accessUnit->size(),
+                trackIndex  /* streamCTR */,
+                &inputCTR,
+                accessUnit->data());
+
+        if (err != OK) {
+            ALOGE("Failed to HDCP-encrypt media data (err %d)",
+                  err);
+
+            return err;
+        }
+
+        HDCP_private_data[0] = 0x00;
+
+        HDCP_private_data[1] =
+            (((trackIndex >> 30) & 3) << 1) | 1;
+
+        HDCP_private_data[2] = (trackIndex >> 22) & 0xff;
+
+        HDCP_private_data[3] =
+            (((trackIndex >> 15) & 0x7f) << 1) | 1;
+
+        HDCP_private_data[4] = (trackIndex >> 7) & 0xff;
+
+        HDCP_private_data[5] =
+            ((trackIndex & 0x7f) << 1) | 1;
+
+        HDCP_private_data[6] = 0x00;
+
+        HDCP_private_data[7] =
+            (((inputCTR >> 60) & 0x0f) << 1) | 1;
+
+        HDCP_private_data[8] = (inputCTR >> 52) & 0xff;
+
+        HDCP_private_data[9] =
+            (((inputCTR >> 45) & 0x7f) << 1) | 1;
+
+        HDCP_private_data[10] = (inputCTR >> 37) & 0xff;
+
+        HDCP_private_data[11] =
+            (((inputCTR >> 30) & 0x7f) << 1) | 1;
+
+        HDCP_private_data[12] = (inputCTR >> 22) & 0xff;
+
+        HDCP_private_data[13] =
+            (((inputCTR >> 15) & 0x7f) << 1) | 1;
+
+        HDCP_private_data[14] = (inputCTR >> 7) & 0xff;
+
+        HDCP_private_data[15] =
+            ((inputCTR & 0x7f) << 1) | 1;
+
+        flags |= TSPacketizer::IS_ENCRYPTED;
+    } else if (manuallyPrependSPSPPS) {
+        flags |= TSPacketizer::PREPEND_SPS_PPS_TO_IDR_FRAMES;
+    }
+
+    int64_t timeUs = ALooper::GetNowUs();
+    if (mPrevTimeUs < 0ll || mPrevTimeUs + 100000ll <= timeUs) {
+        flags |= TSPacketizer::EMIT_PCR;
+        flags |= TSPacketizer::EMIT_PAT_AND_PMT;
+
+        mPrevTimeUs = timeUs;
+    }
+
+    mTSPacketizer->packetize(
+            info.mPacketizerTrackIndex,
+            accessUnit,
+            tsPackets,
+            flags,
+            !isHDCPEncrypted ? NULL : HDCP_private_data,
+            !isHDCPEncrypted ? 0 : sizeof(HDCP_private_data),
+            info.mIsAudio ? 2 : 0 /* numStuffingBytes */);
+
+    return OK;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/wifi-display/MediaSender.h b/media/libstagefright/wifi-display/MediaSender.h
new file mode 100644
index 0000000..834780a
--- /dev/null
+++ b/media/libstagefright/wifi-display/MediaSender.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright 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 MEDIA_SENDER_H_
+
+#define MEDIA_SENDER_H_
+
+#include "rtp/RTPSender.h"
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AHandler.h>
+#include <utils/Errors.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+struct ABuffer;
+struct ANetworkSession;
+struct AMessage;
+struct IHDCP;
+struct TSPacketizer;
+
+// This class facilitates sending of data from one or more media tracks
+// through one or more RTP channels, either providing a 1:1 mapping from
+// track to RTP channel or muxing all tracks into a single RTP channel and
+// using transport stream encapsulation.
+// Optionally the (video) data is encrypted using the provided hdcp object.
+struct MediaSender : public AHandler {
+    enum {
+        kWhatInitDone,
+        kWhatError,
+    };
+
+    MediaSender(
+            const sp<ANetworkSession> &netSession,
+            const sp<AMessage> &notify);
+
+    status_t setHDCP(const sp<IHDCP> &hdcp);
+
+    enum FlagBits {
+        FLAG_MANUALLY_PREPEND_SPS_PPS = 1,
+    };
+    ssize_t addTrack(const sp<AMessage> &format, uint32_t flags);
+
+    // If trackIndex == -1, initialize for transport stream muxing.
+    status_t initAsync(
+            ssize_t trackIndex,
+            RTPSender::TransportMode transportMode,
+            const char *remoteHost,
+            int32_t remoteRTPPort,
+            int32_t remoteRTCPPort,
+            int32_t *localRTPPort);
+
+    status_t queueAccessUnit(
+            size_t trackIndex, const sp<ABuffer> &accessUnit);
+
+protected:
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+    virtual ~MediaSender();
+
+private:
+    enum {
+        kWhatSenderNotify,
+    };
+
+    enum Mode {
+        MODE_UNDEFINED,
+        MODE_TRANSPORT_STREAM,
+        MODE_ELEMENTARY_STREAMS,
+    };
+
+    struct TrackInfo {
+        sp<AMessage> mFormat;
+        uint32_t mFlags;
+        sp<RTPSender> mSender;
+        List<sp<ABuffer> > mAccessUnits;
+        ssize_t mPacketizerTrackIndex;
+        bool mIsAudio;
+    };
+
+    sp<ANetworkSession> mNetSession;
+    sp<AMessage> mNotify;
+
+    sp<IHDCP> mHDCP;
+
+    Mode mMode;
+    int32_t mGeneration;
+
+    Vector<TrackInfo> mTrackInfos;
+
+    sp<TSPacketizer> mTSPacketizer;
+    sp<RTPSender> mTSSender;
+    int64_t mPrevTimeUs;
+
+    size_t mInitDoneCount;
+
+    void onSenderNotify(const sp<AMessage> &msg);
+
+    void notifyInitDone(status_t err);
+    void notifyError(status_t err);
+
+    status_t packetizeAccessUnit(
+            size_t trackIndex,
+            sp<ABuffer> accessUnit,
+            sp<ABuffer> *tsPackets);
+
+    DISALLOW_EVIL_CONSTRUCTORS(MediaSender);
+};
+
+}  // namespace android
+
+#endif  // MEDIA_SENDER_H_
+
diff --git a/media/libstagefright/wifi-display/SNTPClient.cpp b/media/libstagefright/wifi-display/SNTPClient.cpp
new file mode 100644
index 0000000..5c0af6a
--- /dev/null
+++ b/media/libstagefright/wifi-display/SNTPClient.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright 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.
+ */
+
+#include "SNTPClient.h"
+
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/Utils.h>
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+namespace android {
+
+SNTPClient::SNTPClient() {
+}
+
+status_t SNTPClient::requestTime(const char *host) {
+    struct hostent *ent;
+    int64_t requestTimeNTP, requestTimeUs;
+    ssize_t n;
+    int64_t responseTimeUs, responseTimeNTP;
+    int64_t originateTimeNTP, receiveTimeNTP, transmitTimeNTP;
+    int64_t roundTripTimeNTP, clockOffsetNTP;
+
+    status_t err = UNKNOWN_ERROR;
+
+    int s = socket(AF_INET, SOCK_DGRAM, 0);
+
+    if (s < 0) {
+        err = -errno;
+
+        goto bail;
+    }
+
+    ent = gethostbyname(host);
+
+    if (ent == NULL) {
+        err = -ENOENT;
+        goto bail2;
+    }
+
+    struct sockaddr_in hostAddr;
+    memset(hostAddr.sin_zero, 0, sizeof(hostAddr.sin_zero));
+    hostAddr.sin_family = AF_INET;
+    hostAddr.sin_port = htons(kNTPPort);
+    hostAddr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
+
+    uint8_t packet[kNTPPacketSize];
+    memset(packet, 0, sizeof(packet));
+
+    packet[0] = kNTPModeClient | (kNTPVersion << 3);
+
+    requestTimeNTP = getNowNTP();
+    requestTimeUs = ALooper::GetNowUs();
+    writeTimeStamp(&packet[kNTPTransmitTimeOffset], requestTimeNTP);
+
+    n = sendto(
+            s, packet, sizeof(packet), 0,
+            (const struct sockaddr *)&hostAddr, sizeof(hostAddr));
+
+    if (n < 0) {
+        err = -errno;
+        goto bail2;
+    }
+
+    memset(packet, 0, sizeof(packet));
+
+    do {
+        n = recv(s, packet, sizeof(packet), 0);
+    } while (n < 0 && errno == EINTR);
+
+    if (n < 0) {
+        err = -errno;
+        goto bail2;
+    }
+
+    responseTimeUs = ALooper::GetNowUs();
+
+    responseTimeNTP = requestTimeNTP + makeNTP(responseTimeUs - requestTimeUs);
+
+    originateTimeNTP = readTimeStamp(&packet[kNTPOriginateTimeOffset]);
+    receiveTimeNTP = readTimeStamp(&packet[kNTPReceiveTimeOffset]);
+    transmitTimeNTP = readTimeStamp(&packet[kNTPTransmitTimeOffset]);
+
+    roundTripTimeNTP =
+        makeNTP(responseTimeUs - requestTimeUs)
+            - (transmitTimeNTP - receiveTimeNTP);
+
+    clockOffsetNTP =
+        ((receiveTimeNTP - originateTimeNTP)
+            + (transmitTimeNTP - responseTimeNTP)) / 2;
+
+    mTimeReferenceNTP = responseTimeNTP + clockOffsetNTP;
+    mTimeReferenceUs = responseTimeUs;
+    mRoundTripTimeNTP = roundTripTimeNTP;
+
+    err = OK;
+
+bail2:
+    close(s);
+    s = -1;
+
+bail:
+    return err;
+}
+
+int64_t SNTPClient::adjustTimeUs(int64_t timeUs) const {
+    uint64_t nowNTP =
+        mTimeReferenceNTP + makeNTP(timeUs - mTimeReferenceUs);
+
+    int64_t nowUs =
+        (nowNTP >> 32) * 1000000ll
+        + ((nowNTP & 0xffffffff) * 1000000ll) / (1ll << 32);
+
+    nowUs -= ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll;
+
+    return nowUs;
+}
+
+// static
+void SNTPClient::writeTimeStamp(uint8_t *dst, uint64_t ntpTime) {
+    *dst++ = (ntpTime >> 56) & 0xff;
+    *dst++ = (ntpTime >> 48) & 0xff;
+    *dst++ = (ntpTime >> 40) & 0xff;
+    *dst++ = (ntpTime >> 32) & 0xff;
+    *dst++ = (ntpTime >> 24) & 0xff;
+    *dst++ = (ntpTime >> 16) & 0xff;
+    *dst++ = (ntpTime >> 8) & 0xff;
+    *dst++ = ntpTime & 0xff;
+}
+
+// static
+uint64_t SNTPClient::readTimeStamp(const uint8_t *dst) {
+    return U64_AT(dst);
+}
+
+// static
+uint64_t SNTPClient::getNowNTP() {
+    struct timeval tv;
+    gettimeofday(&tv, NULL /* time zone */);
+
+    uint64_t nowUs = tv.tv_sec * 1000000ll + tv.tv_usec;
+
+    nowUs += ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll;
+
+    return makeNTP(nowUs);
+}
+
+// static
+uint64_t SNTPClient::makeNTP(uint64_t deltaUs) {
+    uint64_t hi = deltaUs / 1000000ll;
+    uint64_t lo = ((1ll << 32) * (deltaUs % 1000000ll)) / 1000000ll;
+
+    return (hi << 32) | lo;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/wifi-display/SNTPClient.h b/media/libstagefright/wifi-display/SNTPClient.h
new file mode 100644
index 0000000..967d1fc
--- /dev/null
+++ b/media/libstagefright/wifi-display/SNTPClient.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 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 SNTP_CLIENT_H_
+
+#define SNTP_CLIENT_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+// Implementation of the SNTP (Simple Network Time Protocol)
+struct SNTPClient {
+    SNTPClient();
+
+    status_t requestTime(const char *host);
+
+    // given a time obtained from ALooper::GetNowUs()
+    // return the number of us elapsed since Jan 1 1970 00:00:00 (UTC).
+    int64_t adjustTimeUs(int64_t timeUs) const;
+
+private:
+    enum {
+        kNTPPort = 123,
+        kNTPPacketSize = 48,
+        kNTPModeClient = 3,
+        kNTPVersion = 3,
+        kNTPTransmitTimeOffset = 40,
+        kNTPOriginateTimeOffset = 24,
+        kNTPReceiveTimeOffset = 32,
+    };
+
+    uint64_t mTimeReferenceNTP;
+    int64_t mTimeReferenceUs;
+    int64_t mRoundTripTimeNTP;
+
+    static void writeTimeStamp(uint8_t *dst, uint64_t ntpTime);
+    static uint64_t readTimeStamp(const uint8_t *dst);
+
+    static uint64_t getNowNTP();
+    static uint64_t makeNTP(uint64_t deltaUs);
+
+    DISALLOW_EVIL_CONSTRUCTORS(SNTPClient);
+};
+
+}  // namespace android
+
+#endif  // SNTP_CLIENT_H_
diff --git a/media/libstagefright/wifi-display/TimeSeries.cpp b/media/libstagefright/wifi-display/TimeSeries.cpp
deleted file mode 100644
index d882d98..0000000
--- a/media/libstagefright/wifi-display/TimeSeries.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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 "TimeSeries.h"
-
-#include <math.h>
-#include <string.h>
-
-namespace android {
-
-TimeSeries::TimeSeries()
-    : mCount(0),
-      mSum(0.0) {
-}
-
-void TimeSeries::add(double val) {
-    if (mCount < kHistorySize) {
-        mValues[mCount++] = val;
-        mSum += val;
-    } else {
-        mSum -= mValues[0];
-        memmove(&mValues[0], &mValues[1], (kHistorySize - 1) * sizeof(double));
-        mValues[kHistorySize - 1] = val;
-        mSum += val;
-    }
-}
-
-double TimeSeries::mean() const {
-    if (mCount < 1) {
-        return 0.0;
-    }
-
-    return mSum / mCount;
-}
-
-double TimeSeries::sdev() const {
-    if (mCount < 1) {
-        return 0.0;
-    }
-
-    double m = mean();
-
-    double sum = 0.0;
-    for (size_t i = 0; i < mCount; ++i) {
-        double tmp = mValues[i] - m;
-        tmp *= tmp;
-
-        sum += tmp;
-    }
-
-    return sqrt(sum / mCount);
-}
-
-}  // namespace android
diff --git a/media/libstagefright/wifi-display/TimeSeries.h b/media/libstagefright/wifi-display/TimeSeries.h
deleted file mode 100644
index c818d51..0000000
--- a/media/libstagefright/wifi-display/TimeSeries.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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 TIME_SERIES_H_
-
-#define TIME_SERIES_H_
-
-#include <sys/types.h>
-
-namespace android {
-
-struct TimeSeries {
-    TimeSeries();
-
-    void add(double val);
-
-    double mean() const;
-    double sdev() const;
-
-private:
-    enum {
-        kHistorySize = 20
-    };
-    double mValues[kHistorySize];
-
-    size_t mCount;
-    double mSum;
-};
-
-}  // namespace android
-
-#endif  // TIME_SERIES_H_
-
diff --git a/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp b/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp
new file mode 100644
index 0000000..d0ab60d
--- /dev/null
+++ b/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp
@@ -0,0 +1,324 @@
+/*
+ * Copyright 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 "RTPAssembler"
+#include <utils/Log.h>
+
+#include "RTPAssembler.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/MediaErrors.h>
+
+namespace android {
+
+RTPReceiver::Assembler::Assembler(const sp<AMessage> &notify)
+    : mNotify(notify) {
+}
+
+void RTPReceiver::Assembler::postAccessUnit(
+        const sp<ABuffer> &accessUnit, bool followsDiscontinuity) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", RTPReceiver::kWhatAccessUnit);
+    notify->setBuffer("accessUnit", accessUnit);
+    notify->setInt32("followsDiscontinuity", followsDiscontinuity);
+    notify->post();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+RTPReceiver::TSAssembler::TSAssembler(const sp<AMessage> &notify)
+    : Assembler(notify),
+      mSawDiscontinuity(false) {
+}
+
+void RTPReceiver::TSAssembler::signalDiscontinuity() {
+    mSawDiscontinuity = true;
+}
+
+status_t RTPReceiver::TSAssembler::processPacket(const sp<ABuffer> &packet) {
+    postAccessUnit(packet, mSawDiscontinuity);
+
+    if (mSawDiscontinuity) {
+        mSawDiscontinuity = false;
+    }
+
+    return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+RTPReceiver::H264Assembler::H264Assembler(const sp<AMessage> &notify)
+    : Assembler(notify),
+      mState(0),
+      mIndicator(0),
+      mNALType(0),
+      mAccessUnitRTPTime(0) {
+}
+
+void RTPReceiver::H264Assembler::signalDiscontinuity() {
+    reset();
+}
+
+status_t RTPReceiver::H264Assembler::processPacket(const sp<ABuffer> &packet) {
+    status_t err = internalProcessPacket(packet);
+
+    if (err != OK) {
+        reset();
+    }
+
+    return err;
+}
+
+status_t RTPReceiver::H264Assembler::internalProcessPacket(
+        const sp<ABuffer> &packet) {
+    const uint8_t *data = packet->data();
+    size_t size = packet->size();
+
+    switch (mState) {
+        case 0:
+        {
+            if (size < 1 || (data[0] & 0x80)) {
+                ALOGV("Malformed H264 RTP packet (empty or F-bit set)");
+                return ERROR_MALFORMED;
+            }
+
+            unsigned nalType = data[0] & 0x1f;
+            if (nalType >= 1 && nalType <= 23) {
+                addSingleNALUnit(packet);
+                ALOGV("added single NAL packet");
+            } else if (nalType == 28) {
+                // FU-A
+                unsigned indicator = data[0];
+                CHECK((indicator & 0x1f) == 28);
+
+                if (size < 2) {
+                    ALOGV("Malformed H264 FU-A packet (single byte)");
+                    return ERROR_MALFORMED;
+                }
+
+                if (!(data[1] & 0x80)) {
+                    ALOGV("Malformed H264 FU-A packet (no start bit)");
+                    return ERROR_MALFORMED;
+                }
+
+                mIndicator = data[0];
+                mNALType = data[1] & 0x1f;
+                uint32_t nri = (data[0] >> 5) & 3;
+
+                clearAccumulator();
+
+                uint8_t byte = mNALType | (nri << 5);
+                appendToAccumulator(&byte, 1);
+                appendToAccumulator(data + 2, size - 2);
+
+                int32_t rtpTime;
+                CHECK(packet->meta()->findInt32("rtp-time", &rtpTime));
+                mAccumulator->meta()->setInt32("rtp-time", rtpTime);
+
+                if (data[1] & 0x40) {
+                    // Huh? End bit also set on the first buffer.
+                    addSingleNALUnit(mAccumulator);
+                    clearAccumulator();
+
+                    ALOGV("added FU-A");
+                    break;
+                }
+
+                mState = 1;
+            } else if (nalType == 24) {
+                // STAP-A
+
+                status_t err = addSingleTimeAggregationPacket(packet);
+                if (err != OK) {
+                    return err;
+                }
+            } else {
+                ALOGV("Malformed H264 packet (unknown type %d)", nalType);
+                return ERROR_UNSUPPORTED;
+            }
+            break;
+        }
+
+        case 1:
+        {
+            if (size < 2
+                    || data[0] != mIndicator
+                    || (data[1] & 0x1f) != mNALType
+                    || (data[1] & 0x80)) {
+                ALOGV("Malformed H264 FU-A packet (indicator, "
+                      "type or start bit mismatch)");
+
+                return ERROR_MALFORMED;
+            }
+
+            appendToAccumulator(data + 2, size - 2);
+
+            if (data[1] & 0x40) {
+                addSingleNALUnit(mAccumulator);
+
+                clearAccumulator();
+                mState = 0;
+
+                ALOGV("added FU-A");
+            }
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+
+    int32_t marker;
+    CHECK(packet->meta()->findInt32("M", &marker));
+
+    if (marker) {
+        flushAccessUnit();
+    }
+
+    return OK;
+}
+
+void RTPReceiver::H264Assembler::reset() {
+    mNALUnits.clear();
+
+    clearAccumulator();
+    mState = 0;
+}
+
+void RTPReceiver::H264Assembler::clearAccumulator() {
+    if (mAccumulator != NULL) {
+        // XXX Too expensive.
+        mAccumulator.clear();
+    }
+}
+
+void RTPReceiver::H264Assembler::appendToAccumulator(
+        const void *data, size_t size) {
+    if (mAccumulator == NULL) {
+        mAccumulator = new ABuffer(size);
+        memcpy(mAccumulator->data(), data, size);
+        return;
+    }
+
+    if (mAccumulator->size() + size > mAccumulator->capacity()) {
+        sp<ABuffer> buf = new ABuffer(mAccumulator->size() + size);
+        memcpy(buf->data(), mAccumulator->data(), mAccumulator->size());
+        buf->setRange(0, mAccumulator->size());
+
+        int32_t rtpTime;
+        if (mAccumulator->meta()->findInt32("rtp-time", &rtpTime)) {
+            buf->meta()->setInt32("rtp-time", rtpTime);
+        }
+
+        mAccumulator = buf;
+    }
+
+    memcpy(mAccumulator->data() + mAccumulator->size(), data, size);
+    mAccumulator->setRange(0, mAccumulator->size() + size);
+}
+
+void RTPReceiver::H264Assembler::addSingleNALUnit(const sp<ABuffer> &packet) {
+    if (mNALUnits.empty()) {
+        int32_t rtpTime;
+        CHECK(packet->meta()->findInt32("rtp-time", &rtpTime));
+
+        mAccessUnitRTPTime = rtpTime;
+    }
+
+    mNALUnits.push_back(packet);
+}
+
+void RTPReceiver::H264Assembler::flushAccessUnit() {
+    if (mNALUnits.empty()) {
+        return;
+    }
+
+    size_t totalSize = 0;
+    for (List<sp<ABuffer> >::iterator it = mNALUnits.begin();
+            it != mNALUnits.end(); ++it) {
+        totalSize += 4 + (*it)->size();
+    }
+
+    sp<ABuffer> accessUnit = new ABuffer(totalSize);
+    size_t offset = 0;
+    for (List<sp<ABuffer> >::iterator it = mNALUnits.begin();
+            it != mNALUnits.end(); ++it) {
+        const sp<ABuffer> nalUnit = *it;
+
+        memcpy(accessUnit->data() + offset, "\x00\x00\x00\x01", 4);
+
+        memcpy(accessUnit->data() + offset + 4,
+               nalUnit->data(),
+               nalUnit->size());
+
+        offset += 4 + nalUnit->size();
+    }
+
+    mNALUnits.clear();
+
+    accessUnit->meta()->setInt64("timeUs", mAccessUnitRTPTime * 100ll / 9ll);
+    postAccessUnit(accessUnit, false /* followsDiscontinuity */);
+}
+
+status_t RTPReceiver::H264Assembler::addSingleTimeAggregationPacket(
+        const sp<ABuffer> &packet) {
+    const uint8_t *data = packet->data();
+    size_t size = packet->size();
+
+    if (size < 3) {
+        ALOGV("Malformed H264 STAP-A packet (too small)");
+        return ERROR_MALFORMED;
+    }
+
+    int32_t rtpTime;
+    CHECK(packet->meta()->findInt32("rtp-time", &rtpTime));
+
+    ++data;
+    --size;
+    while (size >= 2) {
+        size_t nalSize = (data[0] << 8) | data[1];
+
+        if (size < nalSize + 2) {
+            ALOGV("Malformed H264 STAP-A packet (incomplete NAL unit)");
+            return ERROR_MALFORMED;
+        }
+
+        sp<ABuffer> unit = new ABuffer(nalSize);
+        memcpy(unit->data(), &data[2], nalSize);
+
+        unit->meta()->setInt32("rtp-time", rtpTime);
+
+        addSingleNALUnit(unit);
+
+        data += 2 + nalSize;
+        size -= 2 + nalSize;
+    }
+
+    if (size != 0) {
+        ALOGV("Unexpected padding at end of STAP-A packet.");
+    }
+
+    ALOGV("added STAP-A");
+
+    return OK;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/wifi-display/rtp/RTPAssembler.h b/media/libstagefright/wifi-display/rtp/RTPAssembler.h
new file mode 100644
index 0000000..e456d32
--- /dev/null
+++ b/media/libstagefright/wifi-display/rtp/RTPAssembler.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright 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 RTP_ASSEMBLER_H_
+
+#define RTP_ASSEMBLER_H_
+
+#include "RTPReceiver.h"
+
+namespace android {
+
+// A helper class to reassemble the payload of RTP packets into access
+// units depending on the packetization scheme.
+struct RTPReceiver::Assembler : public RefBase {
+    Assembler(const sp<AMessage> &notify);
+
+    virtual void signalDiscontinuity() = 0;
+    virtual status_t processPacket(const sp<ABuffer> &packet) = 0;
+
+protected:
+    virtual ~Assembler() {}
+
+    void postAccessUnit(
+            const sp<ABuffer> &accessUnit, bool followsDiscontinuity);
+
+private:
+    sp<AMessage> mNotify;
+
+    DISALLOW_EVIL_CONSTRUCTORS(Assembler);
+};
+
+struct RTPReceiver::TSAssembler : public RTPReceiver::Assembler {
+    TSAssembler(const sp<AMessage> &notify);
+
+    virtual void signalDiscontinuity();
+    virtual status_t processPacket(const sp<ABuffer> &packet);
+
+private:
+    bool mSawDiscontinuity;
+
+    DISALLOW_EVIL_CONSTRUCTORS(TSAssembler);
+};
+
+struct RTPReceiver::H264Assembler : public RTPReceiver::Assembler {
+    H264Assembler(const sp<AMessage> &notify);
+
+    virtual void signalDiscontinuity();
+    virtual status_t processPacket(const sp<ABuffer> &packet);
+
+private:
+    int32_t mState;
+
+    uint8_t mIndicator;
+    uint8_t mNALType;
+
+    sp<ABuffer> mAccumulator;
+
+    List<sp<ABuffer> > mNALUnits;
+    int32_t mAccessUnitRTPTime;
+
+    status_t internalProcessPacket(const sp<ABuffer> &packet);
+
+    void addSingleNALUnit(const sp<ABuffer> &packet);
+    status_t addSingleTimeAggregationPacket(const sp<ABuffer> &packet);
+
+    void flushAccessUnit();
+
+    void clearAccumulator();
+    void appendToAccumulator(const void *data, size_t size);
+
+    void reset();
+
+    DISALLOW_EVIL_CONSTRUCTORS(H264Assembler);
+};
+
+}  // namespace android
+
+#endif  // RTP_ASSEMBLER_H_
+
diff --git a/media/libstagefright/wifi-display/rtp/RTPBase.h b/media/libstagefright/wifi-display/rtp/RTPBase.h
new file mode 100644
index 0000000..6507a6f
--- /dev/null
+++ b/media/libstagefright/wifi-display/rtp/RTPBase.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 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 RTP_BASE_H_
+
+#define RTP_BASE_H_
+
+namespace android {
+
+struct RTPBase {
+    enum PacketizationMode {
+        PACKETIZATION_TRANSPORT_STREAM,
+        PACKETIZATION_H264,
+        PACKETIZATION_AAC,
+    };
+
+    enum TransportMode {
+        TRANSPORT_UNDEFINED,
+        TRANSPORT_UDP,
+        TRANSPORT_TCP,
+        TRANSPORT_TCP_INTERLEAVED,
+    };
+
+    enum {
+        // Really UDP _payload_ size
+        kMaxUDPPacketSize = 1472,   // 1472 good, 1473 bad on Android@Home
+    };
+
+    static int32_t PickRandomRTPPort();
+};
+
+}  // namespace android
+
+#endif  // RTP_BASE_H_
+
+
diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp
new file mode 100644
index 0000000..29482af
--- /dev/null
+++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp
@@ -0,0 +1,899 @@
+/*
+ * Copyright 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 "RTPReceiver"
+#include <utils/Log.h>
+
+#include "RTPAssembler.h"
+#include "RTPReceiver.h"
+
+#include "ANetworkSession.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/MediaErrors.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct RTPReceiver::Source : public RefBase {
+    Source(RTPReceiver *receiver, uint32_t ssrc);
+
+    void onPacketReceived(uint16_t seq, const sp<ABuffer> &buffer);
+
+    void addReportBlock(uint32_t ssrc, const sp<ABuffer> &buf);
+
+protected:
+    virtual ~Source();
+
+private:
+    static const uint32_t kMinSequential = 2;
+    static const uint32_t kMaxDropout = 3000;
+    static const uint32_t kMaxMisorder = 100;
+    static const uint32_t kRTPSeqMod = 1u << 16;
+    static const int64_t kReportIntervalUs = 10000000ll;
+
+    RTPReceiver *mReceiver;
+    uint32_t mSSRC;
+    bool mFirst;
+    uint16_t mMaxSeq;
+    uint32_t mCycles;
+    uint32_t mBaseSeq;
+    uint32_t mReceived;
+    uint32_t mExpectedPrior;
+    uint32_t mReceivedPrior;
+
+    int64_t mFirstArrivalTimeUs;
+    int64_t mFirstRTPTimeUs;
+
+    // Ordered by extended seq number.
+    List<sp<ABuffer> > mPackets;
+
+    int32_t mAwaitingExtSeqNo;
+    bool mRequestedRetransmission;
+
+    int32_t mActivePacketType;
+    sp<Assembler> mActiveAssembler;
+
+    int64_t mNextReportTimeUs;
+
+    int32_t mNumDeclaredLost;
+    int32_t mNumDeclaredLostPrior;
+
+    void queuePacket(const sp<ABuffer> &packet);
+    void dequeueMore();
+
+    sp<ABuffer> getNextPacket();
+    void resync();
+
+    DISALLOW_EVIL_CONSTRUCTORS(Source);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+RTPReceiver::Source::Source(RTPReceiver *receiver, uint32_t ssrc)
+    : mReceiver(receiver),
+      mSSRC(ssrc),
+      mFirst(true),
+      mMaxSeq(0),
+      mCycles(0),
+      mBaseSeq(0),
+      mReceived(0),
+      mExpectedPrior(0),
+      mReceivedPrior(0),
+      mFirstArrivalTimeUs(-1ll),
+      mFirstRTPTimeUs(-1ll),
+      mAwaitingExtSeqNo(-1),
+      mRequestedRetransmission(false),
+      mActivePacketType(-1),
+      mNextReportTimeUs(-1ll),
+      mNumDeclaredLost(0),
+      mNumDeclaredLostPrior(0) {
+}
+
+RTPReceiver::Source::~Source() {
+}
+
+void RTPReceiver::Source::onPacketReceived(
+        uint16_t seq, const sp<ABuffer> &buffer) {
+    if (mFirst) {
+        buffer->setInt32Data(mCycles | seq);
+        queuePacket(buffer);
+
+        mFirst = false;
+        mBaseSeq = seq;
+        mMaxSeq = seq;
+        ++mReceived;
+        return;
+    }
+
+    uint16_t udelta = seq - mMaxSeq;
+
+    if (udelta < kMaxDropout) {
+        // In order, with permissible gap.
+
+        if (seq < mMaxSeq) {
+            // Sequence number wrapped - count another 64K cycle
+            mCycles += kRTPSeqMod;
+        }
+
+        mMaxSeq = seq;
+
+        ++mReceived;
+    } else if (udelta <= kRTPSeqMod - kMaxMisorder) {
+        // The sequence number made a very large jump
+        return;
+    } else {
+        // Duplicate or reordered packet.
+    }
+
+    buffer->setInt32Data(mCycles | seq);
+    queuePacket(buffer);
+}
+
+void RTPReceiver::Source::queuePacket(const sp<ABuffer> &packet) {
+    int32_t newExtendedSeqNo = packet->int32Data();
+
+    if (mFirstArrivalTimeUs < 0ll) {
+        mFirstArrivalTimeUs = ALooper::GetNowUs();
+
+        uint32_t rtpTime;
+        CHECK(packet->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
+
+        mFirstRTPTimeUs = (rtpTime * 100ll) / 9ll;
+    }
+
+    if (mAwaitingExtSeqNo >= 0 && newExtendedSeqNo < mAwaitingExtSeqNo) {
+        // We're no longer interested in these. They're old.
+        ALOGV("dropping stale extSeqNo %d", newExtendedSeqNo);
+        return;
+    }
+
+    if (mPackets.empty()) {
+        mPackets.push_back(packet);
+        dequeueMore();
+        return;
+    }
+
+    List<sp<ABuffer> >::iterator firstIt = mPackets.begin();
+    List<sp<ABuffer> >::iterator it = --mPackets.end();
+    for (;;) {
+        int32_t extendedSeqNo = (*it)->int32Data();
+
+        if (extendedSeqNo == newExtendedSeqNo) {
+            // Duplicate packet.
+            return;
+        }
+
+        if (extendedSeqNo < newExtendedSeqNo) {
+            // Insert new packet after the one at "it".
+            mPackets.insert(++it, packet);
+            break;
+        }
+
+        if (it == firstIt) {
+            // Insert new packet before the first existing one.
+            mPackets.insert(it, packet);
+            break;
+        }
+
+        --it;
+    }
+
+    dequeueMore();
+}
+
+void RTPReceiver::Source::dequeueMore() {
+    int64_t nowUs = ALooper::GetNowUs();
+    if (mNextReportTimeUs < 0ll || nowUs >= mNextReportTimeUs) {
+        if (mNextReportTimeUs >= 0ll) {
+            uint32_t expected = (mMaxSeq | mCycles) - mBaseSeq + 1;
+
+            uint32_t expectedInterval = expected - mExpectedPrior;
+            mExpectedPrior = expected;
+
+            uint32_t receivedInterval = mReceived - mReceivedPrior;
+            mReceivedPrior = mReceived;
+
+            int64_t lostInterval =
+                (int64_t)expectedInterval - (int64_t)receivedInterval;
+
+            int32_t declaredLostInterval =
+                mNumDeclaredLost - mNumDeclaredLostPrior;
+
+            mNumDeclaredLostPrior = mNumDeclaredLost;
+
+            ALOGI("lost %lld packets (%.2f %%), declared %d lost\n",
+                  lostInterval,
+                  100.0f * lostInterval / expectedInterval,
+                  declaredLostInterval);
+        }
+
+        mNextReportTimeUs = nowUs + kReportIntervalUs;
+    }
+
+    for (;;) {
+        sp<ABuffer> packet = getNextPacket();
+
+        if (packet == NULL) {
+            if (mPackets.empty()) {
+                break;
+            }
+
+            CHECK_GE(mAwaitingExtSeqNo, 0);
+
+            const sp<ABuffer> &firstPacket = *mPackets.begin();
+
+            uint32_t rtpTime;
+            CHECK(firstPacket->meta()->findInt32(
+                        "rtp-time", (int32_t *)&rtpTime));
+
+
+            int64_t rtpUs = (rtpTime * 100ll) / 9ll;
+
+            int64_t maxArrivalTimeUs =
+                mFirstArrivalTimeUs + rtpUs - mFirstRTPTimeUs;
+
+            int64_t nowUs = ALooper::GetNowUs();
+
+            CHECK_LT(mAwaitingExtSeqNo, firstPacket->int32Data());
+
+            ALOGV("waiting for %d, comparing against %d, %lld us left",
+                  mAwaitingExtSeqNo,
+                  firstPacket->int32Data(),
+                  maxArrivalTimeUs - nowUs);
+
+            if (maxArrivalTimeUs + kPacketLostAfterUs <= nowUs) {
+                ALOGV("Lost packet extSeqNo %d %s",
+                      mAwaitingExtSeqNo,
+                      mRequestedRetransmission ? "*" : "");
+
+                mRequestedRetransmission = false;
+                if (mActiveAssembler != NULL) {
+                    mActiveAssembler->signalDiscontinuity();
+                }
+
+                // resync();
+                ++mAwaitingExtSeqNo;
+                ++mNumDeclaredLost;
+
+                mReceiver->notifyPacketLost();
+                continue;
+            } else if (kRequestRetransmissionAfterUs > 0
+                    && maxArrivalTimeUs + kRequestRetransmissionAfterUs <= nowUs
+                    && !mRequestedRetransmission
+                    && mAwaitingExtSeqNo >= 0) {
+                mRequestedRetransmission = true;
+                mReceiver->requestRetransmission(mSSRC, mAwaitingExtSeqNo);
+                break;
+            } else {
+                break;
+            }
+        }
+
+        mRequestedRetransmission = false;
+
+        int32_t packetType;
+        CHECK(packet->meta()->findInt32("PT", &packetType));
+
+        if (packetType != mActivePacketType) {
+            mActiveAssembler = mReceiver->makeAssembler(packetType);
+            mActivePacketType = packetType;
+        }
+
+        if (mActiveAssembler == NULL) {
+            continue;
+        }
+
+        status_t err = mActiveAssembler->processPacket(packet);
+        if (err != OK) {
+            ALOGV("assembler returned error %d", err);
+        }
+    }
+}
+
+sp<ABuffer> RTPReceiver::Source::getNextPacket() {
+    if (mPackets.empty()) {
+        return NULL;
+    }
+
+    int32_t extSeqNo = (*mPackets.begin())->int32Data();
+
+    if (mAwaitingExtSeqNo < 0) {
+        mAwaitingExtSeqNo = extSeqNo;
+    } else if (extSeqNo != mAwaitingExtSeqNo) {
+        return NULL;
+    }
+
+    sp<ABuffer> packet = *mPackets.begin();
+    mPackets.erase(mPackets.begin());
+
+    ++mAwaitingExtSeqNo;
+
+    return packet;
+}
+
+void RTPReceiver::Source::resync() {
+    mAwaitingExtSeqNo = -1;
+}
+
+void RTPReceiver::Source::addReportBlock(
+        uint32_t ssrc, const sp<ABuffer> &buf) {
+    uint32_t extMaxSeq = mMaxSeq | mCycles;
+    uint32_t expected = extMaxSeq - mBaseSeq + 1;
+
+    int64_t lost = (int64_t)expected - (int64_t)mReceived;
+    if (lost > 0x7fffff) {
+        lost = 0x7fffff;
+    } else if (lost < -0x800000) {
+        lost = -0x800000;
+    }
+
+    uint32_t expectedInterval = expected - mExpectedPrior;
+    mExpectedPrior = expected;
+
+    uint32_t receivedInterval = mReceived - mReceivedPrior;
+    mReceivedPrior = mReceived;
+
+    int64_t lostInterval = expectedInterval - receivedInterval;
+
+    uint8_t fractionLost;
+    if (expectedInterval == 0 || lostInterval <=0) {
+        fractionLost = 0;
+    } else {
+        fractionLost = (lostInterval << 8) / expectedInterval;
+    }
+
+    uint8_t *ptr = buf->data() + buf->size();
+
+    ptr[0] = ssrc >> 24;
+    ptr[1] = (ssrc >> 16) & 0xff;
+    ptr[2] = (ssrc >> 8) & 0xff;
+    ptr[3] = ssrc & 0xff;
+
+    ptr[4] = fractionLost;
+
+    ptr[5] = (lost >> 16) & 0xff;
+    ptr[6] = (lost >> 8) & 0xff;
+    ptr[7] = lost & 0xff;
+
+    ptr[8] = extMaxSeq >> 24;
+    ptr[9] = (extMaxSeq >> 16) & 0xff;
+    ptr[10] = (extMaxSeq >> 8) & 0xff;
+    ptr[11] = extMaxSeq & 0xff;
+
+    // XXX TODO:
+
+    ptr[12] = 0x00;  // interarrival jitter
+    ptr[13] = 0x00;
+    ptr[14] = 0x00;
+    ptr[15] = 0x00;
+
+    ptr[16] = 0x00;  // last SR
+    ptr[17] = 0x00;
+    ptr[18] = 0x00;
+    ptr[19] = 0x00;
+
+    ptr[20] = 0x00;  // delay since last SR
+    ptr[21] = 0x00;
+    ptr[22] = 0x00;
+    ptr[23] = 0x00;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+RTPReceiver::RTPReceiver(
+        const sp<ANetworkSession> &netSession,
+        const sp<AMessage> &notify)
+    : mNetSession(netSession),
+      mNotify(notify),
+      mMode(TRANSPORT_UNDEFINED),
+      mRTPSessionID(0),
+      mRTCPSessionID(0),
+      mRTPClientSessionID(0) {
+}
+
+RTPReceiver::~RTPReceiver() {
+    if (mRTPClientSessionID != 0) {
+        mNetSession->destroySession(mRTPClientSessionID);
+        mRTPClientSessionID = 0;
+    }
+
+    if (mRTCPSessionID != 0) {
+        mNetSession->destroySession(mRTCPSessionID);
+        mRTCPSessionID = 0;
+    }
+
+    if (mRTPSessionID != 0) {
+        mNetSession->destroySession(mRTPSessionID);
+        mRTPSessionID = 0;
+    }
+}
+
+status_t RTPReceiver::initAsync(TransportMode mode, int32_t *outLocalRTPPort) {
+    if (mMode != TRANSPORT_UNDEFINED || mode == TRANSPORT_UNDEFINED) {
+        return INVALID_OPERATION;
+    }
+
+    CHECK_NE(mMode, TRANSPORT_TCP_INTERLEAVED);
+
+    sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
+
+    sp<AMessage> rtcpNotify;
+    if (mode == TRANSPORT_UDP) {
+        rtcpNotify = new AMessage(kWhatRTCPNotify, id());
+    }
+
+    CHECK_EQ(mRTPSessionID, 0);
+    CHECK_EQ(mRTCPSessionID, 0);
+
+    int32_t localRTPPort;
+
+    struct in_addr ifaceAddr;
+    ifaceAddr.s_addr = INADDR_ANY;
+
+    for (;;) {
+        localRTPPort = PickRandomRTPPort();
+
+        status_t err;
+        if (mode == TRANSPORT_UDP) {
+            err = mNetSession->createUDPSession(
+                    localRTPPort,
+                    rtpNotify,
+                    &mRTPSessionID);
+        } else {
+            CHECK_EQ(mode, TRANSPORT_TCP);
+            err = mNetSession->createTCPDatagramSession(
+                    ifaceAddr,
+                    localRTPPort,
+                    rtpNotify,
+                    &mRTPSessionID);
+        }
+
+        if (err != OK) {
+            continue;
+        }
+
+        if (mode == TRANSPORT_TCP) {
+            break;
+        }
+
+        err = mNetSession->createUDPSession(
+                localRTPPort + 1,
+                rtcpNotify,
+                &mRTCPSessionID);
+
+        if (err == OK) {
+            break;
+        }
+
+        mNetSession->destroySession(mRTPSessionID);
+        mRTPSessionID = 0;
+    }
+
+    mMode = mode;
+    *outLocalRTPPort = localRTPPort;
+
+    return OK;
+}
+
+status_t RTPReceiver::connect(
+        const char *remoteHost, int32_t remoteRTPPort, int32_t remoteRTCPPort) {
+    if (mMode == TRANSPORT_TCP) {
+        return OK;
+    }
+
+    status_t err = mNetSession->connectUDPSession(
+            mRTPSessionID, remoteHost, remoteRTPPort);
+
+    if (err != OK) {
+        notifyInitDone(err);
+        return err;
+    }
+
+    ALOGI("connectUDPSession RTP successful.");
+
+    if (remoteRTCPPort >= 0) {
+        err = mNetSession->connectUDPSession(
+                mRTCPSessionID, remoteHost, remoteRTCPPort);
+
+        if (err != OK) {
+        ALOGI("connect failed w/ err %d", err);
+
+            notifyInitDone(err);
+            return err;
+        }
+
+        scheduleSendRR();
+    }
+
+    notifyInitDone(OK);
+
+    return OK;
+}
+
+void RTPReceiver::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatRTPNotify:
+        case kWhatRTCPNotify:
+            onNetNotify(msg->what() == kWhatRTPNotify, msg);
+            break;
+
+        case kWhatSendRR:
+        {
+            onSendRR();
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+void RTPReceiver::onNetNotify(bool isRTP, const sp<AMessage> &msg) {
+    int32_t reason;
+    CHECK(msg->findInt32("reason", &reason));
+
+    switch (reason) {
+        case ANetworkSession::kWhatError:
+        {
+            int32_t sessionID;
+            CHECK(msg->findInt32("sessionID", &sessionID));
+
+            int32_t err;
+            CHECK(msg->findInt32("err", &err));
+
+            int32_t errorOccuredDuringSend;
+            CHECK(msg->findInt32("send", &errorOccuredDuringSend));
+
+            AString detail;
+            CHECK(msg->findString("detail", &detail));
+
+            ALOGE("An error occurred during %s in session %d "
+                  "(%d, '%s' (%s)).",
+                  errorOccuredDuringSend ? "send" : "receive",
+                  sessionID,
+                  err,
+                  detail.c_str(),
+                  strerror(-err));
+
+            mNetSession->destroySession(sessionID);
+
+            if (sessionID == mRTPSessionID) {
+                mRTPSessionID = 0;
+
+                if (mMode == TRANSPORT_TCP && mRTPClientSessionID == 0) {
+                    notifyInitDone(err);
+                    break;
+                }
+            } else if (sessionID == mRTCPSessionID) {
+                mRTCPSessionID = 0;
+            } else if (sessionID == mRTPClientSessionID) {
+                mRTPClientSessionID = 0;
+            }
+
+            notifyError(err);
+            break;
+        }
+
+        case ANetworkSession::kWhatDatagram:
+        {
+            sp<ABuffer> data;
+            CHECK(msg->findBuffer("data", &data));
+
+            if (isRTP) {
+                onRTPData(data);
+            } else {
+                onRTCPData(data);
+            }
+            break;
+        }
+
+        case ANetworkSession::kWhatClientConnected:
+        {
+            CHECK_EQ(mMode, TRANSPORT_TCP);
+            CHECK(isRTP);
+
+            int32_t sessionID;
+            CHECK(msg->findInt32("sessionID", &sessionID));
+
+            if (mRTPClientSessionID != 0) {
+                // We only allow a single client connection.
+                mNetSession->destroySession(sessionID);
+                sessionID = 0;
+                break;
+            }
+
+            mRTPClientSessionID = sessionID;
+
+            notifyInitDone(OK);
+            break;
+        }
+    }
+}
+
+void RTPReceiver::notifyInitDone(status_t err) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatInitDone);
+    notify->setInt32("err", err);
+    notify->post();
+}
+
+void RTPReceiver::notifyError(status_t err) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatError);
+    notify->setInt32("err", err);
+    notify->post();
+}
+
+void RTPReceiver::notifyPacketLost() {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatPacketLost);
+    notify->post();
+}
+
+status_t RTPReceiver::onRTPData(const sp<ABuffer> &buffer) {
+    size_t size = buffer->size();
+    if (size < 12) {
+        // Too short to be a valid RTP header.
+        return ERROR_MALFORMED;
+    }
+
+    const uint8_t *data = buffer->data();
+
+    if ((data[0] >> 6) != 2) {
+        // Unsupported version.
+        return ERROR_UNSUPPORTED;
+    }
+
+    if (data[0] & 0x20) {
+        // Padding present.
+
+        size_t paddingLength = data[size - 1];
+
+        if (paddingLength + 12 > size) {
+            // If we removed this much padding we'd end up with something
+            // that's too short to be a valid RTP header.
+            return ERROR_MALFORMED;
+        }
+
+        size -= paddingLength;
+    }
+
+    int numCSRCs = data[0] & 0x0f;
+
+    size_t payloadOffset = 12 + 4 * numCSRCs;
+
+    if (size < payloadOffset) {
+        // Not enough data to fit the basic header and all the CSRC entries.
+        return ERROR_MALFORMED;
+    }
+
+    if (data[0] & 0x10) {
+        // Header eXtension present.
+
+        if (size < payloadOffset + 4) {
+            // Not enough data to fit the basic header, all CSRC entries
+            // and the first 4 bytes of the extension header.
+
+            return ERROR_MALFORMED;
+        }
+
+        const uint8_t *extensionData = &data[payloadOffset];
+
+        size_t extensionLength =
+            4 * (extensionData[2] << 8 | extensionData[3]);
+
+        if (size < payloadOffset + 4 + extensionLength) {
+            return ERROR_MALFORMED;
+        }
+
+        payloadOffset += 4 + extensionLength;
+    }
+
+    uint32_t srcId = U32_AT(&data[8]);
+    uint32_t rtpTime = U32_AT(&data[4]);
+    uint16_t seqNo = U16_AT(&data[2]);
+
+    sp<AMessage> meta = buffer->meta();
+    meta->setInt32("ssrc", srcId);
+    meta->setInt32("rtp-time", rtpTime);
+    meta->setInt32("PT", data[1] & 0x7f);
+    meta->setInt32("M", data[1] >> 7);
+
+    buffer->setRange(payloadOffset, size - payloadOffset);
+
+    ssize_t index = mSources.indexOfKey(srcId);
+    sp<Source> source;
+    if (index < 0) {
+        source = new Source(this, srcId);
+        mSources.add(srcId, source);
+    } else {
+        source = mSources.valueAt(index);
+    }
+
+    source->onPacketReceived(seqNo, buffer);
+
+    return OK;
+}
+
+status_t RTPReceiver::onRTCPData(const sp<ABuffer> &data) {
+    ALOGI("onRTCPData");
+    return OK;
+}
+
+void RTPReceiver::addSDES(const sp<ABuffer> &buffer) {
+    uint8_t *data = buffer->data() + buffer->size();
+    data[0] = 0x80 | 1;
+    data[1] = 202;  // SDES
+    data[4] = kSourceID >> 24;  // SSRC
+    data[5] = (kSourceID >> 16) & 0xff;
+    data[6] = (kSourceID >> 8) & 0xff;
+    data[7] = kSourceID & 0xff;
+
+    size_t offset = 8;
+
+    data[offset++] = 1;  // CNAME
+
+    AString cname = "stagefright@somewhere";
+    data[offset++] = cname.size();
+
+    memcpy(&data[offset], cname.c_str(), cname.size());
+    offset += cname.size();
+
+    data[offset++] = 6;  // TOOL
+
+    AString tool = "stagefright/1.0";
+    data[offset++] = tool.size();
+
+    memcpy(&data[offset], tool.c_str(), tool.size());
+    offset += tool.size();
+
+    data[offset++] = 0;
+
+    if ((offset % 4) > 0) {
+        size_t count = 4 - (offset % 4);
+        switch (count) {
+            case 3:
+                data[offset++] = 0;
+            case 2:
+                data[offset++] = 0;
+            case 1:
+                data[offset++] = 0;
+        }
+    }
+
+    size_t numWords = (offset / 4) - 1;
+    data[2] = numWords >> 8;
+    data[3] = numWords & 0xff;
+
+    buffer->setRange(buffer->offset(), buffer->size() + offset);
+}
+
+void RTPReceiver::scheduleSendRR() {
+    (new AMessage(kWhatSendRR, id()))->post(5000000ll);
+}
+
+void RTPReceiver::onSendRR() {
+#if 0
+    sp<ABuffer> buf = new ABuffer(kMaxUDPPacketSize);
+    buf->setRange(0, 0);
+
+    uint8_t *ptr = buf->data();
+    ptr[0] = 0x80 | 0;
+    ptr[1] = 201;  // RR
+    ptr[2] = 0;
+    ptr[3] = 1;
+    ptr[4] = kSourceID >> 24;  // SSRC
+    ptr[5] = (kSourceID >> 16) & 0xff;
+    ptr[6] = (kSourceID >> 8) & 0xff;
+    ptr[7] = kSourceID & 0xff;
+
+    buf->setRange(0, 8);
+
+    size_t numReportBlocks = 0;
+    for (size_t i = 0; i < mSources.size(); ++i) {
+        uint32_t ssrc = mSources.keyAt(i);
+        sp<Source> source = mSources.valueAt(i);
+
+        if (numReportBlocks > 31 || buf->size() + 24 > buf->capacity()) {
+            // Cannot fit another report block.
+            break;
+        }
+
+        source->addReportBlock(ssrc, buf);
+        ++numReportBlocks;
+    }
+
+    ptr[0] |= numReportBlocks;  // 5 bit
+
+    size_t sizeInWordsMinus1 = 1 + 6 * numReportBlocks;
+    ptr[2] = sizeInWordsMinus1 >> 8;
+    ptr[3] = sizeInWordsMinus1 & 0xff;
+
+    buf->setRange(0, (sizeInWordsMinus1 + 1) * 4);
+
+    addSDES(buf);
+
+    mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size());
+#endif
+
+    scheduleSendRR();
+}
+
+status_t RTPReceiver::registerPacketType(
+        uint8_t packetType, PacketizationMode mode) {
+    mPacketTypes.add(packetType, mode);
+
+    return OK;
+}
+
+sp<RTPReceiver::Assembler> RTPReceiver::makeAssembler(uint8_t packetType) {
+    ssize_t index = mPacketTypes.indexOfKey(packetType);
+    if (index < 0) {
+        return NULL;
+    }
+
+    PacketizationMode mode = mPacketTypes.valueAt(index);
+
+    switch (mode) {
+        case PACKETIZATION_TRANSPORT_STREAM:
+            return new TSAssembler(mNotify);
+
+        case PACKETIZATION_H264:
+            return new H264Assembler(mNotify);
+
+        default:
+            return NULL;
+    }
+}
+
+void RTPReceiver::requestRetransmission(uint32_t senderSSRC, int32_t extSeqNo) {
+    int32_t blp = 0;
+
+    sp<ABuffer> buf = new ABuffer(16);
+    buf->setRange(0, 0);
+
+    uint8_t *ptr = buf->data();
+    ptr[0] = 0x80 | 1;  // generic NACK
+    ptr[1] = 205;  // TSFB
+    ptr[2] = 0;
+    ptr[3] = 3;
+    ptr[8] = (senderSSRC >> 24) & 0xff;
+    ptr[9] = (senderSSRC >> 16) & 0xff;
+    ptr[10] = (senderSSRC >> 8) & 0xff;
+    ptr[11] = (senderSSRC & 0xff);
+    ptr[8] = (kSourceID >> 24) & 0xff;
+    ptr[9] = (kSourceID >> 16) & 0xff;
+    ptr[10] = (kSourceID >> 8) & 0xff;
+    ptr[11] = (kSourceID & 0xff);
+    ptr[12] = (extSeqNo >> 8) & 0xff;
+    ptr[13] = (extSeqNo & 0xff);
+    ptr[14] = (blp >> 8) & 0xff;
+    ptr[15] = (blp & 0xff);
+
+    buf->setRange(0, 16);
+
+     mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size());
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.h b/media/libstagefright/wifi-display/rtp/RTPReceiver.h
new file mode 100644
index 0000000..2ae864a
--- /dev/null
+++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright 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 RTP_RECEIVER_H_
+
+#define RTP_RECEIVER_H_
+
+#include "RTPBase.h"
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+struct ABuffer;
+struct ANetworkSession;
+
+// An object of this class facilitates receiving of media data on an RTP
+// channel. The channel is established over a UDP or TCP connection depending
+// on which "TransportMode" was chosen. In addition different RTP packetization
+// schemes are supported such as "Transport Stream Packets over RTP",
+// or "AVC/H.264 encapsulation as specified in RFC 3984 (non-interleaved mode)"
+struct RTPReceiver : public RTPBase, public AHandler {
+    enum {
+        kWhatInitDone,
+        kWhatError,
+        kWhatAccessUnit,
+        kWhatPacketLost,
+    };
+    RTPReceiver(
+            const sp<ANetworkSession> &netSession,
+            const sp<AMessage> &notify);
+
+    status_t registerPacketType(
+            uint8_t packetType, PacketizationMode mode);
+
+    status_t initAsync(TransportMode mode, int32_t *outLocalRTPPort);
+
+    status_t connect(
+            const char *remoteHost,
+            int32_t remoteRTPPort,
+            int32_t remoteRTCPPort);
+
+protected:
+    virtual ~RTPReceiver();
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    enum {
+        kWhatRTPNotify,
+        kWhatRTCPNotify,
+        kWhatSendRR,
+    };
+
+    enum {
+        kSourceID                       = 0xdeadbeef,
+        kPacketLostAfterUs              = 100000,
+        kRequestRetransmissionAfterUs   = -1,
+    };
+
+    struct Assembler;
+    struct H264Assembler;
+    struct Source;
+    struct TSAssembler;
+
+    sp<ANetworkSession> mNetSession;
+    sp<AMessage> mNotify;
+    TransportMode mMode;
+    int32_t mRTPSessionID;
+    int32_t mRTCPSessionID;
+
+    int32_t mRTPClientSessionID;  // in TRANSPORT_TCP mode.
+
+    KeyedVector<uint8_t, PacketizationMode> mPacketTypes;
+    KeyedVector<uint32_t, sp<Source> > mSources;
+
+    void onNetNotify(bool isRTP, const sp<AMessage> &msg);
+    status_t onRTPData(const sp<ABuffer> &data);
+    status_t onRTCPData(const sp<ABuffer> &data);
+    void onSendRR();
+
+    void scheduleSendRR();
+    void addSDES(const sp<ABuffer> &buffer);
+
+    void notifyInitDone(status_t err);
+    void notifyError(status_t err);
+    void notifyPacketLost();
+
+    sp<Assembler> makeAssembler(uint8_t packetType);
+
+    void requestRetransmission(uint32_t senderSSRC, int32_t extSeqNo);
+
+    DISALLOW_EVIL_CONSTRUCTORS(RTPReceiver);
+};
+
+}  // namespace android
+
+#endif  // RTP_RECEIVER_H_
diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp
new file mode 100644
index 0000000..85c5933
--- /dev/null
+++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp
@@ -0,0 +1,701 @@
+/*
+ * Copyright 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 "RTPSender"
+#include <utils/Log.h>
+
+#include "RTPSender.h"
+
+#include "ANetworkSession.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/MediaErrors.h>
+#include <media/stagefright/Utils.h>
+
+#include "include/avc_utils.h"
+
+namespace android {
+
+RTPSender::RTPSender(
+        const sp<ANetworkSession> &netSession,
+        const sp<AMessage> &notify)
+    : mNetSession(netSession),
+      mNotify(notify),
+      mMode(TRANSPORT_UNDEFINED),
+      mRTPSessionID(0),
+      mRTCPSessionID(0),
+      mRTPConnected(false),
+      mRTCPConnected(false),
+      mLastNTPTime(0),
+      mLastRTPTime(0),
+      mNumRTPSent(0),
+      mNumRTPOctetsSent(0),
+      mNumSRsSent(0),
+      mRTPSeqNo(0),
+      mHistorySize(0) {
+}
+
+RTPSender::~RTPSender() {
+    if (mRTCPSessionID != 0) {
+        mNetSession->destroySession(mRTCPSessionID);
+        mRTCPSessionID = 0;
+    }
+
+    if (mRTPSessionID != 0) {
+        mNetSession->destroySession(mRTPSessionID);
+        mRTPSessionID = 0;
+    }
+}
+
+// static
+int32_t RTPBase::PickRandomRTPPort() {
+    // Pick an even integer in range [1024, 65534)
+
+    static const size_t kRange = (65534 - 1024) / 2;
+
+    return (int32_t)(((float)(kRange + 1) * rand()) / RAND_MAX) * 2 + 1024;
+}
+
+status_t RTPSender::initAsync(
+        TransportMode mode,
+        const char *remoteHost,
+        int32_t remoteRTPPort,
+        int32_t remoteRTCPPort,
+        int32_t *outLocalRTPPort) {
+    if (mMode != TRANSPORT_UNDEFINED || mode == TRANSPORT_UNDEFINED) {
+        return INVALID_OPERATION;
+    }
+
+    CHECK_NE(mMode, TRANSPORT_TCP_INTERLEAVED);
+
+    if (mode == TRANSPORT_TCP && remoteRTCPPort >= 0) {
+        return INVALID_OPERATION;
+    }
+
+    sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
+
+    sp<AMessage> rtcpNotify;
+    if (remoteRTCPPort >= 0) {
+        rtcpNotify = new AMessage(kWhatRTCPNotify, id());
+    }
+
+    CHECK_EQ(mRTPSessionID, 0);
+    CHECK_EQ(mRTCPSessionID, 0);
+
+    int32_t localRTPPort;
+
+    for (;;) {
+        localRTPPort = PickRandomRTPPort();
+
+        status_t err;
+        if (mode == TRANSPORT_UDP) {
+            err = mNetSession->createUDPSession(
+                    localRTPPort,
+                    remoteHost,
+                    remoteRTPPort,
+                    rtpNotify,
+                    &mRTPSessionID);
+        } else {
+            CHECK_EQ(mode, TRANSPORT_TCP);
+            err = mNetSession->createTCPDatagramSession(
+                    localRTPPort,
+                    remoteHost,
+                    remoteRTPPort,
+                    rtpNotify,
+                    &mRTPSessionID);
+        }
+
+        if (err != OK) {
+            continue;
+        }
+
+        if (remoteRTCPPort < 0) {
+            break;
+        }
+
+        if (mode == TRANSPORT_UDP) {
+            err = mNetSession->createUDPSession(
+                    localRTPPort + 1,
+                    remoteHost,
+                    remoteRTCPPort,
+                    rtcpNotify,
+                    &mRTCPSessionID);
+        } else {
+            CHECK_EQ(mode, TRANSPORT_TCP);
+            err = mNetSession->createTCPDatagramSession(
+                    localRTPPort + 1,
+                    remoteHost,
+                    remoteRTCPPort,
+                    rtcpNotify,
+                    &mRTCPSessionID);
+        }
+
+        if (err == OK) {
+            break;
+        }
+
+        mNetSession->destroySession(mRTPSessionID);
+        mRTPSessionID = 0;
+    }
+
+    if (mode == TRANSPORT_UDP) {
+        mRTPConnected = true;
+        mRTCPConnected = true;
+    }
+
+    mMode = mode;
+    *outLocalRTPPort = localRTPPort;
+
+    if (mMode == TRANSPORT_UDP) {
+        notifyInitDone(OK);
+    }
+
+    return OK;
+}
+
+status_t RTPSender::queueBuffer(
+        const sp<ABuffer> &buffer, uint8_t packetType, PacketizationMode mode) {
+    status_t err;
+
+    switch (mode) {
+        case PACKETIZATION_TRANSPORT_STREAM:
+            err = queueTSPackets(buffer, packetType);
+            break;
+
+        case PACKETIZATION_H264:
+            err  = queueAVCBuffer(buffer, packetType);
+            break;
+
+        default:
+            TRESPASS();
+    }
+
+    return err;
+}
+
+status_t RTPSender::queueTSPackets(
+        const sp<ABuffer> &tsPackets, uint8_t packetType) {
+    CHECK_EQ(0, tsPackets->size() % 188);
+
+    const size_t numTSPackets = tsPackets->size() / 188;
+
+    size_t srcOffset = 0;
+    while (srcOffset < tsPackets->size()) {
+        sp<ABuffer> udpPacket =
+            new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188);
+
+        udpPacket->setInt32Data(mRTPSeqNo);
+
+        uint8_t *rtp = udpPacket->data();
+        rtp[0] = 0x80;
+        rtp[1] = packetType;
+
+        rtp[2] = (mRTPSeqNo >> 8) & 0xff;
+        rtp[3] = mRTPSeqNo & 0xff;
+        ++mRTPSeqNo;
+
+        int64_t nowUs = ALooper::GetNowUs();
+        uint32_t rtpTime = (nowUs * 9) / 100ll;
+
+        rtp[4] = rtpTime >> 24;
+        rtp[5] = (rtpTime >> 16) & 0xff;
+        rtp[6] = (rtpTime >> 8) & 0xff;
+        rtp[7] = rtpTime & 0xff;
+
+        rtp[8] = kSourceID >> 24;
+        rtp[9] = (kSourceID >> 16) & 0xff;
+        rtp[10] = (kSourceID >> 8) & 0xff;
+        rtp[11] = kSourceID & 0xff;
+
+        size_t numTSPackets = (tsPackets->size() - srcOffset) / 188;
+        if (numTSPackets > kMaxNumTSPacketsPerRTPPacket) {
+            numTSPackets = kMaxNumTSPacketsPerRTPPacket;
+        }
+
+        memcpy(&rtp[12], tsPackets->data() + srcOffset, numTSPackets * 188);
+
+        udpPacket->setRange(0, 12 + numTSPackets * 188);
+        status_t err = sendRTPPacket(udpPacket, true /* storeInHistory */);
+
+        if (err != OK) {
+            return err;
+        }
+
+        srcOffset += numTSPackets * 188;
+    }
+
+    return OK;
+}
+
+status_t RTPSender::queueAVCBuffer(
+        const sp<ABuffer> &accessUnit, uint8_t packetType) {
+    int64_t timeUs;
+    CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
+
+    uint32_t rtpTime = (timeUs * 9 / 100ll);
+
+    List<sp<ABuffer> > packets;
+
+    sp<ABuffer> out = new ABuffer(kMaxUDPPacketSize);
+    size_t outBytesUsed = 12;  // Placeholder for RTP header.
+
+    const uint8_t *data = accessUnit->data();
+    size_t size = accessUnit->size();
+    const uint8_t *nalStart;
+    size_t nalSize;
+    while (getNextNALUnit(
+                &data, &size, &nalStart, &nalSize,
+                true /* startCodeFollows */) == OK) {
+        size_t bytesNeeded = nalSize + 2;
+        if (outBytesUsed == 12) {
+            ++bytesNeeded;
+        }
+
+        if (outBytesUsed + bytesNeeded > out->capacity()) {
+            bool emitSingleNALPacket = false;
+
+            if (outBytesUsed == 12
+                    && outBytesUsed + nalSize <= out->capacity()) {
+                // We haven't emitted anything into the current packet yet and
+                // this NAL unit fits into a single-NAL-unit-packet while
+                // it wouldn't have fit as part of a STAP-A packet.
+
+                memcpy(out->data() + outBytesUsed, nalStart, nalSize);
+                outBytesUsed += nalSize;
+
+                emitSingleNALPacket = true;
+            }
+
+            if (outBytesUsed > 12) {
+                out->setRange(0, outBytesUsed);
+                packets.push_back(out);
+                out = new ABuffer(kMaxUDPPacketSize);
+                outBytesUsed = 12;  // Placeholder for RTP header
+            }
+
+            if (emitSingleNALPacket) {
+                continue;
+            }
+        }
+
+        if (outBytesUsed + bytesNeeded <= out->capacity()) {
+            uint8_t *dst = out->data() + outBytesUsed;
+
+            if (outBytesUsed == 12) {
+                *dst++ = 24;  // STAP-A header
+            }
+
+            *dst++ = (nalSize >> 8) & 0xff;
+            *dst++ = nalSize & 0xff;
+            memcpy(dst, nalStart, nalSize);
+
+            outBytesUsed += bytesNeeded;
+            continue;
+        }
+
+        // This single NAL unit does not fit into a single RTP packet,
+        // we need to emit an FU-A.
+
+        CHECK_EQ(outBytesUsed, 12u);
+
+        uint8_t nalType = nalStart[0] & 0x1f;
+        uint8_t nri = (nalStart[0] >> 5) & 3;
+
+        size_t srcOffset = 1;
+        while (srcOffset < nalSize) {
+            size_t copy = out->capacity() - outBytesUsed - 2;
+            if (copy > nalSize - srcOffset) {
+                copy = nalSize - srcOffset;
+            }
+
+            uint8_t *dst = out->data() + outBytesUsed;
+            dst[0] = (nri << 5) | 28;
+
+            dst[1] = nalType;
+
+            if (srcOffset == 1) {
+                dst[1] |= 0x80;
+            }
+
+            if (srcOffset + copy == nalSize) {
+                dst[1] |= 0x40;
+            }
+
+            memcpy(&dst[2], nalStart + srcOffset, copy);
+            srcOffset += copy;
+
+            out->setRange(0, outBytesUsed + copy + 2);
+
+            packets.push_back(out);
+            out = new ABuffer(kMaxUDPPacketSize);
+            outBytesUsed = 12;  // Placeholder for RTP header
+        }
+    }
+
+    if (outBytesUsed > 12) {
+        out->setRange(0, outBytesUsed);
+        packets.push_back(out);
+    }
+
+    while (!packets.empty()) {
+        sp<ABuffer> out = *packets.begin();
+        packets.erase(packets.begin());
+
+        out->setInt32Data(mRTPSeqNo);
+
+        bool last = packets.empty();
+
+        uint8_t *dst = out->data();
+
+        dst[0] = 0x80;
+
+        dst[1] = packetType;
+        if (last) {
+            dst[1] |= 1 << 7;  // M-bit
+        }
+
+        dst[2] = (mRTPSeqNo >> 8) & 0xff;
+        dst[3] = mRTPSeqNo & 0xff;
+        ++mRTPSeqNo;
+
+        dst[4] = rtpTime >> 24;
+        dst[5] = (rtpTime >> 16) & 0xff;
+        dst[6] = (rtpTime >> 8) & 0xff;
+        dst[7] = rtpTime & 0xff;
+        dst[8] = kSourceID >> 24;
+        dst[9] = (kSourceID >> 16) & 0xff;
+        dst[10] = (kSourceID >> 8) & 0xff;
+        dst[11] = kSourceID & 0xff;
+
+        status_t err = sendRTPPacket(out, true /* storeInHistory */);
+
+        if (err != OK) {
+            return err;
+        }
+    }
+
+    return OK;
+}
+
+status_t RTPSender::sendRTPPacket(
+        const sp<ABuffer> &buffer, bool storeInHistory) {
+    CHECK(mRTPConnected);
+
+    status_t err = mNetSession->sendRequest(
+            mRTPSessionID, buffer->data(), buffer->size());
+
+    if (err != OK) {
+        return err;
+    }
+
+    mLastNTPTime = GetNowNTP();
+    mLastRTPTime = U32_AT(buffer->data() + 4);
+
+    ++mNumRTPSent;
+    mNumRTPOctetsSent += buffer->size() - 12;
+
+    if (storeInHistory) {
+        if (mHistorySize == kMaxHistorySize) {
+            mHistory.erase(mHistory.begin());
+        } else {
+            ++mHistorySize;
+        }
+        mHistory.push_back(buffer);
+    }
+
+    return OK;
+}
+
+// static
+uint64_t RTPSender::GetNowNTP() {
+    struct timeval tv;
+    gettimeofday(&tv, NULL /* timezone */);
+
+    uint64_t nowUs = tv.tv_sec * 1000000ll + tv.tv_usec;
+
+    nowUs += ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll;
+
+    uint64_t hi = nowUs / 1000000ll;
+    uint64_t lo = ((1ll << 32) * (nowUs % 1000000ll)) / 1000000ll;
+
+    return (hi << 32) | lo;
+}
+
+void RTPSender::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatRTPNotify:
+        case kWhatRTCPNotify:
+            onNetNotify(msg->what() == kWhatRTPNotify, msg);
+            break;
+
+        default:
+            TRESPASS();
+    }
+}
+
+void RTPSender::onNetNotify(bool isRTP, const sp<AMessage> &msg) {
+    int32_t reason;
+    CHECK(msg->findInt32("reason", &reason));
+
+    switch (reason) {
+        case ANetworkSession::kWhatError:
+        {
+            int32_t sessionID;
+            CHECK(msg->findInt32("sessionID", &sessionID));
+
+            int32_t err;
+            CHECK(msg->findInt32("err", &err));
+
+            int32_t errorOccuredDuringSend;
+            CHECK(msg->findInt32("send", &errorOccuredDuringSend));
+
+            AString detail;
+            CHECK(msg->findString("detail", &detail));
+
+            ALOGE("An error occurred during %s in session %d "
+                  "(%d, '%s' (%s)).",
+                  errorOccuredDuringSend ? "send" : "receive",
+                  sessionID,
+                  err,
+                  detail.c_str(),
+                  strerror(-err));
+
+            mNetSession->destroySession(sessionID);
+
+            if (sessionID == mRTPSessionID) {
+                mRTPSessionID = 0;
+            } else if (sessionID == mRTCPSessionID) {
+                mRTCPSessionID = 0;
+            }
+
+            if (mMode == TRANSPORT_TCP) {
+                if (!mRTPConnected
+                        || (mRTCPSessionID > 0 && !mRTCPConnected)) {
+                    notifyInitDone(err);
+                    break;
+                }
+            }
+
+            notifyError(err);
+            break;
+        }
+
+        case ANetworkSession::kWhatDatagram:
+        {
+            sp<ABuffer> data;
+            CHECK(msg->findBuffer("data", &data));
+
+            if (isRTP) {
+                ALOGW("Huh? Received data on RTP connection...");
+            } else {
+                onRTCPData(data);
+            }
+            break;
+        }
+
+        case ANetworkSession::kWhatConnected:
+        {
+            CHECK_EQ(mMode, TRANSPORT_TCP);
+
+            int32_t sessionID;
+            CHECK(msg->findInt32("sessionID", &sessionID));
+
+            if  (isRTP) {
+                CHECK_EQ(sessionID, mRTPSessionID);
+                mRTPConnected = true;
+            } else {
+                CHECK_EQ(sessionID, mRTCPSessionID);
+                mRTCPConnected = true;
+            }
+
+            if (mRTPConnected && (mRTCPSessionID == 0 || mRTCPConnected)) {
+                notifyInitDone(OK);
+            }
+            break;
+        }
+    }
+}
+
+status_t RTPSender::onRTCPData(const sp<ABuffer> &buffer) {
+    const uint8_t *data = buffer->data();
+    size_t size = buffer->size();
+
+    while (size > 0) {
+        if (size < 8) {
+            // Too short to be a valid RTCP header
+            return ERROR_MALFORMED;
+        }
+
+        if ((data[0] >> 6) != 2) {
+            // Unsupported version.
+            return ERROR_UNSUPPORTED;
+        }
+
+        if (data[0] & 0x20) {
+            // Padding present.
+
+            size_t paddingLength = data[size - 1];
+
+            if (paddingLength + 12 > size) {
+                // If we removed this much padding we'd end up with something
+                // that's too short to be a valid RTP header.
+                return ERROR_MALFORMED;
+            }
+
+            size -= paddingLength;
+        }
+
+        size_t headerLength = 4 * (data[2] << 8 | data[3]) + 4;
+
+        if (size < headerLength) {
+            // Only received a partial packet?
+            return ERROR_MALFORMED;
+        }
+
+        switch (data[1]) {
+            case 200:
+            case 201:  // RR
+                parseReceiverReport(data, headerLength);
+                break;
+
+            case 202:  // SDES
+            case 203:
+            case 204:  // APP
+                break;
+
+            case 205:  // TSFB (transport layer specific feedback)
+                parseTSFB(data, headerLength);
+                break;
+
+            case 206:  // PSFB (payload specific feedback)
+                // hexdump(data, headerLength);
+                break;
+
+            default:
+            {
+                ALOGW("Unknown RTCP packet type %u of size %d",
+                     (unsigned)data[1], headerLength);
+                break;
+            }
+        }
+
+        data += headerLength;
+        size -= headerLength;
+    }
+
+    return OK;
+}
+
+status_t RTPSender::parseReceiverReport(const uint8_t *data, size_t size) {
+    // hexdump(data, size);
+
+    float fractionLost = data[12] / 256.0f;
+
+    ALOGI("lost %.2f %% of packets during report interval.",
+          100.0f * fractionLost);
+
+    return OK;
+}
+
+status_t RTPSender::parseTSFB(const uint8_t *data, size_t size) {
+    if ((data[0] & 0x1f) != 1) {
+        return ERROR_UNSUPPORTED;  // We only support NACK for now.
+    }
+
+    uint32_t srcId = U32_AT(&data[8]);
+    if (srcId != kSourceID) {
+        return ERROR_MALFORMED;
+    }
+
+    for (size_t i = 12; i < size; i += 4) {
+        uint16_t seqNo = U16_AT(&data[i]);
+        uint16_t blp = U16_AT(&data[i + 2]);
+
+        List<sp<ABuffer> >::iterator it = mHistory.begin();
+        bool foundSeqNo = false;
+        while (it != mHistory.end()) {
+            const sp<ABuffer> &buffer = *it;
+
+            uint16_t bufferSeqNo = buffer->int32Data() & 0xffff;
+
+            bool retransmit = false;
+            if (bufferSeqNo == seqNo) {
+                retransmit = true;
+            } else if (blp != 0) {
+                for (size_t i = 0; i < 16; ++i) {
+                    if ((blp & (1 << i))
+                        && (bufferSeqNo == ((seqNo + i + 1) & 0xffff))) {
+                        blp &= ~(1 << i);
+                        retransmit = true;
+                    }
+                }
+            }
+
+            if (retransmit) {
+                ALOGV("retransmitting seqNo %d", bufferSeqNo);
+
+                CHECK_EQ((status_t)OK,
+                         sendRTPPacket(buffer, false /* storeInHistory */));
+
+                if (bufferSeqNo == seqNo) {
+                    foundSeqNo = true;
+                }
+
+                if (foundSeqNo && blp == 0) {
+                    break;
+                }
+            }
+
+            ++it;
+        }
+
+        if (!foundSeqNo || blp != 0) {
+            ALOGI("Some sequence numbers were no longer available for "
+                  "retransmission (seqNo = %d, foundSeqNo = %d, blp = 0x%04x)",
+                  seqNo, foundSeqNo, blp);
+
+            if (!mHistory.empty()) {
+                int32_t earliest = (*mHistory.begin())->int32Data() & 0xffff;
+                int32_t latest = (*--mHistory.end())->int32Data() & 0xffff;
+
+                ALOGI("have seq numbers from %d - %d", earliest, latest);
+            }
+        }
+    }
+
+    return OK;
+}
+
+void RTPSender::notifyInitDone(status_t err) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatInitDone);
+    notify->setInt32("err", err);
+    notify->post();
+}
+
+void RTPSender::notifyError(status_t err) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatError);
+    notify->setInt32("err", err);
+    notify->post();
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.h b/media/libstagefright/wifi-display/rtp/RTPSender.h
new file mode 100644
index 0000000..2b683a4
--- /dev/null
+++ b/media/libstagefright/wifi-display/rtp/RTPSender.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright 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 RTP_SENDER_H_
+
+#define RTP_SENDER_H_
+
+#include "RTPBase.h"
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+struct ABuffer;
+struct ANetworkSession;
+
+// An object of this class facilitates sending of media data over an RTP
+// channel. The channel is established over a UDP or TCP connection depending
+// on which "TransportMode" was chosen. In addition different RTP packetization
+// schemes are supported such as "Transport Stream Packets over RTP",
+// or "AVC/H.264 encapsulation as specified in RFC 3984 (non-interleaved mode)"
+struct RTPSender : public RTPBase, public AHandler {
+    enum {
+        kWhatInitDone,
+        kWhatError,
+    };
+    RTPSender(
+            const sp<ANetworkSession> &netSession,
+            const sp<AMessage> &notify);
+
+    status_t initAsync(
+              TransportMode mode,
+              const char *remoteHost,
+              int32_t remoteRTPPort,
+              int32_t remoteRTCPPort,
+              int32_t *outLocalRTPPort);
+
+    status_t queueBuffer(
+            const sp<ABuffer> &buffer,
+            uint8_t packetType,
+            PacketizationMode mode);
+
+protected:
+    virtual ~RTPSender();
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    enum {
+        kWhatRTPNotify,
+        kWhatRTCPNotify,
+    };
+
+    enum {
+        kMaxNumTSPacketsPerRTPPacket = (kMaxUDPPacketSize - 12) / 188,
+        kMaxHistorySize              = 1024,
+        kSourceID                    = 0xdeadbeef,
+    };
+
+    sp<ANetworkSession> mNetSession;
+    sp<AMessage> mNotify;
+    TransportMode mMode;
+    int32_t mRTPSessionID;
+    int32_t mRTCPSessionID;
+    bool mRTPConnected;
+    bool mRTCPConnected;
+
+    uint64_t mLastNTPTime;
+    uint32_t mLastRTPTime;
+    uint32_t mNumRTPSent;
+    uint32_t mNumRTPOctetsSent;
+    uint32_t mNumSRsSent;
+
+    uint32_t mRTPSeqNo;
+
+    List<sp<ABuffer> > mHistory;
+    size_t mHistorySize;
+
+    static uint64_t GetNowNTP();
+
+    status_t queueTSPackets(const sp<ABuffer> &tsPackets, uint8_t packetType);
+    status_t queueAVCBuffer(const sp<ABuffer> &accessUnit, uint8_t packetType);
+
+    status_t sendRTPPacket(const sp<ABuffer> &packet, bool storeInHistory);
+
+    void onNetNotify(bool isRTP, const sp<AMessage> &msg);
+
+    status_t onRTCPData(const sp<ABuffer> &data);
+    status_t parseReceiverReport(const uint8_t *data, size_t size);
+    status_t parseTSFB(const uint8_t *data, size_t size);
+
+    void notifyInitDone(status_t err);
+    void notifyError(status_t err);
+
+    DISALLOW_EVIL_CONSTRUCTORS(RTPSender);
+};
+
+}  // namespace android
+
+#endif  // RTP_SENDER_H_
diff --git a/media/libstagefright/wifi-display/rtptest.cpp b/media/libstagefright/wifi-display/rtptest.cpp
new file mode 100644
index 0000000..607d9d2
--- /dev/null
+++ b/media/libstagefright/wifi-display/rtptest.cpp
@@ -0,0 +1,382 @@
+/*
+ * Copyright 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_NEBUG 0
+#define LOG_TAG "rtptest"
+#include <utils/Log.h>
+
+#include "ANetworkSession.h"
+#include "rtp/RTPSender.h"
+#include "rtp/RTPReceiver.h"
+
+#include <binder/ProcessState.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AHandler.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/NuMediaExtractor.h>
+
+namespace android {
+
+struct TestHandler : public AHandler {
+    TestHandler(const sp<ANetworkSession> &netSession);
+
+    void listen();
+    void connect(const char *host, int32_t port);
+
+protected:
+    virtual ~TestHandler();
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    enum {
+        kWhatListen,
+        kWhatConnect,
+        kWhatReceiverNotify,
+        kWhatSenderNotify,
+        kWhatSendMore,
+        kWhatStop,
+    };
+
+    sp<ANetworkSession> mNetSession;
+    sp<NuMediaExtractor> mExtractor;
+    sp<RTPSender> mSender;
+    sp<RTPReceiver> mReceiver;
+
+    size_t mMaxSampleSize;
+
+    int64_t mFirstTimeRealUs;
+    int64_t mFirstTimeMediaUs;
+
+    status_t readMore();
+
+    DISALLOW_EVIL_CONSTRUCTORS(TestHandler);
+};
+
+TestHandler::TestHandler(const sp<ANetworkSession> &netSession)
+    : mNetSession(netSession),
+      mMaxSampleSize(1024 * 1024),
+      mFirstTimeRealUs(-1ll),
+      mFirstTimeMediaUs(-1ll) {
+}
+
+TestHandler::~TestHandler() {
+}
+
+void TestHandler::listen() {
+    sp<AMessage> msg = new AMessage(kWhatListen, id());
+    msg->post();
+}
+
+void TestHandler::connect(const char *host, int32_t port) {
+    sp<AMessage> msg = new AMessage(kWhatConnect, id());
+    msg->setString("host", host);
+    msg->setInt32("port", port);
+    msg->post();
+}
+
+void TestHandler::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatListen:
+        {
+            sp<AMessage> notify = new AMessage(kWhatReceiverNotify, id());
+            mReceiver = new RTPReceiver(mNetSession, notify);
+            looper()->registerHandler(mReceiver);
+
+            CHECK_EQ((status_t)OK,
+                     mReceiver->registerPacketType(
+                         33, RTPReceiver::PACKETIZATION_H264));
+
+            int32_t receiverRTPPort;
+            CHECK_EQ((status_t)OK,
+                     mReceiver->initAsync(
+                         RTPReceiver::TRANSPORT_UDP, &receiverRTPPort));
+
+            printf("picked receiverRTPPort %d\n", receiverRTPPort);
+
+#if 0
+            CHECK_EQ((status_t)OK,
+                     mReceiver->connect(
+                         "127.0.0.1", senderRTPPort, senderRTPPort + 1));
+#endif
+            break;
+        }
+
+        case kWhatConnect:
+        {
+            AString host;
+            CHECK(msg->findString("host", &host));
+
+            int32_t receiverRTPPort;
+            CHECK(msg->findInt32("port", &receiverRTPPort));
+
+            mExtractor = new NuMediaExtractor;
+            CHECK_EQ((status_t)OK,
+                     mExtractor->setDataSource(
+                             "/sdcard/Frame Counter HD 30FPS_1080p.mp4"));
+
+            bool haveVideo = false;
+            for (size_t i = 0; i < mExtractor->countTracks(); ++i) {
+                sp<AMessage> format;
+                CHECK_EQ((status_t)OK, mExtractor->getTrackFormat(i, &format));
+
+                AString mime;
+                CHECK(format->findString("mime", &mime));
+
+                if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str())) {
+                    mExtractor->selectTrack(i);
+                    haveVideo = true;
+                    break;
+                }
+            }
+
+            CHECK(haveVideo);
+
+            sp<AMessage> notify = new AMessage(kWhatSenderNotify, id());
+            mSender = new RTPSender(mNetSession, notify);
+            looper()->registerHandler(mSender);
+
+            int32_t senderRTPPort;
+            CHECK_EQ((status_t)OK,
+                     mSender->initAsync(
+                         RTPSender::TRANSPORT_UDP,
+                         host.c_str(),
+                         receiverRTPPort,
+                         receiverRTPPort + 1,
+                         &senderRTPPort));
+
+            printf("picked senderRTPPort %d\n", senderRTPPort);
+            break;
+        }
+
+        case kWhatSenderNotify:
+        {
+            ALOGI("kWhatSenderNotify");
+
+            int32_t what;
+            CHECK(msg->findInt32("what", &what));
+
+            switch (what) {
+                case RTPSender::kWhatInitDone:
+                {
+                    int32_t err;
+                    CHECK(msg->findInt32("err", &err));
+
+                    ALOGI("RTPSender::initAsync completed w/ err %d", err);
+
+                    if (err == OK) {
+                        err = readMore();
+
+                        if (err != OK) {
+                            (new AMessage(kWhatStop, id()))->post();
+                        }
+                    }
+                    break;
+                }
+
+                case RTPSender::kWhatError:
+                    break;
+            }
+            break;
+        }
+
+        case kWhatReceiverNotify:
+        {
+            ALOGI("kWhatReceiverNotify");
+
+            int32_t what;
+            CHECK(msg->findInt32("what", &what));
+
+            switch (what) {
+                case RTPReceiver::kWhatInitDone:
+                {
+                    int32_t err;
+                    CHECK(msg->findInt32("err", &err));
+
+                    ALOGI("RTPReceiver::initAsync completed w/ err %d", err);
+                    break;
+                }
+
+                case RTPSender::kWhatError:
+                    break;
+            }
+            break;
+        }
+
+        case kWhatSendMore:
+        {
+            sp<ABuffer> accessUnit;
+            CHECK(msg->findBuffer("accessUnit", &accessUnit));
+
+            CHECK_EQ((status_t)OK,
+                     mSender->queueBuffer(
+                         accessUnit,
+                         33,
+                         RTPSender::PACKETIZATION_H264));
+
+            status_t err = readMore();
+
+            if (err != OK) {
+                (new AMessage(kWhatStop, id()))->post();
+            }
+            break;
+        }
+
+        case kWhatStop:
+        {
+            if (mReceiver != NULL) {
+                looper()->unregisterHandler(mReceiver->id());
+                mReceiver.clear();
+            }
+
+            if (mSender != NULL) {
+                looper()->unregisterHandler(mSender->id());
+                mSender.clear();
+            }
+
+            mExtractor.clear();
+
+            looper()->stop();
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+status_t TestHandler::readMore() {
+    int64_t timeUs;
+    status_t err = mExtractor->getSampleTime(&timeUs);
+
+    if (err != OK) {
+        return err;
+    }
+
+    sp<ABuffer> accessUnit = new ABuffer(mMaxSampleSize);
+    CHECK_EQ((status_t)OK, mExtractor->readSampleData(accessUnit));
+
+    accessUnit->meta()->setInt64("timeUs", timeUs);
+
+    CHECK_EQ((status_t)OK, mExtractor->advance());
+
+    int64_t nowUs = ALooper::GetNowUs();
+    int64_t whenUs;
+
+    if (mFirstTimeRealUs < 0ll) {
+        mFirstTimeRealUs = whenUs = nowUs;
+        mFirstTimeMediaUs = timeUs;
+    } else {
+        whenUs = mFirstTimeRealUs + timeUs - mFirstTimeMediaUs;
+    }
+
+    sp<AMessage> msg = new AMessage(kWhatSendMore, id());
+    msg->setBuffer("accessUnit", accessUnit);
+    msg->post(whenUs - nowUs);
+
+    return OK;
+}
+
+}  // namespace android
+
+static void usage(const char *me) {
+    fprintf(stderr,
+            "usage: %s -c host:port\tconnect to remote host\n"
+            "               -l       \tlisten\n",
+            me);
+}
+
+int main(int argc, char **argv) {
+    using namespace android;
+
+    // srand(time(NULL));
+
+    ProcessState::self()->startThreadPool();
+
+    DataSource::RegisterDefaultSniffers();
+
+    bool listen = false;
+    int32_t connectToPort = -1;
+    AString connectToHost;
+
+    int res;
+    while ((res = getopt(argc, argv, "hc:l")) >= 0) {
+        switch (res) {
+            case 'c':
+            {
+                const char *colonPos = strrchr(optarg, ':');
+
+                if (colonPos == NULL) {
+                    usage(argv[0]);
+                    exit(1);
+                }
+
+                connectToHost.setTo(optarg, colonPos - optarg);
+
+                char *end;
+                connectToPort = strtol(colonPos + 1, &end, 10);
+
+                if (*end != '\0' || end == colonPos + 1
+                        || connectToPort < 1 || connectToPort > 65535) {
+                    fprintf(stderr, "Illegal port specified.\n");
+                    exit(1);
+                }
+                break;
+            }
+
+            case 'l':
+            {
+                listen = true;
+                break;
+            }
+
+            case '?':
+            case 'h':
+                usage(argv[0]);
+                exit(1);
+        }
+    }
+
+    if (!listen && connectToPort < 0) {
+        fprintf(stderr,
+                "You need to select either client or server mode.\n");
+        exit(1);
+    }
+
+    sp<ANetworkSession> netSession = new ANetworkSession;
+    netSession->start();
+
+    sp<ALooper> looper = new ALooper;
+
+    sp<TestHandler> handler = new TestHandler(netSession);
+    looper->registerHandler(handler);
+
+    if (listen) {
+        handler->listen();
+    }
+
+    if (connectToPort >= 0) {
+        handler->connect(connectToHost.c_str(), connectToPort);
+    }
+
+    looper->start(true /* runOnCallingThread */);
+
+    return 0;
+}
+
diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp
index 70369bb..b53252d 100644
--- a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp
+++ b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp
@@ -20,15 +20,13 @@
 
 #include "DirectRenderer.h"
 
-#include "AnotherPacketSource.h"
-#include "ATSParser.h"
-
 #include <gui/SurfaceComposerClient.h>
 #include <gui/Surface.h>
 #include <media/ICrypto.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/MediaCodec.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
@@ -36,30 +34,13 @@
 
 namespace android {
 
-#if 1
-// static
-const int64_t DirectRenderer::kPacketLostDelayUs = 80000ll;
-
-// static
-const int64_t DirectRenderer::kPacketLateDelayUs = 60000ll;
-#else
-// static
-const int64_t DirectRenderer::kPacketLostDelayUs = 1000000ll;
-
-// static
-const int64_t DirectRenderer::kPacketLateDelayUs = -1ll;
-#endif
-
 DirectRenderer::DirectRenderer(
-        const sp<AMessage> &notifyLost,
         const sp<IGraphicBufferProducer> &bufferProducer)
-    : mNotifyLost(notifyLost),
-      mSurfaceTex(bufferProducer),
-      mTSParser(new ATSParser(ATSParser::ALIGNED_VIDEO_DATA)),
+    : mSurfaceTex(bufferProducer),
       mVideoDecoderNotificationPending(false),
-      mAwaitingExtSeqNo(-1),
-      mRequestedRetransmission(false),
-      mPacketLostGeneration(0) {
+      mRenderPending(false),
+      mFirstRenderTimeUs(-1ll),
+      mFirstRenderRealUs(-1ll) {
 }
 
 DirectRenderer::~DirectRenderer() {
@@ -74,263 +55,84 @@
 
 void DirectRenderer::onMessageReceived(const sp<AMessage> &msg) {
     switch (msg->what()) {
-        case kWhatQueueBuffer:
-        {
-            sp<ABuffer> buffer;
-            CHECK(msg->findBuffer("buffer", &buffer));
-
-            onQueueBuffer(buffer);
-
-            dequeueMore();
-            break;
-        }
-
-        case kWhatPacketLate:
-        case kWhatPacketLost:
-        {
-            int32_t generation;
-            CHECK(msg->findInt32("generation", &generation));
-
-            if (generation != mPacketLostGeneration) {
-                // stale.
-                break;
-            }
-
-            if (msg->what() == kWhatPacketLate) {
-                CHECK(!mRequestedRetransmission);
-                CHECK_GE(mAwaitingExtSeqNo, 0);
-
-                ALOGV("packet extSeqNo %d is late, requesting retransmission.",
-                      mAwaitingExtSeqNo);
-
-                sp<AMessage> notify = mNotifyLost->dup();
-                notify->setInt32("seqNo", (mAwaitingExtSeqNo & 0xffff));
-                notify->post();
-
-                mRequestedRetransmission = true;
-                break;
-            }
-
-            ALOGW("lost packet extSeqNo %d", mAwaitingExtSeqNo);
-
-            sp<AMessage> extra;
-            mTSParser->signalDiscontinuity(
-                    ATSParser::DISCONTINUITY_TIME, extra);
-
-            mAwaitingExtSeqNo = -1;
-            mRequestedRetransmission = false;
-            dequeueMore();
-            break;
-        }
-
         case kWhatVideoDecoderNotify:
         {
             onVideoDecoderNotify();
             break;
         }
 
+        case kWhatRender:
+        {
+            onRender();
+            break;
+        }
+
         default:
             TRESPASS();
     }
 }
 
-void DirectRenderer::onQueueBuffer(const sp<ABuffer> &buffer) {
-    int32_t newExtendedSeqNo = buffer->int32Data();
-
-    if (mPackets.empty()) {
-        mPackets.push_back(buffer);
+void DirectRenderer::setFormat(
+        size_t trackIndex, const sp<AMessage> &format) {
+    if (trackIndex == 1) {
+        // Ignore audio for now.
         return;
     }
 
-    if (mAwaitingExtSeqNo > 0 && newExtendedSeqNo < mAwaitingExtSeqNo) {
-        // We're no longer interested in these. They're old.
+    CHECK(mVideoDecoder == NULL);
+
+    AString mime;
+    CHECK(format->findString("mime", &mime));
+
+    mVideoDecoderLooper = new ALooper;
+    mVideoDecoderLooper->setName("video codec looper");
+
+    mVideoDecoderLooper->start(
+            false /* runOnCallingThread */,
+            false /* canCallJava */,
+            PRIORITY_DEFAULT);
+
+    mVideoDecoder = MediaCodec::CreateByType(
+            mVideoDecoderLooper, mime.c_str(), false /* encoder */);
+
+    CHECK(mVideoDecoder != NULL);
+
+    status_t err = mVideoDecoder->configure(
+            format,
+            mSurfaceTex == NULL
+                ? NULL : new Surface(mSurfaceTex),
+            NULL /* crypto */,
+            0 /* flags */);
+    CHECK_EQ(err, (status_t)OK);
+
+    err = mVideoDecoder->start();
+    CHECK_EQ(err, (status_t)OK);
+
+    err = mVideoDecoder->getInputBuffers(
+            &mVideoDecoderInputBuffers);
+    CHECK_EQ(err, (status_t)OK);
+
+    scheduleVideoDecoderNotification();
+}
+
+void DirectRenderer::queueAccessUnit(
+        size_t trackIndex, const sp<ABuffer> &accessUnit) {
+    if (trackIndex == 1) {
+        // Ignore audio for now.
         return;
     }
 
-    List<sp<ABuffer> >::iterator firstIt = mPackets.begin();
-    List<sp<ABuffer> >::iterator it = --mPackets.end();
-    for (;;) {
-        int32_t extendedSeqNo = (*it)->int32Data();
+    if (mVideoDecoder == NULL) {
+        sp<AMessage> format = new AMessage;
+        format->setString("mime", "video/avc");
+        format->setInt32("width", 640);
+        format->setInt32("height", 360);
 
-        if (extendedSeqNo == newExtendedSeqNo) {
-            // Duplicate packet.
-            return;
-        }
-
-        if (extendedSeqNo < newExtendedSeqNo) {
-            // Insert new packet after the one at "it".
-            mPackets.insert(++it, buffer);
-            return;
-        }
-
-        if (it == firstIt) {
-            // Insert new packet before the first existing one.
-            mPackets.insert(it, buffer);
-            return;
-        }
-
-        --it;
-    }
-}
-
-void DirectRenderer::dequeueMore() {
-    if (mAwaitingExtSeqNo >= 0) {
-        // Remove all packets before the one we're looking for, they had
-        // their chance.
-        while (!mPackets.empty()
-                && (*mPackets.begin())->int32Data() < mAwaitingExtSeqNo) {
-            ALOGV("dropping late packet extSeqNo %d",
-                  (*mPackets.begin())->int32Data());
-
-            mPackets.erase(mPackets.begin());
-        }
+        setFormat(0, format);
     }
 
-    bool packetLostScheduled = (mAwaitingExtSeqNo >= 0);
-
-    while (!mPackets.empty()) {
-        sp<ABuffer> buffer = *mPackets.begin();
-        int32_t extSeqNo = buffer->int32Data();
-
-        if (mAwaitingExtSeqNo >= 0 && extSeqNo != mAwaitingExtSeqNo) {
-            break;
-        }
-
-        mPackets.erase(mPackets.begin());
-
-        if (packetLostScheduled) {
-            packetLostScheduled = false;
-            cancelPacketLost();
-        }
-
-        if (mRequestedRetransmission) {
-            ALOGV("recovered after requesting retransmission of extSeqNo %d",
-                  mAwaitingExtSeqNo);
-        }
-
-        CHECK_EQ(buffer->size() % 188, 0u);
-
-        for (size_t offset = 0; offset < buffer->size(); offset += 188) {
-            status_t err = mTSParser->feedTSPacket(
-                    buffer->data() + offset, 188);
-
-            CHECK_EQ(err, (status_t)OK);
-        }
-
-        mAwaitingExtSeqNo = extSeqNo + 1;
-        mRequestedRetransmission = false;
-    }
-
-    if (!packetLostScheduled && mAwaitingExtSeqNo >= 0) {
-        schedulePacketLost();
-    }
-
-    dequeueAccessUnits();
-}
-
-void DirectRenderer::dequeueAccessUnits() {
-    sp<AnotherPacketSource> audioSource =
-        static_cast<AnotherPacketSource *>(
-                mTSParser->getSource(ATSParser::AUDIO).get());
-
-    if (audioSource != NULL) {
-        status_t finalResult;
-        size_t n = 0;
-        while (audioSource->hasBufferAvailable(&finalResult)) {
-            sp<ABuffer> accessUnit;
-            status_t err = audioSource->dequeueAccessUnit(&accessUnit);
-            if (err == OK) {
-                ++n;
-            }
-        }
-
-        if (n > 0) {
-            ALOGV("dequeued %d audio access units.", n);
-        }
-    }
-
-    sp<AnotherPacketSource> videoSource =
-        static_cast<AnotherPacketSource *>(
-                mTSParser->getSource(ATSParser::VIDEO).get());
-
-    if (videoSource != NULL) {
-        if (mVideoDecoder == NULL) {
-            sp<MetaData> meta = videoSource->getFormat();
-            if (meta != NULL) {
-                sp<AMessage> videoFormat;
-                status_t err = convertMetaDataToMessage(meta, &videoFormat);
-                CHECK_EQ(err, (status_t)OK);
-
-                AString mime;
-                CHECK(videoFormat->findString("mime", &mime));
-
-                mVideoDecoderLooper = new ALooper;
-                mVideoDecoderLooper->setName("video codec looper");
-
-                mVideoDecoderLooper->start(
-                        false /* runOnCallingThread */,
-                        false /* canCallJava */,
-                        PRIORITY_DEFAULT);
-
-                mVideoDecoder = MediaCodec::CreateByType(
-                        mVideoDecoderLooper, mime.c_str(), false /* encoder */);
-
-                CHECK(mVideoDecoder != NULL);
-
-                err = mVideoDecoder->configure(
-                        videoFormat,
-                        mSurfaceTex == NULL
-                            ? NULL : new Surface(mSurfaceTex),
-                        NULL /* crypto */,
-                        0 /* flags */);
-
-                CHECK_EQ(err, (status_t)OK);
-
-                err = mVideoDecoder->start();
-                CHECK_EQ(err, (status_t)OK);
-
-                err = mVideoDecoder->getInputBuffers(
-                        &mVideoDecoderInputBuffers);
-                CHECK_EQ(err, (status_t)OK);
-
-                scheduleVideoDecoderNotification();
-            }
-        }
-
-        status_t finalResult;
-        size_t n = 0;
-        while (videoSource->hasBufferAvailable(&finalResult)) {
-            sp<ABuffer> accessUnit;
-            status_t err = videoSource->dequeueAccessUnit(&accessUnit);
-            if (err == OK) {
-                mVideoAccessUnits.push_back(accessUnit);
-                ++n;
-            }
-        }
-
-        if (n > 0) {
-            ALOGV("dequeued %d video access units.", n);
-            queueVideoDecoderInputBuffers();
-        }
-    }
-}
-
-void DirectRenderer::schedulePacketLost() {
-    sp<AMessage> msg;
-
-    if (kPacketLateDelayUs > 0ll) {
-        msg = new AMessage(kWhatPacketLate, id());
-        msg->setInt32("generation", mPacketLostGeneration);
-        msg->post(kPacketLateDelayUs);
-    }
-
-    msg = new AMessage(kWhatPacketLost, id());
-    msg->setInt32("generation", mPacketLostGeneration);
-    msg->post(kPacketLostDelayUs);
-}
-
-void DirectRenderer::cancelPacketLost() {
-    ++mPacketLostGeneration;
+    mVideoAccessUnits.push_back(accessUnit);
+    queueVideoDecoderInputBuffers();
 }
 
 void DirectRenderer::queueVideoDecoderInputBuffers() {
@@ -406,8 +208,7 @@
                 &flags);
 
         if (err == OK) {
-            err = mVideoDecoder->renderOutputBufferAndRelease(index);
-            CHECK_EQ(err, (status_t)OK);
+            queueOutputBuffer(index, timeUs);
         } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) {
             // We don't care.
         } else if (err == INFO_FORMAT_CHANGED) {
@@ -422,6 +223,62 @@
     scheduleVideoDecoderNotification();
 }
 
+void DirectRenderer::queueOutputBuffer(size_t index, int64_t timeUs) {
+#if 0
+    OutputInfo info;
+    info.mIndex = index;
+    info.mTimeUs = timeUs;
+    mOutputBuffers.push_back(info);
+
+    scheduleRenderIfNecessary();
+#else
+    status_t err = mVideoDecoder->renderOutputBufferAndRelease(index);
+    CHECK_EQ(err, (status_t)OK);
+#endif
+}
+
+void DirectRenderer::scheduleRenderIfNecessary() {
+    if (mRenderPending || mOutputBuffers.empty()) {
+        return;
+    }
+
+    mRenderPending = true;
+
+    int64_t timeUs = (*mOutputBuffers.begin()).mTimeUs;
+    int64_t nowUs = ALooper::GetNowUs();
+
+    if (mFirstRenderTimeUs < 0ll) {
+        mFirstRenderTimeUs = timeUs;
+        mFirstRenderRealUs = nowUs;
+    }
+
+    int64_t whenUs = timeUs - mFirstRenderTimeUs + mFirstRenderRealUs;
+    int64_t delayUs = whenUs - nowUs;
+
+    (new AMessage(kWhatRender, id()))->post(delayUs);
+}
+
+void DirectRenderer::onRender() {
+    mRenderPending = false;
+
+    int64_t nowUs = ALooper::GetNowUs();
+
+    while (!mOutputBuffers.empty()) {
+        const OutputInfo &info = *mOutputBuffers.begin();
+
+        if (info.mTimeUs > nowUs) {
+            break;
+        }
+
+        status_t err = mVideoDecoder->renderOutputBufferAndRelease(info.mIndex);
+        CHECK_EQ(err, (status_t)OK);
+
+        mOutputBuffers.erase(mOutputBuffers.begin());
+    }
+
+    scheduleRenderIfNecessary();
+}
+
 void DirectRenderer::scheduleVideoDecoderNotification() {
     if (mVideoDecoderNotificationPending) {
         return;
diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.h b/media/libstagefright/wifi-display/sink/DirectRenderer.h
index 2babcb8..7219080 100644
--- a/media/libstagefright/wifi-display/sink/DirectRenderer.h
+++ b/media/libstagefright/wifi-display/sink/DirectRenderer.h
@@ -23,7 +23,6 @@
 namespace android {
 
 struct ABuffer;
-struct ATSParser;
 struct IGraphicBufferProducer;
 struct MediaCodec;
 
@@ -32,13 +31,10 @@
 // delay. Primarily meant to finetune packet loss discovery and minimize
 // latency.
 struct DirectRenderer : public AHandler {
-    DirectRenderer(
-            const sp<AMessage> &notifyLost,
-            const sp<IGraphicBufferProducer> &bufferProducer);
+    DirectRenderer(const sp<IGraphicBufferProducer> &bufferProducer);
 
-    enum {
-        kWhatQueueBuffer = 'queB',
-    };
+    void setFormat(size_t trackIndex, const sp<AMessage> &format);
+    void queueAccessUnit(size_t trackIndex, const sp<ABuffer> &accessUnit);
 
 protected:
     virtual void onMessageReceived(const sp<AMessage> &msg);
@@ -46,22 +42,17 @@
 
 private:
     enum {
-        kWhatPacketLate,
-        kWhatPacketLost,
         kWhatVideoDecoderNotify,
+        kWhatRender,
     };
 
-    static const int64_t kPacketLateDelayUs;
-    static const int64_t kPacketLostDelayUs;
+    struct OutputInfo {
+        size_t mIndex;
+        int64_t mTimeUs;
+    };
 
-    sp<AMessage> mNotifyLost;
     sp<IGraphicBufferProducer> mSurfaceTex;
 
-    // Ordered by extended seq number.
-    List<sp<ABuffer> > mPackets;
-
-    sp<ATSParser> mTSParser;
-
     sp<ALooper> mVideoDecoderLooper;
     sp<MediaCodec> mVideoDecoder;
     Vector<sp<ABuffer> > mVideoDecoderInputBuffers;
@@ -70,21 +61,19 @@
 
     List<sp<ABuffer> > mVideoAccessUnits;
 
-    int32_t mAwaitingExtSeqNo;
-    bool mRequestedRetransmission;
-    int32_t mPacketLostGeneration;
+    List<OutputInfo> mOutputBuffers;
+    bool mRenderPending;
+    int64_t mFirstRenderTimeUs;
+    int64_t mFirstRenderRealUs;
 
-    void onQueueBuffer(const sp<ABuffer> &buffer);
     void onVideoDecoderNotify();
-
-    void dequeueMore();
-    void dequeueAccessUnits();
-
-    void schedulePacketLost();
-    void cancelPacketLost();
+    void onRender();
 
     void queueVideoDecoderInputBuffers();
     void scheduleVideoDecoderNotification();
+    void scheduleRenderIfNecessary();
+
+    void queueOutputBuffer(size_t index, int64_t timeUs);
 
     DISALLOW_EVIL_CONSTRUCTORS(DirectRenderer);
 };
diff --git a/media/libstagefright/wifi-display/sink/LinearRegression.cpp b/media/libstagefright/wifi-display/sink/LinearRegression.cpp
deleted file mode 100644
index 8cfce37..0000000
--- a/media/libstagefright/wifi-display/sink/LinearRegression.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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 "LinearRegression"
-#include <utils/Log.h>
-
-#include "LinearRegression.h"
-
-#include <math.h>
-#include <string.h>
-
-namespace android {
-
-LinearRegression::LinearRegression(size_t historySize)
-    : mHistorySize(historySize),
-      mCount(0),
-      mHistory(new Point[mHistorySize]),
-      mSumX(0.0),
-      mSumY(0.0) {
-}
-
-LinearRegression::~LinearRegression() {
-    delete[] mHistory;
-    mHistory = NULL;
-}
-
-void LinearRegression::addPoint(float x, float y) {
-    if (mCount == mHistorySize) {
-        const Point &oldest = mHistory[0];
-
-        mSumX -= oldest.mX;
-        mSumY -= oldest.mY;
-
-        memmove(&mHistory[0], &mHistory[1], (mHistorySize - 1) * sizeof(Point));
-        --mCount;
-    }
-
-    Point *newest = &mHistory[mCount++];
-    newest->mX = x;
-    newest->mY = y;
-
-    mSumX += x;
-    mSumY += y;
-}
-
-bool LinearRegression::approxLine(float *n1, float *n2, float *b) const {
-    static const float kEpsilon = 1.0E-4;
-
-    if (mCount < 2) {
-        return false;
-    }
-
-    float sumX2 = 0.0f;
-    float sumY2 = 0.0f;
-    float sumXY = 0.0f;
-
-    float meanX = mSumX / (float)mCount;
-    float meanY = mSumY / (float)mCount;
-
-    for (size_t i = 0; i < mCount; ++i) {
-        const Point &p = mHistory[i];
-
-        float x = p.mX - meanX;
-        float y = p.mY - meanY;
-
-        sumX2 += x * x;
-        sumY2 += y * y;
-        sumXY += x * y;
-    }
-
-    float T = sumX2 + sumY2;
-    float D = sumX2 * sumY2 - sumXY * sumXY;
-    float root = sqrt(T * T * 0.25 - D);
-
-    float L1 = T * 0.5 - root;
-
-    if (fabs(sumXY) > kEpsilon) {
-        *n1 = 1.0;
-        *n2 = (2.0 * L1 - sumX2) / sumXY;
-
-        float mag = sqrt((*n1) * (*n1) + (*n2) * (*n2));
-
-        *n1 /= mag;
-        *n2 /= mag;
-    } else {
-        *n1 = 0.0;
-        *n2 = 1.0;
-    }
-
-    *b = (*n1) * meanX + (*n2) * meanY;
-
-    return true;
-}
-
-}  // namespace android
-
diff --git a/media/libstagefright/wifi-display/sink/LinearRegression.h b/media/libstagefright/wifi-display/sink/LinearRegression.h
deleted file mode 100644
index ca6f5a1..0000000
--- a/media/libstagefright/wifi-display/sink/LinearRegression.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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 LINEAR_REGRESSION_H_
-
-#define LINEAR_REGRESSION_H_
-
-#include <sys/types.h>
-#include <media/stagefright/foundation/ABase.h>
-
-namespace android {
-
-// Helper class to fit a line to a set of points minimizing the sum of
-// squared (orthogonal) distances from line to individual points.
-struct LinearRegression {
-    LinearRegression(size_t historySize);
-    ~LinearRegression();
-
-    void addPoint(float x, float y);
-
-    bool approxLine(float *n1, float *n2, float *b) const;
-
-private:
-    struct Point {
-        float mX, mY;
-    };
-
-    size_t mHistorySize;
-    size_t mCount;
-    Point *mHistory;
-
-    float mSumX, mSumY;
-
-    DISALLOW_EVIL_CONSTRUCTORS(LinearRegression);
-};
-
-}  // namespace android
-
-#endif  // LINEAR_REGRESSION_H_
diff --git a/media/libstagefright/wifi-display/sink/RTPSink.cpp b/media/libstagefright/wifi-display/sink/RTPSink.cpp
deleted file mode 100644
index 3c90a1e..0000000
--- a/media/libstagefright/wifi-display/sink/RTPSink.cpp
+++ /dev/null
@@ -1,870 +0,0 @@
-/*
- * 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 "RTPSink"
-#include <utils/Log.h>
-
-#include "RTPSink.h"
-
-#include "ANetworkSession.h"
-
-#if USE_TUNNEL_RENDERER
-#include "TunnelRenderer.h"
-#define RENDERER_CLASS TunnelRenderer
-#else
-#include "DirectRenderer.h"
-#define RENDERER_CLASS DirectRenderer
-#endif
-
-#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/MediaErrors.h>
-#include <media/stagefright/Utils.h>
-
-namespace android {
-
-struct RTPSink::Source : public RefBase {
-    Source(uint16_t seq, const sp<ABuffer> &buffer,
-           const sp<AMessage> queueBufferMsg);
-
-    bool updateSeq(uint16_t seq, const sp<ABuffer> &buffer);
-
-    void addReportBlock(uint32_t ssrc, const sp<ABuffer> &buf);
-
-protected:
-    virtual ~Source();
-
-private:
-    static const uint32_t kMinSequential = 2;
-    static const uint32_t kMaxDropout = 3000;
-    static const uint32_t kMaxMisorder = 100;
-    static const uint32_t kRTPSeqMod = 1u << 16;
-
-    sp<AMessage> mQueueBufferMsg;
-
-    uint16_t mMaxSeq;
-    uint32_t mCycles;
-    uint32_t mBaseSeq;
-    uint32_t mBadSeq;
-    uint32_t mProbation;
-    uint32_t mReceived;
-    uint32_t mExpectedPrior;
-    uint32_t mReceivedPrior;
-
-    void initSeq(uint16_t seq);
-    void queuePacket(const sp<ABuffer> &buffer);
-
-    DISALLOW_EVIL_CONSTRUCTORS(Source);
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
-RTPSink::Source::Source(
-        uint16_t seq, const sp<ABuffer> &buffer,
-        const sp<AMessage> queueBufferMsg)
-    : mQueueBufferMsg(queueBufferMsg),
-      mProbation(kMinSequential) {
-    initSeq(seq);
-    mMaxSeq = seq - 1;
-
-    buffer->setInt32Data(mCycles | seq);
-    queuePacket(buffer);
-}
-
-RTPSink::Source::~Source() {
-}
-
-void RTPSink::Source::initSeq(uint16_t seq) {
-    mMaxSeq = seq;
-    mCycles = 0;
-    mBaseSeq = seq;
-    mBadSeq = kRTPSeqMod + 1;
-    mReceived = 0;
-    mExpectedPrior = 0;
-    mReceivedPrior = 0;
-}
-
-bool RTPSink::Source::updateSeq(uint16_t seq, const sp<ABuffer> &buffer) {
-    uint16_t udelta = seq - mMaxSeq;
-
-    if (mProbation) {
-        // Startup phase
-
-        if (seq == mMaxSeq + 1) {
-            buffer->setInt32Data(mCycles | seq);
-            queuePacket(buffer);
-
-            --mProbation;
-            mMaxSeq = seq;
-            if (mProbation == 0) {
-                initSeq(seq);
-                ++mReceived;
-
-                return true;
-            }
-        } else {
-            // Packet out of sequence, restart startup phase
-
-            mProbation = kMinSequential - 1;
-            mMaxSeq = seq;
-
-#if 0
-            mPackets.clear();
-            mTotalBytesQueued = 0;
-            ALOGI("XXX cleared packets");
-#endif
-
-            buffer->setInt32Data(mCycles | seq);
-            queuePacket(buffer);
-        }
-
-        return false;
-    }
-
-    if (udelta < kMaxDropout) {
-        // In order, with permissible gap.
-
-        if (seq < mMaxSeq) {
-            // Sequence number wrapped - count another 64K cycle
-            mCycles += kRTPSeqMod;
-        }
-
-        mMaxSeq = seq;
-    } else if (udelta <= kRTPSeqMod - kMaxMisorder) {
-        // The sequence number made a very large jump
-
-        if (seq == mBadSeq) {
-            // Two sequential packets -- assume that the other side
-            // restarted without telling us so just re-sync
-            // (i.e. pretend this was the first packet)
-
-            initSeq(seq);
-        } else {
-            mBadSeq = (seq + 1) & (kRTPSeqMod - 1);
-
-            return false;
-        }
-    } else {
-        // Duplicate or reordered packet.
-    }
-
-    ++mReceived;
-
-    buffer->setInt32Data(mCycles | seq);
-    queuePacket(buffer);
-
-    return true;
-}
-
-void RTPSink::Source::queuePacket(const sp<ABuffer> &buffer) {
-    sp<AMessage> msg = mQueueBufferMsg->dup();
-    msg->setBuffer("buffer", buffer);
-    msg->post();
-}
-
-void RTPSink::Source::addReportBlock(
-        uint32_t ssrc, const sp<ABuffer> &buf) {
-    uint32_t extMaxSeq = mMaxSeq | mCycles;
-    uint32_t expected = extMaxSeq - mBaseSeq + 1;
-
-    int64_t lost = (int64_t)expected - (int64_t)mReceived;
-    if (lost > 0x7fffff) {
-        lost = 0x7fffff;
-    } else if (lost < -0x800000) {
-        lost = -0x800000;
-    }
-
-    uint32_t expectedInterval = expected - mExpectedPrior;
-    mExpectedPrior = expected;
-
-    uint32_t receivedInterval = mReceived - mReceivedPrior;
-    mReceivedPrior = mReceived;
-
-    int64_t lostInterval = expectedInterval - receivedInterval;
-
-    uint8_t fractionLost;
-    if (expectedInterval == 0 || lostInterval <=0) {
-        fractionLost = 0;
-    } else {
-        fractionLost = (lostInterval << 8) / expectedInterval;
-    }
-
-    uint8_t *ptr = buf->data() + buf->size();
-
-    ptr[0] = ssrc >> 24;
-    ptr[1] = (ssrc >> 16) & 0xff;
-    ptr[2] = (ssrc >> 8) & 0xff;
-    ptr[3] = ssrc & 0xff;
-
-    ptr[4] = fractionLost;
-
-    ptr[5] = (lost >> 16) & 0xff;
-    ptr[6] = (lost >> 8) & 0xff;
-    ptr[7] = lost & 0xff;
-
-    ptr[8] = extMaxSeq >> 24;
-    ptr[9] = (extMaxSeq >> 16) & 0xff;
-    ptr[10] = (extMaxSeq >> 8) & 0xff;
-    ptr[11] = extMaxSeq & 0xff;
-
-    // XXX TODO:
-
-    ptr[12] = 0x00;  // interarrival jitter
-    ptr[13] = 0x00;
-    ptr[14] = 0x00;
-    ptr[15] = 0x00;
-
-    ptr[16] = 0x00;  // last SR
-    ptr[17] = 0x00;
-    ptr[18] = 0x00;
-    ptr[19] = 0x00;
-
-    ptr[20] = 0x00;  // delay since last SR
-    ptr[21] = 0x00;
-    ptr[22] = 0x00;
-    ptr[23] = 0x00;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-RTPSink::RTPSink(
-        const sp<ANetworkSession> &netSession,
-        const sp<IGraphicBufferProducer> &bufferProducer,
-        const sp<AMessage> &notify)
-    : mNetSession(netSession),
-      mSurfaceTex(bufferProducer),
-      mNotify(notify),
-      mUsingTCPTransport(false),
-      mUsingTCPInterleaving(false),
-      mRTPPort(0),
-      mRTPSessionID(0),
-      mRTCPSessionID(0),
-      mRTPClientSessionID(0),
-      mRTCPClientSessionID(0),
-      mFirstArrivalTimeUs(-1ll),
-      mNumPacketsReceived(0ll),
-      mRegression(1000),
-      mMaxDelayMs(-1ll) {
-}
-
-RTPSink::~RTPSink() {
-    if (mRTCPClientSessionID != 0) {
-        mNetSession->destroySession(mRTCPClientSessionID);
-    }
-
-    if (mRTPClientSessionID != 0) {
-        mNetSession->destroySession(mRTPClientSessionID);
-    }
-
-    if (mRTCPSessionID != 0) {
-        mNetSession->destroySession(mRTCPSessionID);
-    }
-
-    if (mRTPSessionID != 0) {
-        mNetSession->destroySession(mRTPSessionID);
-    }
-}
-
-status_t RTPSink::init(bool usingTCPTransport, bool usingTCPInterleaving) {
-    mUsingTCPTransport = usingTCPTransport;
-    mUsingTCPInterleaving = usingTCPInterleaving;
-
-    if (usingTCPInterleaving) {
-        return OK;
-    }
-
-    int clientRtp;
-
-    sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
-    sp<AMessage> rtcpNotify = new AMessage(kWhatRTCPNotify, id());
-    for (clientRtp = 15550;; clientRtp += 2) {
-        int32_t rtpSession;
-        status_t err;
-        struct in_addr ifaceAddr;
-        if (usingTCPTransport) {
-            ifaceAddr.s_addr = INADDR_ANY;
-            err = mNetSession->createTCPDatagramSession(
-                        ifaceAddr, clientRtp, rtpNotify, &rtpSession);
-        } else {
-            err = mNetSession->createUDPSession(
-                        clientRtp, rtpNotify, &rtpSession);
-        }
-
-        if (err != OK) {
-            ALOGI("failed to create RTP socket on port %d", clientRtp);
-            continue;
-        }
-
-        int32_t rtcpSession;
-        if (usingTCPTransport) {
-            err = mNetSession->createTCPDatagramSession(
-                    ifaceAddr, clientRtp + 1, rtcpNotify, &rtcpSession);
-        } else {
-            err = mNetSession->createUDPSession(
-                    clientRtp + 1, rtcpNotify, &rtcpSession);
-        }
-
-        if (err == OK) {
-            mRTPPort = clientRtp;
-            mRTPSessionID = rtpSession;
-            mRTCPSessionID = rtcpSession;
-            break;
-        }
-
-        ALOGI("failed to create RTCP socket on port %d", clientRtp + 1);
-        mNetSession->destroySession(rtpSession);
-    }
-
-    if (mRTPPort == 0) {
-        return UNKNOWN_ERROR;
-    }
-
-    return OK;
-}
-
-int32_t RTPSink::getRTPPort() const {
-    return mRTPPort;
-}
-
-void RTPSink::onMessageReceived(const sp<AMessage> &msg) {
-    switch (msg->what()) {
-        case kWhatRTPNotify:
-        case kWhatRTCPNotify:
-        {
-            int32_t reason;
-            CHECK(msg->findInt32("reason", &reason));
-
-            switch (reason) {
-                case ANetworkSession::kWhatError:
-                {
-                    int32_t sessionID;
-                    CHECK(msg->findInt32("sessionID", &sessionID));
-
-                    int32_t err;
-                    CHECK(msg->findInt32("err", &err));
-
-                    AString detail;
-                    CHECK(msg->findString("detail", &detail));
-
-                    ALOGE("An error occurred in session %d (%d, '%s/%s').",
-                          sessionID,
-                          err,
-                          detail.c_str(),
-                          strerror(-err));
-
-                    mNetSession->destroySession(sessionID);
-
-                    if (sessionID == mRTPSessionID) {
-                        mRTPSessionID = 0;
-                    } else if (sessionID == mRTCPSessionID) {
-                        mRTCPSessionID = 0;
-                    }
-                    break;
-                }
-
-                case ANetworkSession::kWhatDatagram:
-                {
-                    int32_t sessionID;
-                    CHECK(msg->findInt32("sessionID", &sessionID));
-
-                    sp<ABuffer> data;
-                    CHECK(msg->findBuffer("data", &data));
-
-                    status_t err;
-                    if (msg->what() == kWhatRTPNotify) {
-                        err = parseRTP(data);
-                    } else {
-                        err = parseRTCP(data);
-                    }
-                    break;
-                }
-
-                case ANetworkSession::kWhatClientConnected:
-                {
-                    int32_t sessionID;
-                    CHECK(msg->findInt32("sessionID", &sessionID));
-                    ALOGI("TCP session %d now connected", sessionID);
-
-                    int32_t serverPort;
-                    CHECK(msg->findInt32("server-port", &serverPort));
-
-                    if (serverPort == mRTPPort) {
-                        mRTPClientSessionID = sessionID;
-                    } else {
-                        CHECK_EQ(serverPort, mRTPPort + 1);
-                        mRTCPClientSessionID = sessionID;
-                    }
-                    break;
-                }
-
-                default:
-                    TRESPASS();
-            }
-            break;
-        }
-
-        case kWhatSendRR:
-        {
-            onSendRR();
-            break;
-        }
-
-        case kWhatPacketLost:
-        {
-            onPacketLost(msg);
-            break;
-        }
-
-        case kWhatInject:
-        {
-            int32_t isRTP;
-            CHECK(msg->findInt32("isRTP", &isRTP));
-
-            sp<ABuffer> buffer;
-            CHECK(msg->findBuffer("buffer", &buffer));
-
-            status_t err;
-            if (isRTP) {
-                err = parseRTP(buffer);
-            } else {
-                err = parseRTCP(buffer);
-            }
-            break;
-        }
-
-        default:
-            TRESPASS();
-    }
-}
-
-status_t RTPSink::injectPacket(bool isRTP, const sp<ABuffer> &buffer) {
-    sp<AMessage> msg = new AMessage(kWhatInject, id());
-    msg->setInt32("isRTP", isRTP);
-    msg->setBuffer("buffer", buffer);
-    msg->post();
-
-    return OK;
-}
-
-status_t RTPSink::parseRTP(const sp<ABuffer> &buffer) {
-    size_t size = buffer->size();
-    if (size < 12) {
-        // Too short to be a valid RTP header.
-        return ERROR_MALFORMED;
-    }
-
-    const uint8_t *data = buffer->data();
-
-    if ((data[0] >> 6) != 2) {
-        // Unsupported version.
-        return ERROR_UNSUPPORTED;
-    }
-
-    if (data[0] & 0x20) {
-        // Padding present.
-
-        size_t paddingLength = data[size - 1];
-
-        if (paddingLength + 12 > size) {
-            // If we removed this much padding we'd end up with something
-            // that's too short to be a valid RTP header.
-            return ERROR_MALFORMED;
-        }
-
-        size -= paddingLength;
-    }
-
-    int numCSRCs = data[0] & 0x0f;
-
-    size_t payloadOffset = 12 + 4 * numCSRCs;
-
-    if (size < payloadOffset) {
-        // Not enough data to fit the basic header and all the CSRC entries.
-        return ERROR_MALFORMED;
-    }
-
-    if (data[0] & 0x10) {
-        // Header eXtension present.
-
-        if (size < payloadOffset + 4) {
-            // Not enough data to fit the basic header, all CSRC entries
-            // and the first 4 bytes of the extension header.
-
-            return ERROR_MALFORMED;
-        }
-
-        const uint8_t *extensionData = &data[payloadOffset];
-
-        size_t extensionLength =
-            4 * (extensionData[2] << 8 | extensionData[3]);
-
-        if (size < payloadOffset + 4 + extensionLength) {
-            return ERROR_MALFORMED;
-        }
-
-        payloadOffset += 4 + extensionLength;
-    }
-
-    uint32_t srcId = U32_AT(&data[8]);
-    uint32_t rtpTime = U32_AT(&data[4]);
-    uint16_t seqNo = U16_AT(&data[2]);
-
-#if 0
-    int64_t arrivalTimeUs;
-    CHECK(buffer->meta()->findInt64("arrivalTimeUs", &arrivalTimeUs));
-
-    if (mFirstArrivalTimeUs < 0ll) {
-        mFirstArrivalTimeUs = arrivalTimeUs;
-    }
-    arrivalTimeUs -= mFirstArrivalTimeUs;
-
-    int64_t arrivalTimeMedia = (arrivalTimeUs * 9ll) / 100ll;
-
-    ALOGV("seqNo: %d, SSRC 0x%08x, diff %lld",
-            seqNo, srcId, rtpTime - arrivalTimeMedia);
-
-    mRegression.addPoint((float)rtpTime, (float)arrivalTimeMedia);
-
-    ++mNumPacketsReceived;
-
-    float n1, n2, b;
-    if (mRegression.approxLine(&n1, &n2, &b)) {
-        ALOGV("Line %lld: %.2f %.2f %.2f, slope %.2f",
-              mNumPacketsReceived, n1, n2, b, -n1 / n2);
-
-        float expectedArrivalTimeMedia = (b - n1 * (float)rtpTime) / n2;
-        float latenessMs = (arrivalTimeMedia - expectedArrivalTimeMedia) / 90.0;
-
-        if (mMaxDelayMs < 0ll || latenessMs > mMaxDelayMs) {
-            mMaxDelayMs = latenessMs;
-            ALOGI("packet was %.2f ms late", latenessMs);
-        }
-    }
-#endif
-
-    sp<AMessage> meta = buffer->meta();
-    meta->setInt32("ssrc", srcId);
-    meta->setInt32("rtp-time", rtpTime);
-    meta->setInt32("PT", data[1] & 0x7f);
-    meta->setInt32("M", data[1] >> 7);
-
-    buffer->setRange(payloadOffset, size - payloadOffset);
-
-    ssize_t index = mSources.indexOfKey(srcId);
-    if (index < 0) {
-        if (mRenderer == NULL) {
-            sp<AMessage> notifyLost = new AMessage(kWhatPacketLost, id());
-            notifyLost->setInt32("ssrc", srcId);
-
-            mRenderer = new RENDERER_CLASS(notifyLost, mSurfaceTex);
-            looper()->registerHandler(mRenderer);
-        }
-
-        sp<AMessage> queueBufferMsg =
-            new AMessage(RENDERER_CLASS::kWhatQueueBuffer, mRenderer->id());
-
-        sp<Source> source = new Source(seqNo, buffer, queueBufferMsg);
-        mSources.add(srcId, source);
-    } else {
-        mSources.valueAt(index)->updateSeq(seqNo, buffer);
-    }
-
-    return OK;
-}
-
-status_t RTPSink::parseRTCP(const sp<ABuffer> &buffer) {
-    const uint8_t *data = buffer->data();
-    size_t size = buffer->size();
-
-    while (size > 0) {
-        if (size < 8) {
-            // Too short to be a valid RTCP header
-            return ERROR_MALFORMED;
-        }
-
-        if ((data[0] >> 6) != 2) {
-            // Unsupported version.
-            return ERROR_UNSUPPORTED;
-        }
-
-        if (data[0] & 0x20) {
-            // Padding present.
-
-            size_t paddingLength = data[size - 1];
-
-            if (paddingLength + 12 > size) {
-                // If we removed this much padding we'd end up with something
-                // that's too short to be a valid RTP header.
-                return ERROR_MALFORMED;
-            }
-
-            size -= paddingLength;
-        }
-
-        size_t headerLength = 4 * (data[2] << 8 | data[3]) + 4;
-
-        if (size < headerLength) {
-            // Only received a partial packet?
-            return ERROR_MALFORMED;
-        }
-
-        switch (data[1]) {
-            case 200:
-            {
-                parseSR(data, headerLength);
-                break;
-            }
-
-            case 201:  // RR
-            case 202:  // SDES
-            case 204:  // APP
-                break;
-
-            case 205:  // TSFB (transport layer specific feedback)
-            case 206:  // PSFB (payload specific feedback)
-                // hexdump(data, headerLength);
-                break;
-
-            case 203:
-            {
-                parseBYE(data, headerLength);
-                break;
-            }
-
-            default:
-            {
-                ALOGW("Unknown RTCP packet type %u of size %d",
-                     (unsigned)data[1], headerLength);
-                break;
-            }
-        }
-
-        data += headerLength;
-        size -= headerLength;
-    }
-
-    return OK;
-}
-
-status_t RTPSink::parseBYE(const uint8_t *data, size_t size) {
-    size_t SC = data[0] & 0x3f;
-
-    if (SC == 0 || size < (4 + SC * 4)) {
-        // Packet too short for the minimal BYE header.
-        return ERROR_MALFORMED;
-    }
-
-    uint32_t id = U32_AT(&data[4]);
-
-    return OK;
-}
-
-status_t RTPSink::parseSR(const uint8_t *data, size_t size) {
-    size_t RC = data[0] & 0x1f;
-
-    if (size < (7 + RC * 6) * 4) {
-        // Packet too short for the minimal SR header.
-        return ERROR_MALFORMED;
-    }
-
-    uint32_t id = U32_AT(&data[4]);
-    uint64_t ntpTime = U64_AT(&data[8]);
-    uint32_t rtpTime = U32_AT(&data[16]);
-
-    ALOGV("SR: ssrc 0x%08x, ntpTime 0x%016llx, rtpTime 0x%08x",
-          id, ntpTime, rtpTime);
-
-    return OK;
-}
-
-status_t RTPSink::connect(
-        const char *host, int32_t remoteRtpPort, int32_t remoteRtcpPort) {
-    ALOGI("connecting RTP/RTCP sockets to %s:{%d,%d}",
-          host, remoteRtpPort, remoteRtcpPort);
-
-    status_t err =
-        mNetSession->connectUDPSession(mRTPSessionID, host, remoteRtpPort);
-
-    if (err != OK) {
-        return err;
-    }
-
-    err = mNetSession->connectUDPSession(mRTCPSessionID, host, remoteRtcpPort);
-
-    if (err != OK) {
-        return err;
-    }
-
-#if 0
-    sp<ABuffer> buf = new ABuffer(1500);
-    memset(buf->data(), 0, buf->size());
-
-    mNetSession->sendRequest(
-            mRTPSessionID, buf->data(), buf->size());
-
-    mNetSession->sendRequest(
-            mRTCPSessionID, buf->data(), buf->size());
-#endif
-
-    if (!mUsingTCPTransport) {
-        scheduleSendRR();
-    }
-
-    return OK;
-}
-
-void RTPSink::scheduleSendRR() {
-    (new AMessage(kWhatSendRR, id()))->post(2000000ll);
-}
-
-void RTPSink::addSDES(const sp<ABuffer> &buffer) {
-    uint8_t *data = buffer->data() + buffer->size();
-    data[0] = 0x80 | 1;
-    data[1] = 202;  // SDES
-    data[4] = 0xde;  // SSRC
-    data[5] = 0xad;
-    data[6] = 0xbe;
-    data[7] = 0xef;
-
-    size_t offset = 8;
-
-    data[offset++] = 1;  // CNAME
-
-    AString cname = "stagefright@somewhere";
-    data[offset++] = cname.size();
-
-    memcpy(&data[offset], cname.c_str(), cname.size());
-    offset += cname.size();
-
-    data[offset++] = 6;  // TOOL
-
-    AString tool = "stagefright/1.0";
-    data[offset++] = tool.size();
-
-    memcpy(&data[offset], tool.c_str(), tool.size());
-    offset += tool.size();
-
-    data[offset++] = 0;
-
-    if ((offset % 4) > 0) {
-        size_t count = 4 - (offset % 4);
-        switch (count) {
-            case 3:
-                data[offset++] = 0;
-            case 2:
-                data[offset++] = 0;
-            case 1:
-                data[offset++] = 0;
-        }
-    }
-
-    size_t numWords = (offset / 4) - 1;
-    data[2] = numWords >> 8;
-    data[3] = numWords & 0xff;
-
-    buffer->setRange(buffer->offset(), buffer->size() + offset);
-}
-
-void RTPSink::onSendRR() {
-    sp<ABuffer> buf = new ABuffer(1500);
-    buf->setRange(0, 0);
-
-    uint8_t *ptr = buf->data();
-    ptr[0] = 0x80 | 0;
-    ptr[1] = 201;  // RR
-    ptr[2] = 0;
-    ptr[3] = 1;
-    ptr[4] = 0xde;  // SSRC
-    ptr[5] = 0xad;
-    ptr[6] = 0xbe;
-    ptr[7] = 0xef;
-
-    buf->setRange(0, 8);
-
-    size_t numReportBlocks = 0;
-    for (size_t i = 0; i < mSources.size(); ++i) {
-        uint32_t ssrc = mSources.keyAt(i);
-        sp<Source> source = mSources.valueAt(i);
-
-        if (numReportBlocks > 31 || buf->size() + 24 > buf->capacity()) {
-            // Cannot fit another report block.
-            break;
-        }
-
-        source->addReportBlock(ssrc, buf);
-        ++numReportBlocks;
-    }
-
-    ptr[0] |= numReportBlocks;  // 5 bit
-
-    size_t sizeInWordsMinus1 = 1 + 6 * numReportBlocks;
-    ptr[2] = sizeInWordsMinus1 >> 8;
-    ptr[3] = sizeInWordsMinus1 & 0xff;
-
-    buf->setRange(0, (sizeInWordsMinus1 + 1) * 4);
-
-    addSDES(buf);
-
-    mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size());
-
-    scheduleSendRR();
-}
-
-void RTPSink::onPacketLost(const sp<AMessage> &msg) {
-    if (mUsingTCPTransport) {
-        ALOGW("huh? lost a packet even though using reliable transport?");
-        return;
-    }
-
-    uint32_t srcId;
-    CHECK(msg->findInt32("ssrc", (int32_t *)&srcId));
-
-    int32_t seqNo;
-    CHECK(msg->findInt32("seqNo", &seqNo));
-
-    int32_t blp = 0;
-
-    sp<ABuffer> buf = new ABuffer(16);
-    buf->setRange(0, 0);
-
-    uint8_t *ptr = buf->data();
-    ptr[0] = 0x80 | 1;  // generic NACK
-    ptr[1] = 205;  // TSFB
-    ptr[2] = 0;
-    ptr[3] = 3;
-    ptr[4] = 0xde;  // sender SSRC
-    ptr[5] = 0xad;
-    ptr[6] = 0xbe;
-    ptr[7] = 0xef;
-    ptr[8] = (srcId >> 24) & 0xff;
-    ptr[9] = (srcId >> 16) & 0xff;
-    ptr[10] = (srcId >> 8) & 0xff;
-    ptr[11] = (srcId & 0xff);
-    ptr[12] = (seqNo >> 8) & 0xff;
-    ptr[13] = (seqNo & 0xff);
-    ptr[14] = (blp >> 8) & 0xff;
-    ptr[15] = (blp & 0xff);
-
-    buf->setRange(0, 16);
-
-    mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size());
-}
-
-}  // namespace android
-
diff --git a/media/libstagefright/wifi-display/sink/RTPSink.h b/media/libstagefright/wifi-display/sink/RTPSink.h
deleted file mode 100644
index 4706c6d..0000000
--- a/media/libstagefright/wifi-display/sink/RTPSink.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * 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 RTP_SINK_H_
-
-#define RTP_SINK_H_
-
-#include <media/stagefright/foundation/AHandler.h>
-
-#include "LinearRegression.h"
-
-#include <gui/Surface.h>
-
-#define USE_TUNNEL_RENDERER     0
-
-namespace android {
-
-struct ABuffer;
-struct ANetworkSession;
-
-#if USE_TUNNEL_RENDERER
-struct TunnelRenderer;
-#else
-struct DirectRenderer;
-#endif
-
-// Creates a pair of sockets for RTP/RTCP traffic, instantiates a renderer
-// for incoming transport stream data and occasionally sends statistics over
-// the RTCP channel.
-struct RTPSink : public AHandler {
-    RTPSink(const sp<ANetworkSession> &netSession,
-            const sp<IGraphicBufferProducer> &bufferProducer,
-            const sp<AMessage> &notify);
-
-    // If TCP interleaving is used, no UDP sockets are created, instead
-    // incoming RTP/RTCP packets (arriving on the RTSP control connection)
-    // are manually injected by WifiDisplaySink.
-    status_t init(bool usingTCPTransport, bool usingTCPInterleaving);
-
-    status_t connect(
-            const char *host, int32_t remoteRtpPort, int32_t remoteRtcpPort);
-
-    int32_t getRTPPort() const;
-
-    status_t injectPacket(bool isRTP, const sp<ABuffer> &buffer);
-
-protected:
-    virtual void onMessageReceived(const sp<AMessage> &msg);
-    virtual ~RTPSink();
-
-private:
-    enum {
-        kWhatRTPNotify,
-        kWhatRTCPNotify,
-        kWhatSendRR,
-        kWhatPacketLost,
-        kWhatInject,
-    };
-
-    struct Source;
-    struct StreamSource;
-
-    sp<ANetworkSession> mNetSession;
-    sp<IGraphicBufferProducer> mSurfaceTex;
-    sp<AMessage> mNotify;
-    KeyedVector<uint32_t, sp<Source> > mSources;
-
-    bool mUsingTCPTransport;
-    bool mUsingTCPInterleaving;
-
-    int32_t mRTPPort;
-
-    int32_t mRTPSessionID;   // in TCP unicast mode these are just server
-    int32_t mRTCPSessionID;  // sockets. No data is transferred through them.
-
-    int32_t mRTPClientSessionID;  // in TCP unicast mode
-    int32_t mRTCPClientSessionID;
-
-    int64_t mFirstArrivalTimeUs;
-    int64_t mNumPacketsReceived;
-    LinearRegression mRegression;
-    int64_t mMaxDelayMs;
-
-#if USE_TUNNEL_RENDERER
-    sp<TunnelRenderer> mRenderer;
-#else
-    sp<DirectRenderer> mRenderer;
-#endif
-
-    status_t parseRTP(const sp<ABuffer> &buffer);
-    status_t parseRTCP(const sp<ABuffer> &buffer);
-    status_t parseBYE(const uint8_t *data, size_t size);
-    status_t parseSR(const uint8_t *data, size_t size);
-
-    void addSDES(const sp<ABuffer> &buffer);
-    void onSendRR();
-    void onPacketLost(const sp<AMessage> &msg);
-    void scheduleSendRR();
-
-    DISALLOW_EVIL_CONSTRUCTORS(RTPSink);
-};
-
-}  // namespace android
-
-#endif  // RTP_SINK_H_
diff --git a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp
index 75f9d73..d9d8a76 100644
--- a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp
+++ b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp
@@ -158,175 +158,17 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 TunnelRenderer::TunnelRenderer(
-        const sp<AMessage> &notifyLost,
         const sp<IGraphicBufferProducer> &bufferProducer)
-    : mNotifyLost(notifyLost),
-      mSurfaceTex(bufferProducer),
-      mTotalBytesQueued(0ll),
-      mLastDequeuedExtSeqNo(-1),
-      mFirstFailedAttemptUs(-1ll),
-      mRequestedRetransmission(false) {
+    : mSurfaceTex(bufferProducer),
+      mStartup(true) {
 }
 
 TunnelRenderer::~TunnelRenderer() {
     destroyPlayer();
 }
 
-void TunnelRenderer::queueBuffer(const sp<ABuffer> &buffer) {
-    Mutex::Autolock autoLock(mLock);
-
-    mTotalBytesQueued += buffer->size();
-
-    if (mPackets.empty()) {
-        mPackets.push_back(buffer);
-        return;
-    }
-
-    int32_t newExtendedSeqNo = buffer->int32Data();
-
-    List<sp<ABuffer> >::iterator firstIt = mPackets.begin();
-    List<sp<ABuffer> >::iterator it = --mPackets.end();
-    for (;;) {
-        int32_t extendedSeqNo = (*it)->int32Data();
-
-        if (extendedSeqNo == newExtendedSeqNo) {
-            // Duplicate packet.
-            return;
-        }
-
-        if (extendedSeqNo < newExtendedSeqNo) {
-            // Insert new packet after the one at "it".
-            mPackets.insert(++it, buffer);
-            return;
-        }
-
-        if (it == firstIt) {
-            // Insert new packet before the first existing one.
-            mPackets.insert(it, buffer);
-            return;
-        }
-
-        --it;
-    }
-}
-
-sp<ABuffer> TunnelRenderer::dequeueBuffer() {
-    Mutex::Autolock autoLock(mLock);
-
-    sp<ABuffer> buffer;
-    int32_t extSeqNo;
-    while (!mPackets.empty()) {
-        buffer = *mPackets.begin();
-        extSeqNo = buffer->int32Data();
-
-        if (mLastDequeuedExtSeqNo < 0 || extSeqNo > mLastDequeuedExtSeqNo) {
-            break;
-        }
-
-        // This is a retransmission of a packet we've already returned.
-
-        mTotalBytesQueued -= buffer->size();
-        buffer.clear();
-        extSeqNo = -1;
-
-        mPackets.erase(mPackets.begin());
-    }
-
-    if (mPackets.empty()) {
-        if (mFirstFailedAttemptUs < 0ll) {
-            mFirstFailedAttemptUs = ALooper::GetNowUs();
-            mRequestedRetransmission = false;
-        } else {
-            ALOGV("no packets available for %.2f secs",
-                    (ALooper::GetNowUs() - mFirstFailedAttemptUs) / 1E6);
-        }
-
-        return NULL;
-    }
-
-    if (mLastDequeuedExtSeqNo < 0 || extSeqNo == mLastDequeuedExtSeqNo + 1) {
-        if (mRequestedRetransmission) {
-            ALOGI("Recovered after requesting retransmission of %d",
-                  extSeqNo);
-        }
-
-        mLastDequeuedExtSeqNo = extSeqNo;
-        mFirstFailedAttemptUs = -1ll;
-        mRequestedRetransmission = false;
-
-        mPackets.erase(mPackets.begin());
-
-        mTotalBytesQueued -= buffer->size();
-
-        return buffer;
-    }
-
-    if (mFirstFailedAttemptUs < 0ll) {
-        mFirstFailedAttemptUs = ALooper::GetNowUs();
-
-        ALOGV("failed to get the correct packet the first time.");
-        return NULL;
-    }
-
-    if (mFirstFailedAttemptUs + 50000ll > ALooper::GetNowUs()) {
-        // We're willing to wait a little while to get the right packet.
-
-#if 1
-        if (!mRequestedRetransmission) {
-            ALOGI("requesting retransmission of extSeqNo %d (seqNo %d)",
-                  mLastDequeuedExtSeqNo + 1,
-                  (mLastDequeuedExtSeqNo + 1) & 0xffff);
-
-            sp<AMessage> notify = mNotifyLost->dup();
-            notify->setInt32("seqNo", (mLastDequeuedExtSeqNo + 1) & 0xffff);
-            notify->post();
-
-            mRequestedRetransmission = true;
-        } else
-#endif
-        {
-            ALOGV("still waiting for the correct packet to arrive.");
-        }
-
-        return NULL;
-    }
-
-    ALOGI("dropping packet. extSeqNo %d didn't arrive in time",
-            mLastDequeuedExtSeqNo + 1);
-
-    // Permanent failure, we never received the packet.
-    mLastDequeuedExtSeqNo = extSeqNo;
-    mFirstFailedAttemptUs = -1ll;
-    mRequestedRetransmission = false;
-
-    mTotalBytesQueued -= buffer->size();
-
-    mPackets.erase(mPackets.begin());
-
-    return buffer;
-}
-
 void TunnelRenderer::onMessageReceived(const sp<AMessage> &msg) {
     switch (msg->what()) {
-        case kWhatQueueBuffer:
-        {
-            sp<ABuffer> buffer;
-            CHECK(msg->findBuffer("buffer", &buffer));
-
-            queueBuffer(buffer);
-
-            if (mStreamSource == NULL) {
-                if (mTotalBytesQueued > 0ll) {
-                    initPlayer();
-                } else {
-                    ALOGI("Have %lld bytes queued...", mTotalBytesQueued);
-                }
-            } else {
-                mStreamSource->doSomeWork();
-            }
-            break;
-        }
-
         default:
             TRESPASS();
     }
@@ -396,5 +238,31 @@
     }
 }
 
+void TunnelRenderer::queueBuffer(const sp<ABuffer> &buffer) {
+    {
+        Mutex::Autolock autoLock(mLock);
+        mBuffers.push_back(buffer);
+    }
+
+    if (mStartup) {
+        initPlayer();
+        mStartup = false;
+    }
+
+    mStreamSource->doSomeWork();
+}
+
+sp<ABuffer> TunnelRenderer::dequeueBuffer() {
+    Mutex::Autolock autoLock(mLock);
+    if (mBuffers.empty()) {
+        return NULL;
+    }
+
+    sp<ABuffer> buf = *mBuffers.begin();
+    mBuffers.erase(mBuffers.begin());
+
+    return buf;
+}
+
 }  // namespace android
 
diff --git a/media/libstagefright/wifi-display/sink/TunnelRenderer.h b/media/libstagefright/wifi-display/sink/TunnelRenderer.h
index 52e6e66..8e96665 100644
--- a/media/libstagefright/wifi-display/sink/TunnelRenderer.h
+++ b/media/libstagefright/wifi-display/sink/TunnelRenderer.h
@@ -34,16 +34,11 @@
 // and sends the resulting transport stream to a mediaplayer instance
 // for playback.
 struct TunnelRenderer : public AHandler {
-    TunnelRenderer(
-            const sp<AMessage> &notifyLost,
-            const sp<IGraphicBufferProducer> &bufferProducer);
+    TunnelRenderer(const sp<IGraphicBufferProducer> &bufferProducer);
 
+    void queueBuffer(const sp<ABuffer> &buffer);
     sp<ABuffer> dequeueBuffer();
 
-    enum {
-        kWhatQueueBuffer,
-    };
-
 protected:
     virtual void onMessageReceived(const sp<AMessage> &msg);
     virtual ~TunnelRenderer();
@@ -54,11 +49,10 @@
 
     mutable Mutex mLock;
 
-    sp<AMessage> mNotifyLost;
     sp<IGraphicBufferProducer> mSurfaceTex;
 
-    List<sp<ABuffer> > mPackets;
-    int64_t mTotalBytesQueued;
+    bool mStartup;
+    List<sp<ABuffer> > mBuffers;
 
     sp<SurfaceComposerClient> mComposerClient;
     sp<SurfaceControl> mSurfaceControl;
@@ -67,15 +61,9 @@
     sp<IMediaPlayer> mPlayer;
     sp<StreamSource> mStreamSource;
 
-    int32_t mLastDequeuedExtSeqNo;
-    int64_t mFirstFailedAttemptUs;
-    bool mRequestedRetransmission;
-
     void initPlayer();
     void destroyPlayer();
 
-    void queueBuffer(const sp<ABuffer> &buffer);
-
     DISALLOW_EVIL_CONSTRUCTORS(TunnelRenderer);
 };
 
diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
index 55581a6..a6f58cd 100644
--- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
+++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
@@ -19,8 +19,11 @@
 #include <utils/Log.h>
 
 #include "WifiDisplaySink.h"
+
+#include "DirectRenderer.h"
+#include "MediaReceiver.h"
 #include "ParsedMessage.h"
-#include "RTPSink.h"
+#include "TunnelRenderer.h"
 
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -42,7 +45,8 @@
       mUsingTCPTransport(false),
       mUsingTCPInterleaving(false),
       mSessionID(0),
-      mNextCSeq(1) {
+      mNextCSeq(1),
+      mIDRFrameRequestPending(false) {
 #if 1
     // We support any and all resolutions, but prefer 720p30
     mSinkSupportedVideoFormats.setNativeResolution(
@@ -50,11 +54,11 @@
 
     mSinkSupportedVideoFormats.enableAll();
 #else
-    // We only support 800 x 600 p60.
+    // We only support 640 x 360 p30.
     mSinkSupportedVideoFormats.disableAll();
 
     mSinkSupportedVideoFormats.setNativeResolution(
-            VideoFormats::RESOLUTION_VESA, 1);  // 800 x 600 p60
+            VideoFormats::RESOLUTION_HH, 6);  // 640 x 360 p30
 #endif
 }
 
@@ -212,20 +216,6 @@
                     break;
                 }
 
-                case ANetworkSession::kWhatBinaryData:
-                {
-                    CHECK(mUsingTCPInterleaving);
-
-                    int32_t channel;
-                    CHECK(msg->findInt32("channel", &channel));
-
-                    sp<ABuffer> data;
-                    CHECK(msg->findBuffer("data", &data));
-
-                    mRTPSink->injectPacket(channel == 0 /* isRTP */, data);
-                    break;
-                }
-
                 default:
                     TRESPASS();
             }
@@ -238,15 +228,80 @@
             break;
         }
 
-        case kWhatRequestIDRFrame:
+        case kWhatMediaReceiverNotify:
         {
-            ALOGI("requesting IDR frame");
-            sendIDRFrameRequest(mSessionID);
+            onMediaReceiverNotify(msg);
             break;
         }
 
-        case kWhatRTPSinkNotify:
+        default:
+            TRESPASS();
+    }
+}
+
+void WifiDisplaySink::onMediaReceiverNotify(const sp<AMessage> &msg) {
+    int32_t what;
+    CHECK(msg->findInt32("what", &what));
+
+    switch (what) {
+        case MediaReceiver::kWhatInitDone:
         {
+            status_t err;
+            CHECK(msg->findInt32("err", &err));
+
+            ALOGI("MediaReceiver initialization completed w/ err %d", err);
+            break;
+        }
+
+        case MediaReceiver::kWhatError:
+        {
+            status_t err;
+            CHECK(msg->findInt32("err", &err));
+
+            ALOGE("MediaReceiver signaled error %d", err);
+            break;
+        }
+
+        case MediaReceiver::kWhatAccessUnit:
+        {
+            if (mRenderer == NULL) {
+#if USE_TUNNEL_RENDERER
+                mRenderer = new TunnelRenderer(mSurfaceTex);
+#else
+                mRenderer = new DirectRenderer(mSurfaceTex);
+#endif
+
+                looper()->registerHandler(mRenderer);
+            }
+
+            sp<ABuffer> accessUnit;
+            CHECK(msg->findBuffer("accessUnit", &accessUnit));
+
+#if USE_TUNNEL_RENDERER
+            mRenderer->queueBuffer(accessUnit);
+#else
+            size_t trackIndex;
+            CHECK(msg->findSize("trackIndex", &trackIndex));
+
+            sp<AMessage> format;
+            if (msg->findMessage("format", &format)) {
+                mRenderer->setFormat(trackIndex, format);
+            }
+
+            mRenderer->queueAccessUnit(trackIndex, accessUnit);
+#endif
+            break;
+        }
+
+        case MediaReceiver::kWhatPacketLost:
+        {
+#if 0
+            if (!mIDRFrameRequestPending) {
+                ALOGI("requesting IDR frame");
+
+                sendIDRFrameRequest(mSessionID);
+            }
+#endif
             break;
         }
 
@@ -381,7 +436,8 @@
         ALOGW("Server picked an odd numbered RTP port.");
     }
 
-    return mRTPSink->connect(sourceHost.c_str(), rtpPort, rtcpPort);
+    return mMediaReceiver->connectTrack(
+            0 /* trackIndex */, sourceHost.c_str(), rtpPort, rtcpPort);
 }
 
 status_t WifiDisplaySink::onReceivePlayResponse(
@@ -402,6 +458,9 @@
 
 status_t WifiDisplaySink::onReceiveIDRFrameRequestResponse(
         int32_t sessionID, const sp<ParsedMessage> &msg) {
+    CHECK(mIDRFrameRequestPending);
+    mIDRFrameRequestPending = false;
+
     return OK;
 }
 
@@ -539,16 +598,48 @@
 }
 
 status_t WifiDisplaySink::sendSetup(int32_t sessionID, const char *uri) {
-    sp<AMessage> notify = new AMessage(kWhatRTPSinkNotify, id());
+    sp<AMessage> notify = new AMessage(kWhatMediaReceiverNotify, id());
 
-    mRTPSink = new RTPSink(mNetSession, mSurfaceTex, notify);
-    looper()->registerHandler(mRTPSink);
+    mMediaReceiverLooper = new ALooper;
+    mMediaReceiverLooper->setName("media_receiver");
 
-    status_t err = mRTPSink->init(mUsingTCPTransport, mUsingTCPInterleaving);
+    mMediaReceiverLooper->start(
+            false /* runOnCallingThread */,
+            false /* canCallJava */,
+            PRIORITY_AUDIO);
+
+    mMediaReceiver = new MediaReceiver(mNetSession, notify);
+    mMediaReceiverLooper->registerHandler(mMediaReceiver);
+
+    RTPReceiver::TransportMode mode = RTPReceiver::TRANSPORT_UDP;
+    if (mUsingTCPTransport) {
+        if (mUsingTCPInterleaving) {
+            mode = RTPReceiver::TRANSPORT_TCP_INTERLEAVED;
+        } else {
+            mode = RTPReceiver::TRANSPORT_TCP;
+        }
+    }
+
+    int32_t localRTPPort;
+    status_t err = mMediaReceiver->addTrack(mode, &localRTPPort);
+
+    if (err == OK) {
+        err = mMediaReceiver->initAsync(
+#if USE_TUNNEL_RENDERER
+                MediaReceiver::MODE_TRANSPORT_STREAM_RAW
+#else
+                MediaReceiver::MODE_TRANSPORT_STREAM
+#endif
+                );
+    }
 
     if (err != OK) {
-        looper()->unregisterHandler(mRTPSink->id());
-        mRTPSink.clear();
+        mMediaReceiverLooper->unregisterHandler(mMediaReceiver->id());
+        mMediaReceiver.clear();
+
+        mMediaReceiverLooper->stop();
+        mMediaReceiverLooper.clear();
+
         return err;
     }
 
@@ -556,17 +647,19 @@
 
     AppendCommonResponse(&request, mNextCSeq);
 
-    if (mUsingTCPInterleaving) {
+    if (mode == RTPReceiver::TRANSPORT_TCP_INTERLEAVED) {
         request.append("Transport: RTP/AVP/TCP;interleaved=0-1\r\n");
-    } else {
-        int32_t rtpPort = mRTPSink->getRTPPort();
-
+    } else if (mode == RTPReceiver::TRANSPORT_TCP) {
         request.append(
                 StringPrintf(
-                    "Transport: RTP/AVP/%s;unicast;client_port=%d-%d\r\n",
-                    mUsingTCPTransport ? "TCP" : "UDP",
-                    rtpPort,
-                    rtpPort + 1));
+                    "Transport: RTP/AVP/TCP;unicast;client_port=%d\r\n",
+                    localRTPPort));
+    } else {
+        request.append(
+                StringPrintf(
+                    "Transport: RTP/AVP/UDP;unicast;client_port=%d-%d\r\n",
+                    localRTPPort,
+                    localRTPPort + 1));
     }
 
     request.append("\r\n");
@@ -611,6 +704,8 @@
 }
 
 status_t WifiDisplaySink::sendIDRFrameRequest(int32_t sessionID) {
+    CHECK(!mIDRFrameRequestPending);
+
     AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n";
 
     AppendCommonResponse(&request, mNextCSeq);
@@ -636,6 +731,8 @@
 
     ++mNextCSeq;
 
+    mIDRFrameRequestPending = true;
+
     return OK;
 }
 
diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h
index 8b5ff6b..01af58b 100644
--- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h
+++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h
@@ -28,8 +28,12 @@
 namespace android {
 
 struct AMessage;
+struct DirectRenderer;
+struct MediaReceiver;
 struct ParsedMessage;
-struct RTPSink;
+struct TunnelRenderer;
+
+#define USE_TUNNEL_RENDERER     0
 
 // Represents the RTSP client acting as a wifi display sink.
 // Connects to a wifi display source and renders the incoming
@@ -68,8 +72,7 @@
         kWhatStart,
         kWhatRTSPNotify,
         kWhatStop,
-        kWhatRequestIDRFrame,
-        kWhatRTPSinkNotify,
+        kWhatMediaReceiverNotify,
     };
 
     struct ResponseID {
@@ -100,10 +103,20 @@
 
     KeyedVector<ResponseID, HandleRTSPResponseFunc> mResponseHandlers;
 
-    sp<RTPSink> mRTPSink;
+    sp<ALooper> mMediaReceiverLooper;
+    sp<MediaReceiver> mMediaReceiver;
+
+#if USE_TUNNEL_RENDERER
+    sp<TunnelRenderer> mRenderer;
+#else
+    sp<DirectRenderer> mRenderer;
+#endif
+
     AString mPlaybackSessionID;
     int32_t mPlaybackSessionTimeoutSecs;
 
+    bool mIDRFrameRequestPending;
+
     status_t sendM2(int32_t sessionID);
     status_t sendSetup(int32_t sessionID, const char *uri);
     status_t sendPlay(int32_t sessionID, const char *uri);
@@ -143,6 +156,8 @@
             int32_t cseq,
             const sp<ParsedMessage> &data);
 
+    void onMediaReceiverNotify(const sp<AMessage> &msg);
+
     void sendErrorResponse(
             int32_t sessionID,
             const char *errorDetail,
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
index ede4e60..ea195b3 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
@@ -23,8 +23,6 @@
 #include "Converter.h"
 #include "MediaPuller.h"
 #include "RepeaterSource.h"
-#include "Sender.h"
-#include "TSPacketizer.h"
 #include "include/avc_utils.h"
 #include "WifiDisplaySource.h"
 
@@ -65,9 +63,9 @@
     bool isAudio() const;
 
     const sp<Converter> &converter() const;
-    ssize_t packetizerTrackIndex() const;
 
-    void setPacketizerTrackIndex(size_t index);
+    ssize_t mediaSenderTrackIndex() const;
+    void setMediaSenderTrackIndex(size_t index);
 
     status_t start();
     void stopAsync();
@@ -107,7 +105,7 @@
     sp<MediaPuller> mMediaPuller;
     sp<Converter> mConverter;
     bool mStarted;
-    ssize_t mPacketizerTrackIndex;
+    ssize_t mMediaSenderTrackIndex;
     bool mIsAudio;
     List<sp<ABuffer> > mQueuedAccessUnits;
     sp<RepeaterSource> mRepeaterSource;
@@ -131,7 +129,6 @@
       mMediaPuller(mediaPuller),
       mConverter(converter),
       mStarted(false),
-      mPacketizerTrackIndex(-1),
       mIsAudio(IsAudioFormat(mConverter->getOutputFormat())),
       mLastOutputBufferQueuedTimeUs(-1ll) {
 }
@@ -161,13 +158,14 @@
     return mConverter;
 }
 
-ssize_t WifiDisplaySource::PlaybackSession::Track::packetizerTrackIndex() const {
-    return mPacketizerTrackIndex;
+ssize_t WifiDisplaySource::PlaybackSession::Track::mediaSenderTrackIndex() const {
+    CHECK_GE(mMediaSenderTrackIndex, 0);
+    return mMediaSenderTrackIndex;
 }
 
-void WifiDisplaySource::PlaybackSession::Track::setPacketizerTrackIndex(size_t index) {
-    CHECK_LT(mPacketizerTrackIndex, 0);
-    mPacketizerTrackIndex = index;
+void WifiDisplaySource::PlaybackSession::Track::setMediaSenderTrackIndex(
+        size_t index) {
+    mMediaSenderTrackIndex = index;
 }
 
 status_t WifiDisplaySource::PlaybackSession::Track::start() {
@@ -331,22 +329,28 @@
       mNotify(notify),
       mInterfaceAddr(interfaceAddr),
       mHDCP(hdcp),
+      mLocalRTPPort(-1),
       mWeAreDead(false),
       mPaused(false),
       mLastLifesignUs(),
       mVideoTrackIndex(-1),
-      mPrevTimeUs(-1ll),
-      mAllTracksHavePacketizerIndex(false) {
+      mPrevTimeUs(-1ll) {
 }
 
 status_t WifiDisplaySource::PlaybackSession::init(
         const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
-        Sender::TransportMode transportMode,
+        RTPSender::TransportMode transportMode,
         bool enableAudio,
         bool usePCMAudio,
         bool enableVideo,
         VideoFormats::ResolutionType videoResolutionType,
         size_t videoResolutionIndex) {
+    sp<AMessage> notify = new AMessage(kWhatMediaSenderNotify, id());
+    mMediaSender = new MediaSender(mNetSession, notify);
+    looper()->registerHandler(mMediaSender);
+
+    mMediaSender->setHDCP(mHDCP);
+
     status_t err = setupPacketizer(
             enableAudio,
             usePCMAudio,
@@ -354,26 +358,22 @@
             videoResolutionType,
             videoResolutionIndex);
 
-    if (err != OK) {
-        return err;
+    if (err == OK) {
+        err = mMediaSender->initAsync(
+                -1 /* trackIndex */,
+                transportMode,
+                clientIP,
+                clientRtp,
+                clientRtcp,
+                &mLocalRTPPort);
     }
 
-    sp<AMessage> notify = new AMessage(kWhatSenderNotify, id());
-    mSender = new Sender(mNetSession, notify);
-
-    mSenderLooper = new ALooper;
-    mSenderLooper->setName("sender_looper");
-
-    mSenderLooper->start(
-            false /* runOnCallingThread */,
-            false /* canCallJava */,
-            PRIORITY_AUDIO);
-
-    mSenderLooper->registerHandler(mSender);
-
-    err = mSender->init(clientIP, clientRtp, clientRtcp, transportMode);
-
     if (err != OK) {
+        mLocalRTPPort = -1;
+
+        looper()->unregisterHandler(mMediaSender->id());
+        mMediaSender.clear();
+
         return err;
     }
 
@@ -386,7 +386,7 @@
 }
 
 int32_t WifiDisplaySource::PlaybackSession::getRTPPort() const {
-    return mSender->getRTPPort();
+    return mLocalRTPPort;
 }
 
 int64_t WifiDisplaySource::PlaybackSession::getLastLifesignUs() const {
@@ -406,18 +406,10 @@
 }
 
 status_t WifiDisplaySource::PlaybackSession::finishPlay() {
-    // XXX Give the dongle a second to bind its sockets.
-    (new AMessage(kWhatFinishPlay, id()))->post(1000000ll);
     return OK;
 }
 
-status_t WifiDisplaySource::PlaybackSession::onFinishPlay() {
-    return mSender->finishInit();
-}
-
-status_t WifiDisplaySource::PlaybackSession::onFinishPlay2() {
-    mSender->scheduleSendSR();
-
+status_t WifiDisplaySource::PlaybackSession::onMediaSenderInitialized() {
     for (size_t i = 0; i < mTracks.size(); ++i) {
         CHECK_EQ((status_t)OK, mTracks.editValueAt(i)->start());
     }
@@ -464,44 +456,18 @@
             CHECK(msg->findSize("trackIndex", &trackIndex));
 
             if (what == Converter::kWhatAccessUnit) {
-                const sp<Track> &track = mTracks.valueFor(trackIndex);
-
-                ssize_t packetizerTrackIndex = track->packetizerTrackIndex();
-
-                if (packetizerTrackIndex < 0) {
-                    sp<AMessage> trackFormat = track->getFormat()->dup();
-                    if (mHDCP != NULL && !track->isAudio()) {
-                        // HDCP2.0 _and_ HDCP 2.1 specs say to set the version
-                        // inside the HDCP descriptor to 0x20!!!
-                        trackFormat->setInt32("hdcp-version", 0x20);
-                    }
-                    packetizerTrackIndex = mPacketizer->addTrack(trackFormat);
-
-                    CHECK_GE(packetizerTrackIndex, 0);
-
-                    track->setPacketizerTrackIndex(packetizerTrackIndex);
-
-                    if (allTracksHavePacketizerIndex()) {
-                        status_t err = packetizeQueuedAccessUnits();
-
-                        if (err != OK) {
-                            notifySessionDead();
-                            break;
-                        }
-                    }
-                }
-
                 sp<ABuffer> accessUnit;
                 CHECK(msg->findBuffer("accessUnit", &accessUnit));
 
-                if (!allTracksHavePacketizerIndex()) {
-                    track->queueAccessUnit(accessUnit);
-                    break;
+                const sp<Track> &track = mTracks.valueFor(trackIndex);
+
+                status_t err = mMediaSender->queueAccessUnit(
+                        track->mediaSenderTrackIndex(),
+                        accessUnit);
+
+                if (err != OK) {
+                    notifySessionDead();
                 }
-
-                track->queueOutputBuffer(accessUnit);
-
-                drainAccessUnits();
                 break;
             } else if (what == Converter::kWhatEOS) {
                 CHECK_EQ(what, Converter::kWhatEOS);
@@ -533,37 +499,25 @@
             break;
         }
 
-        case kWhatSenderNotify:
+        case kWhatMediaSenderNotify:
         {
             int32_t what;
             CHECK(msg->findInt32("what", &what));
 
-            if (what == Sender::kWhatInitDone) {
-                onFinishPlay2();
-            } else if (what == Sender::kWhatSessionDead) {
+            if (what == MediaSender::kWhatInitDone) {
+                status_t err;
+                CHECK(msg->findInt32("err", &err));
+
+                if (err == OK) {
+                    onMediaSenderInitialized();
+                } else {
+                    notifySessionDead();
+                }
+            } else if (what == MediaSender::kWhatError) {
                 notifySessionDead();
-            } else if (what == Sender::kWhatBinaryData) {
-                sp<AMessage> notify = mNotify->dup();
-                notify->setInt32("what", kWhatBinaryData);
-
-                int32_t channel;
-                CHECK(msg->findInt32("channel", &channel));
-                notify->setInt32("channel", channel);
-
-                sp<ABuffer> data;
-                CHECK(msg->findBuffer("data", &data));
-                notify->setBuffer("data", data);
-                notify->post();
             } else {
                 TRESPASS();
             }
-
-            break;
-        }
-
-        case kWhatFinishPlay:
-        {
-            onFinishPlay();
             break;
         }
 
@@ -588,11 +542,8 @@
                     break;
                 }
 
-                mSenderLooper->unregisterHandler(mSender->id());
-                mSender.clear();
-                mSenderLooper.clear();
-
-                mPacketizer.clear();
+                looper()->unregisterHandler(mMediaSender->id());
+                mMediaSender.clear();
 
                 sp<AMessage> notify = mNotify->dup();
                 notify->setInt32("what", kWhatSessionDestroyed);
@@ -601,28 +552,6 @@
             break;
         }
 
-        case kWhatPacketize:
-        {
-            size_t trackIndex;
-            CHECK(msg->findSize("trackIndex", &trackIndex));
-
-            sp<ABuffer> accessUnit;
-            CHECK(msg->findBuffer("accessUnit", &accessUnit));
-
-#if 0
-            if ((ssize_t)trackIndex == mVideoTrackIndex) {
-                int64_t nowUs = ALooper::GetNowUs();
-                static int64_t prevNowUs = 0ll;
-
-                ALOGI("sending AU, dNowUs=%lld us", nowUs - prevNowUs);
-
-                prevNowUs = nowUs;
-            }
-#endif
-
-            break;
-        }
-
         case kWhatPause:
         {
             if (mPaused) {
@@ -664,8 +593,6 @@
         size_t videoResolutionIndex) {
     CHECK(enableAudio || enableVideo);
 
-    mPacketizer = new TSPacketizer;
-
     if (enableVideo) {
         status_t err = addVideoSource(
                 videoResolutionType, videoResolutionIndex);
@@ -763,6 +690,17 @@
         mVideoTrackIndex = trackIndex;
     }
 
+    uint32_t flags = 0;
+    if (converter->needToManuallyPrependSPSPPS()) {
+        flags |= MediaSender::FLAG_MANUALLY_PREPEND_SPS_PPS;
+    }
+
+    ssize_t mediaSenderTrackIndex =
+        mMediaSender->addTrack(converter->getOutputFormat(), flags);
+    CHECK_GE(mediaSenderTrackIndex, 0);
+
+    track->setMediaSenderTrackIndex(mediaSenderTrackIndex);
+
     return OK;
 }
 
@@ -832,168 +770,6 @@
     }
 }
 
-bool WifiDisplaySource::PlaybackSession::allTracksHavePacketizerIndex() {
-    if (mAllTracksHavePacketizerIndex) {
-        return true;
-    }
-
-    for (size_t i = 0; i < mTracks.size(); ++i) {
-        if (mTracks.valueAt(i)->packetizerTrackIndex() < 0) {
-            return false;
-        }
-    }
-
-    mAllTracksHavePacketizerIndex = true;
-
-    return true;
-}
-
-status_t WifiDisplaySource::PlaybackSession::packetizeAccessUnit(
-        size_t trackIndex, sp<ABuffer> accessUnit,
-        sp<ABuffer> *packets) {
-    const sp<Track> &track = mTracks.valueFor(trackIndex);
-
-    uint32_t flags = 0;
-
-    bool isHDCPEncrypted = false;
-    uint64_t inputCTR;
-    uint8_t HDCP_private_data[16];
-
-    bool manuallyPrependSPSPPS =
-        !track->isAudio()
-        && track->converter()->needToManuallyPrependSPSPPS()
-        && IsIDR(accessUnit);
-
-    if (mHDCP != NULL && !track->isAudio()) {
-        isHDCPEncrypted = true;
-
-        if (manuallyPrependSPSPPS) {
-            accessUnit = mPacketizer->prependCSD(
-                    track->packetizerTrackIndex(), accessUnit);
-        }
-
-        status_t err = mHDCP->encrypt(
-                accessUnit->data(), accessUnit->size(),
-                trackIndex  /* streamCTR */,
-                &inputCTR,
-                accessUnit->data());
-
-        if (err != OK) {
-            ALOGE("Failed to HDCP-encrypt media data (err %d)",
-                  err);
-
-            return err;
-        }
-
-        HDCP_private_data[0] = 0x00;
-
-        HDCP_private_data[1] =
-            (((trackIndex >> 30) & 3) << 1) | 1;
-
-        HDCP_private_data[2] = (trackIndex >> 22) & 0xff;
-
-        HDCP_private_data[3] =
-            (((trackIndex >> 15) & 0x7f) << 1) | 1;
-
-        HDCP_private_data[4] = (trackIndex >> 7) & 0xff;
-
-        HDCP_private_data[5] =
-            ((trackIndex & 0x7f) << 1) | 1;
-
-        HDCP_private_data[6] = 0x00;
-
-        HDCP_private_data[7] =
-            (((inputCTR >> 60) & 0x0f) << 1) | 1;
-
-        HDCP_private_data[8] = (inputCTR >> 52) & 0xff;
-
-        HDCP_private_data[9] =
-            (((inputCTR >> 45) & 0x7f) << 1) | 1;
-
-        HDCP_private_data[10] = (inputCTR >> 37) & 0xff;
-
-        HDCP_private_data[11] =
-            (((inputCTR >> 30) & 0x7f) << 1) | 1;
-
-        HDCP_private_data[12] = (inputCTR >> 22) & 0xff;
-
-        HDCP_private_data[13] =
-            (((inputCTR >> 15) & 0x7f) << 1) | 1;
-
-        HDCP_private_data[14] = (inputCTR >> 7) & 0xff;
-
-        HDCP_private_data[15] =
-            ((inputCTR & 0x7f) << 1) | 1;
-
-#if 0
-        ALOGI("HDCP_private_data:");
-        hexdump(HDCP_private_data, sizeof(HDCP_private_data));
-
-        ABitReader br(HDCP_private_data, sizeof(HDCP_private_data));
-        CHECK_EQ(br.getBits(13), 0);
-        CHECK_EQ(br.getBits(2), (trackIndex >> 30) & 3);
-        CHECK_EQ(br.getBits(1), 1u);
-        CHECK_EQ(br.getBits(15), (trackIndex >> 15) & 0x7fff);
-        CHECK_EQ(br.getBits(1), 1u);
-        CHECK_EQ(br.getBits(15), trackIndex & 0x7fff);
-        CHECK_EQ(br.getBits(1), 1u);
-        CHECK_EQ(br.getBits(11), 0);
-        CHECK_EQ(br.getBits(4), (inputCTR >> 60) & 0xf);
-        CHECK_EQ(br.getBits(1), 1u);
-        CHECK_EQ(br.getBits(15), (inputCTR >> 45) & 0x7fff);
-        CHECK_EQ(br.getBits(1), 1u);
-        CHECK_EQ(br.getBits(15), (inputCTR >> 30) & 0x7fff);
-        CHECK_EQ(br.getBits(1), 1u);
-        CHECK_EQ(br.getBits(15), (inputCTR >> 15) & 0x7fff);
-        CHECK_EQ(br.getBits(1), 1u);
-        CHECK_EQ(br.getBits(15), inputCTR & 0x7fff);
-        CHECK_EQ(br.getBits(1), 1u);
-#endif
-
-        flags |= TSPacketizer::IS_ENCRYPTED;
-    } else if (manuallyPrependSPSPPS) {
-        flags |= TSPacketizer::PREPEND_SPS_PPS_TO_IDR_FRAMES;
-    }
-
-    int64_t timeUs = ALooper::GetNowUs();
-    if (mPrevTimeUs < 0ll || mPrevTimeUs + 100000ll <= timeUs) {
-        flags |= TSPacketizer::EMIT_PCR;
-        flags |= TSPacketizer::EMIT_PAT_AND_PMT;
-
-        mPrevTimeUs = timeUs;
-    }
-
-    mPacketizer->packetize(
-            track->packetizerTrackIndex(), accessUnit, packets, flags,
-            !isHDCPEncrypted ? NULL : HDCP_private_data,
-            !isHDCPEncrypted ? 0 : sizeof(HDCP_private_data),
-            track->isAudio() ? 2 : 0 /* numStuffingBytes */);
-
-    return OK;
-}
-
-status_t WifiDisplaySource::PlaybackSession::packetizeQueuedAccessUnits() {
-    for (;;) {
-        bool gotMoreData = false;
-        for (size_t i = 0; i < mTracks.size(); ++i) {
-            size_t trackIndex = mTracks.keyAt(i);
-            const sp<Track> &track = mTracks.valueAt(i);
-
-            sp<ABuffer> accessUnit = track->dequeueAccessUnit();
-            if (accessUnit != NULL) {
-                track->queueOutputBuffer(accessUnit);
-                gotMoreData = true;
-            }
-        }
-
-        if (!gotMoreData) {
-            break;
-        }
-    }
-
-    return OK;
-}
-
 void WifiDisplaySource::PlaybackSession::notifySessionDead() {
     // Inform WifiDisplaySource of our premature death (wish).
     sp<AMessage> notify = mNotify->dup();
@@ -1003,78 +779,5 @@
     mWeAreDead = true;
 }
 
-void WifiDisplaySource::PlaybackSession::drainAccessUnits() {
-    ALOGV("audio/video has %d/%d buffers ready.",
-            mTracks.valueFor(1)->countQueuedOutputBuffers(),
-            mTracks.valueFor(0)->countQueuedOutputBuffers());
-
-    while (drainAccessUnit()) {
-    }
-}
-
-bool WifiDisplaySource::PlaybackSession::drainAccessUnit() {
-    ssize_t minTrackIndex = -1;
-    int64_t minTimeUs = -1ll;
-
-    for (size_t i = 0; i < mTracks.size(); ++i) {
-        const sp<Track> &track = mTracks.valueAt(i);
-
-        int64_t timeUs;
-        if (track->hasOutputBuffer(&timeUs)) {
-            if (minTrackIndex < 0 || timeUs < minTimeUs) {
-                minTrackIndex = mTracks.keyAt(i);
-                minTimeUs = timeUs;
-            }
-        }
-#if SUSPEND_VIDEO_IF_IDLE
-        else if (!track->isSuspended()) {
-            // We still consider this track "live", so it should keep
-            // delivering output data whose time stamps we'll have to
-            // consider for proper interleaving.
-            return false;
-        }
-#else
-        else {
-            // We need access units available on all tracks to be able to
-            // dequeue the earliest one.
-            return false;
-        }
-#endif
-    }
-
-    if (minTrackIndex < 0) {
-        return false;
-    }
-
-    const sp<Track> &track = mTracks.valueFor(minTrackIndex);
-    sp<ABuffer> accessUnit = track->dequeueOutputBuffer();
-
-    sp<ABuffer> packets;
-    status_t err = packetizeAccessUnit(minTrackIndex, accessUnit, &packets);
-
-    if (err != OK) {
-        notifySessionDead();
-        return false;
-    }
-
-    if ((ssize_t)minTrackIndex == mVideoTrackIndex) {
-        packets->meta()->setInt32("isVideo", 1);
-    }
-    mSender->queuePackets(minTimeUs, packets);
-
-#if 0
-    if (minTrackIndex == mVideoTrackIndex) {
-        int64_t nowUs = ALooper::GetNowUs();
-
-        // Latency from "data acquired" to "ready to send if we wanted to".
-        ALOGI("[%s] latencyUs = %lld ms",
-              minTrackIndex == mVideoTrackIndex ? "video" : "audio",
-              (nowUs - minTimeUs) / 1000ll);
-    }
-#endif
-
-    return true;
-}
-
 }  // namespace android
 
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h
index 7365c78..cd6da85 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.h
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.h
@@ -18,7 +18,7 @@
 
 #define PLAYBACK_SESSION_H_
 
-#include "Sender.h"
+#include "MediaSender.h"
 #include "VideoFormats.h"
 #include "WifiDisplaySource.h"
 
@@ -30,7 +30,7 @@
 struct IGraphicBufferProducer;
 struct MediaPuller;
 struct MediaSource;
-struct TSPacketizer;
+struct MediaSender;
 
 // Encapsulates the state of an RTP/RTCP session in the context of wifi
 // display.
@@ -43,7 +43,7 @@
 
     status_t init(
             const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
-            Sender::TransportMode transportMode,
+            RTPSender::TransportMode transportMode,
             bool enableAudio,
             bool usePCMAudio,
             bool enableVideo,
@@ -83,26 +83,25 @@
         kWhatMediaPullerNotify,
         kWhatConverterNotify,
         kWhatTrackNotify,
-        kWhatSenderNotify,
         kWhatUpdateSurface,
-        kWhatFinishPlay,
-        kWhatPacketize,
         kWhatPause,
         kWhatResume,
+        kWhatMediaSenderNotify,
     };
 
     sp<ANetworkSession> mNetSession;
-    sp<Sender> mSender;
-    sp<ALooper> mSenderLooper;
     sp<AMessage> mNotify;
     in_addr mInterfaceAddr;
     sp<IHDCP> mHDCP;
+
+    sp<MediaSender> mMediaSender;
+    int32_t mLocalRTPPort;
+
     bool mWeAreDead;
     bool mPaused;
 
     int64_t mLastLifesignUs;
 
-    sp<TSPacketizer> mPacketizer;
     sp<BufferQueue> mBufferQueue;
 
     KeyedVector<size_t, sp<Track> > mTracks;
@@ -110,8 +109,6 @@
 
     int64_t mPrevTimeUs;
 
-    bool mAllTracksHavePacketizerIndex;
-
     status_t setupPacketizer(
             bool enableAudio,
             bool usePCMAudio,
@@ -132,27 +129,10 @@
 
     status_t addAudioSource(bool usePCMAudio);
 
-    ssize_t appendTSData(
-            const void *data, size_t size, bool timeDiscontinuity, bool flush);
-
-    status_t onFinishPlay();
-    status_t onFinishPlay2();
-
-    bool allTracksHavePacketizerIndex();
-
-    status_t packetizeAccessUnit(
-            size_t trackIndex, sp<ABuffer> accessUnit,
-            sp<ABuffer> *packets);
-
-    status_t packetizeQueuedAccessUnits();
+    status_t onMediaSenderInitialized();
 
     void notifySessionDead();
 
-    void drainAccessUnits();
-
-    // Returns true iff an access unit was successfully drained.
-    bool drainAccessUnit();
-
     DISALLOW_EVIL_CONSTRUCTORS(PlaybackSession);
 };
 
diff --git a/media/libstagefright/wifi-display/source/RepeaterSource.h b/media/libstagefright/wifi-display/source/RepeaterSource.h
index a13973c..146af32 100644
--- a/media/libstagefright/wifi-display/source/RepeaterSource.h
+++ b/media/libstagefright/wifi-display/source/RepeaterSource.h
@@ -6,7 +6,7 @@
 #include <media/stagefright/foundation/AHandlerReflector.h>
 #include <media/stagefright/MediaSource.h>
 
-#define SUSPEND_VIDEO_IF_IDLE   1
+#define SUSPEND_VIDEO_IF_IDLE   0
 
 namespace android {
 
diff --git a/media/libstagefright/wifi-display/source/Sender.cpp b/media/libstagefright/wifi-display/source/Sender.cpp
deleted file mode 100644
index 8b7d93f..0000000
--- a/media/libstagefright/wifi-display/source/Sender.cpp
+++ /dev/null
@@ -1,878 +0,0 @@
-/*
- * 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 "Sender"
-#include <utils/Log.h>
-
-#include "Sender.h"
-
-#include "ANetworkSession.h"
-#include "TimeSeries.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/MediaErrors.h>
-#include <media/stagefright/Utils.h>
-
-namespace android {
-
-static size_t kMaxRTPPacketSize = 1500;
-static size_t kMaxNumTSPacketsPerRTPPacket = (kMaxRTPPacketSize - 12) / 188;
-
-Sender::Sender(
-        const sp<ANetworkSession> &netSession,
-        const sp<AMessage> &notify)
-    : mNetSession(netSession),
-      mNotify(notify),
-      mTransportMode(TRANSPORT_UDP),
-      mRTPChannel(0),
-      mRTCPChannel(0),
-      mRTPPort(0),
-      mRTPSessionID(0),
-      mRTCPSessionID(0),
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-      mRTPRetransmissionSessionID(0),
-      mRTCPRetransmissionSessionID(0),
-#endif
-      mClientRTPPort(0),
-      mClientRTCPPort(0),
-      mRTPConnected(false),
-      mRTCPConnected(false),
-      mFirstOutputBufferReadyTimeUs(-1ll),
-      mFirstOutputBufferSentTimeUs(-1ll),
-      mRTPSeqNo(0),
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-      mRTPRetransmissionSeqNo(0),
-#endif
-      mLastNTPTime(0),
-      mLastRTPTime(0),
-      mNumRTPSent(0),
-      mNumRTPOctetsSent(0),
-      mNumSRsSent(0),
-      mSendSRPending(false)
-#if ENABLE_RETRANSMISSION
-      ,mHistoryLength(0)
-#endif
-#if TRACK_BANDWIDTH
-      ,mFirstPacketTimeUs(-1ll)
-      ,mTotalBytesSent(0ll)
-#endif
-#if LOG_TRANSPORT_STREAM
-    ,mLogFile(NULL)
-#endif
-{
-#if LOG_TRANSPORT_STREAM
-    mLogFile = fopen("/system/etc/log.ts", "wb");
-#endif
-}
-
-Sender::~Sender() {
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-    if (mRTCPRetransmissionSessionID != 0) {
-        mNetSession->destroySession(mRTCPRetransmissionSessionID);
-    }
-
-    if (mRTPRetransmissionSessionID != 0) {
-        mNetSession->destroySession(mRTPRetransmissionSessionID);
-    }
-#endif
-
-    if (mRTCPSessionID != 0) {
-        mNetSession->destroySession(mRTCPSessionID);
-    }
-
-    if (mRTPSessionID != 0) {
-        mNetSession->destroySession(mRTPSessionID);
-    }
-
-#if LOG_TRANSPORT_STREAM
-    if (mLogFile != NULL) {
-        fclose(mLogFile);
-        mLogFile = NULL;
-    }
-#endif
-}
-
-status_t Sender::init(
-        const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
-        TransportMode transportMode) {
-    mClientIP = clientIP;
-    mTransportMode = transportMode;
-
-    if (transportMode == TRANSPORT_TCP_INTERLEAVED) {
-        mRTPChannel = clientRtp;
-        mRTCPChannel = clientRtcp;
-        mRTPPort = 0;
-        mRTPSessionID = 0;
-        mRTCPSessionID = 0;
-        return OK;
-    }
-
-    mRTPChannel = 0;
-    mRTCPChannel = 0;
-
-    if (mTransportMode == TRANSPORT_TCP) {
-        // XXX This is wrong, we need to allocate sockets here, we only
-        // need to do this because the dongles are not establishing their
-        // end until after PLAY instead of before SETUP.
-        mRTPPort = 20000;
-        mRTPSessionID = 0;
-        mRTCPSessionID = 0;
-        mClientRTPPort = clientRtp;
-        mClientRTCPPort = clientRtcp;
-        return OK;
-    }
-
-    int serverRtp;
-
-    sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
-    sp<AMessage> rtcpNotify = new AMessage(kWhatRTCPNotify, id());
-
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-    sp<AMessage> rtpRetransmissionNotify =
-        new AMessage(kWhatRTPRetransmissionNotify, id());
-
-    sp<AMessage> rtcpRetransmissionNotify =
-        new AMessage(kWhatRTCPRetransmissionNotify, id());
-#endif
-
-    status_t err;
-    for (serverRtp = 15550;; serverRtp += 2) {
-        int32_t rtpSession;
-        if (mTransportMode == TRANSPORT_UDP) {
-            err = mNetSession->createUDPSession(
-                        serverRtp, clientIP, clientRtp,
-                        rtpNotify, &rtpSession);
-        } else {
-            err = mNetSession->createTCPDatagramSession(
-                        serverRtp, clientIP, clientRtp,
-                        rtpNotify, &rtpSession);
-        }
-
-        if (err != OK) {
-            ALOGI("failed to create RTP socket on port %d", serverRtp);
-            continue;
-        }
-
-        int32_t rtcpSession = 0;
-
-        if (clientRtcp >= 0) {
-            if (mTransportMode == TRANSPORT_UDP) {
-                err = mNetSession->createUDPSession(
-                        serverRtp + 1, clientIP, clientRtcp,
-                        rtcpNotify, &rtcpSession);
-            } else {
-                err = mNetSession->createTCPDatagramSession(
-                        serverRtp + 1, clientIP, clientRtcp,
-                        rtcpNotify, &rtcpSession);
-            }
-
-            if (err != OK) {
-                ALOGI("failed to create RTCP socket on port %d", serverRtp + 1);
-
-                mNetSession->destroySession(rtpSession);
-                continue;
-            }
-        }
-
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-        if (mTransportMode == TRANSPORT_UDP) {
-            int32_t rtpRetransmissionSession;
-
-            err = mNetSession->createUDPSession(
-                        serverRtp + kRetransmissionPortOffset,
-                        clientIP,
-                        clientRtp + kRetransmissionPortOffset,
-                        rtpRetransmissionNotify,
-                        &rtpRetransmissionSession);
-
-            if (err != OK) {
-                mNetSession->destroySession(rtcpSession);
-                mNetSession->destroySession(rtpSession);
-                continue;
-            }
-
-            CHECK_GE(clientRtcp, 0);
-
-            int32_t rtcpRetransmissionSession;
-            err = mNetSession->createUDPSession(
-                        serverRtp + 1 + kRetransmissionPortOffset,
-                        clientIP,
-                        clientRtp + 1 + kRetransmissionPortOffset,
-                        rtcpRetransmissionNotify,
-                        &rtcpRetransmissionSession);
-
-            if (err != OK) {
-                mNetSession->destroySession(rtpRetransmissionSession);
-                mNetSession->destroySession(rtcpSession);
-                mNetSession->destroySession(rtpSession);
-                continue;
-            }
-
-            mRTPRetransmissionSessionID = rtpRetransmissionSession;
-            mRTCPRetransmissionSessionID = rtcpRetransmissionSession;
-
-            ALOGI("rtpRetransmissionSessionID = %d, "
-                  "rtcpRetransmissionSessionID = %d",
-                  rtpRetransmissionSession, rtcpRetransmissionSession);
-        }
-#endif
-
-        mRTPPort = serverRtp;
-        mRTPSessionID = rtpSession;
-        mRTCPSessionID = rtcpSession;
-
-        ALOGI("rtpSessionID = %d, rtcpSessionID = %d", rtpSession, rtcpSession);
-        break;
-    }
-
-    if (mRTPPort == 0) {
-        return UNKNOWN_ERROR;
-    }
-
-    return OK;
-}
-
-status_t Sender::finishInit() {
-    if (mTransportMode != TRANSPORT_TCP) {
-        notifyInitDone();
-        return OK;
-    }
-
-    sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
-
-    status_t err = mNetSession->createTCPDatagramSession(
-                mRTPPort, mClientIP.c_str(), mClientRTPPort,
-                rtpNotify, &mRTPSessionID);
-
-    if (err != OK) {
-        return err;
-    }
-
-    if (mClientRTCPPort >= 0) {
-        sp<AMessage> rtcpNotify = new AMessage(kWhatRTCPNotify, id());
-
-        err = mNetSession->createTCPDatagramSession(
-                mRTPPort + 1, mClientIP.c_str(), mClientRTCPPort,
-                rtcpNotify, &mRTCPSessionID);
-
-        if (err != OK) {
-            return err;
-        }
-    }
-
-    return OK;
-}
-
-int32_t Sender::getRTPPort() const {
-    return mRTPPort;
-}
-
-void Sender::queuePackets(
-        int64_t timeUs, const sp<ABuffer> &tsPackets) {
-    const size_t numTSPackets = tsPackets->size() / 188;
-
-    const size_t numRTPPackets =
-        (numTSPackets + kMaxNumTSPacketsPerRTPPacket - 1)
-            / kMaxNumTSPacketsPerRTPPacket;
-
-    sp<ABuffer> udpPackets = new ABuffer(
-            numRTPPackets * (12 + kMaxNumTSPacketsPerRTPPacket * 188));
-
-    udpPackets->meta()->setInt64("timeUs", timeUs);
-
-    size_t dstOffset = 0;
-    for (size_t i = 0; i < numTSPackets; ++i) {
-        if ((i % kMaxNumTSPacketsPerRTPPacket) == 0) {
-            static const bool kMarkerBit = false;
-
-            uint8_t *rtp = udpPackets->data() + dstOffset;
-            rtp[0] = 0x80;
-            rtp[1] = 33 | (kMarkerBit ? (1 << 7) : 0);  // M-bit
-            rtp[2] = (mRTPSeqNo >> 8) & 0xff;
-            rtp[3] = mRTPSeqNo & 0xff;
-            rtp[4] = 0x00;  // rtp time to be filled in later.
-            rtp[5] = 0x00;
-            rtp[6] = 0x00;
-            rtp[7] = 0x00;
-            rtp[8] = kSourceID >> 24;
-            rtp[9] = (kSourceID >> 16) & 0xff;
-            rtp[10] = (kSourceID >> 8) & 0xff;
-            rtp[11] = kSourceID & 0xff;
-
-            ++mRTPSeqNo;
-
-            dstOffset += 12;
-        }
-
-        memcpy(udpPackets->data() + dstOffset,
-               tsPackets->data() + 188 * i,
-               188);
-
-        dstOffset += 188;
-    }
-
-    udpPackets->setRange(0, dstOffset);
-
-    sp<AMessage> msg = new AMessage(kWhatDrainQueue, id());
-    msg->setBuffer("udpPackets", udpPackets);
-    msg->post();
-
-#if LOG_TRANSPORT_STREAM
-    if (mLogFile != NULL) {
-        fwrite(tsPackets->data(), 1, tsPackets->size(), mLogFile);
-    }
-#endif
-}
-
-void Sender::onMessageReceived(const sp<AMessage> &msg) {
-    switch (msg->what()) {
-        case kWhatRTPNotify:
-        case kWhatRTCPNotify:
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-        case kWhatRTPRetransmissionNotify:
-        case kWhatRTCPRetransmissionNotify:
-#endif
-        {
-            int32_t reason;
-            CHECK(msg->findInt32("reason", &reason));
-
-            switch (reason) {
-                case ANetworkSession::kWhatError:
-                {
-                    int32_t sessionID;
-                    CHECK(msg->findInt32("sessionID", &sessionID));
-
-                    int32_t err;
-                    CHECK(msg->findInt32("err", &err));
-
-                    int32_t errorOccuredDuringSend;
-                    CHECK(msg->findInt32("send", &errorOccuredDuringSend));
-
-                    AString detail;
-                    CHECK(msg->findString("detail", &detail));
-
-                    if ((msg->what() == kWhatRTPNotify
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-                            || msg->what() == kWhatRTPRetransmissionNotify
-#endif
-                        ) && !errorOccuredDuringSend) {
-                        // This is ok, we don't expect to receive anything on
-                        // the RTP socket.
-                        break;
-                    }
-
-                    ALOGE("An error occurred during %s in session %d "
-                          "(%d, '%s' (%s)).",
-                          errorOccuredDuringSend ? "send" : "receive",
-                          sessionID,
-                          err,
-                          detail.c_str(),
-                          strerror(-err));
-
-                    mNetSession->destroySession(sessionID);
-
-                    if (sessionID == mRTPSessionID) {
-                        mRTPSessionID = 0;
-                    } else if (sessionID == mRTCPSessionID) {
-                        mRTCPSessionID = 0;
-                    }
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-                    else if (sessionID == mRTPRetransmissionSessionID) {
-                        mRTPRetransmissionSessionID = 0;
-                    } else if (sessionID == mRTCPRetransmissionSessionID) {
-                        mRTCPRetransmissionSessionID = 0;
-                    }
-#endif
-
-                    notifySessionDead();
-                    break;
-                }
-
-                case ANetworkSession::kWhatDatagram:
-                {
-                    int32_t sessionID;
-                    CHECK(msg->findInt32("sessionID", &sessionID));
-
-                    sp<ABuffer> data;
-                    CHECK(msg->findBuffer("data", &data));
-
-                    status_t err;
-                    if (msg->what() == kWhatRTCPNotify
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-                            || msg->what() == kWhatRTCPRetransmissionNotify
-#endif
-                       )
-                    {
-                        err = parseRTCP(data);
-                    }
-                    break;
-                }
-
-                case ANetworkSession::kWhatConnected:
-                {
-                    CHECK_EQ(mTransportMode, TRANSPORT_TCP);
-
-                    int32_t sessionID;
-                    CHECK(msg->findInt32("sessionID", &sessionID));
-
-                    if (sessionID == mRTPSessionID) {
-                        CHECK(!mRTPConnected);
-                        mRTPConnected = true;
-                        ALOGI("RTP Session now connected.");
-                    } else if (sessionID == mRTCPSessionID) {
-                        CHECK(!mRTCPConnected);
-                        mRTCPConnected = true;
-                        ALOGI("RTCP Session now connected.");
-                    } else {
-                        TRESPASS();
-                    }
-
-                    if (mRTPConnected
-                            && (mClientRTCPPort < 0 || mRTCPConnected)) {
-                        notifyInitDone();
-                    }
-                    break;
-                }
-
-                default:
-                    TRESPASS();
-            }
-            break;
-        }
-
-        case kWhatDrainQueue:
-        {
-            sp<ABuffer> udpPackets;
-            CHECK(msg->findBuffer("udpPackets", &udpPackets));
-
-            onDrainQueue(udpPackets);
-            break;
-        }
-
-        case kWhatSendSR:
-        {
-            mSendSRPending = false;
-
-            if (mRTCPSessionID == 0) {
-                break;
-            }
-
-            onSendSR();
-
-            scheduleSendSR();
-            break;
-        }
-    }
-}
-
-void Sender::scheduleSendSR() {
-    if (mSendSRPending || mRTCPSessionID == 0) {
-        return;
-    }
-
-    mSendSRPending = true;
-    (new AMessage(kWhatSendSR, id()))->post(kSendSRIntervalUs);
-}
-
-void Sender::addSR(const sp<ABuffer> &buffer) {
-    uint8_t *data = buffer->data() + buffer->size();
-
-    // TODO: Use macros/utility functions to clean up all the bitshifts below.
-
-    data[0] = 0x80 | 0;
-    data[1] = 200;  // SR
-    data[2] = 0;
-    data[3] = 6;
-    data[4] = kSourceID >> 24;
-    data[5] = (kSourceID >> 16) & 0xff;
-    data[6] = (kSourceID >> 8) & 0xff;
-    data[7] = kSourceID & 0xff;
-
-    data[8] = mLastNTPTime >> (64 - 8);
-    data[9] = (mLastNTPTime >> (64 - 16)) & 0xff;
-    data[10] = (mLastNTPTime >> (64 - 24)) & 0xff;
-    data[11] = (mLastNTPTime >> 32) & 0xff;
-    data[12] = (mLastNTPTime >> 24) & 0xff;
-    data[13] = (mLastNTPTime >> 16) & 0xff;
-    data[14] = (mLastNTPTime >> 8) & 0xff;
-    data[15] = mLastNTPTime & 0xff;
-
-    data[16] = (mLastRTPTime >> 24) & 0xff;
-    data[17] = (mLastRTPTime >> 16) & 0xff;
-    data[18] = (mLastRTPTime >> 8) & 0xff;
-    data[19] = mLastRTPTime & 0xff;
-
-    data[20] = mNumRTPSent >> 24;
-    data[21] = (mNumRTPSent >> 16) & 0xff;
-    data[22] = (mNumRTPSent >> 8) & 0xff;
-    data[23] = mNumRTPSent & 0xff;
-
-    data[24] = mNumRTPOctetsSent >> 24;
-    data[25] = (mNumRTPOctetsSent >> 16) & 0xff;
-    data[26] = (mNumRTPOctetsSent >> 8) & 0xff;
-    data[27] = mNumRTPOctetsSent & 0xff;
-
-    buffer->setRange(buffer->offset(), buffer->size() + 28);
-}
-
-void Sender::addSDES(const sp<ABuffer> &buffer) {
-    uint8_t *data = buffer->data() + buffer->size();
-    data[0] = 0x80 | 1;
-    data[1] = 202;  // SDES
-    data[4] = kSourceID >> 24;
-    data[5] = (kSourceID >> 16) & 0xff;
-    data[6] = (kSourceID >> 8) & 0xff;
-    data[7] = kSourceID & 0xff;
-
-    size_t offset = 8;
-
-    data[offset++] = 1;  // CNAME
-
-    static const char *kCNAME = "someone@somewhere";
-    data[offset++] = strlen(kCNAME);
-
-    memcpy(&data[offset], kCNAME, strlen(kCNAME));
-    offset += strlen(kCNAME);
-
-    data[offset++] = 7;  // NOTE
-
-    static const char *kNOTE = "Hell's frozen over.";
-    data[offset++] = strlen(kNOTE);
-
-    memcpy(&data[offset], kNOTE, strlen(kNOTE));
-    offset += strlen(kNOTE);
-
-    data[offset++] = 0;
-
-    if ((offset % 4) > 0) {
-        size_t count = 4 - (offset % 4);
-        switch (count) {
-            case 3:
-                data[offset++] = 0;
-            case 2:
-                data[offset++] = 0;
-            case 1:
-                data[offset++] = 0;
-        }
-    }
-
-    size_t numWords = (offset / 4) - 1;
-    data[2] = numWords >> 8;
-    data[3] = numWords & 0xff;
-
-    buffer->setRange(buffer->offset(), buffer->size() + offset);
-}
-
-// static
-uint64_t Sender::GetNowNTP() {
-    uint64_t nowUs = ALooper::GetNowUs();
-
-    nowUs += ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll;
-
-    uint64_t hi = nowUs / 1000000ll;
-    uint64_t lo = ((1ll << 32) * (nowUs % 1000000ll)) / 1000000ll;
-
-    return (hi << 32) | lo;
-}
-
-void Sender::onSendSR() {
-    sp<ABuffer> buffer = new ABuffer(1500);
-    buffer->setRange(0, 0);
-
-    addSR(buffer);
-    addSDES(buffer);
-
-    if (mTransportMode == TRANSPORT_TCP_INTERLEAVED) {
-        sp<AMessage> notify = mNotify->dup();
-        notify->setInt32("what", kWhatBinaryData);
-        notify->setInt32("channel", mRTCPChannel);
-        notify->setBuffer("data", buffer);
-        notify->post();
-    } else {
-        sendPacket(mRTCPSessionID, buffer->data(), buffer->size());
-    }
-
-    ++mNumSRsSent;
-}
-
-#if ENABLE_RETRANSMISSION
-status_t Sender::parseTSFB(
-        const uint8_t *data, size_t size) {
-    if ((data[0] & 0x1f) != 1) {
-        return ERROR_UNSUPPORTED;  // We only support NACK for now.
-    }
-
-    uint32_t srcId = U32_AT(&data[8]);
-    if (srcId != kSourceID) {
-        return ERROR_MALFORMED;
-    }
-
-    for (size_t i = 12; i < size; i += 4) {
-        uint16_t seqNo = U16_AT(&data[i]);
-        uint16_t blp = U16_AT(&data[i + 2]);
-
-        List<sp<ABuffer> >::iterator it = mHistory.begin();
-        bool foundSeqNo = false;
-        while (it != mHistory.end()) {
-            const sp<ABuffer> &buffer = *it;
-
-            uint16_t bufferSeqNo = buffer->int32Data() & 0xffff;
-
-            bool retransmit = false;
-            if (bufferSeqNo == seqNo) {
-                retransmit = true;
-            } else if (blp != 0) {
-                for (size_t i = 0; i < 16; ++i) {
-                    if ((blp & (1 << i))
-                        && (bufferSeqNo == ((seqNo + i + 1) & 0xffff))) {
-                        blp &= ~(1 << i);
-                        retransmit = true;
-                    }
-                }
-            }
-
-            if (retransmit) {
-                ALOGI("retransmitting seqNo %d", bufferSeqNo);
-
-#if RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-                sp<ABuffer> retransRTP = new ABuffer(2 + buffer->size());
-                uint8_t *rtp = retransRTP->data();
-                memcpy(rtp, buffer->data(), 12);
-                rtp[2] = (mRTPRetransmissionSeqNo >> 8) & 0xff;
-                rtp[3] = mRTPRetransmissionSeqNo & 0xff;
-                rtp[12] = (bufferSeqNo >> 8) & 0xff;
-                rtp[13] = bufferSeqNo & 0xff;
-                memcpy(&rtp[14], buffer->data() + 12, buffer->size() - 12);
-
-                ++mRTPRetransmissionSeqNo;
-
-                sendPacket(
-                        mRTPRetransmissionSessionID,
-                        retransRTP->data(), retransRTP->size());
-#else
-                sendPacket(
-                        mRTPSessionID, buffer->data(), buffer->size());
-#endif
-
-                if (bufferSeqNo == seqNo) {
-                    foundSeqNo = true;
-                }
-
-                if (foundSeqNo && blp == 0) {
-                    break;
-                }
-            }
-
-            ++it;
-        }
-
-        if (!foundSeqNo || blp != 0) {
-            ALOGI("Some sequence numbers were no longer available for "
-                  "retransmission (seqNo = %d, foundSeqNo = %d, blp = 0x%04x)",
-                  seqNo, foundSeqNo, blp);
-
-            if (!mHistory.empty()) {
-                int32_t earliest = (*mHistory.begin())->int32Data() & 0xffff;
-                int32_t latest = (*--mHistory.end())->int32Data() & 0xffff;
-
-                ALOGI("have seq numbers from %d - %d", earliest, latest);
-            }
-        }
-    }
-
-    return OK;
-}
-#endif
-
-status_t Sender::parseRTCP(
-        const sp<ABuffer> &buffer) {
-    const uint8_t *data = buffer->data();
-    size_t size = buffer->size();
-
-    while (size > 0) {
-        if (size < 8) {
-            // Too short to be a valid RTCP header
-            return ERROR_MALFORMED;
-        }
-
-        if ((data[0] >> 6) != 2) {
-            // Unsupported version.
-            return ERROR_UNSUPPORTED;
-        }
-
-        if (data[0] & 0x20) {
-            // Padding present.
-
-            size_t paddingLength = data[size - 1];
-
-            if (paddingLength + 12 > size) {
-                // If we removed this much padding we'd end up with something
-                // that's too short to be a valid RTP header.
-                return ERROR_MALFORMED;
-            }
-
-            size -= paddingLength;
-        }
-
-        size_t headerLength = 4 * (data[2] << 8 | data[3]) + 4;
-
-        if (size < headerLength) {
-            // Only received a partial packet?
-            return ERROR_MALFORMED;
-        }
-
-        switch (data[1]) {
-            case 200:
-            case 201:  // RR
-            case 202:  // SDES
-            case 203:
-            case 204:  // APP
-                break;
-
-#if ENABLE_RETRANSMISSION
-            case 205:  // TSFB (transport layer specific feedback)
-                parseTSFB(data, headerLength);
-                break;
-#endif
-
-            case 206:  // PSFB (payload specific feedback)
-                hexdump(data, headerLength);
-                break;
-
-            default:
-            {
-                ALOGW("Unknown RTCP packet type %u of size %d",
-                     (unsigned)data[1], headerLength);
-                break;
-            }
-        }
-
-        data += headerLength;
-        size -= headerLength;
-    }
-
-    return OK;
-}
-
-status_t Sender::sendPacket(
-        int32_t sessionID, const void *data, size_t size) {
-    return mNetSession->sendRequest(sessionID, data, size);
-}
-
-void Sender::notifyInitDone() {
-    sp<AMessage> notify = mNotify->dup();
-    notify->setInt32("what", kWhatInitDone);
-    notify->post();
-}
-
-void Sender::notifySessionDead() {
-    sp<AMessage> notify = mNotify->dup();
-    notify->setInt32("what", kWhatSessionDead);
-    notify->post();
-}
-
-void Sender::onDrainQueue(const sp<ABuffer> &udpPackets) {
-    static const size_t kFullRTPPacketSize =
-        12 + 188 * kMaxNumTSPacketsPerRTPPacket;
-
-    size_t srcOffset = 0;
-    while (srcOffset < udpPackets->size()) {
-        uint8_t *rtp = udpPackets->data() + srcOffset;
-
-        size_t rtpPacketSize = udpPackets->size() - srcOffset;
-        if (rtpPacketSize > kFullRTPPacketSize) {
-            rtpPacketSize = kFullRTPPacketSize;
-        }
-
-        int64_t nowUs = ALooper::GetNowUs();
-        mLastNTPTime = GetNowNTP();
-
-        // 90kHz time scale
-        uint32_t rtpTime = (nowUs * 9ll) / 100ll;
-
-        rtp[4] = rtpTime >> 24;
-        rtp[5] = (rtpTime >> 16) & 0xff;
-        rtp[6] = (rtpTime >> 8) & 0xff;
-        rtp[7] = rtpTime & 0xff;
-
-        ++mNumRTPSent;
-        mNumRTPOctetsSent += rtpPacketSize - 12;
-
-        mLastRTPTime = rtpTime;
-
-        if (mTransportMode == TRANSPORT_TCP_INTERLEAVED) {
-            sp<AMessage> notify = mNotify->dup();
-            notify->setInt32("what", kWhatBinaryData);
-
-            sp<ABuffer> data = new ABuffer(rtpPacketSize);
-            memcpy(data->data(), rtp, rtpPacketSize);
-
-            notify->setInt32("channel", mRTPChannel);
-            notify->setBuffer("data", data);
-            notify->post();
-        } else {
-            sendPacket(mRTPSessionID, rtp, rtpPacketSize);
-
-#if TRACK_BANDWIDTH
-            mTotalBytesSent += rtpPacketSize->size();
-            int64_t delayUs = ALooper::GetNowUs() - mFirstPacketTimeUs;
-
-            if (delayUs > 0ll) {
-                ALOGI("approx. net bandwidth used: %.2f Mbit/sec",
-                        mTotalBytesSent * 8.0 / delayUs);
-            }
-#endif
-        }
-
-#if ENABLE_RETRANSMISSION
-        addToHistory(rtp, rtpPacketSize);
-#endif
-
-        srcOffset += rtpPacketSize;
-    }
-
-#if 0
-    int64_t timeUs;
-    CHECK(udpPackets->meta()->findInt64("timeUs", &timeUs));
-
-    ALOGI("dTimeUs = %lld us", ALooper::GetNowUs() - timeUs);
-#endif
-}
-
-#if ENABLE_RETRANSMISSION
-void Sender::addToHistory(const uint8_t *rtp, size_t rtpPacketSize) {
-    sp<ABuffer> packet = new ABuffer(rtpPacketSize);
-    memcpy(packet->data(), rtp, rtpPacketSize);
-
-    unsigned rtpSeqNo = U16_AT(&rtp[2]);
-    packet->setInt32Data(rtpSeqNo);
-
-    mHistory.push_back(packet);
-    ++mHistoryLength;
-
-    if (mHistoryLength > kMaxHistoryLength) {
-        mHistory.erase(mHistory.begin());
-        --mHistoryLength;
-    }
-}
-#endif
-
-}  // namespace android
-
diff --git a/media/libstagefright/wifi-display/source/Sender.h b/media/libstagefright/wifi-display/source/Sender.h
deleted file mode 100644
index 66951f7..0000000
--- a/media/libstagefright/wifi-display/source/Sender.h
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * 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 SENDER_H_
-
-#define SENDER_H_
-
-#include <media/stagefright/foundation/AHandler.h>
-
-namespace android {
-
-#define LOG_TRANSPORT_STREAM            0
-#define TRACK_BANDWIDTH                 0
-
-#define ENABLE_RETRANSMISSION                   1
-
-// If retransmission is enabled the following define determines what
-// kind we support, if RETRANSMISSION_ACCORDING_TO_RFC_XXXX is 0
-// we'll send NACKs on the original RTCP channel and retransmit packets
-// on the original RTP channel, otherwise a separate channel pair is used
-// for this purpose.
-#define RETRANSMISSION_ACCORDING_TO_RFC_XXXX    0
-
-struct ABuffer;
-struct ANetworkSession;
-
-struct Sender : public AHandler {
-    Sender(const sp<ANetworkSession> &netSession, const sp<AMessage> &notify);
-
-    enum {
-        kWhatInitDone,
-        kWhatSessionDead,
-        kWhatBinaryData,
-    };
-
-    enum TransportMode {
-        TRANSPORT_UDP,
-        TRANSPORT_TCP_INTERLEAVED,
-        TRANSPORT_TCP,
-    };
-    status_t init(
-            const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
-            TransportMode transportMode);
-
-    status_t finishInit();
-
-    int32_t getRTPPort() const;
-
-    void queuePackets(int64_t timeUs, const sp<ABuffer> &tsPackets);
-    void scheduleSendSR();
-
-protected:
-    virtual ~Sender();
-    virtual void onMessageReceived(const sp<AMessage> &msg);
-
-private:
-    enum {
-        kWhatDrainQueue,
-        kWhatSendSR,
-        kWhatRTPNotify,
-        kWhatRTCPNotify,
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-        kWhatRTPRetransmissionNotify,
-        kWhatRTCPRetransmissionNotify,
-#endif
-    };
-
-    static const int64_t kSendSRIntervalUs = 10000000ll;
-
-    static const uint32_t kSourceID = 0xdeadbeef;
-    static const size_t kMaxHistoryLength = 128;
-
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-    static const size_t kRetransmissionPortOffset = 120;
-#endif
-
-    sp<ANetworkSession> mNetSession;
-    sp<AMessage> mNotify;
-
-    TransportMode mTransportMode;
-    AString mClientIP;
-
-    // in TCP mode
-    int32_t mRTPChannel;
-    int32_t mRTCPChannel;
-
-    // in UDP mode
-    int32_t mRTPPort;
-    int32_t mRTPSessionID;
-    int32_t mRTCPSessionID;
-
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-    int32_t mRTPRetransmissionSessionID;
-    int32_t mRTCPRetransmissionSessionID;
-#endif
-
-    int32_t mClientRTPPort;
-    int32_t mClientRTCPPort;
-    bool mRTPConnected;
-    bool mRTCPConnected;
-
-    int64_t mFirstOutputBufferReadyTimeUs;
-    int64_t mFirstOutputBufferSentTimeUs;
-
-    uint32_t mRTPSeqNo;
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-    uint32_t mRTPRetransmissionSeqNo;
-#endif
-
-    uint64_t mLastNTPTime;
-    uint32_t mLastRTPTime;
-    uint32_t mNumRTPSent;
-    uint32_t mNumRTPOctetsSent;
-    uint32_t mNumSRsSent;
-
-    bool mSendSRPending;
-
-#if ENABLE_RETRANSMISSION
-    List<sp<ABuffer> > mHistory;
-    size_t mHistoryLength;
-#endif
-
-#if TRACK_BANDWIDTH
-    int64_t mFirstPacketTimeUs;
-    uint64_t mTotalBytesSent;
-#endif
-
-#if LOG_TRANSPORT_STREAM
-    FILE *mLogFile;
-#endif
-
-    void onSendSR();
-    void addSR(const sp<ABuffer> &buffer);
-    void addSDES(const sp<ABuffer> &buffer);
-    static uint64_t GetNowNTP();
-
-#if ENABLE_RETRANSMISSION
-    status_t parseTSFB(const uint8_t *data, size_t size);
-    void addToHistory(const uint8_t *rtp, size_t rtpPacketSize);
-#endif
-
-    status_t parseRTCP(const sp<ABuffer> &buffer);
-
-    status_t sendPacket(int32_t sessionID, const void *data, size_t size);
-
-    void notifyInitDone();
-    void notifySessionDead();
-
-    void onDrainQueue(const sp<ABuffer> &udpPackets);
-
-    DISALLOW_EVIL_CONSTRUCTORS(Sender);
-};
-
-}  // namespace android
-
-#endif  // SENDER_H_
diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.cpp b/media/libstagefright/wifi-display/source/TSPacketizer.cpp
index ef57a4d..8420529 100644
--- a/media/libstagefright/wifi-display/source/TSPacketizer.cpp
+++ b/media/libstagefright/wifi-display/source/TSPacketizer.cpp
@@ -58,6 +58,7 @@
     sp<ABuffer> descriptorAt(size_t index) const;
 
     void finalize();
+    void extractCSDIfNecessary();
 
 protected:
     virtual ~Track();
@@ -77,6 +78,7 @@
 
     bool mAudioLacksATDSHeaders;
     bool mFinalized;
+    bool mExtractedCSD;
 
     DISALLOW_EVIL_CONSTRUCTORS(Track);
 };
@@ -90,14 +92,21 @@
       mStreamID(streamID),
       mContinuityCounter(0),
       mAudioLacksATDSHeaders(false),
-      mFinalized(false) {
+      mFinalized(false),
+      mExtractedCSD(false) {
     CHECK(format->findString("mime", &mMIME));
+}
+
+void TSPacketizer::Track::extractCSDIfNecessary() {
+    if (mExtractedCSD) {
+        return;
+    }
 
     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)) {
+            if (!mFormat->findBuffer(StringPrintf("csd-%d", i).c_str(), &csd)) {
                 break;
             }
 
@@ -111,6 +120,8 @@
             }
         }
     }
+
+    mExtractedCSD = true;
 }
 
 TSPacketizer::Track::~Track() {
@@ -407,6 +418,17 @@
     return mTracks.add(track);
 }
 
+status_t TSPacketizer::extractCSDIfNecessary(size_t trackIndex) {
+    if (trackIndex >= mTracks.size()) {
+        return -ERANGE;
+    }
+
+    const sp<Track> &track = mTracks.itemAt(trackIndex);
+    track->extractCSDIfNecessary();
+
+    return OK;
+}
+
 status_t TSPacketizer::packetize(
         size_t trackIndex,
         const sp<ABuffer> &_accessUnit,
diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.h b/media/libstagefright/wifi-display/source/TSPacketizer.h
index a37917d..5d1d70e 100644
--- a/media/libstagefright/wifi-display/source/TSPacketizer.h
+++ b/media/libstagefright/wifi-display/source/TSPacketizer.h
@@ -50,6 +50,8 @@
             const uint8_t *PES_private_data, size_t PES_private_data_len,
             size_t numStuffingBytes = 0);
 
+    status_t extractCSDIfNecessary(size_t trackIndex);
+
     // XXX to be removed once encoder config option takes care of this for
     // encrypted mode.
     sp<ABuffer> prependCSD(
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
index 825ebc6..07eb237 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
@@ -22,7 +22,7 @@
 #include "PlaybackSession.h"
 #include "Parameters.h"
 #include "ParsedMessage.h"
-#include "Sender.h"
+#include "rtp/RTPSender.h"
 
 #include <binder/IServiceManager.h>
 #include <gui/IGraphicBufferProducer.h>
@@ -1140,7 +1140,7 @@
         return ERROR_MALFORMED;
     }
 
-    Sender::TransportMode transportMode = Sender::TRANSPORT_UDP;
+    RTPSender::TransportMode transportMode = RTPSender::TRANSPORT_UDP;
 
     int clientRtp, clientRtcp;
     if (transport.startsWith("RTP/AVP/TCP;")) {
@@ -1149,7 +1149,7 @@
                     transport.c_str(), "interleaved", &interleaved)
                 && sscanf(interleaved.c_str(), "%d-%d",
                           &clientRtp, &clientRtcp) == 2) {
-            transportMode = Sender::TRANSPORT_TCP_INTERLEAVED;
+            transportMode = RTPSender::TRANSPORT_TCP_INTERLEAVED;
         } else {
             bool badRequest = false;
 
@@ -1171,7 +1171,7 @@
                 return ERROR_MALFORMED;
             }
 
-            transportMode = Sender::TRANSPORT_TCP;
+            transportMode = RTPSender::TRANSPORT_TCP;
         }
     } else if (transport.startsWith("RTP/AVP;unicast;")
             || transport.startsWith("RTP/AVP/UDP;unicast;")) {
@@ -1263,7 +1263,7 @@
     AString response = "RTSP/1.0 200 OK\r\n";
     AppendCommonResponse(&response, cseq, playbackSessionID);
 
-    if (transportMode == Sender::TRANSPORT_TCP_INTERLEAVED) {
+    if (transportMode == RTPSender::TRANSPORT_TCP_INTERLEAVED) {
         response.append(
                 StringPrintf(
                     "Transport: RTP/AVP/TCP;interleaved=%d-%d;",
@@ -1272,7 +1272,7 @@
         int32_t serverRtp = playbackSession->getRTPPort();
 
         AString transportString = "UDP";
-        if (transportMode == Sender::TRANSPORT_TCP) {
+        if (transportMode == RTPSender::TRANSPORT_TCP) {
             transportString = "TCP";
         }