Merge "Clean up the native code to match Java update" into jb-mr2-dev
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 9d07ed5..db5a7ab 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -226,7 +226,7 @@
      * This includes the latency due to AudioTrack buffer size, AudioMixer (if any)
      * and audio hardware driver.
      */
-            uint32_t     latency() const    { return mLatency; }
+            uint32_t    latency() const     { return mLatency; }
 
     /* getters, see constructors and set() */
 
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/SingleStateQueue.h b/include/media/SingleStateQueue.h
new file mode 100644
index 0000000..04c5fd0
--- /dev/null
+++ b/include/media/SingleStateQueue.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SINGLE_STATE_QUEUE_H
+#define SINGLE_STATE_QUEUE_H
+
+// Non-blocking single element state queue, or
+// Non-blocking single-reader / single-writer multi-word atomic load / store
+
+#include <stdint.h>
+
+namespace android {
+
+template<typename T> class SingleStateQueue {
+
+public:
+
+    class Mutator;
+    class Observer;
+
+    struct Shared {
+        // needs to be part of a union so don't define constructor or destructor
+
+        friend class Mutator;
+        friend class Observer;
+
+private:
+        void                init() { mAck = 0; mSequence = 0; }
+
+        volatile int32_t    mAck;
+#if 0
+        int                 mPad[7];
+        // cache line boundary
+#endif
+        volatile int32_t    mSequence;
+        T                   mValue;
+    };
+
+    class Mutator {
+    public:
+        Mutator(Shared *shared);
+        /*virtual*/ ~Mutator() { }
+
+        // push new value onto state queue, overwriting previous value;
+        // returns a sequence number which can be used with ack()
+        int32_t push(const T& value);
+
+        // return true if most recent push has been observed
+        bool ack();
+
+        // return true if a push with specified sequence number or later has been observed
+        bool ack(int32_t sequence);
+
+    private:
+        int32_t     mSequence;
+        Shared * const mShared;
+    };
+
+    class Observer {
+    public:
+        Observer(Shared *shared);
+        /*virtual*/ ~Observer() { }
+
+        // return true if value has changed
+        bool poll(T& value);
+
+    private:
+        int32_t     mSequence;
+        int         mSeed;  // for PRNG
+        Shared * const mShared;
+    };
+
+#if 0
+    SingleStateQueue(void /*Shared*/ *shared);
+    /*virtual*/ ~SingleStateQueue() { }
+
+    static size_t size() { return sizeof(Shared); }
+#endif
+
+};
+
+}   // namespace android
+
+#endif  // SINGLE_STATE_QUEUE_H
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 317b6f0..96baf34 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',
@@ -275,6 +281,9 @@
     status_t requestIDRFrame();
     status_t setParameters(const sp<AMessage> &params);
 
+    // Send EOS on input stream.
+    void onSignalEndOfInputStream();
+
     DISALLOW_EVIL_CONSTRUCTORS(ACodec);
 };
 
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..35f46dc 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',
@@ -206,6 +212,8 @@
 
     sp<AMessage> mActivityNotify;
 
+    bool mHaveInputSurface;
+
     MediaCodec(const sp<ALooper> &looper);
 
     static status_t PostAndAwaitResponse(
diff --git a/include/private/media/StaticAudioTrackState.h b/include/private/media/StaticAudioTrackState.h
new file mode 100644
index 0000000..46a5946
--- /dev/null
+++ b/include/private/media/StaticAudioTrackState.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef STATIC_AUDIO_TRACK_STATE_H
+#define STATIC_AUDIO_TRACK_STATE_H
+
+namespace android {
+
+// Represents a single state of an AudioTrack that was created in static mode (shared memory buffer
+// supplied by the client).  This state needs to be communicated from the client to server.  As this
+// state is too large to be updated atomically without a mutex, and mutexes aren't allowed here, the
+// state is wrapped by a SingleStateQueue.
+struct StaticAudioTrackState {
+    // do not define constructors, destructors, or virtual methods
+    size_t  mLoopStart;
+    size_t  mLoopEnd;
+    int     mLoopCount;
+};
+
+}   // namespace android
+
+#endif  // STATIC_AUDIO_TRACK_STATE_H
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index 52fa3e1..6b48991 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -53,6 +53,13 @@
     SoundPool.cpp \
     SoundPoolThread.cpp
 
+LOCAL_SRC_FILES += ../libnbaio/roundup.c
+
+# for <cutils/atomic-inline.h>
+LOCAL_CFLAGS += -DANDROID_SMP=$(if $(findstring true,$(TARGET_CPU_SMP)),1,0)
+LOCAL_SRC_FILES += SingleStateQueue.cpp
+LOCAL_CFLAGS += -DSINGLE_STATE_QUEUE_INSTANTIATIONS='"SingleStateQueueInstantiations.cpp"'
+
 LOCAL_SHARED_LIBRARIES := \
 	libui libcutils libutils libbinder libsonivox libicuuc libexpat \
         libcamera_client libstagefright_foundation \
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 0a2b0b0..40ff1bf 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -47,9 +47,9 @@
     *frameCount = 0;
 
     size_t size = 0;
-    if (AudioSystem::getInputBufferSize(sampleRate, format, channelMask, &size)
-            != NO_ERROR) {
-        ALOGE("AudioSystem could not query the input buffer size.");
+    status_t status = AudioSystem::getInputBufferSize(sampleRate, format, channelMask, &size);
+    if (status != NO_ERROR) {
+        ALOGE("AudioSystem could not query the input buffer size; status %d", status);
         return NO_INIT;
     }
 
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/libmedia/SingleStateQueue.cpp b/media/libmedia/SingleStateQueue.cpp
new file mode 100644
index 0000000..3503baa
--- /dev/null
+++ b/media/libmedia/SingleStateQueue.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <new>
+#include <cutils/atomic.h>
+#include <cutils/atomic-inline.h> // for android_memory_barrier()
+#include <media/SingleStateQueue.h>
+
+namespace android {
+
+template<typename T> SingleStateQueue<T>::Mutator::Mutator(Shared *shared)
+    : mSequence(0), mShared((Shared *) shared)
+{
+    // exactly one of Mutator and Observer must initialize, currently it is Observer
+    //shared->init();
+}
+
+template<typename T> int32_t SingleStateQueue<T>::Mutator::push(const T& value)
+{
+    Shared *shared = mShared;
+    int32_t sequence = mSequence;
+    sequence++;
+    android_atomic_acquire_store(sequence, &shared->mSequence);
+    shared->mValue = value;
+    sequence++;
+    android_atomic_release_store(sequence, &shared->mSequence);
+    mSequence = sequence;
+    // consider signalling a futex here, if we know that observer is waiting
+    return sequence;
+}
+
+template<typename T> bool SingleStateQueue<T>::Mutator::ack()
+{
+    return mShared->mAck - mSequence == 0;
+}
+
+template<typename T> bool SingleStateQueue<T>::Mutator::ack(int32_t sequence)
+{
+    // this relies on 2's complement rollover to detect an ancient sequence number
+    return mShared->mAck - sequence >= 0;
+}
+
+template<typename T> SingleStateQueue<T>::Observer::Observer(Shared *shared)
+    : mSequence(0), mSeed(1), mShared((Shared *) shared)
+{
+    // exactly one of Mutator and Observer must initialize, currently it is Observer
+    shared->init();
+}
+
+template<typename T> bool SingleStateQueue<T>::Observer::poll(T& value)
+{
+    Shared *shared = mShared;
+    int32_t before = shared->mSequence;
+    if (before == mSequence) {
+        return false;
+    }
+    for (int tries = 0; ; ) {
+        const int MAX_TRIES = 5;
+        if (before & 1) {
+            if (++tries >= MAX_TRIES) {
+                return false;
+            }
+            before = shared->mSequence;
+        } else {
+            android_memory_barrier();
+            T temp = shared->mValue;
+            int32_t after = android_atomic_release_load(&shared->mSequence);
+            if (after == before) {
+                value = temp;
+                shared->mAck = before;
+                mSequence = before;
+                return true;
+            }
+            if (++tries >= MAX_TRIES) {
+                return false;
+            }
+            before = after;
+        }
+    }
+}
+
+#if 0
+template<typename T> SingleStateQueue<T>::SingleStateQueue(void /*Shared*/ *shared)
+{
+    ((Shared *) shared)->init();
+}
+#endif
+
+}   // namespace android
+
+// hack for gcc
+#ifdef SINGLE_STATE_QUEUE_INSTANTIATIONS
+#include SINGLE_STATE_QUEUE_INSTANTIATIONS
+#endif
diff --git a/media/libmedia/SingleStateQueueInstantiations.cpp b/media/libmedia/SingleStateQueueInstantiations.cpp
new file mode 100644
index 0000000..2afebe9
--- /dev/null
+++ b/media/libmedia/SingleStateQueueInstantiations.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/SingleStateQueue.h>
+#include <private/media/StaticAudioTrackState.h>
+
+// FIXME hack for gcc
+
+namespace android {
+
+template class SingleStateQueue<StaticAudioTrackState>; // typedef StaticAudioTrackSingleStateQueue
+
+}
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index a6cc4eb..1a2eeb1 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);
 
@@ -392,6 +394,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 +2479,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 +3250,13 @@
             break;
         }
 
+        case ACodec::kWhatCreateInputSurface:
+        {
+            onCreateInputSurface(msg);
+            handled = true;
+            break;
+        }
+
         case ACodec::kWhatStart:
         {
             onStart();
@@ -3310,6 +3335,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");
 
@@ -3359,6 +3410,12 @@
             return true;
         }
 
+        case kWhatSignalEndOfInputStream:
+        {
+            mCodec->onSignalEndOfInputStream();
+            return true;
+        }
+
         default:
             return BaseState::onMessageReceived(msg);
     }
@@ -3404,6 +3461,12 @@
             return true;
         }
 
+        case kWhatSignalEndOfInputStream:
+        {
+            mCodec->onSignalEndOfInputStream();
+            return true;
+        }
+
         default:
             return BaseState::onMessageReceived(msg);
     }
@@ -3573,6 +3636,13 @@
             break;
         }
 
+        case ACodec::kWhatSignalEndOfInputStream:
+        {
+            mCodec->onSignalEndOfInputStream();
+            handled = true;
+            break;
+        }
+
         default:
             handled = BaseState::onMessageReceived(msg);
             break;
@@ -3606,6 +3676,17 @@
     return OK;
 }
 
+void ACodec::onSignalEndOfInputStream() {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", ACodec::kWhatSignaledInputEOS);
+
+    status_t err = mOMX->signalEndOfInputStream(mNode);
+    if (err != OK) {
+        notify->setInt32("err", err);
+    }
+    notify->post();
+}
+
 bool ACodec::ExecutingState::onOMXEvent(
         OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
     switch (event) {
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 83be0fd..0d89c0f 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,12 +63,14 @@
     : mState(UNINITIALIZED),
       mLooper(looper),
       mCodec(new ACodec),
+      mReplyID(0),
       mFlags(0),
       mSoftRenderer(NULL),
       mDequeueInputTimeoutGeneration(0),
       mDequeueInputReplyID(0),
       mDequeueOutputTimeoutGeneration(0),
-      mDequeueOutputReplyID(0) {
+      mDequeueOutputReplyID(0),
+      mHaveInputSurface(false) {
 }
 
 MediaCodec::~MediaCodec() {
@@ -154,6 +157,26 @@
     return PostAndAwaitResponse(msg, &response);
 }
 
+status_t MediaCodec::createInputSurface(
+        sp<IGraphicBufferProducer>* bufferProducer) {
+    sp<AMessage> msg = new AMessage(kWhatCreateInputSurface, id());
+
+    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());
 
@@ -288,6 +311,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());
 
@@ -571,10 +601,44 @@
                     CHECK_EQ(mState, CONFIGURING);
                     setState(CONFIGURED);
 
+                    // reset input surface flag
+                    mHaveInputSurface = false;
+
                     (new AMessage)->postReply(mReplyID);
                     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);
+                        mHaveInputSurface = true;
+                    } 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 +945,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;
@@ -950,6 +1033,14 @@
             uint32_t replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
+            if (mHaveInputSurface) {
+                ALOGE("dequeueInputBuffer can't be used with input surface");
+                sp<AMessage> response = new AMessage;
+                response->setInt32("err", INVALID_OPERATION);
+                response->postReply(replyID);
+                break;
+            }
+
             if (handleDequeueInputBuffer(replyID, true /* new request */)) {
                 break;
             }
@@ -1093,6 +1184,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..211e1d1
--- /dev/null
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -0,0 +1,446 @@
+/*
+ * 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"
+//#define LOG_NDEBUG 0
+#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::omxLoaded(){
+    Mutex::Autolock autoLock(mMutex);
+    ALOGV("--> loaded");
+    CHECK(mExecuting);
+
+    ALOGV("Dropped down to loaded, 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;
+}
+
+status_t GraphicBufferSource::signalEndOfInputStream() {
+    Mutex::Autolock autoLock(mMutex);
+    ALOGV("signalEndOfInputStream: exec=%d avail=%d eos=%d",
+            mExecuting, mNumFramesAvailable, mEndOfStream);
+
+    if (mEndOfStream) {
+        ALOGE("EOS was already signaled");
+        return INVALID_OPERATION;
+    }
+
+    // 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();
+    }
+
+    return OK;
+}
+
+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..6a34bc5
--- /dev/null
+++ b/media/libstagefright/omx/GraphicBufferSource.h
@@ -0,0 +1,175 @@
+/*
+ * 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_StateLoaded, indicating that
+    // we are shutting down.
+    void omxLoaded();
+
+    // 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.
+    status_t 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..f3d8d14 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,64 @@
     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);
+
+    if (def.format.video.eColorFormat != OMX_COLOR_FormatAndroidOpaque) {
+        ALOGE("createInputSurface requires AndroidOpaque color format");
+        return INVALID_OPERATION;
+    }
+
+    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 can only be used with Surface input");
+        return INVALID_OPERATION;
+    };
+    return bufferSource->signalEndOfInputStream();
+}
+
 status_t OMXNodeInstance::allocateBuffer(
         OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer,
         void **buffer_data) {
@@ -560,6 +643,11 @@
 
     addActiveBuffer(portIndex, *buffer);
 
+    sp<GraphicBufferSource> bufferSource(getGraphicBufferSource());
+    if (bufferSource != NULL && portIndex == kPortIndexInput) {
+        bufferSource->addCodecBuffer(header);
+    }
+
     return OK;
 }
 
@@ -592,6 +680,11 @@
 
     addActiveBuffer(portIndex, *buffer);
 
+    sp<GraphicBufferSource> bufferSource(getGraphicBufferSource());
+    if (bufferSource != NULL && portIndex == kPortIndexInput) {
+        bufferSource->addCodecBuffer(header);
+    }
+
     return OK;
 }
 
@@ -646,6 +739,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 +795,25 @@
     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_StateLoaded) {
+            // Must be shutting down -- won't have a GraphicBufferSource
+            // on the way up.
+            bufferSource->omxLoaded();
+            setGraphicBufferSource(NULL);
+        }
+    }
+}
+
 // static
 OMX_ERRORTYPE OMXNodeInstance::OnEvent(
         OMX_IN OMX_HANDLETYPE hComponent,
@@ -707,6 +839,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/MediaSender.cpp b/media/libstagefright/wifi-display/MediaSender.cpp
new file mode 100644
index 0000000..105c642
--- /dev/null
+++ b/media/libstagefright/wifi-display/MediaSender.cpp
@@ -0,0 +1,451 @@
+/*
+ * 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),
+      mLogFile(NULL) {
+    // mLogFile = fopen("/data/misc/log.ts", "wb");
+}
+
+MediaSender::~MediaSender() {
+    if (mLogFile != NULL) {
+        fclose(mLogFile);
+        mLogFile = NULL;
+    }
+}
+
+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;
+        }
+
+        uint32_t flags = 0;
+        if (mHDCP != NULL) {
+            // XXX Determine proper HDCP version.
+            flags |= TSPacketizer::EMIT_HDCP20_DESCRIPTOR;
+        }
+        mTSPacketizer = new TSPacketizer(flags);
+
+        status_t err = OK;
+        for (size_t i = 0; i < mTrackInfos.size(); ++i) {
+            TrackInfo *info = &mTrackInfos.editItemAt(i);
+
+            ssize_t packetizerTrackIndex =
+                mTSPacketizer->addTrack(info->mFormat);
+
+            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) {
+                if (mLogFile != NULL) {
+                    fwrite(tsPackets->data(), 1, tsPackets->size(), mLogFile);
+                }
+
+                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..9a50f9a
--- /dev/null
+++ b/media/libstagefright/wifi-display/MediaSender.h
@@ -0,0 +1,128 @@
+/*
+ * 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;
+
+    FILE *mLogFile;
+
+    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/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/VideoFormats.cpp b/media/libstagefright/wifi-display/VideoFormats.cpp
index 9ad8c3c..d171c6f 100644
--- a/media/libstagefright/wifi-display/VideoFormats.cpp
+++ b/media/libstagefright/wifi-display/VideoFormats.cpp
@@ -256,7 +256,7 @@
     return GetConfiguration(mNativeType, mNativeIndex, NULL, NULL, NULL, NULL);
 }
 
-AString VideoFormats::getFormatSpec() const {
+AString VideoFormats::getFormatSpec(bool forM4Message) const {
     CHECK_EQ(kNumResolutionTypes, 3);
 
     // wfd_video_formats:
@@ -277,7 +277,7 @@
 
     return StringPrintf(
             "%02x 00 02 02 %08x %08x %08x 00 0000 0000 00 none none",
-            (mNativeIndex << 3) | mNativeType,
+            forM4Message ? 0x00 : ((mNativeIndex << 3) | mNativeType),
             mResolutionEnabled[0],
             mResolutionEnabled[1],
             mResolutionEnabled[2]);
@@ -289,6 +289,10 @@
         const VideoFormats &sourceSupported,
         ResolutionType *chosenType,
         size_t *chosenIndex) {
+#if 0
+    // Support for the native format is a great idea, the spec includes
+    // these features, but nobody supports it and the tests don't validate it.
+
     ResolutionType nativeType;
     size_t nativeIndex;
     sinkSupported.getNativeResolution(&nativeType, &nativeIndex);
@@ -316,6 +320,7 @@
         ALOGW("Source advertised native resolution that it doesn't "
               "actually support... ignoring");
     }
+#endif
 
     bool first = true;
     uint32_t bestScore = 0;
diff --git a/media/libstagefright/wifi-display/VideoFormats.h b/media/libstagefright/wifi-display/VideoFormats.h
index a84407a..69e2197 100644
--- a/media/libstagefright/wifi-display/VideoFormats.h
+++ b/media/libstagefright/wifi-display/VideoFormats.h
@@ -60,7 +60,7 @@
             bool *interlaced);
 
     bool parseFormatSpec(const char *spec);
-    AString getFormatSpec() const;
+    AString getFormatSpec(bool forM4Message = false) const;
 
     static bool PickBestFormat(
             const VideoFormats &sinkSupported,
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/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/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/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..53b7187 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() {
@@ -314,12 +325,31 @@
         mDescriptors.push_back(descriptor);
     }
 
-    int32_t hdcpVersion;
-    if (mFormat->findInt32("hdcp-version", &hdcpVersion)) {
+    mFinalized = true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+TSPacketizer::TSPacketizer(uint32_t flags)
+    : mFlags(flags),
+      mPATContinuityCounter(0),
+      mPMTContinuityCounter(0) {
+    initCrcTable();
+
+    if (flags & (EMIT_HDCP20_DESCRIPTOR | EMIT_HDCP21_DESCRIPTOR)) {
+        int32_t hdcpVersion;
+        if (flags & EMIT_HDCP20_DESCRIPTOR) {
+            CHECK(!(flags & EMIT_HDCP21_DESCRIPTOR));
+            hdcpVersion = 0x20;
+        } else {
+            CHECK(!(flags & EMIT_HDCP20_DESCRIPTOR));
+
+            // HDCP2.0 _and_ HDCP 2.1 specs say to set the version
+            // inside the HDCP descriptor to 0x20!!!
+            hdcpVersion = 0x20;
+        }
+
         // HDCP descriptor
-
-        CHECK(hdcpVersion == 0x20 || hdcpVersion == 0x21);
-
         sp<ABuffer> descriptor = new ABuffer(7);
         uint8_t *data = descriptor->data();
         data[0] = 0x05;  // descriptor_tag
@@ -330,18 +360,8 @@
         data[5] = 'P';
         data[6] = hdcpVersion;
 
-        mDescriptors.push_back(descriptor);
+        mProgramInfoDescriptors.push_back(descriptor);
     }
-
-    mFinalized = true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-TSPacketizer::TSPacketizer()
-    : mPATContinuityCounter(0),
-      mPMTContinuityCounter(0) {
-    initCrcTable();
 }
 
 TSPacketizer::~TSPacketizer() {
@@ -407,6 +427,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,
@@ -583,8 +614,9 @@
         // reserved = b111
         // PCR_PID = kPCR_PID (13 bits)
         // reserved = b1111
-        // program_info_length = 0x000
-        //   one or more elementary stream descriptions follow:
+        // program_info_length = 0x???
+        //   program_info_descriptors follow
+        // one or more elementary stream descriptions follow:
         //   stream_type = 0x??
         //   reserved = b111
         //   elementary_PID = b? ???? ???? ???? (13 bits)
@@ -616,8 +648,21 @@
         *ptr++ = 0x00;
         *ptr++ = 0xe0 | (kPID_PCR >> 8);
         *ptr++ = kPID_PCR & 0xff;
-        *ptr++ = 0xf0;
-        *ptr++ = 0x00;
+
+        size_t program_info_length = 0;
+        for (size_t i = 0; i < mProgramInfoDescriptors.size(); ++i) {
+            program_info_length += mProgramInfoDescriptors.itemAt(i)->size();
+        }
+
+        CHECK_LT(program_info_length, 0x400);
+        *ptr++ = 0xf0 | (program_info_length >> 8);
+        *ptr++ = (program_info_length & 0xff);
+
+        for (size_t i = 0; i < mProgramInfoDescriptors.size(); ++i) {
+            const sp<ABuffer> &desc = mProgramInfoDescriptors.itemAt(i);
+            memcpy(ptr, desc->data(), desc->size());
+            ptr += desc->size();
+        }
 
         for (size_t i = 0; i < mTracks.size(); ++i) {
             const sp<Track> &track = mTracks.itemAt(i);
diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.h b/media/libstagefright/wifi-display/source/TSPacketizer.h
index a37917d..4a664ee 100644
--- a/media/libstagefright/wifi-display/source/TSPacketizer.h
+++ b/media/libstagefright/wifi-display/source/TSPacketizer.h
@@ -32,7 +32,11 @@
 // Emits metadata tables (PAT and PMT) and timestamp stream (PCR) based
 // on flags.
 struct TSPacketizer : public RefBase {
-    TSPacketizer();
+    enum {
+        EMIT_HDCP20_DESCRIPTOR = 1,
+        EMIT_HDCP21_DESCRIPTOR = 2,
+    };
+    TSPacketizer(uint32_t flags);
 
     // Returns trackIndex or error.
     ssize_t addTrack(const sp<AMessage> &format);
@@ -50,6 +54,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(
@@ -66,8 +72,11 @@
 
     struct Track;
 
+    uint32_t mFlags;
     Vector<sp<Track> > mTracks;
 
+    Vector<sp<ABuffer> > mProgramInfoDescriptors;
+
     unsigned mPATContinuityCounter;
     unsigned mPMTContinuityCounter;
 
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
index 825ebc6..b8524f6 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>
@@ -59,18 +59,10 @@
       mHDCPPort(0),
       mHDCPInitializationComplete(false),
       mSetupTriggerDeferred(false) {
-    mSupportedSourceVideoFormats.enableAll();
+    mSupportedSourceVideoFormats.disableAll();
 
     mSupportedSourceVideoFormats.setNativeResolution(
             VideoFormats::RESOLUTION_CEA, 5);  // 1280x720 p30
-
-    // Disable resolutions above 1080p since the encoder won't be able to
-    // handle them.
-    mSupportedSourceVideoFormats.setResolutionEnabled(
-            VideoFormats::RESOLUTION_VESA, 28, false);  // 1920x1200 p30
-
-    mSupportedSourceVideoFormats.setResolutionEnabled(
-            VideoFormats::RESOLUTION_VESA, 29, false);  // 1920x1200 p60
 }
 
 WifiDisplaySource::~WifiDisplaySource() {
@@ -607,7 +599,7 @@
         chosenVideoFormat.setNativeResolution(
                 mChosenVideoResolutionType, mChosenVideoResolutionIndex);
 
-        body.append(chosenVideoFormat.getFormatSpec());
+        body.append(chosenVideoFormat.getFormatSpec(true /* forM4Message */));
         body.append("\r\n");
     }
 
@@ -1140,7 +1132,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 +1141,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 +1163,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 +1255,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 +1264,7 @@
         int32_t serverRtp = playbackSession->getRTPPort();
 
         AString transportString = "UDP";
-        if (transportMode == Sender::TRANSPORT_TCP) {
+        if (transportMode == RTPSender::TRANSPORT_TCP) {
             transportString = "TCP";
         }
 
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 7daef99..7806f48 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -104,5 +104,4 @@
 
 include $(BUILD_EXECUTABLE)
 
-
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index e81267f..b3de526 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -464,7 +464,7 @@
         PlaybackThread *thread = checkPlaybackThread_l(output);
         PlaybackThread *effectThread = NULL;
         if (thread == NULL) {
-            ALOGE("unknown output thread");
+            ALOGE("no playback thread found for output handle %d", output);
             lStatus = BAD_VALUE;
             goto Exit;
         }
@@ -589,7 +589,7 @@
     Mutex::Autolock _l(mLock);
     PlaybackThread *thread = checkPlaybackThread_l(output);
     if (thread == NULL) {
-        ALOGW("latency() unknown thread %d", output);
+        ALOGW("latency(): no playback thread found for output handle %d", output);
         return 0;
     }
     return thread->latency();
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index 2832e96..24a6dfe 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -388,9 +388,9 @@
                 if (ATRACE_ENABLED()) {
                     // I wish we had formatted trace names
                     char traceName[16];
-                    strcpy(traceName, "framesReady");
-                    traceName[11] = i + (i < 10 ? '0' : 'A' - 10);
-                    traceName[12] = '\0';
+                    strcpy(traceName, "fRdy");
+                    traceName[4] = i + (i < 10 ? '0' : 'A' - 10);
+                    traceName[5] = '\0';
                     ATRACE_INT(traceName, framesReady);
                 }
                 FastTrackDump *ftDump = &dumpState->mTracks[i];
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index adec938..a749d7a 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -77,15 +77,9 @@
 
     virtual size_t framesReady() const;
 
-    bool isPausing() const {
-        return mState == PAUSING;
-    }
-    bool isPaused() const {
-        return mState == PAUSED;
-    }
-    bool isResuming() const {
-        return mState == RESUMING;
-    }
+    bool isPausing() const { return mState == PAUSING; }
+    bool isPaused() const { return mState == PAUSED; }
+    bool isResuming() const { return mState == RESUMING; }
     bool isReady() const;
     void setPaused() { mState = PAUSED; }
     void reset();
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index ec8ffa0..9d98f0b 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2795,7 +2795,7 @@
                 // No buffers for this track. Give it a few chances to
                 // fill a buffer, then remove it from active list.
                 if (--(track->mRetryCount) <= 0) {
-                    ALOGV("BUFFER TIMEOUT: remove(%d) from active list on thread %p", name, this);
+                    ALOGI("BUFFER TIMEOUT: remove(%d) from active list on thread %p", name, this);
                     tracksToRemove->add(track);
                     // indicate to client process that the track was disabled because of underrun;
                     // it will then automatically call start() when data is available
@@ -3725,7 +3725,8 @@
                                 readInto = mRsmpInBuffer;
                                 mRsmpInIndex = 0;
                             }
-                            mBytesRead = mInput->stream->read(mInput->stream, readInto, mInputBytes);
+                            mBytesRead = mInput->stream->read(mInput->stream, readInto,
+                                    mInputBytes);
                             if (mBytesRead <= 0) {
                                 if ((mBytesRead < 0) && (mActiveTrack->mState == TrackBase::ACTIVE))
                                 {
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index fecbfda..fac7071 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -68,9 +68,7 @@
     // but putting it in TrackBase avoids the complexity of virtual inheritance
     virtual size_t  framesReady() const { return SIZE_MAX; }
 
-    audio_format_t format() const {
-        return mFormat;
-    }
+    audio_format_t format() const { return mFormat; }
 
     uint32_t channelCount() const { return mChannelCount; }
 
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index b816338..a6ab4f8 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -607,7 +607,7 @@
         track_state state = mState;
         // here the track could be either new, or restarted
         // in both cases "unstop" the track
-        if (mState == PAUSED) {
+        if (state == PAUSED) {
             mState = TrackBase::RESUMING;
             ALOGV("PAUSED => RESUMING (%d) on thread %p", mName, this);
         } else {
diff --git a/services/camera/libcameraservice/camera2/FrameProcessor.cpp b/services/camera/libcameraservice/camera2/FrameProcessor.cpp
index 3129a0b..1f2659c 100644
--- a/services/camera/libcameraservice/camera2/FrameProcessor.cpp
+++ b/services/camera/libcameraservice/camera2/FrameProcessor.cpp
@@ -247,6 +247,10 @@
                 metadata.number_of_faces--;
                 continue;
             }
+            if (faceScores[i] > 100) {
+                ALOGW("%s: Face index %d with out of range score %d",
+                        __FUNCTION__, i, faceScores[i]);
+            }
 
             camera_face_t face;