Merge "Correct MediaCodec + Surface behavior" into jb-mr2-dev
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/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/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/wifi-display/ANetworkSession.cpp b/media/libstagefright/wifi-display/ANetworkSession.cpp
index 06f71f4..cb6011c 100644
--- a/media/libstagefright/wifi-display/ANetworkSession.cpp
+++ b/media/libstagefright/wifi-display/ANetworkSession.cpp
@@ -23,6 +23,7 @@
 
 #include <arpa/inet.h>
 #include <fcntl.h>
+#include <linux/tcp.h>
 #include <net/if.h>
 #include <netdb.h>
 #include <netinet/in.h>
@@ -314,6 +315,9 @@
             sp<ABuffer> packet = new ABuffer(packetSize);
             memcpy(packet->data(), mInBuffer.c_str() + 2, packetSize);
 
+            int64_t nowUs = ALooper::GetNowUs();
+            packet->meta()->setInt64("arrivalTimeUs", nowUs);
+
             sp<AMessage> notify = mNotify->dup();
             notify->setInt32("sessionID", mSessionID);
             notify->setInt32("reason", kWhatDatagram);
@@ -770,6 +774,14 @@
             err = -errno;
             goto bail2;
         }
+    } else if (mode == kModeCreateTCPDatagramSessionActive) {
+        int flag = 1;
+        res = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
+
+        if (res < 0) {
+            err = -errno;
+            goto bail2;
+        }
     }
 
     err = MakeSocketNonBlocking(s);
diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk
index 5095e82..19f560c 100644
--- a/media/libstagefright/wifi-display/Android.mk
+++ b/media/libstagefright/wifi-display/Android.mk
@@ -4,21 +4,23 @@
 
 LOCAL_SRC_FILES:= \
         ANetworkSession.cpp             \
+        MediaReceiver.cpp               \
+        MediaSender.cpp                 \
         Parameters.cpp                  \
         ParsedMessage.cpp               \
+        rtp/RTPAssembler.cpp            \
+        rtp/RTPReceiver.cpp             \
+        rtp/RTPSender.cpp               \
         sink/DirectRenderer.cpp         \
-        sink/LinearRegression.cpp       \
-        sink/RTPSink.cpp                \
         sink/TunnelRenderer.cpp         \
         sink/WifiDisplaySink.cpp        \
+        SNTPClient.cpp                  \
         source/Converter.cpp            \
         source/MediaPuller.cpp          \
         source/PlaybackSession.cpp      \
         source/RepeaterSource.cpp       \
-        source/Sender.cpp               \
         source/TSPacketizer.cpp         \
         source/WifiDisplaySource.cpp    \
-        TimeSeries.cpp                  \
         VideoFormats.cpp                \
 
 LOCAL_C_INCLUDES:= \
@@ -85,3 +87,25 @@
 LOCAL_MODULE_TAGS := debug
 
 include $(BUILD_EXECUTABLE)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+        rtptest.cpp                 \
+
+LOCAL_SHARED_LIBRARIES:= \
+        libbinder                       \
+        libgui                          \
+        libmedia                        \
+        libstagefright                  \
+        libstagefright_foundation       \
+        libstagefright_wfd              \
+        libutils                        \
+
+LOCAL_MODULE:= rtptest
+
+LOCAL_MODULE_TAGS := debug
+
+include $(BUILD_EXECUTABLE)
diff --git a/media/libstagefright/wifi-display/MediaReceiver.cpp b/media/libstagefright/wifi-display/MediaReceiver.cpp
new file mode 100644
index 0000000..3c92d41
--- /dev/null
+++ b/media/libstagefright/wifi-display/MediaReceiver.cpp
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaReceiver"
+#include <utils/Log.h>
+
+#include "MediaReceiver.h"
+
+#include "ANetworkSession.h"
+#include "AnotherPacketSource.h"
+#include "rtp/RTPReceiver.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+MediaReceiver::MediaReceiver(
+        const sp<ANetworkSession> &netSession,
+        const sp<AMessage> &notify)
+    : mNetSession(netSession),
+      mNotify(notify),
+      mMode(MODE_UNDEFINED),
+      mGeneration(0),
+      mInitStatus(OK),
+      mInitDoneCount(0) {
+}
+
+MediaReceiver::~MediaReceiver() {
+}
+
+ssize_t MediaReceiver::addTrack(
+        RTPReceiver::TransportMode transportMode,
+        int32_t *localRTPPort) {
+    if (mMode != MODE_UNDEFINED) {
+        return INVALID_OPERATION;
+    }
+
+    size_t trackIndex = mTrackInfos.size();
+
+    TrackInfo info;
+
+    sp<AMessage> notify = new AMessage(kWhatReceiverNotify, id());
+    notify->setInt32("generation", mGeneration);
+    notify->setSize("trackIndex", trackIndex);
+
+    info.mReceiver = new RTPReceiver(mNetSession, notify);
+    looper()->registerHandler(info.mReceiver);
+
+    info.mReceiver->registerPacketType(
+            33, RTPReceiver::PACKETIZATION_TRANSPORT_STREAM);
+
+    info.mReceiver->registerPacketType(
+            96, RTPReceiver::PACKETIZATION_AAC);
+
+    info.mReceiver->registerPacketType(
+            97, RTPReceiver::PACKETIZATION_H264);
+
+    status_t err = info.mReceiver->initAsync(transportMode, localRTPPort);
+
+    if (err != OK) {
+        looper()->unregisterHandler(info.mReceiver->id());
+        info.mReceiver.clear();
+
+        return err;
+    }
+
+    mTrackInfos.push_back(info);
+
+    return trackIndex;
+}
+
+status_t MediaReceiver::connectTrack(
+        size_t trackIndex,
+        const char *remoteHost,
+        int32_t remoteRTPPort,
+        int32_t remoteRTCPPort) {
+    if (trackIndex >= mTrackInfos.size()) {
+        return -ERANGE;
+    }
+
+    TrackInfo *info = &mTrackInfos.editItemAt(trackIndex);
+    return info->mReceiver->connect(remoteHost, remoteRTPPort, remoteRTCPPort);
+}
+
+status_t MediaReceiver::initAsync(Mode mode) {
+    if ((mode == MODE_TRANSPORT_STREAM || mode == MODE_TRANSPORT_STREAM_RAW)
+            && mTrackInfos.size() > 1) {
+        return INVALID_OPERATION;
+    }
+
+    sp<AMessage> msg = new AMessage(kWhatInit, id());
+    msg->setInt32("mode", mode);
+    msg->post();
+
+    return OK;
+}
+
+void MediaReceiver::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatInit:
+        {
+            int32_t mode;
+            CHECK(msg->findInt32("mode", &mode));
+
+            CHECK_EQ(mMode, MODE_UNDEFINED);
+            mMode = (Mode)mode;
+
+            if (mInitStatus != OK || mInitDoneCount == mTrackInfos.size()) {
+                notifyInitDone(mInitStatus);
+            }
+
+            mTSParser = new ATSParser(ATSParser::ALIGNED_VIDEO_DATA);
+            mFormatKnownMask = 0;
+            break;
+        }
+
+        case kWhatReceiverNotify:
+        {
+            int32_t generation;
+            CHECK(msg->findInt32("generation", &generation));
+            if (generation != mGeneration) {
+                break;
+            }
+
+            onReceiverNotify(msg);
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+void MediaReceiver::onReceiverNotify(const sp<AMessage> &msg) {
+    int32_t what;
+    CHECK(msg->findInt32("what", &what));
+
+    switch (what) {
+        case RTPReceiver::kWhatInitDone:
+        {
+            ++mInitDoneCount;
+
+            int32_t err;
+            CHECK(msg->findInt32("err", &err));
+
+            if (err != OK) {
+                mInitStatus = err;
+                ++mGeneration;
+            }
+
+            if (mMode != MODE_UNDEFINED) {
+                if (mInitStatus != OK || mInitDoneCount == mTrackInfos.size()) {
+                    notifyInitDone(mInitStatus);
+                }
+            }
+            break;
+        }
+
+        case RTPReceiver::kWhatError:
+        {
+            int32_t err;
+            CHECK(msg->findInt32("err", &err));
+
+            notifyError(err);
+            break;
+        }
+
+        case RTPReceiver::kWhatAccessUnit:
+        {
+            size_t trackIndex;
+            CHECK(msg->findSize("trackIndex", &trackIndex));
+
+            sp<ABuffer> accessUnit;
+            CHECK(msg->findBuffer("accessUnit", &accessUnit));
+
+            int32_t followsDiscontinuity;
+            if (!msg->findInt32(
+                        "followsDiscontinuity", &followsDiscontinuity)) {
+                followsDiscontinuity = 0;
+            }
+
+            if (mMode == MODE_TRANSPORT_STREAM) {
+                if (followsDiscontinuity) {
+                    mTSParser->signalDiscontinuity(
+                            ATSParser::DISCONTINUITY_TIME, NULL /* extra */);
+                }
+
+                for (size_t offset = 0;
+                        offset < accessUnit->size(); offset += 188) {
+                    status_t err = mTSParser->feedTSPacket(
+                             accessUnit->data() + offset, 188);
+
+                    if (err != OK) {
+                        notifyError(err);
+                        break;
+                    }
+                }
+
+                drainPackets(0 /* trackIndex */, ATSParser::VIDEO);
+                drainPackets(1 /* trackIndex */, ATSParser::AUDIO);
+            } else {
+                postAccessUnit(trackIndex, accessUnit, NULL);
+            }
+            break;
+        }
+
+        case RTPReceiver::kWhatPacketLost:
+        {
+            notifyPacketLost();
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+void MediaReceiver::drainPackets(
+        size_t trackIndex, ATSParser::SourceType type) {
+    sp<AnotherPacketSource> source =
+        static_cast<AnotherPacketSource *>(
+                mTSParser->getSource(type).get());
+
+    if (source == NULL) {
+        return;
+    }
+
+    sp<AMessage> format;
+    if (!(mFormatKnownMask & (1ul << trackIndex))) {
+        sp<MetaData> meta = source->getFormat();
+        CHECK(meta != NULL);
+
+        CHECK_EQ((status_t)OK, convertMetaDataToMessage(meta, &format));
+
+        mFormatKnownMask |= 1ul << trackIndex;
+    }
+
+    status_t finalResult;
+    while (source->hasBufferAvailable(&finalResult)) {
+        sp<ABuffer> accessUnit;
+        status_t err = source->dequeueAccessUnit(&accessUnit);
+        if (err == OK) {
+            postAccessUnit(trackIndex, accessUnit, format);
+            format.clear();
+        } else if (err != INFO_DISCONTINUITY) {
+            notifyError(err);
+        }
+    }
+
+    if (finalResult != OK) {
+        notifyError(finalResult);
+    }
+}
+
+void MediaReceiver::notifyInitDone(status_t err) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatInitDone);
+    notify->setInt32("err", err);
+    notify->post();
+}
+
+void MediaReceiver::notifyError(status_t err) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatError);
+    notify->setInt32("err", err);
+    notify->post();
+}
+
+void MediaReceiver::notifyPacketLost() {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatPacketLost);
+    notify->post();
+}
+
+void MediaReceiver::postAccessUnit(
+        size_t trackIndex,
+        const sp<ABuffer> &accessUnit,
+        const sp<AMessage> &format) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatAccessUnit);
+    notify->setSize("trackIndex", trackIndex);
+    notify->setBuffer("accessUnit", accessUnit);
+
+    if (format != NULL) {
+        notify->setMessage("format", format);
+    }
+
+    notify->post();
+}
+
+}  // namespace android
+
+
diff --git a/media/libstagefright/wifi-display/MediaReceiver.h b/media/libstagefright/wifi-display/MediaReceiver.h
new file mode 100644
index 0000000..7adc3c4
--- /dev/null
+++ b/media/libstagefright/wifi-display/MediaReceiver.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/stagefright/foundation/AHandler.h>
+
+#include "ATSParser.h"
+#include "rtp/RTPReceiver.h"
+
+namespace android {
+
+struct ABuffer;
+struct ANetworkSession;
+struct AMessage;
+struct ATSParser;
+
+// This class facilitates receiving of media data for one or more tracks
+// over RTP. Either a 1:1 track to RTP channel mapping is used or a single
+// RTP channel provides the data for a transport stream that is consequently
+// demuxed and its track's data provided to the observer.
+struct MediaReceiver : public AHandler {
+    enum {
+        kWhatInitDone,
+        kWhatError,
+        kWhatAccessUnit,
+        kWhatPacketLost,
+    };
+
+    MediaReceiver(
+            const sp<ANetworkSession> &netSession,
+            const sp<AMessage> &notify);
+
+    ssize_t addTrack(
+            RTPReceiver::TransportMode transportMode,
+            int32_t *localRTPPort);
+
+    status_t connectTrack(
+            size_t trackIndex,
+            const char *remoteHost,
+            int32_t remoteRTPPort,
+            int32_t remoteRTCPPort);
+
+    enum Mode {
+        MODE_UNDEFINED,
+        MODE_TRANSPORT_STREAM,
+        MODE_TRANSPORT_STREAM_RAW,
+        MODE_ELEMENTARY_STREAMS,
+    };
+    status_t initAsync(Mode mode);
+
+protected:
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+    virtual ~MediaReceiver();
+
+private:
+    enum {
+        kWhatInit,
+        kWhatReceiverNotify,
+    };
+
+    struct TrackInfo {
+        sp<RTPReceiver> mReceiver;
+    };
+
+    sp<ANetworkSession> mNetSession;
+    sp<AMessage> mNotify;
+
+    Mode mMode;
+    int32_t mGeneration;
+
+    Vector<TrackInfo> mTrackInfos;
+
+    status_t mInitStatus;
+    size_t mInitDoneCount;
+
+    sp<ATSParser> mTSParser;
+    uint32_t mFormatKnownMask;
+
+    void onReceiverNotify(const sp<AMessage> &msg);
+
+    void drainPackets(size_t trackIndex, ATSParser::SourceType type);
+
+    void notifyInitDone(status_t err);
+    void notifyError(status_t err);
+    void notifyPacketLost();
+
+    void postAccessUnit(
+            size_t trackIndex,
+            const sp<ABuffer> &accessUnit,
+            const sp<AMessage> &format);
+
+    DISALLOW_EVIL_CONSTRUCTORS(MediaReceiver);
+};
+
+}  // namespace android
+
diff --git a/media/libstagefright/wifi-display/MediaSender.cpp b/media/libstagefright/wifi-display/MediaSender.cpp
new file mode 100644
index 0000000..900aa82
--- /dev/null
+++ b/media/libstagefright/wifi-display/MediaSender.cpp
@@ -0,0 +1,443 @@
+/*
+ * Copyright 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaSender"
+#include <utils/Log.h>
+
+#include "MediaSender.h"
+
+#include "ANetworkSession.h"
+#include "rtp/RTPSender.h"
+#include "source/TSPacketizer.h"
+
+#include "include/avc_utils.h"
+
+#include <media/IHDCP.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+namespace android {
+
+MediaSender::MediaSender(
+        const sp<ANetworkSession> &netSession,
+        const sp<AMessage> &notify)
+    : mNetSession(netSession),
+      mNotify(notify),
+      mMode(MODE_UNDEFINED),
+      mGeneration(0),
+      mPrevTimeUs(-1ll),
+      mInitDoneCount(0) {
+}
+
+MediaSender::~MediaSender() {
+}
+
+status_t MediaSender::setHDCP(const sp<IHDCP> &hdcp) {
+    if (mMode != MODE_UNDEFINED) {
+        return INVALID_OPERATION;
+    }
+
+    mHDCP = hdcp;
+
+    return OK;
+}
+
+ssize_t MediaSender::addTrack(const sp<AMessage> &format, uint32_t flags) {
+    if (mMode != MODE_UNDEFINED) {
+        return INVALID_OPERATION;
+    }
+
+    TrackInfo info;
+    info.mFormat = format;
+    info.mFlags = flags;
+    info.mPacketizerTrackIndex = -1;
+
+    AString mime;
+    CHECK(format->findString("mime", &mime));
+    info.mIsAudio = !strncasecmp("audio/", mime.c_str(), 6);
+
+    size_t index = mTrackInfos.size();
+    mTrackInfos.push_back(info);
+
+    return index;
+}
+
+status_t MediaSender::initAsync(
+        ssize_t trackIndex,
+        RTPSender::TransportMode transportMode,
+        const char *remoteHost,
+        int32_t remoteRTPPort,
+        int32_t remoteRTCPPort,
+        int32_t *localRTPPort) {
+    if (trackIndex < 0) {
+        if (mMode != MODE_UNDEFINED) {
+            return INVALID_OPERATION;
+        }
+
+        mTSPacketizer = new TSPacketizer;
+
+        status_t err = OK;
+        for (size_t i = 0; i < mTrackInfos.size(); ++i) {
+            TrackInfo *info = &mTrackInfos.editItemAt(i);
+
+            sp<AMessage> trackFormat = info->mFormat;
+            if (mHDCP != NULL && !info->mIsAudio) {
+                // HDCP2.0 _and_ HDCP 2.1 specs say to set the version
+                // inside the HDCP descriptor to 0x20!!!
+                trackFormat->setInt32("hdcp-version", 0x20);
+            }
+
+            ssize_t packetizerTrackIndex =
+                mTSPacketizer->addTrack(trackFormat);
+
+            if (packetizerTrackIndex < 0) {
+                err = packetizerTrackIndex;
+                break;
+            }
+
+            info->mPacketizerTrackIndex = packetizerTrackIndex;
+        }
+
+        if (err == OK) {
+            sp<AMessage> notify = new AMessage(kWhatSenderNotify, id());
+            notify->setInt32("generation", mGeneration);
+            mTSSender = new RTPSender(mNetSession, notify);
+            looper()->registerHandler(mTSSender);
+
+            err = mTSSender->initAsync(
+                    transportMode,
+                    remoteHost,
+                    remoteRTPPort,
+                    remoteRTCPPort,
+                    localRTPPort);
+
+            if (err != OK) {
+                looper()->unregisterHandler(mTSSender->id());
+                mTSSender.clear();
+            }
+        }
+
+        if (err != OK) {
+            for (size_t i = 0; i < mTrackInfos.size(); ++i) {
+                TrackInfo *info = &mTrackInfos.editItemAt(i);
+                info->mPacketizerTrackIndex = -1;
+            }
+
+            mTSPacketizer.clear();
+            return err;
+        }
+
+        mMode = MODE_TRANSPORT_STREAM;
+        mInitDoneCount = 1;
+
+        return OK;
+    }
+
+    if (mMode == MODE_TRANSPORT_STREAM) {
+        return INVALID_OPERATION;
+    }
+
+    if ((size_t)trackIndex >= mTrackInfos.size()) {
+        return -ERANGE;
+    }
+
+    TrackInfo *info = &mTrackInfos.editItemAt(trackIndex);
+
+    if (info->mSender != NULL) {
+        return INVALID_OPERATION;
+    }
+
+    sp<AMessage> notify = new AMessage(kWhatSenderNotify, id());
+    notify->setInt32("generation", mGeneration);
+    notify->setSize("trackIndex", trackIndex);
+
+    info->mSender = new RTPSender(mNetSession, notify);
+    looper()->registerHandler(info->mSender);
+
+    status_t err = info->mSender->initAsync(
+            transportMode,
+            remoteHost,
+            remoteRTPPort,
+            remoteRTCPPort,
+            localRTPPort);
+
+    if (err != OK) {
+        looper()->unregisterHandler(info->mSender->id());
+        info->mSender.clear();
+
+        return err;
+    }
+
+    if (mMode == MODE_UNDEFINED) {
+        mInitDoneCount = mTrackInfos.size();
+    }
+
+    mMode = MODE_ELEMENTARY_STREAMS;
+
+    return OK;
+}
+
+status_t MediaSender::queueAccessUnit(
+        size_t trackIndex, const sp<ABuffer> &accessUnit) {
+    if (mMode == MODE_UNDEFINED) {
+        return INVALID_OPERATION;
+    }
+
+    if (trackIndex >= mTrackInfos.size()) {
+        return -ERANGE;
+    }
+
+    if (mMode == MODE_TRANSPORT_STREAM) {
+        TrackInfo *info = &mTrackInfos.editItemAt(trackIndex);
+        info->mAccessUnits.push_back(accessUnit);
+
+        mTSPacketizer->extractCSDIfNecessary(info->mPacketizerTrackIndex);
+
+        for (;;) {
+            ssize_t minTrackIndex = -1;
+            int64_t minTimeUs = -1ll;
+
+            for (size_t i = 0; i < mTrackInfos.size(); ++i) {
+                const TrackInfo &info = mTrackInfos.itemAt(i);
+
+                if (info.mAccessUnits.empty()) {
+                    minTrackIndex = -1;
+                    minTimeUs = -1ll;
+                    break;
+                }
+
+                int64_t timeUs;
+                const sp<ABuffer> &accessUnit = *info.mAccessUnits.begin();
+                CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
+
+                if (minTrackIndex < 0 || timeUs < minTimeUs) {
+                    minTrackIndex = i;
+                    minTimeUs = timeUs;
+                }
+            }
+
+            if (minTrackIndex < 0) {
+                return OK;
+            }
+
+            TrackInfo *info = &mTrackInfos.editItemAt(minTrackIndex);
+            sp<ABuffer> accessUnit = *info->mAccessUnits.begin();
+            info->mAccessUnits.erase(info->mAccessUnits.begin());
+
+            sp<ABuffer> tsPackets;
+            status_t err = packetizeAccessUnit(
+                    minTrackIndex, accessUnit, &tsPackets);
+
+            if (err == OK) {
+                err = mTSSender->queueBuffer(
+                        tsPackets,
+                        33 /* packetType */,
+                        RTPSender::PACKETIZATION_TRANSPORT_STREAM);
+            }
+
+            if (err != OK) {
+                return err;
+            }
+        }
+    }
+
+    TrackInfo *info = &mTrackInfos.editItemAt(trackIndex);
+
+    return info->mSender->queueBuffer(
+            accessUnit,
+            info->mIsAudio ? 96 : 97 /* packetType */,
+            info->mIsAudio
+                ? RTPSender::PACKETIZATION_AAC : RTPSender::PACKETIZATION_H264);
+}
+
+void MediaSender::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatSenderNotify:
+        {
+            int32_t generation;
+            CHECK(msg->findInt32("generation", &generation));
+            if (generation != mGeneration) {
+                break;
+            }
+
+            onSenderNotify(msg);
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+void MediaSender::onSenderNotify(const sp<AMessage> &msg) {
+    int32_t what;
+    CHECK(msg->findInt32("what", &what));
+
+    switch (what) {
+        case RTPSender::kWhatInitDone:
+        {
+            --mInitDoneCount;
+
+            int32_t err;
+            CHECK(msg->findInt32("err", &err));
+
+            if (err != OK) {
+                notifyInitDone(err);
+                ++mGeneration;
+                break;
+            }
+
+            if (mInitDoneCount == 0) {
+                notifyInitDone(OK);
+            }
+            break;
+        }
+
+        case RTPSender::kWhatError:
+        {
+            int32_t err;
+            CHECK(msg->findInt32("err", &err));
+
+            notifyError(err);
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+void MediaSender::notifyInitDone(status_t err) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatInitDone);
+    notify->setInt32("err", err);
+    notify->post();
+}
+
+void MediaSender::notifyError(status_t err) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatError);
+    notify->setInt32("err", err);
+    notify->post();
+}
+
+status_t MediaSender::packetizeAccessUnit(
+        size_t trackIndex,
+        sp<ABuffer> accessUnit,
+        sp<ABuffer> *tsPackets) {
+    const TrackInfo &info = mTrackInfos.itemAt(trackIndex);
+
+    uint32_t flags = 0;
+
+    bool isHDCPEncrypted = false;
+    uint64_t inputCTR;
+    uint8_t HDCP_private_data[16];
+
+    bool manuallyPrependSPSPPS =
+        !info.mIsAudio
+        && (info.mFlags & FLAG_MANUALLY_PREPEND_SPS_PPS)
+        && IsIDR(accessUnit);
+
+    if (mHDCP != NULL && !info.mIsAudio) {
+        isHDCPEncrypted = true;
+
+        if (manuallyPrependSPSPPS) {
+            accessUnit = mTSPacketizer->prependCSD(
+                    info.mPacketizerTrackIndex, accessUnit);
+        }
+
+        status_t err = mHDCP->encrypt(
+                accessUnit->data(), accessUnit->size(),
+                trackIndex  /* streamCTR */,
+                &inputCTR,
+                accessUnit->data());
+
+        if (err != OK) {
+            ALOGE("Failed to HDCP-encrypt media data (err %d)",
+                  err);
+
+            return err;
+        }
+
+        HDCP_private_data[0] = 0x00;
+
+        HDCP_private_data[1] =
+            (((trackIndex >> 30) & 3) << 1) | 1;
+
+        HDCP_private_data[2] = (trackIndex >> 22) & 0xff;
+
+        HDCP_private_data[3] =
+            (((trackIndex >> 15) & 0x7f) << 1) | 1;
+
+        HDCP_private_data[4] = (trackIndex >> 7) & 0xff;
+
+        HDCP_private_data[5] =
+            ((trackIndex & 0x7f) << 1) | 1;
+
+        HDCP_private_data[6] = 0x00;
+
+        HDCP_private_data[7] =
+            (((inputCTR >> 60) & 0x0f) << 1) | 1;
+
+        HDCP_private_data[8] = (inputCTR >> 52) & 0xff;
+
+        HDCP_private_data[9] =
+            (((inputCTR >> 45) & 0x7f) << 1) | 1;
+
+        HDCP_private_data[10] = (inputCTR >> 37) & 0xff;
+
+        HDCP_private_data[11] =
+            (((inputCTR >> 30) & 0x7f) << 1) | 1;
+
+        HDCP_private_data[12] = (inputCTR >> 22) & 0xff;
+
+        HDCP_private_data[13] =
+            (((inputCTR >> 15) & 0x7f) << 1) | 1;
+
+        HDCP_private_data[14] = (inputCTR >> 7) & 0xff;
+
+        HDCP_private_data[15] =
+            ((inputCTR & 0x7f) << 1) | 1;
+
+        flags |= TSPacketizer::IS_ENCRYPTED;
+    } else if (manuallyPrependSPSPPS) {
+        flags |= TSPacketizer::PREPEND_SPS_PPS_TO_IDR_FRAMES;
+    }
+
+    int64_t timeUs = ALooper::GetNowUs();
+    if (mPrevTimeUs < 0ll || mPrevTimeUs + 100000ll <= timeUs) {
+        flags |= TSPacketizer::EMIT_PCR;
+        flags |= TSPacketizer::EMIT_PAT_AND_PMT;
+
+        mPrevTimeUs = timeUs;
+    }
+
+    mTSPacketizer->packetize(
+            info.mPacketizerTrackIndex,
+            accessUnit,
+            tsPackets,
+            flags,
+            !isHDCPEncrypted ? NULL : HDCP_private_data,
+            !isHDCPEncrypted ? 0 : sizeof(HDCP_private_data),
+            info.mIsAudio ? 2 : 0 /* numStuffingBytes */);
+
+    return OK;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/wifi-display/MediaSender.h b/media/libstagefright/wifi-display/MediaSender.h
new file mode 100644
index 0000000..834780a
--- /dev/null
+++ b/media/libstagefright/wifi-display/MediaSender.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_SENDER_H_
+
+#define MEDIA_SENDER_H_
+
+#include "rtp/RTPSender.h"
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AHandler.h>
+#include <utils/Errors.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+struct ABuffer;
+struct ANetworkSession;
+struct AMessage;
+struct IHDCP;
+struct TSPacketizer;
+
+// This class facilitates sending of data from one or more media tracks
+// through one or more RTP channels, either providing a 1:1 mapping from
+// track to RTP channel or muxing all tracks into a single RTP channel and
+// using transport stream encapsulation.
+// Optionally the (video) data is encrypted using the provided hdcp object.
+struct MediaSender : public AHandler {
+    enum {
+        kWhatInitDone,
+        kWhatError,
+    };
+
+    MediaSender(
+            const sp<ANetworkSession> &netSession,
+            const sp<AMessage> &notify);
+
+    status_t setHDCP(const sp<IHDCP> &hdcp);
+
+    enum FlagBits {
+        FLAG_MANUALLY_PREPEND_SPS_PPS = 1,
+    };
+    ssize_t addTrack(const sp<AMessage> &format, uint32_t flags);
+
+    // If trackIndex == -1, initialize for transport stream muxing.
+    status_t initAsync(
+            ssize_t trackIndex,
+            RTPSender::TransportMode transportMode,
+            const char *remoteHost,
+            int32_t remoteRTPPort,
+            int32_t remoteRTCPPort,
+            int32_t *localRTPPort);
+
+    status_t queueAccessUnit(
+            size_t trackIndex, const sp<ABuffer> &accessUnit);
+
+protected:
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+    virtual ~MediaSender();
+
+private:
+    enum {
+        kWhatSenderNotify,
+    };
+
+    enum Mode {
+        MODE_UNDEFINED,
+        MODE_TRANSPORT_STREAM,
+        MODE_ELEMENTARY_STREAMS,
+    };
+
+    struct TrackInfo {
+        sp<AMessage> mFormat;
+        uint32_t mFlags;
+        sp<RTPSender> mSender;
+        List<sp<ABuffer> > mAccessUnits;
+        ssize_t mPacketizerTrackIndex;
+        bool mIsAudio;
+    };
+
+    sp<ANetworkSession> mNetSession;
+    sp<AMessage> mNotify;
+
+    sp<IHDCP> mHDCP;
+
+    Mode mMode;
+    int32_t mGeneration;
+
+    Vector<TrackInfo> mTrackInfos;
+
+    sp<TSPacketizer> mTSPacketizer;
+    sp<RTPSender> mTSSender;
+    int64_t mPrevTimeUs;
+
+    size_t mInitDoneCount;
+
+    void onSenderNotify(const sp<AMessage> &msg);
+
+    void notifyInitDone(status_t err);
+    void notifyError(status_t err);
+
+    status_t packetizeAccessUnit(
+            size_t trackIndex,
+            sp<ABuffer> accessUnit,
+            sp<ABuffer> *tsPackets);
+
+    DISALLOW_EVIL_CONSTRUCTORS(MediaSender);
+};
+
+}  // namespace android
+
+#endif  // MEDIA_SENDER_H_
+
diff --git a/media/libstagefright/wifi-display/SNTPClient.cpp b/media/libstagefright/wifi-display/SNTPClient.cpp
new file mode 100644
index 0000000..5c0af6a
--- /dev/null
+++ b/media/libstagefright/wifi-display/SNTPClient.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SNTPClient.h"
+
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/Utils.h>
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+namespace android {
+
+SNTPClient::SNTPClient() {
+}
+
+status_t SNTPClient::requestTime(const char *host) {
+    struct hostent *ent;
+    int64_t requestTimeNTP, requestTimeUs;
+    ssize_t n;
+    int64_t responseTimeUs, responseTimeNTP;
+    int64_t originateTimeNTP, receiveTimeNTP, transmitTimeNTP;
+    int64_t roundTripTimeNTP, clockOffsetNTP;
+
+    status_t err = UNKNOWN_ERROR;
+
+    int s = socket(AF_INET, SOCK_DGRAM, 0);
+
+    if (s < 0) {
+        err = -errno;
+
+        goto bail;
+    }
+
+    ent = gethostbyname(host);
+
+    if (ent == NULL) {
+        err = -ENOENT;
+        goto bail2;
+    }
+
+    struct sockaddr_in hostAddr;
+    memset(hostAddr.sin_zero, 0, sizeof(hostAddr.sin_zero));
+    hostAddr.sin_family = AF_INET;
+    hostAddr.sin_port = htons(kNTPPort);
+    hostAddr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
+
+    uint8_t packet[kNTPPacketSize];
+    memset(packet, 0, sizeof(packet));
+
+    packet[0] = kNTPModeClient | (kNTPVersion << 3);
+
+    requestTimeNTP = getNowNTP();
+    requestTimeUs = ALooper::GetNowUs();
+    writeTimeStamp(&packet[kNTPTransmitTimeOffset], requestTimeNTP);
+
+    n = sendto(
+            s, packet, sizeof(packet), 0,
+            (const struct sockaddr *)&hostAddr, sizeof(hostAddr));
+
+    if (n < 0) {
+        err = -errno;
+        goto bail2;
+    }
+
+    memset(packet, 0, sizeof(packet));
+
+    do {
+        n = recv(s, packet, sizeof(packet), 0);
+    } while (n < 0 && errno == EINTR);
+
+    if (n < 0) {
+        err = -errno;
+        goto bail2;
+    }
+
+    responseTimeUs = ALooper::GetNowUs();
+
+    responseTimeNTP = requestTimeNTP + makeNTP(responseTimeUs - requestTimeUs);
+
+    originateTimeNTP = readTimeStamp(&packet[kNTPOriginateTimeOffset]);
+    receiveTimeNTP = readTimeStamp(&packet[kNTPReceiveTimeOffset]);
+    transmitTimeNTP = readTimeStamp(&packet[kNTPTransmitTimeOffset]);
+
+    roundTripTimeNTP =
+        makeNTP(responseTimeUs - requestTimeUs)
+            - (transmitTimeNTP - receiveTimeNTP);
+
+    clockOffsetNTP =
+        ((receiveTimeNTP - originateTimeNTP)
+            + (transmitTimeNTP - responseTimeNTP)) / 2;
+
+    mTimeReferenceNTP = responseTimeNTP + clockOffsetNTP;
+    mTimeReferenceUs = responseTimeUs;
+    mRoundTripTimeNTP = roundTripTimeNTP;
+
+    err = OK;
+
+bail2:
+    close(s);
+    s = -1;
+
+bail:
+    return err;
+}
+
+int64_t SNTPClient::adjustTimeUs(int64_t timeUs) const {
+    uint64_t nowNTP =
+        mTimeReferenceNTP + makeNTP(timeUs - mTimeReferenceUs);
+
+    int64_t nowUs =
+        (nowNTP >> 32) * 1000000ll
+        + ((nowNTP & 0xffffffff) * 1000000ll) / (1ll << 32);
+
+    nowUs -= ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll;
+
+    return nowUs;
+}
+
+// static
+void SNTPClient::writeTimeStamp(uint8_t *dst, uint64_t ntpTime) {
+    *dst++ = (ntpTime >> 56) & 0xff;
+    *dst++ = (ntpTime >> 48) & 0xff;
+    *dst++ = (ntpTime >> 40) & 0xff;
+    *dst++ = (ntpTime >> 32) & 0xff;
+    *dst++ = (ntpTime >> 24) & 0xff;
+    *dst++ = (ntpTime >> 16) & 0xff;
+    *dst++ = (ntpTime >> 8) & 0xff;
+    *dst++ = ntpTime & 0xff;
+}
+
+// static
+uint64_t SNTPClient::readTimeStamp(const uint8_t *dst) {
+    return U64_AT(dst);
+}
+
+// static
+uint64_t SNTPClient::getNowNTP() {
+    struct timeval tv;
+    gettimeofday(&tv, NULL /* time zone */);
+
+    uint64_t nowUs = tv.tv_sec * 1000000ll + tv.tv_usec;
+
+    nowUs += ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll;
+
+    return makeNTP(nowUs);
+}
+
+// static
+uint64_t SNTPClient::makeNTP(uint64_t deltaUs) {
+    uint64_t hi = deltaUs / 1000000ll;
+    uint64_t lo = ((1ll << 32) * (deltaUs % 1000000ll)) / 1000000ll;
+
+    return (hi << 32) | lo;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/wifi-display/SNTPClient.h b/media/libstagefright/wifi-display/SNTPClient.h
new file mode 100644
index 0000000..967d1fc
--- /dev/null
+++ b/media/libstagefright/wifi-display/SNTPClient.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SNTP_CLIENT_H_
+
+#define SNTP_CLIENT_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+// Implementation of the SNTP (Simple Network Time Protocol)
+struct SNTPClient {
+    SNTPClient();
+
+    status_t requestTime(const char *host);
+
+    // given a time obtained from ALooper::GetNowUs()
+    // return the number of us elapsed since Jan 1 1970 00:00:00 (UTC).
+    int64_t adjustTimeUs(int64_t timeUs) const;
+
+private:
+    enum {
+        kNTPPort = 123,
+        kNTPPacketSize = 48,
+        kNTPModeClient = 3,
+        kNTPVersion = 3,
+        kNTPTransmitTimeOffset = 40,
+        kNTPOriginateTimeOffset = 24,
+        kNTPReceiveTimeOffset = 32,
+    };
+
+    uint64_t mTimeReferenceNTP;
+    int64_t mTimeReferenceUs;
+    int64_t mRoundTripTimeNTP;
+
+    static void writeTimeStamp(uint8_t *dst, uint64_t ntpTime);
+    static uint64_t readTimeStamp(const uint8_t *dst);
+
+    static uint64_t getNowNTP();
+    static uint64_t makeNTP(uint64_t deltaUs);
+
+    DISALLOW_EVIL_CONSTRUCTORS(SNTPClient);
+};
+
+}  // namespace android
+
+#endif  // SNTP_CLIENT_H_
diff --git a/media/libstagefright/wifi-display/TimeSeries.cpp b/media/libstagefright/wifi-display/TimeSeries.cpp
deleted file mode 100644
index d882d98..0000000
--- a/media/libstagefright/wifi-display/TimeSeries.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TimeSeries.h"
-
-#include <math.h>
-#include <string.h>
-
-namespace android {
-
-TimeSeries::TimeSeries()
-    : mCount(0),
-      mSum(0.0) {
-}
-
-void TimeSeries::add(double val) {
-    if (mCount < kHistorySize) {
-        mValues[mCount++] = val;
-        mSum += val;
-    } else {
-        mSum -= mValues[0];
-        memmove(&mValues[0], &mValues[1], (kHistorySize - 1) * sizeof(double));
-        mValues[kHistorySize - 1] = val;
-        mSum += val;
-    }
-}
-
-double TimeSeries::mean() const {
-    if (mCount < 1) {
-        return 0.0;
-    }
-
-    return mSum / mCount;
-}
-
-double TimeSeries::sdev() const {
-    if (mCount < 1) {
-        return 0.0;
-    }
-
-    double m = mean();
-
-    double sum = 0.0;
-    for (size_t i = 0; i < mCount; ++i) {
-        double tmp = mValues[i] - m;
-        tmp *= tmp;
-
-        sum += tmp;
-    }
-
-    return sqrt(sum / mCount);
-}
-
-}  // namespace android
diff --git a/media/libstagefright/wifi-display/TimeSeries.h b/media/libstagefright/wifi-display/TimeSeries.h
deleted file mode 100644
index c818d51..0000000
--- a/media/libstagefright/wifi-display/TimeSeries.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef TIME_SERIES_H_
-
-#define TIME_SERIES_H_
-
-#include <sys/types.h>
-
-namespace android {
-
-struct TimeSeries {
-    TimeSeries();
-
-    void add(double val);
-
-    double mean() const;
-    double sdev() const;
-
-private:
-    enum {
-        kHistorySize = 20
-    };
-    double mValues[kHistorySize];
-
-    size_t mCount;
-    double mSum;
-};
-
-}  // namespace android
-
-#endif  // TIME_SERIES_H_
-
diff --git a/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp b/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp
new file mode 100644
index 0000000..d0ab60d
--- /dev/null
+++ b/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp
@@ -0,0 +1,324 @@
+/*
+ * Copyright 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "RTPAssembler"
+#include <utils/Log.h>
+
+#include "RTPAssembler.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+RTPReceiver::Assembler::Assembler(const sp<AMessage> &notify)
+    : mNotify(notify) {
+}
+
+void RTPReceiver::Assembler::postAccessUnit(
+        const sp<ABuffer> &accessUnit, bool followsDiscontinuity) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", RTPReceiver::kWhatAccessUnit);
+    notify->setBuffer("accessUnit", accessUnit);
+    notify->setInt32("followsDiscontinuity", followsDiscontinuity);
+    notify->post();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+RTPReceiver::TSAssembler::TSAssembler(const sp<AMessage> &notify)
+    : Assembler(notify),
+      mSawDiscontinuity(false) {
+}
+
+void RTPReceiver::TSAssembler::signalDiscontinuity() {
+    mSawDiscontinuity = true;
+}
+
+status_t RTPReceiver::TSAssembler::processPacket(const sp<ABuffer> &packet) {
+    postAccessUnit(packet, mSawDiscontinuity);
+
+    if (mSawDiscontinuity) {
+        mSawDiscontinuity = false;
+    }
+
+    return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+RTPReceiver::H264Assembler::H264Assembler(const sp<AMessage> &notify)
+    : Assembler(notify),
+      mState(0),
+      mIndicator(0),
+      mNALType(0),
+      mAccessUnitRTPTime(0) {
+}
+
+void RTPReceiver::H264Assembler::signalDiscontinuity() {
+    reset();
+}
+
+status_t RTPReceiver::H264Assembler::processPacket(const sp<ABuffer> &packet) {
+    status_t err = internalProcessPacket(packet);
+
+    if (err != OK) {
+        reset();
+    }
+
+    return err;
+}
+
+status_t RTPReceiver::H264Assembler::internalProcessPacket(
+        const sp<ABuffer> &packet) {
+    const uint8_t *data = packet->data();
+    size_t size = packet->size();
+
+    switch (mState) {
+        case 0:
+        {
+            if (size < 1 || (data[0] & 0x80)) {
+                ALOGV("Malformed H264 RTP packet (empty or F-bit set)");
+                return ERROR_MALFORMED;
+            }
+
+            unsigned nalType = data[0] & 0x1f;
+            if (nalType >= 1 && nalType <= 23) {
+                addSingleNALUnit(packet);
+                ALOGV("added single NAL packet");
+            } else if (nalType == 28) {
+                // FU-A
+                unsigned indicator = data[0];
+                CHECK((indicator & 0x1f) == 28);
+
+                if (size < 2) {
+                    ALOGV("Malformed H264 FU-A packet (single byte)");
+                    return ERROR_MALFORMED;
+                }
+
+                if (!(data[1] & 0x80)) {
+                    ALOGV("Malformed H264 FU-A packet (no start bit)");
+                    return ERROR_MALFORMED;
+                }
+
+                mIndicator = data[0];
+                mNALType = data[1] & 0x1f;
+                uint32_t nri = (data[0] >> 5) & 3;
+
+                clearAccumulator();
+
+                uint8_t byte = mNALType | (nri << 5);
+                appendToAccumulator(&byte, 1);
+                appendToAccumulator(data + 2, size - 2);
+
+                int32_t rtpTime;
+                CHECK(packet->meta()->findInt32("rtp-time", &rtpTime));
+                mAccumulator->meta()->setInt32("rtp-time", rtpTime);
+
+                if (data[1] & 0x40) {
+                    // Huh? End bit also set on the first buffer.
+                    addSingleNALUnit(mAccumulator);
+                    clearAccumulator();
+
+                    ALOGV("added FU-A");
+                    break;
+                }
+
+                mState = 1;
+            } else if (nalType == 24) {
+                // STAP-A
+
+                status_t err = addSingleTimeAggregationPacket(packet);
+                if (err != OK) {
+                    return err;
+                }
+            } else {
+                ALOGV("Malformed H264 packet (unknown type %d)", nalType);
+                return ERROR_UNSUPPORTED;
+            }
+            break;
+        }
+
+        case 1:
+        {
+            if (size < 2
+                    || data[0] != mIndicator
+                    || (data[1] & 0x1f) != mNALType
+                    || (data[1] & 0x80)) {
+                ALOGV("Malformed H264 FU-A packet (indicator, "
+                      "type or start bit mismatch)");
+
+                return ERROR_MALFORMED;
+            }
+
+            appendToAccumulator(data + 2, size - 2);
+
+            if (data[1] & 0x40) {
+                addSingleNALUnit(mAccumulator);
+
+                clearAccumulator();
+                mState = 0;
+
+                ALOGV("added FU-A");
+            }
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+
+    int32_t marker;
+    CHECK(packet->meta()->findInt32("M", &marker));
+
+    if (marker) {
+        flushAccessUnit();
+    }
+
+    return OK;
+}
+
+void RTPReceiver::H264Assembler::reset() {
+    mNALUnits.clear();
+
+    clearAccumulator();
+    mState = 0;
+}
+
+void RTPReceiver::H264Assembler::clearAccumulator() {
+    if (mAccumulator != NULL) {
+        // XXX Too expensive.
+        mAccumulator.clear();
+    }
+}
+
+void RTPReceiver::H264Assembler::appendToAccumulator(
+        const void *data, size_t size) {
+    if (mAccumulator == NULL) {
+        mAccumulator = new ABuffer(size);
+        memcpy(mAccumulator->data(), data, size);
+        return;
+    }
+
+    if (mAccumulator->size() + size > mAccumulator->capacity()) {
+        sp<ABuffer> buf = new ABuffer(mAccumulator->size() + size);
+        memcpy(buf->data(), mAccumulator->data(), mAccumulator->size());
+        buf->setRange(0, mAccumulator->size());
+
+        int32_t rtpTime;
+        if (mAccumulator->meta()->findInt32("rtp-time", &rtpTime)) {
+            buf->meta()->setInt32("rtp-time", rtpTime);
+        }
+
+        mAccumulator = buf;
+    }
+
+    memcpy(mAccumulator->data() + mAccumulator->size(), data, size);
+    mAccumulator->setRange(0, mAccumulator->size() + size);
+}
+
+void RTPReceiver::H264Assembler::addSingleNALUnit(const sp<ABuffer> &packet) {
+    if (mNALUnits.empty()) {
+        int32_t rtpTime;
+        CHECK(packet->meta()->findInt32("rtp-time", &rtpTime));
+
+        mAccessUnitRTPTime = rtpTime;
+    }
+
+    mNALUnits.push_back(packet);
+}
+
+void RTPReceiver::H264Assembler::flushAccessUnit() {
+    if (mNALUnits.empty()) {
+        return;
+    }
+
+    size_t totalSize = 0;
+    for (List<sp<ABuffer> >::iterator it = mNALUnits.begin();
+            it != mNALUnits.end(); ++it) {
+        totalSize += 4 + (*it)->size();
+    }
+
+    sp<ABuffer> accessUnit = new ABuffer(totalSize);
+    size_t offset = 0;
+    for (List<sp<ABuffer> >::iterator it = mNALUnits.begin();
+            it != mNALUnits.end(); ++it) {
+        const sp<ABuffer> nalUnit = *it;
+
+        memcpy(accessUnit->data() + offset, "\x00\x00\x00\x01", 4);
+
+        memcpy(accessUnit->data() + offset + 4,
+               nalUnit->data(),
+               nalUnit->size());
+
+        offset += 4 + nalUnit->size();
+    }
+
+    mNALUnits.clear();
+
+    accessUnit->meta()->setInt64("timeUs", mAccessUnitRTPTime * 100ll / 9ll);
+    postAccessUnit(accessUnit, false /* followsDiscontinuity */);
+}
+
+status_t RTPReceiver::H264Assembler::addSingleTimeAggregationPacket(
+        const sp<ABuffer> &packet) {
+    const uint8_t *data = packet->data();
+    size_t size = packet->size();
+
+    if (size < 3) {
+        ALOGV("Malformed H264 STAP-A packet (too small)");
+        return ERROR_MALFORMED;
+    }
+
+    int32_t rtpTime;
+    CHECK(packet->meta()->findInt32("rtp-time", &rtpTime));
+
+    ++data;
+    --size;
+    while (size >= 2) {
+        size_t nalSize = (data[0] << 8) | data[1];
+
+        if (size < nalSize + 2) {
+            ALOGV("Malformed H264 STAP-A packet (incomplete NAL unit)");
+            return ERROR_MALFORMED;
+        }
+
+        sp<ABuffer> unit = new ABuffer(nalSize);
+        memcpy(unit->data(), &data[2], nalSize);
+
+        unit->meta()->setInt32("rtp-time", rtpTime);
+
+        addSingleNALUnit(unit);
+
+        data += 2 + nalSize;
+        size -= 2 + nalSize;
+    }
+
+    if (size != 0) {
+        ALOGV("Unexpected padding at end of STAP-A packet.");
+    }
+
+    ALOGV("added STAP-A");
+
+    return OK;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/wifi-display/rtp/RTPAssembler.h b/media/libstagefright/wifi-display/rtp/RTPAssembler.h
new file mode 100644
index 0000000..e456d32
--- /dev/null
+++ b/media/libstagefright/wifi-display/rtp/RTPAssembler.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RTP_ASSEMBLER_H_
+
+#define RTP_ASSEMBLER_H_
+
+#include "RTPReceiver.h"
+
+namespace android {
+
+// A helper class to reassemble the payload of RTP packets into access
+// units depending on the packetization scheme.
+struct RTPReceiver::Assembler : public RefBase {
+    Assembler(const sp<AMessage> &notify);
+
+    virtual void signalDiscontinuity() = 0;
+    virtual status_t processPacket(const sp<ABuffer> &packet) = 0;
+
+protected:
+    virtual ~Assembler() {}
+
+    void postAccessUnit(
+            const sp<ABuffer> &accessUnit, bool followsDiscontinuity);
+
+private:
+    sp<AMessage> mNotify;
+
+    DISALLOW_EVIL_CONSTRUCTORS(Assembler);
+};
+
+struct RTPReceiver::TSAssembler : public RTPReceiver::Assembler {
+    TSAssembler(const sp<AMessage> &notify);
+
+    virtual void signalDiscontinuity();
+    virtual status_t processPacket(const sp<ABuffer> &packet);
+
+private:
+    bool mSawDiscontinuity;
+
+    DISALLOW_EVIL_CONSTRUCTORS(TSAssembler);
+};
+
+struct RTPReceiver::H264Assembler : public RTPReceiver::Assembler {
+    H264Assembler(const sp<AMessage> &notify);
+
+    virtual void signalDiscontinuity();
+    virtual status_t processPacket(const sp<ABuffer> &packet);
+
+private:
+    int32_t mState;
+
+    uint8_t mIndicator;
+    uint8_t mNALType;
+
+    sp<ABuffer> mAccumulator;
+
+    List<sp<ABuffer> > mNALUnits;
+    int32_t mAccessUnitRTPTime;
+
+    status_t internalProcessPacket(const sp<ABuffer> &packet);
+
+    void addSingleNALUnit(const sp<ABuffer> &packet);
+    status_t addSingleTimeAggregationPacket(const sp<ABuffer> &packet);
+
+    void flushAccessUnit();
+
+    void clearAccumulator();
+    void appendToAccumulator(const void *data, size_t size);
+
+    void reset();
+
+    DISALLOW_EVIL_CONSTRUCTORS(H264Assembler);
+};
+
+}  // namespace android
+
+#endif  // RTP_ASSEMBLER_H_
+
diff --git a/media/libstagefright/wifi-display/rtp/RTPBase.h b/media/libstagefright/wifi-display/rtp/RTPBase.h
new file mode 100644
index 0000000..6507a6f
--- /dev/null
+++ b/media/libstagefright/wifi-display/rtp/RTPBase.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RTP_BASE_H_
+
+#define RTP_BASE_H_
+
+namespace android {
+
+struct RTPBase {
+    enum PacketizationMode {
+        PACKETIZATION_TRANSPORT_STREAM,
+        PACKETIZATION_H264,
+        PACKETIZATION_AAC,
+    };
+
+    enum TransportMode {
+        TRANSPORT_UNDEFINED,
+        TRANSPORT_UDP,
+        TRANSPORT_TCP,
+        TRANSPORT_TCP_INTERLEAVED,
+    };
+
+    enum {
+        // Really UDP _payload_ size
+        kMaxUDPPacketSize = 1472,   // 1472 good, 1473 bad on Android@Home
+    };
+
+    static int32_t PickRandomRTPPort();
+};
+
+}  // namespace android
+
+#endif  // RTP_BASE_H_
+
+
diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp
new file mode 100644
index 0000000..29482af
--- /dev/null
+++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp
@@ -0,0 +1,899 @@
+/*
+ * Copyright 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "RTPReceiver"
+#include <utils/Log.h>
+
+#include "RTPAssembler.h"
+#include "RTPReceiver.h"
+
+#include "ANetworkSession.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct RTPReceiver::Source : public RefBase {
+    Source(RTPReceiver *receiver, uint32_t ssrc);
+
+    void onPacketReceived(uint16_t seq, const sp<ABuffer> &buffer);
+
+    void addReportBlock(uint32_t ssrc, const sp<ABuffer> &buf);
+
+protected:
+    virtual ~Source();
+
+private:
+    static const uint32_t kMinSequential = 2;
+    static const uint32_t kMaxDropout = 3000;
+    static const uint32_t kMaxMisorder = 100;
+    static const uint32_t kRTPSeqMod = 1u << 16;
+    static const int64_t kReportIntervalUs = 10000000ll;
+
+    RTPReceiver *mReceiver;
+    uint32_t mSSRC;
+    bool mFirst;
+    uint16_t mMaxSeq;
+    uint32_t mCycles;
+    uint32_t mBaseSeq;
+    uint32_t mReceived;
+    uint32_t mExpectedPrior;
+    uint32_t mReceivedPrior;
+
+    int64_t mFirstArrivalTimeUs;
+    int64_t mFirstRTPTimeUs;
+
+    // Ordered by extended seq number.
+    List<sp<ABuffer> > mPackets;
+
+    int32_t mAwaitingExtSeqNo;
+    bool mRequestedRetransmission;
+
+    int32_t mActivePacketType;
+    sp<Assembler> mActiveAssembler;
+
+    int64_t mNextReportTimeUs;
+
+    int32_t mNumDeclaredLost;
+    int32_t mNumDeclaredLostPrior;
+
+    void queuePacket(const sp<ABuffer> &packet);
+    void dequeueMore();
+
+    sp<ABuffer> getNextPacket();
+    void resync();
+
+    DISALLOW_EVIL_CONSTRUCTORS(Source);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+RTPReceiver::Source::Source(RTPReceiver *receiver, uint32_t ssrc)
+    : mReceiver(receiver),
+      mSSRC(ssrc),
+      mFirst(true),
+      mMaxSeq(0),
+      mCycles(0),
+      mBaseSeq(0),
+      mReceived(0),
+      mExpectedPrior(0),
+      mReceivedPrior(0),
+      mFirstArrivalTimeUs(-1ll),
+      mFirstRTPTimeUs(-1ll),
+      mAwaitingExtSeqNo(-1),
+      mRequestedRetransmission(false),
+      mActivePacketType(-1),
+      mNextReportTimeUs(-1ll),
+      mNumDeclaredLost(0),
+      mNumDeclaredLostPrior(0) {
+}
+
+RTPReceiver::Source::~Source() {
+}
+
+void RTPReceiver::Source::onPacketReceived(
+        uint16_t seq, const sp<ABuffer> &buffer) {
+    if (mFirst) {
+        buffer->setInt32Data(mCycles | seq);
+        queuePacket(buffer);
+
+        mFirst = false;
+        mBaseSeq = seq;
+        mMaxSeq = seq;
+        ++mReceived;
+        return;
+    }
+
+    uint16_t udelta = seq - mMaxSeq;
+
+    if (udelta < kMaxDropout) {
+        // In order, with permissible gap.
+
+        if (seq < mMaxSeq) {
+            // Sequence number wrapped - count another 64K cycle
+            mCycles += kRTPSeqMod;
+        }
+
+        mMaxSeq = seq;
+
+        ++mReceived;
+    } else if (udelta <= kRTPSeqMod - kMaxMisorder) {
+        // The sequence number made a very large jump
+        return;
+    } else {
+        // Duplicate or reordered packet.
+    }
+
+    buffer->setInt32Data(mCycles | seq);
+    queuePacket(buffer);
+}
+
+void RTPReceiver::Source::queuePacket(const sp<ABuffer> &packet) {
+    int32_t newExtendedSeqNo = packet->int32Data();
+
+    if (mFirstArrivalTimeUs < 0ll) {
+        mFirstArrivalTimeUs = ALooper::GetNowUs();
+
+        uint32_t rtpTime;
+        CHECK(packet->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
+
+        mFirstRTPTimeUs = (rtpTime * 100ll) / 9ll;
+    }
+
+    if (mAwaitingExtSeqNo >= 0 && newExtendedSeqNo < mAwaitingExtSeqNo) {
+        // We're no longer interested in these. They're old.
+        ALOGV("dropping stale extSeqNo %d", newExtendedSeqNo);
+        return;
+    }
+
+    if (mPackets.empty()) {
+        mPackets.push_back(packet);
+        dequeueMore();
+        return;
+    }
+
+    List<sp<ABuffer> >::iterator firstIt = mPackets.begin();
+    List<sp<ABuffer> >::iterator it = --mPackets.end();
+    for (;;) {
+        int32_t extendedSeqNo = (*it)->int32Data();
+
+        if (extendedSeqNo == newExtendedSeqNo) {
+            // Duplicate packet.
+            return;
+        }
+
+        if (extendedSeqNo < newExtendedSeqNo) {
+            // Insert new packet after the one at "it".
+            mPackets.insert(++it, packet);
+            break;
+        }
+
+        if (it == firstIt) {
+            // Insert new packet before the first existing one.
+            mPackets.insert(it, packet);
+            break;
+        }
+
+        --it;
+    }
+
+    dequeueMore();
+}
+
+void RTPReceiver::Source::dequeueMore() {
+    int64_t nowUs = ALooper::GetNowUs();
+    if (mNextReportTimeUs < 0ll || nowUs >= mNextReportTimeUs) {
+        if (mNextReportTimeUs >= 0ll) {
+            uint32_t expected = (mMaxSeq | mCycles) - mBaseSeq + 1;
+
+            uint32_t expectedInterval = expected - mExpectedPrior;
+            mExpectedPrior = expected;
+
+            uint32_t receivedInterval = mReceived - mReceivedPrior;
+            mReceivedPrior = mReceived;
+
+            int64_t lostInterval =
+                (int64_t)expectedInterval - (int64_t)receivedInterval;
+
+            int32_t declaredLostInterval =
+                mNumDeclaredLost - mNumDeclaredLostPrior;
+
+            mNumDeclaredLostPrior = mNumDeclaredLost;
+
+            ALOGI("lost %lld packets (%.2f %%), declared %d lost\n",
+                  lostInterval,
+                  100.0f * lostInterval / expectedInterval,
+                  declaredLostInterval);
+        }
+
+        mNextReportTimeUs = nowUs + kReportIntervalUs;
+    }
+
+    for (;;) {
+        sp<ABuffer> packet = getNextPacket();
+
+        if (packet == NULL) {
+            if (mPackets.empty()) {
+                break;
+            }
+
+            CHECK_GE(mAwaitingExtSeqNo, 0);
+
+            const sp<ABuffer> &firstPacket = *mPackets.begin();
+
+            uint32_t rtpTime;
+            CHECK(firstPacket->meta()->findInt32(
+                        "rtp-time", (int32_t *)&rtpTime));
+
+
+            int64_t rtpUs = (rtpTime * 100ll) / 9ll;
+
+            int64_t maxArrivalTimeUs =
+                mFirstArrivalTimeUs + rtpUs - mFirstRTPTimeUs;
+
+            int64_t nowUs = ALooper::GetNowUs();
+
+            CHECK_LT(mAwaitingExtSeqNo, firstPacket->int32Data());
+
+            ALOGV("waiting for %d, comparing against %d, %lld us left",
+                  mAwaitingExtSeqNo,
+                  firstPacket->int32Data(),
+                  maxArrivalTimeUs - nowUs);
+
+            if (maxArrivalTimeUs + kPacketLostAfterUs <= nowUs) {
+                ALOGV("Lost packet extSeqNo %d %s",
+                      mAwaitingExtSeqNo,
+                      mRequestedRetransmission ? "*" : "");
+
+                mRequestedRetransmission = false;
+                if (mActiveAssembler != NULL) {
+                    mActiveAssembler->signalDiscontinuity();
+                }
+
+                // resync();
+                ++mAwaitingExtSeqNo;
+                ++mNumDeclaredLost;
+
+                mReceiver->notifyPacketLost();
+                continue;
+            } else if (kRequestRetransmissionAfterUs > 0
+                    && maxArrivalTimeUs + kRequestRetransmissionAfterUs <= nowUs
+                    && !mRequestedRetransmission
+                    && mAwaitingExtSeqNo >= 0) {
+                mRequestedRetransmission = true;
+                mReceiver->requestRetransmission(mSSRC, mAwaitingExtSeqNo);
+                break;
+            } else {
+                break;
+            }
+        }
+
+        mRequestedRetransmission = false;
+
+        int32_t packetType;
+        CHECK(packet->meta()->findInt32("PT", &packetType));
+
+        if (packetType != mActivePacketType) {
+            mActiveAssembler = mReceiver->makeAssembler(packetType);
+            mActivePacketType = packetType;
+        }
+
+        if (mActiveAssembler == NULL) {
+            continue;
+        }
+
+        status_t err = mActiveAssembler->processPacket(packet);
+        if (err != OK) {
+            ALOGV("assembler returned error %d", err);
+        }
+    }
+}
+
+sp<ABuffer> RTPReceiver::Source::getNextPacket() {
+    if (mPackets.empty()) {
+        return NULL;
+    }
+
+    int32_t extSeqNo = (*mPackets.begin())->int32Data();
+
+    if (mAwaitingExtSeqNo < 0) {
+        mAwaitingExtSeqNo = extSeqNo;
+    } else if (extSeqNo != mAwaitingExtSeqNo) {
+        return NULL;
+    }
+
+    sp<ABuffer> packet = *mPackets.begin();
+    mPackets.erase(mPackets.begin());
+
+    ++mAwaitingExtSeqNo;
+
+    return packet;
+}
+
+void RTPReceiver::Source::resync() {
+    mAwaitingExtSeqNo = -1;
+}
+
+void RTPReceiver::Source::addReportBlock(
+        uint32_t ssrc, const sp<ABuffer> &buf) {
+    uint32_t extMaxSeq = mMaxSeq | mCycles;
+    uint32_t expected = extMaxSeq - mBaseSeq + 1;
+
+    int64_t lost = (int64_t)expected - (int64_t)mReceived;
+    if (lost > 0x7fffff) {
+        lost = 0x7fffff;
+    } else if (lost < -0x800000) {
+        lost = -0x800000;
+    }
+
+    uint32_t expectedInterval = expected - mExpectedPrior;
+    mExpectedPrior = expected;
+
+    uint32_t receivedInterval = mReceived - mReceivedPrior;
+    mReceivedPrior = mReceived;
+
+    int64_t lostInterval = expectedInterval - receivedInterval;
+
+    uint8_t fractionLost;
+    if (expectedInterval == 0 || lostInterval <=0) {
+        fractionLost = 0;
+    } else {
+        fractionLost = (lostInterval << 8) / expectedInterval;
+    }
+
+    uint8_t *ptr = buf->data() + buf->size();
+
+    ptr[0] = ssrc >> 24;
+    ptr[1] = (ssrc >> 16) & 0xff;
+    ptr[2] = (ssrc >> 8) & 0xff;
+    ptr[3] = ssrc & 0xff;
+
+    ptr[4] = fractionLost;
+
+    ptr[5] = (lost >> 16) & 0xff;
+    ptr[6] = (lost >> 8) & 0xff;
+    ptr[7] = lost & 0xff;
+
+    ptr[8] = extMaxSeq >> 24;
+    ptr[9] = (extMaxSeq >> 16) & 0xff;
+    ptr[10] = (extMaxSeq >> 8) & 0xff;
+    ptr[11] = extMaxSeq & 0xff;
+
+    // XXX TODO:
+
+    ptr[12] = 0x00;  // interarrival jitter
+    ptr[13] = 0x00;
+    ptr[14] = 0x00;
+    ptr[15] = 0x00;
+
+    ptr[16] = 0x00;  // last SR
+    ptr[17] = 0x00;
+    ptr[18] = 0x00;
+    ptr[19] = 0x00;
+
+    ptr[20] = 0x00;  // delay since last SR
+    ptr[21] = 0x00;
+    ptr[22] = 0x00;
+    ptr[23] = 0x00;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+RTPReceiver::RTPReceiver(
+        const sp<ANetworkSession> &netSession,
+        const sp<AMessage> &notify)
+    : mNetSession(netSession),
+      mNotify(notify),
+      mMode(TRANSPORT_UNDEFINED),
+      mRTPSessionID(0),
+      mRTCPSessionID(0),
+      mRTPClientSessionID(0) {
+}
+
+RTPReceiver::~RTPReceiver() {
+    if (mRTPClientSessionID != 0) {
+        mNetSession->destroySession(mRTPClientSessionID);
+        mRTPClientSessionID = 0;
+    }
+
+    if (mRTCPSessionID != 0) {
+        mNetSession->destroySession(mRTCPSessionID);
+        mRTCPSessionID = 0;
+    }
+
+    if (mRTPSessionID != 0) {
+        mNetSession->destroySession(mRTPSessionID);
+        mRTPSessionID = 0;
+    }
+}
+
+status_t RTPReceiver::initAsync(TransportMode mode, int32_t *outLocalRTPPort) {
+    if (mMode != TRANSPORT_UNDEFINED || mode == TRANSPORT_UNDEFINED) {
+        return INVALID_OPERATION;
+    }
+
+    CHECK_NE(mMode, TRANSPORT_TCP_INTERLEAVED);
+
+    sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
+
+    sp<AMessage> rtcpNotify;
+    if (mode == TRANSPORT_UDP) {
+        rtcpNotify = new AMessage(kWhatRTCPNotify, id());
+    }
+
+    CHECK_EQ(mRTPSessionID, 0);
+    CHECK_EQ(mRTCPSessionID, 0);
+
+    int32_t localRTPPort;
+
+    struct in_addr ifaceAddr;
+    ifaceAddr.s_addr = INADDR_ANY;
+
+    for (;;) {
+        localRTPPort = PickRandomRTPPort();
+
+        status_t err;
+        if (mode == TRANSPORT_UDP) {
+            err = mNetSession->createUDPSession(
+                    localRTPPort,
+                    rtpNotify,
+                    &mRTPSessionID);
+        } else {
+            CHECK_EQ(mode, TRANSPORT_TCP);
+            err = mNetSession->createTCPDatagramSession(
+                    ifaceAddr,
+                    localRTPPort,
+                    rtpNotify,
+                    &mRTPSessionID);
+        }
+
+        if (err != OK) {
+            continue;
+        }
+
+        if (mode == TRANSPORT_TCP) {
+            break;
+        }
+
+        err = mNetSession->createUDPSession(
+                localRTPPort + 1,
+                rtcpNotify,
+                &mRTCPSessionID);
+
+        if (err == OK) {
+            break;
+        }
+
+        mNetSession->destroySession(mRTPSessionID);
+        mRTPSessionID = 0;
+    }
+
+    mMode = mode;
+    *outLocalRTPPort = localRTPPort;
+
+    return OK;
+}
+
+status_t RTPReceiver::connect(
+        const char *remoteHost, int32_t remoteRTPPort, int32_t remoteRTCPPort) {
+    if (mMode == TRANSPORT_TCP) {
+        return OK;
+    }
+
+    status_t err = mNetSession->connectUDPSession(
+            mRTPSessionID, remoteHost, remoteRTPPort);
+
+    if (err != OK) {
+        notifyInitDone(err);
+        return err;
+    }
+
+    ALOGI("connectUDPSession RTP successful.");
+
+    if (remoteRTCPPort >= 0) {
+        err = mNetSession->connectUDPSession(
+                mRTCPSessionID, remoteHost, remoteRTCPPort);
+
+        if (err != OK) {
+        ALOGI("connect failed w/ err %d", err);
+
+            notifyInitDone(err);
+            return err;
+        }
+
+        scheduleSendRR();
+    }
+
+    notifyInitDone(OK);
+
+    return OK;
+}
+
+void RTPReceiver::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatRTPNotify:
+        case kWhatRTCPNotify:
+            onNetNotify(msg->what() == kWhatRTPNotify, msg);
+            break;
+
+        case kWhatSendRR:
+        {
+            onSendRR();
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+void RTPReceiver::onNetNotify(bool isRTP, const sp<AMessage> &msg) {
+    int32_t reason;
+    CHECK(msg->findInt32("reason", &reason));
+
+    switch (reason) {
+        case ANetworkSession::kWhatError:
+        {
+            int32_t sessionID;
+            CHECK(msg->findInt32("sessionID", &sessionID));
+
+            int32_t err;
+            CHECK(msg->findInt32("err", &err));
+
+            int32_t errorOccuredDuringSend;
+            CHECK(msg->findInt32("send", &errorOccuredDuringSend));
+
+            AString detail;
+            CHECK(msg->findString("detail", &detail));
+
+            ALOGE("An error occurred during %s in session %d "
+                  "(%d, '%s' (%s)).",
+                  errorOccuredDuringSend ? "send" : "receive",
+                  sessionID,
+                  err,
+                  detail.c_str(),
+                  strerror(-err));
+
+            mNetSession->destroySession(sessionID);
+
+            if (sessionID == mRTPSessionID) {
+                mRTPSessionID = 0;
+
+                if (mMode == TRANSPORT_TCP && mRTPClientSessionID == 0) {
+                    notifyInitDone(err);
+                    break;
+                }
+            } else if (sessionID == mRTCPSessionID) {
+                mRTCPSessionID = 0;
+            } else if (sessionID == mRTPClientSessionID) {
+                mRTPClientSessionID = 0;
+            }
+
+            notifyError(err);
+            break;
+        }
+
+        case ANetworkSession::kWhatDatagram:
+        {
+            sp<ABuffer> data;
+            CHECK(msg->findBuffer("data", &data));
+
+            if (isRTP) {
+                onRTPData(data);
+            } else {
+                onRTCPData(data);
+            }
+            break;
+        }
+
+        case ANetworkSession::kWhatClientConnected:
+        {
+            CHECK_EQ(mMode, TRANSPORT_TCP);
+            CHECK(isRTP);
+
+            int32_t sessionID;
+            CHECK(msg->findInt32("sessionID", &sessionID));
+
+            if (mRTPClientSessionID != 0) {
+                // We only allow a single client connection.
+                mNetSession->destroySession(sessionID);
+                sessionID = 0;
+                break;
+            }
+
+            mRTPClientSessionID = sessionID;
+
+            notifyInitDone(OK);
+            break;
+        }
+    }
+}
+
+void RTPReceiver::notifyInitDone(status_t err) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatInitDone);
+    notify->setInt32("err", err);
+    notify->post();
+}
+
+void RTPReceiver::notifyError(status_t err) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatError);
+    notify->setInt32("err", err);
+    notify->post();
+}
+
+void RTPReceiver::notifyPacketLost() {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatPacketLost);
+    notify->post();
+}
+
+status_t RTPReceiver::onRTPData(const sp<ABuffer> &buffer) {
+    size_t size = buffer->size();
+    if (size < 12) {
+        // Too short to be a valid RTP header.
+        return ERROR_MALFORMED;
+    }
+
+    const uint8_t *data = buffer->data();
+
+    if ((data[0] >> 6) != 2) {
+        // Unsupported version.
+        return ERROR_UNSUPPORTED;
+    }
+
+    if (data[0] & 0x20) {
+        // Padding present.
+
+        size_t paddingLength = data[size - 1];
+
+        if (paddingLength + 12 > size) {
+            // If we removed this much padding we'd end up with something
+            // that's too short to be a valid RTP header.
+            return ERROR_MALFORMED;
+        }
+
+        size -= paddingLength;
+    }
+
+    int numCSRCs = data[0] & 0x0f;
+
+    size_t payloadOffset = 12 + 4 * numCSRCs;
+
+    if (size < payloadOffset) {
+        // Not enough data to fit the basic header and all the CSRC entries.
+        return ERROR_MALFORMED;
+    }
+
+    if (data[0] & 0x10) {
+        // Header eXtension present.
+
+        if (size < payloadOffset + 4) {
+            // Not enough data to fit the basic header, all CSRC entries
+            // and the first 4 bytes of the extension header.
+
+            return ERROR_MALFORMED;
+        }
+
+        const uint8_t *extensionData = &data[payloadOffset];
+
+        size_t extensionLength =
+            4 * (extensionData[2] << 8 | extensionData[3]);
+
+        if (size < payloadOffset + 4 + extensionLength) {
+            return ERROR_MALFORMED;
+        }
+
+        payloadOffset += 4 + extensionLength;
+    }
+
+    uint32_t srcId = U32_AT(&data[8]);
+    uint32_t rtpTime = U32_AT(&data[4]);
+    uint16_t seqNo = U16_AT(&data[2]);
+
+    sp<AMessage> meta = buffer->meta();
+    meta->setInt32("ssrc", srcId);
+    meta->setInt32("rtp-time", rtpTime);
+    meta->setInt32("PT", data[1] & 0x7f);
+    meta->setInt32("M", data[1] >> 7);
+
+    buffer->setRange(payloadOffset, size - payloadOffset);
+
+    ssize_t index = mSources.indexOfKey(srcId);
+    sp<Source> source;
+    if (index < 0) {
+        source = new Source(this, srcId);
+        mSources.add(srcId, source);
+    } else {
+        source = mSources.valueAt(index);
+    }
+
+    source->onPacketReceived(seqNo, buffer);
+
+    return OK;
+}
+
+status_t RTPReceiver::onRTCPData(const sp<ABuffer> &data) {
+    ALOGI("onRTCPData");
+    return OK;
+}
+
+void RTPReceiver::addSDES(const sp<ABuffer> &buffer) {
+    uint8_t *data = buffer->data() + buffer->size();
+    data[0] = 0x80 | 1;
+    data[1] = 202;  // SDES
+    data[4] = kSourceID >> 24;  // SSRC
+    data[5] = (kSourceID >> 16) & 0xff;
+    data[6] = (kSourceID >> 8) & 0xff;
+    data[7] = kSourceID & 0xff;
+
+    size_t offset = 8;
+
+    data[offset++] = 1;  // CNAME
+
+    AString cname = "stagefright@somewhere";
+    data[offset++] = cname.size();
+
+    memcpy(&data[offset], cname.c_str(), cname.size());
+    offset += cname.size();
+
+    data[offset++] = 6;  // TOOL
+
+    AString tool = "stagefright/1.0";
+    data[offset++] = tool.size();
+
+    memcpy(&data[offset], tool.c_str(), tool.size());
+    offset += tool.size();
+
+    data[offset++] = 0;
+
+    if ((offset % 4) > 0) {
+        size_t count = 4 - (offset % 4);
+        switch (count) {
+            case 3:
+                data[offset++] = 0;
+            case 2:
+                data[offset++] = 0;
+            case 1:
+                data[offset++] = 0;
+        }
+    }
+
+    size_t numWords = (offset / 4) - 1;
+    data[2] = numWords >> 8;
+    data[3] = numWords & 0xff;
+
+    buffer->setRange(buffer->offset(), buffer->size() + offset);
+}
+
+void RTPReceiver::scheduleSendRR() {
+    (new AMessage(kWhatSendRR, id()))->post(5000000ll);
+}
+
+void RTPReceiver::onSendRR() {
+#if 0
+    sp<ABuffer> buf = new ABuffer(kMaxUDPPacketSize);
+    buf->setRange(0, 0);
+
+    uint8_t *ptr = buf->data();
+    ptr[0] = 0x80 | 0;
+    ptr[1] = 201;  // RR
+    ptr[2] = 0;
+    ptr[3] = 1;
+    ptr[4] = kSourceID >> 24;  // SSRC
+    ptr[5] = (kSourceID >> 16) & 0xff;
+    ptr[6] = (kSourceID >> 8) & 0xff;
+    ptr[7] = kSourceID & 0xff;
+
+    buf->setRange(0, 8);
+
+    size_t numReportBlocks = 0;
+    for (size_t i = 0; i < mSources.size(); ++i) {
+        uint32_t ssrc = mSources.keyAt(i);
+        sp<Source> source = mSources.valueAt(i);
+
+        if (numReportBlocks > 31 || buf->size() + 24 > buf->capacity()) {
+            // Cannot fit another report block.
+            break;
+        }
+
+        source->addReportBlock(ssrc, buf);
+        ++numReportBlocks;
+    }
+
+    ptr[0] |= numReportBlocks;  // 5 bit
+
+    size_t sizeInWordsMinus1 = 1 + 6 * numReportBlocks;
+    ptr[2] = sizeInWordsMinus1 >> 8;
+    ptr[3] = sizeInWordsMinus1 & 0xff;
+
+    buf->setRange(0, (sizeInWordsMinus1 + 1) * 4);
+
+    addSDES(buf);
+
+    mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size());
+#endif
+
+    scheduleSendRR();
+}
+
+status_t RTPReceiver::registerPacketType(
+        uint8_t packetType, PacketizationMode mode) {
+    mPacketTypes.add(packetType, mode);
+
+    return OK;
+}
+
+sp<RTPReceiver::Assembler> RTPReceiver::makeAssembler(uint8_t packetType) {
+    ssize_t index = mPacketTypes.indexOfKey(packetType);
+    if (index < 0) {
+        return NULL;
+    }
+
+    PacketizationMode mode = mPacketTypes.valueAt(index);
+
+    switch (mode) {
+        case PACKETIZATION_TRANSPORT_STREAM:
+            return new TSAssembler(mNotify);
+
+        case PACKETIZATION_H264:
+            return new H264Assembler(mNotify);
+
+        default:
+            return NULL;
+    }
+}
+
+void RTPReceiver::requestRetransmission(uint32_t senderSSRC, int32_t extSeqNo) {
+    int32_t blp = 0;
+
+    sp<ABuffer> buf = new ABuffer(16);
+    buf->setRange(0, 0);
+
+    uint8_t *ptr = buf->data();
+    ptr[0] = 0x80 | 1;  // generic NACK
+    ptr[1] = 205;  // TSFB
+    ptr[2] = 0;
+    ptr[3] = 3;
+    ptr[8] = (senderSSRC >> 24) & 0xff;
+    ptr[9] = (senderSSRC >> 16) & 0xff;
+    ptr[10] = (senderSSRC >> 8) & 0xff;
+    ptr[11] = (senderSSRC & 0xff);
+    ptr[8] = (kSourceID >> 24) & 0xff;
+    ptr[9] = (kSourceID >> 16) & 0xff;
+    ptr[10] = (kSourceID >> 8) & 0xff;
+    ptr[11] = (kSourceID & 0xff);
+    ptr[12] = (extSeqNo >> 8) & 0xff;
+    ptr[13] = (extSeqNo & 0xff);
+    ptr[14] = (blp >> 8) & 0xff;
+    ptr[15] = (blp & 0xff);
+
+    buf->setRange(0, 16);
+
+     mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size());
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.h b/media/libstagefright/wifi-display/rtp/RTPReceiver.h
new file mode 100644
index 0000000..2ae864a
--- /dev/null
+++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RTP_RECEIVER_H_
+
+#define RTP_RECEIVER_H_
+
+#include "RTPBase.h"
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+struct ABuffer;
+struct ANetworkSession;
+
+// An object of this class facilitates receiving of media data on an RTP
+// channel. The channel is established over a UDP or TCP connection depending
+// on which "TransportMode" was chosen. In addition different RTP packetization
+// schemes are supported such as "Transport Stream Packets over RTP",
+// or "AVC/H.264 encapsulation as specified in RFC 3984 (non-interleaved mode)"
+struct RTPReceiver : public RTPBase, public AHandler {
+    enum {
+        kWhatInitDone,
+        kWhatError,
+        kWhatAccessUnit,
+        kWhatPacketLost,
+    };
+    RTPReceiver(
+            const sp<ANetworkSession> &netSession,
+            const sp<AMessage> &notify);
+
+    status_t registerPacketType(
+            uint8_t packetType, PacketizationMode mode);
+
+    status_t initAsync(TransportMode mode, int32_t *outLocalRTPPort);
+
+    status_t connect(
+            const char *remoteHost,
+            int32_t remoteRTPPort,
+            int32_t remoteRTCPPort);
+
+protected:
+    virtual ~RTPReceiver();
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    enum {
+        kWhatRTPNotify,
+        kWhatRTCPNotify,
+        kWhatSendRR,
+    };
+
+    enum {
+        kSourceID                       = 0xdeadbeef,
+        kPacketLostAfterUs              = 100000,
+        kRequestRetransmissionAfterUs   = -1,
+    };
+
+    struct Assembler;
+    struct H264Assembler;
+    struct Source;
+    struct TSAssembler;
+
+    sp<ANetworkSession> mNetSession;
+    sp<AMessage> mNotify;
+    TransportMode mMode;
+    int32_t mRTPSessionID;
+    int32_t mRTCPSessionID;
+
+    int32_t mRTPClientSessionID;  // in TRANSPORT_TCP mode.
+
+    KeyedVector<uint8_t, PacketizationMode> mPacketTypes;
+    KeyedVector<uint32_t, sp<Source> > mSources;
+
+    void onNetNotify(bool isRTP, const sp<AMessage> &msg);
+    status_t onRTPData(const sp<ABuffer> &data);
+    status_t onRTCPData(const sp<ABuffer> &data);
+    void onSendRR();
+
+    void scheduleSendRR();
+    void addSDES(const sp<ABuffer> &buffer);
+
+    void notifyInitDone(status_t err);
+    void notifyError(status_t err);
+    void notifyPacketLost();
+
+    sp<Assembler> makeAssembler(uint8_t packetType);
+
+    void requestRetransmission(uint32_t senderSSRC, int32_t extSeqNo);
+
+    DISALLOW_EVIL_CONSTRUCTORS(RTPReceiver);
+};
+
+}  // namespace android
+
+#endif  // RTP_RECEIVER_H_
diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp
new file mode 100644
index 0000000..85c5933
--- /dev/null
+++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp
@@ -0,0 +1,701 @@
+/*
+ * Copyright 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "RTPSender"
+#include <utils/Log.h>
+
+#include "RTPSender.h"
+
+#include "ANetworkSession.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/Utils.h>
+
+#include "include/avc_utils.h"
+
+namespace android {
+
+RTPSender::RTPSender(
+        const sp<ANetworkSession> &netSession,
+        const sp<AMessage> &notify)
+    : mNetSession(netSession),
+      mNotify(notify),
+      mMode(TRANSPORT_UNDEFINED),
+      mRTPSessionID(0),
+      mRTCPSessionID(0),
+      mRTPConnected(false),
+      mRTCPConnected(false),
+      mLastNTPTime(0),
+      mLastRTPTime(0),
+      mNumRTPSent(0),
+      mNumRTPOctetsSent(0),
+      mNumSRsSent(0),
+      mRTPSeqNo(0),
+      mHistorySize(0) {
+}
+
+RTPSender::~RTPSender() {
+    if (mRTCPSessionID != 0) {
+        mNetSession->destroySession(mRTCPSessionID);
+        mRTCPSessionID = 0;
+    }
+
+    if (mRTPSessionID != 0) {
+        mNetSession->destroySession(mRTPSessionID);
+        mRTPSessionID = 0;
+    }
+}
+
+// static
+int32_t RTPBase::PickRandomRTPPort() {
+    // Pick an even integer in range [1024, 65534)
+
+    static const size_t kRange = (65534 - 1024) / 2;
+
+    return (int32_t)(((float)(kRange + 1) * rand()) / RAND_MAX) * 2 + 1024;
+}
+
+status_t RTPSender::initAsync(
+        TransportMode mode,
+        const char *remoteHost,
+        int32_t remoteRTPPort,
+        int32_t remoteRTCPPort,
+        int32_t *outLocalRTPPort) {
+    if (mMode != TRANSPORT_UNDEFINED || mode == TRANSPORT_UNDEFINED) {
+        return INVALID_OPERATION;
+    }
+
+    CHECK_NE(mMode, TRANSPORT_TCP_INTERLEAVED);
+
+    if (mode == TRANSPORT_TCP && remoteRTCPPort >= 0) {
+        return INVALID_OPERATION;
+    }
+
+    sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
+
+    sp<AMessage> rtcpNotify;
+    if (remoteRTCPPort >= 0) {
+        rtcpNotify = new AMessage(kWhatRTCPNotify, id());
+    }
+
+    CHECK_EQ(mRTPSessionID, 0);
+    CHECK_EQ(mRTCPSessionID, 0);
+
+    int32_t localRTPPort;
+
+    for (;;) {
+        localRTPPort = PickRandomRTPPort();
+
+        status_t err;
+        if (mode == TRANSPORT_UDP) {
+            err = mNetSession->createUDPSession(
+                    localRTPPort,
+                    remoteHost,
+                    remoteRTPPort,
+                    rtpNotify,
+                    &mRTPSessionID);
+        } else {
+            CHECK_EQ(mode, TRANSPORT_TCP);
+            err = mNetSession->createTCPDatagramSession(
+                    localRTPPort,
+                    remoteHost,
+                    remoteRTPPort,
+                    rtpNotify,
+                    &mRTPSessionID);
+        }
+
+        if (err != OK) {
+            continue;
+        }
+
+        if (remoteRTCPPort < 0) {
+            break;
+        }
+
+        if (mode == TRANSPORT_UDP) {
+            err = mNetSession->createUDPSession(
+                    localRTPPort + 1,
+                    remoteHost,
+                    remoteRTCPPort,
+                    rtcpNotify,
+                    &mRTCPSessionID);
+        } else {
+            CHECK_EQ(mode, TRANSPORT_TCP);
+            err = mNetSession->createTCPDatagramSession(
+                    localRTPPort + 1,
+                    remoteHost,
+                    remoteRTCPPort,
+                    rtcpNotify,
+                    &mRTCPSessionID);
+        }
+
+        if (err == OK) {
+            break;
+        }
+
+        mNetSession->destroySession(mRTPSessionID);
+        mRTPSessionID = 0;
+    }
+
+    if (mode == TRANSPORT_UDP) {
+        mRTPConnected = true;
+        mRTCPConnected = true;
+    }
+
+    mMode = mode;
+    *outLocalRTPPort = localRTPPort;
+
+    if (mMode == TRANSPORT_UDP) {
+        notifyInitDone(OK);
+    }
+
+    return OK;
+}
+
+status_t RTPSender::queueBuffer(
+        const sp<ABuffer> &buffer, uint8_t packetType, PacketizationMode mode) {
+    status_t err;
+
+    switch (mode) {
+        case PACKETIZATION_TRANSPORT_STREAM:
+            err = queueTSPackets(buffer, packetType);
+            break;
+
+        case PACKETIZATION_H264:
+            err  = queueAVCBuffer(buffer, packetType);
+            break;
+
+        default:
+            TRESPASS();
+    }
+
+    return err;
+}
+
+status_t RTPSender::queueTSPackets(
+        const sp<ABuffer> &tsPackets, uint8_t packetType) {
+    CHECK_EQ(0, tsPackets->size() % 188);
+
+    const size_t numTSPackets = tsPackets->size() / 188;
+
+    size_t srcOffset = 0;
+    while (srcOffset < tsPackets->size()) {
+        sp<ABuffer> udpPacket =
+            new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188);
+
+        udpPacket->setInt32Data(mRTPSeqNo);
+
+        uint8_t *rtp = udpPacket->data();
+        rtp[0] = 0x80;
+        rtp[1] = packetType;
+
+        rtp[2] = (mRTPSeqNo >> 8) & 0xff;
+        rtp[3] = mRTPSeqNo & 0xff;
+        ++mRTPSeqNo;
+
+        int64_t nowUs = ALooper::GetNowUs();
+        uint32_t rtpTime = (nowUs * 9) / 100ll;
+
+        rtp[4] = rtpTime >> 24;
+        rtp[5] = (rtpTime >> 16) & 0xff;
+        rtp[6] = (rtpTime >> 8) & 0xff;
+        rtp[7] = rtpTime & 0xff;
+
+        rtp[8] = kSourceID >> 24;
+        rtp[9] = (kSourceID >> 16) & 0xff;
+        rtp[10] = (kSourceID >> 8) & 0xff;
+        rtp[11] = kSourceID & 0xff;
+
+        size_t numTSPackets = (tsPackets->size() - srcOffset) / 188;
+        if (numTSPackets > kMaxNumTSPacketsPerRTPPacket) {
+            numTSPackets = kMaxNumTSPacketsPerRTPPacket;
+        }
+
+        memcpy(&rtp[12], tsPackets->data() + srcOffset, numTSPackets * 188);
+
+        udpPacket->setRange(0, 12 + numTSPackets * 188);
+        status_t err = sendRTPPacket(udpPacket, true /* storeInHistory */);
+
+        if (err != OK) {
+            return err;
+        }
+
+        srcOffset += numTSPackets * 188;
+    }
+
+    return OK;
+}
+
+status_t RTPSender::queueAVCBuffer(
+        const sp<ABuffer> &accessUnit, uint8_t packetType) {
+    int64_t timeUs;
+    CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
+
+    uint32_t rtpTime = (timeUs * 9 / 100ll);
+
+    List<sp<ABuffer> > packets;
+
+    sp<ABuffer> out = new ABuffer(kMaxUDPPacketSize);
+    size_t outBytesUsed = 12;  // Placeholder for RTP header.
+
+    const uint8_t *data = accessUnit->data();
+    size_t size = accessUnit->size();
+    const uint8_t *nalStart;
+    size_t nalSize;
+    while (getNextNALUnit(
+                &data, &size, &nalStart, &nalSize,
+                true /* startCodeFollows */) == OK) {
+        size_t bytesNeeded = nalSize + 2;
+        if (outBytesUsed == 12) {
+            ++bytesNeeded;
+        }
+
+        if (outBytesUsed + bytesNeeded > out->capacity()) {
+            bool emitSingleNALPacket = false;
+
+            if (outBytesUsed == 12
+                    && outBytesUsed + nalSize <= out->capacity()) {
+                // We haven't emitted anything into the current packet yet and
+                // this NAL unit fits into a single-NAL-unit-packet while
+                // it wouldn't have fit as part of a STAP-A packet.
+
+                memcpy(out->data() + outBytesUsed, nalStart, nalSize);
+                outBytesUsed += nalSize;
+
+                emitSingleNALPacket = true;
+            }
+
+            if (outBytesUsed > 12) {
+                out->setRange(0, outBytesUsed);
+                packets.push_back(out);
+                out = new ABuffer(kMaxUDPPacketSize);
+                outBytesUsed = 12;  // Placeholder for RTP header
+            }
+
+            if (emitSingleNALPacket) {
+                continue;
+            }
+        }
+
+        if (outBytesUsed + bytesNeeded <= out->capacity()) {
+            uint8_t *dst = out->data() + outBytesUsed;
+
+            if (outBytesUsed == 12) {
+                *dst++ = 24;  // STAP-A header
+            }
+
+            *dst++ = (nalSize >> 8) & 0xff;
+            *dst++ = nalSize & 0xff;
+            memcpy(dst, nalStart, nalSize);
+
+            outBytesUsed += bytesNeeded;
+            continue;
+        }
+
+        // This single NAL unit does not fit into a single RTP packet,
+        // we need to emit an FU-A.
+
+        CHECK_EQ(outBytesUsed, 12u);
+
+        uint8_t nalType = nalStart[0] & 0x1f;
+        uint8_t nri = (nalStart[0] >> 5) & 3;
+
+        size_t srcOffset = 1;
+        while (srcOffset < nalSize) {
+            size_t copy = out->capacity() - outBytesUsed - 2;
+            if (copy > nalSize - srcOffset) {
+                copy = nalSize - srcOffset;
+            }
+
+            uint8_t *dst = out->data() + outBytesUsed;
+            dst[0] = (nri << 5) | 28;
+
+            dst[1] = nalType;
+
+            if (srcOffset == 1) {
+                dst[1] |= 0x80;
+            }
+
+            if (srcOffset + copy == nalSize) {
+                dst[1] |= 0x40;
+            }
+
+            memcpy(&dst[2], nalStart + srcOffset, copy);
+            srcOffset += copy;
+
+            out->setRange(0, outBytesUsed + copy + 2);
+
+            packets.push_back(out);
+            out = new ABuffer(kMaxUDPPacketSize);
+            outBytesUsed = 12;  // Placeholder for RTP header
+        }
+    }
+
+    if (outBytesUsed > 12) {
+        out->setRange(0, outBytesUsed);
+        packets.push_back(out);
+    }
+
+    while (!packets.empty()) {
+        sp<ABuffer> out = *packets.begin();
+        packets.erase(packets.begin());
+
+        out->setInt32Data(mRTPSeqNo);
+
+        bool last = packets.empty();
+
+        uint8_t *dst = out->data();
+
+        dst[0] = 0x80;
+
+        dst[1] = packetType;
+        if (last) {
+            dst[1] |= 1 << 7;  // M-bit
+        }
+
+        dst[2] = (mRTPSeqNo >> 8) & 0xff;
+        dst[3] = mRTPSeqNo & 0xff;
+        ++mRTPSeqNo;
+
+        dst[4] = rtpTime >> 24;
+        dst[5] = (rtpTime >> 16) & 0xff;
+        dst[6] = (rtpTime >> 8) & 0xff;
+        dst[7] = rtpTime & 0xff;
+        dst[8] = kSourceID >> 24;
+        dst[9] = (kSourceID >> 16) & 0xff;
+        dst[10] = (kSourceID >> 8) & 0xff;
+        dst[11] = kSourceID & 0xff;
+
+        status_t err = sendRTPPacket(out, true /* storeInHistory */);
+
+        if (err != OK) {
+            return err;
+        }
+    }
+
+    return OK;
+}
+
+status_t RTPSender::sendRTPPacket(
+        const sp<ABuffer> &buffer, bool storeInHistory) {
+    CHECK(mRTPConnected);
+
+    status_t err = mNetSession->sendRequest(
+            mRTPSessionID, buffer->data(), buffer->size());
+
+    if (err != OK) {
+        return err;
+    }
+
+    mLastNTPTime = GetNowNTP();
+    mLastRTPTime = U32_AT(buffer->data() + 4);
+
+    ++mNumRTPSent;
+    mNumRTPOctetsSent += buffer->size() - 12;
+
+    if (storeInHistory) {
+        if (mHistorySize == kMaxHistorySize) {
+            mHistory.erase(mHistory.begin());
+        } else {
+            ++mHistorySize;
+        }
+        mHistory.push_back(buffer);
+    }
+
+    return OK;
+}
+
+// static
+uint64_t RTPSender::GetNowNTP() {
+    struct timeval tv;
+    gettimeofday(&tv, NULL /* timezone */);
+
+    uint64_t nowUs = tv.tv_sec * 1000000ll + tv.tv_usec;
+
+    nowUs += ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll;
+
+    uint64_t hi = nowUs / 1000000ll;
+    uint64_t lo = ((1ll << 32) * (nowUs % 1000000ll)) / 1000000ll;
+
+    return (hi << 32) | lo;
+}
+
+void RTPSender::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatRTPNotify:
+        case kWhatRTCPNotify:
+            onNetNotify(msg->what() == kWhatRTPNotify, msg);
+            break;
+
+        default:
+            TRESPASS();
+    }
+}
+
+void RTPSender::onNetNotify(bool isRTP, const sp<AMessage> &msg) {
+    int32_t reason;
+    CHECK(msg->findInt32("reason", &reason));
+
+    switch (reason) {
+        case ANetworkSession::kWhatError:
+        {
+            int32_t sessionID;
+            CHECK(msg->findInt32("sessionID", &sessionID));
+
+            int32_t err;
+            CHECK(msg->findInt32("err", &err));
+
+            int32_t errorOccuredDuringSend;
+            CHECK(msg->findInt32("send", &errorOccuredDuringSend));
+
+            AString detail;
+            CHECK(msg->findString("detail", &detail));
+
+            ALOGE("An error occurred during %s in session %d "
+                  "(%d, '%s' (%s)).",
+                  errorOccuredDuringSend ? "send" : "receive",
+                  sessionID,
+                  err,
+                  detail.c_str(),
+                  strerror(-err));
+
+            mNetSession->destroySession(sessionID);
+
+            if (sessionID == mRTPSessionID) {
+                mRTPSessionID = 0;
+            } else if (sessionID == mRTCPSessionID) {
+                mRTCPSessionID = 0;
+            }
+
+            if (mMode == TRANSPORT_TCP) {
+                if (!mRTPConnected
+                        || (mRTCPSessionID > 0 && !mRTCPConnected)) {
+                    notifyInitDone(err);
+                    break;
+                }
+            }
+
+            notifyError(err);
+            break;
+        }
+
+        case ANetworkSession::kWhatDatagram:
+        {
+            sp<ABuffer> data;
+            CHECK(msg->findBuffer("data", &data));
+
+            if (isRTP) {
+                ALOGW("Huh? Received data on RTP connection...");
+            } else {
+                onRTCPData(data);
+            }
+            break;
+        }
+
+        case ANetworkSession::kWhatConnected:
+        {
+            CHECK_EQ(mMode, TRANSPORT_TCP);
+
+            int32_t sessionID;
+            CHECK(msg->findInt32("sessionID", &sessionID));
+
+            if  (isRTP) {
+                CHECK_EQ(sessionID, mRTPSessionID);
+                mRTPConnected = true;
+            } else {
+                CHECK_EQ(sessionID, mRTCPSessionID);
+                mRTCPConnected = true;
+            }
+
+            if (mRTPConnected && (mRTCPSessionID == 0 || mRTCPConnected)) {
+                notifyInitDone(OK);
+            }
+            break;
+        }
+    }
+}
+
+status_t RTPSender::onRTCPData(const sp<ABuffer> &buffer) {
+    const uint8_t *data = buffer->data();
+    size_t size = buffer->size();
+
+    while (size > 0) {
+        if (size < 8) {
+            // Too short to be a valid RTCP header
+            return ERROR_MALFORMED;
+        }
+
+        if ((data[0] >> 6) != 2) {
+            // Unsupported version.
+            return ERROR_UNSUPPORTED;
+        }
+
+        if (data[0] & 0x20) {
+            // Padding present.
+
+            size_t paddingLength = data[size - 1];
+
+            if (paddingLength + 12 > size) {
+                // If we removed this much padding we'd end up with something
+                // that's too short to be a valid RTP header.
+                return ERROR_MALFORMED;
+            }
+
+            size -= paddingLength;
+        }
+
+        size_t headerLength = 4 * (data[2] << 8 | data[3]) + 4;
+
+        if (size < headerLength) {
+            // Only received a partial packet?
+            return ERROR_MALFORMED;
+        }
+
+        switch (data[1]) {
+            case 200:
+            case 201:  // RR
+                parseReceiverReport(data, headerLength);
+                break;
+
+            case 202:  // SDES
+            case 203:
+            case 204:  // APP
+                break;
+
+            case 205:  // TSFB (transport layer specific feedback)
+                parseTSFB(data, headerLength);
+                break;
+
+            case 206:  // PSFB (payload specific feedback)
+                // hexdump(data, headerLength);
+                break;
+
+            default:
+            {
+                ALOGW("Unknown RTCP packet type %u of size %d",
+                     (unsigned)data[1], headerLength);
+                break;
+            }
+        }
+
+        data += headerLength;
+        size -= headerLength;
+    }
+
+    return OK;
+}
+
+status_t RTPSender::parseReceiverReport(const uint8_t *data, size_t size) {
+    // hexdump(data, size);
+
+    float fractionLost = data[12] / 256.0f;
+
+    ALOGI("lost %.2f %% of packets during report interval.",
+          100.0f * fractionLost);
+
+    return OK;
+}
+
+status_t RTPSender::parseTSFB(const uint8_t *data, size_t size) {
+    if ((data[0] & 0x1f) != 1) {
+        return ERROR_UNSUPPORTED;  // We only support NACK for now.
+    }
+
+    uint32_t srcId = U32_AT(&data[8]);
+    if (srcId != kSourceID) {
+        return ERROR_MALFORMED;
+    }
+
+    for (size_t i = 12; i < size; i += 4) {
+        uint16_t seqNo = U16_AT(&data[i]);
+        uint16_t blp = U16_AT(&data[i + 2]);
+
+        List<sp<ABuffer> >::iterator it = mHistory.begin();
+        bool foundSeqNo = false;
+        while (it != mHistory.end()) {
+            const sp<ABuffer> &buffer = *it;
+
+            uint16_t bufferSeqNo = buffer->int32Data() & 0xffff;
+
+            bool retransmit = false;
+            if (bufferSeqNo == seqNo) {
+                retransmit = true;
+            } else if (blp != 0) {
+                for (size_t i = 0; i < 16; ++i) {
+                    if ((blp & (1 << i))
+                        && (bufferSeqNo == ((seqNo + i + 1) & 0xffff))) {
+                        blp &= ~(1 << i);
+                        retransmit = true;
+                    }
+                }
+            }
+
+            if (retransmit) {
+                ALOGV("retransmitting seqNo %d", bufferSeqNo);
+
+                CHECK_EQ((status_t)OK,
+                         sendRTPPacket(buffer, false /* storeInHistory */));
+
+                if (bufferSeqNo == seqNo) {
+                    foundSeqNo = true;
+                }
+
+                if (foundSeqNo && blp == 0) {
+                    break;
+                }
+            }
+
+            ++it;
+        }
+
+        if (!foundSeqNo || blp != 0) {
+            ALOGI("Some sequence numbers were no longer available for "
+                  "retransmission (seqNo = %d, foundSeqNo = %d, blp = 0x%04x)",
+                  seqNo, foundSeqNo, blp);
+
+            if (!mHistory.empty()) {
+                int32_t earliest = (*mHistory.begin())->int32Data() & 0xffff;
+                int32_t latest = (*--mHistory.end())->int32Data() & 0xffff;
+
+                ALOGI("have seq numbers from %d - %d", earliest, latest);
+            }
+        }
+    }
+
+    return OK;
+}
+
+void RTPSender::notifyInitDone(status_t err) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatInitDone);
+    notify->setInt32("err", err);
+    notify->post();
+}
+
+void RTPSender::notifyError(status_t err) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatError);
+    notify->setInt32("err", err);
+    notify->post();
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.h b/media/libstagefright/wifi-display/rtp/RTPSender.h
new file mode 100644
index 0000000..2b683a4
--- /dev/null
+++ b/media/libstagefright/wifi-display/rtp/RTPSender.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RTP_SENDER_H_
+
+#define RTP_SENDER_H_
+
+#include "RTPBase.h"
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+struct ABuffer;
+struct ANetworkSession;
+
+// An object of this class facilitates sending of media data over an RTP
+// channel. The channel is established over a UDP or TCP connection depending
+// on which "TransportMode" was chosen. In addition different RTP packetization
+// schemes are supported such as "Transport Stream Packets over RTP",
+// or "AVC/H.264 encapsulation as specified in RFC 3984 (non-interleaved mode)"
+struct RTPSender : public RTPBase, public AHandler {
+    enum {
+        kWhatInitDone,
+        kWhatError,
+    };
+    RTPSender(
+            const sp<ANetworkSession> &netSession,
+            const sp<AMessage> &notify);
+
+    status_t initAsync(
+              TransportMode mode,
+              const char *remoteHost,
+              int32_t remoteRTPPort,
+              int32_t remoteRTCPPort,
+              int32_t *outLocalRTPPort);
+
+    status_t queueBuffer(
+            const sp<ABuffer> &buffer,
+            uint8_t packetType,
+            PacketizationMode mode);
+
+protected:
+    virtual ~RTPSender();
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    enum {
+        kWhatRTPNotify,
+        kWhatRTCPNotify,
+    };
+
+    enum {
+        kMaxNumTSPacketsPerRTPPacket = (kMaxUDPPacketSize - 12) / 188,
+        kMaxHistorySize              = 1024,
+        kSourceID                    = 0xdeadbeef,
+    };
+
+    sp<ANetworkSession> mNetSession;
+    sp<AMessage> mNotify;
+    TransportMode mMode;
+    int32_t mRTPSessionID;
+    int32_t mRTCPSessionID;
+    bool mRTPConnected;
+    bool mRTCPConnected;
+
+    uint64_t mLastNTPTime;
+    uint32_t mLastRTPTime;
+    uint32_t mNumRTPSent;
+    uint32_t mNumRTPOctetsSent;
+    uint32_t mNumSRsSent;
+
+    uint32_t mRTPSeqNo;
+
+    List<sp<ABuffer> > mHistory;
+    size_t mHistorySize;
+
+    static uint64_t GetNowNTP();
+
+    status_t queueTSPackets(const sp<ABuffer> &tsPackets, uint8_t packetType);
+    status_t queueAVCBuffer(const sp<ABuffer> &accessUnit, uint8_t packetType);
+
+    status_t sendRTPPacket(const sp<ABuffer> &packet, bool storeInHistory);
+
+    void onNetNotify(bool isRTP, const sp<AMessage> &msg);
+
+    status_t onRTCPData(const sp<ABuffer> &data);
+    status_t parseReceiverReport(const uint8_t *data, size_t size);
+    status_t parseTSFB(const uint8_t *data, size_t size);
+
+    void notifyInitDone(status_t err);
+    void notifyError(status_t err);
+
+    DISALLOW_EVIL_CONSTRUCTORS(RTPSender);
+};
+
+}  // namespace android
+
+#endif  // RTP_SENDER_H_
diff --git a/media/libstagefright/wifi-display/rtptest.cpp b/media/libstagefright/wifi-display/rtptest.cpp
new file mode 100644
index 0000000..607d9d2
--- /dev/null
+++ b/media/libstagefright/wifi-display/rtptest.cpp
@@ -0,0 +1,382 @@
+/*
+ * Copyright 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NEBUG 0
+#define LOG_TAG "rtptest"
+#include <utils/Log.h>
+
+#include "ANetworkSession.h"
+#include "rtp/RTPSender.h"
+#include "rtp/RTPReceiver.h"
+
+#include <binder/ProcessState.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AHandler.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/NuMediaExtractor.h>
+
+namespace android {
+
+struct TestHandler : public AHandler {
+    TestHandler(const sp<ANetworkSession> &netSession);
+
+    void listen();
+    void connect(const char *host, int32_t port);
+
+protected:
+    virtual ~TestHandler();
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    enum {
+        kWhatListen,
+        kWhatConnect,
+        kWhatReceiverNotify,
+        kWhatSenderNotify,
+        kWhatSendMore,
+        kWhatStop,
+    };
+
+    sp<ANetworkSession> mNetSession;
+    sp<NuMediaExtractor> mExtractor;
+    sp<RTPSender> mSender;
+    sp<RTPReceiver> mReceiver;
+
+    size_t mMaxSampleSize;
+
+    int64_t mFirstTimeRealUs;
+    int64_t mFirstTimeMediaUs;
+
+    status_t readMore();
+
+    DISALLOW_EVIL_CONSTRUCTORS(TestHandler);
+};
+
+TestHandler::TestHandler(const sp<ANetworkSession> &netSession)
+    : mNetSession(netSession),
+      mMaxSampleSize(1024 * 1024),
+      mFirstTimeRealUs(-1ll),
+      mFirstTimeMediaUs(-1ll) {
+}
+
+TestHandler::~TestHandler() {
+}
+
+void TestHandler::listen() {
+    sp<AMessage> msg = new AMessage(kWhatListen, id());
+    msg->post();
+}
+
+void TestHandler::connect(const char *host, int32_t port) {
+    sp<AMessage> msg = new AMessage(kWhatConnect, id());
+    msg->setString("host", host);
+    msg->setInt32("port", port);
+    msg->post();
+}
+
+void TestHandler::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatListen:
+        {
+            sp<AMessage> notify = new AMessage(kWhatReceiverNotify, id());
+            mReceiver = new RTPReceiver(mNetSession, notify);
+            looper()->registerHandler(mReceiver);
+
+            CHECK_EQ((status_t)OK,
+                     mReceiver->registerPacketType(
+                         33, RTPReceiver::PACKETIZATION_H264));
+
+            int32_t receiverRTPPort;
+            CHECK_EQ((status_t)OK,
+                     mReceiver->initAsync(
+                         RTPReceiver::TRANSPORT_UDP, &receiverRTPPort));
+
+            printf("picked receiverRTPPort %d\n", receiverRTPPort);
+
+#if 0
+            CHECK_EQ((status_t)OK,
+                     mReceiver->connect(
+                         "127.0.0.1", senderRTPPort, senderRTPPort + 1));
+#endif
+            break;
+        }
+
+        case kWhatConnect:
+        {
+            AString host;
+            CHECK(msg->findString("host", &host));
+
+            int32_t receiverRTPPort;
+            CHECK(msg->findInt32("port", &receiverRTPPort));
+
+            mExtractor = new NuMediaExtractor;
+            CHECK_EQ((status_t)OK,
+                     mExtractor->setDataSource(
+                             "/sdcard/Frame Counter HD 30FPS_1080p.mp4"));
+
+            bool haveVideo = false;
+            for (size_t i = 0; i < mExtractor->countTracks(); ++i) {
+                sp<AMessage> format;
+                CHECK_EQ((status_t)OK, mExtractor->getTrackFormat(i, &format));
+
+                AString mime;
+                CHECK(format->findString("mime", &mime));
+
+                if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str())) {
+                    mExtractor->selectTrack(i);
+                    haveVideo = true;
+                    break;
+                }
+            }
+
+            CHECK(haveVideo);
+
+            sp<AMessage> notify = new AMessage(kWhatSenderNotify, id());
+            mSender = new RTPSender(mNetSession, notify);
+            looper()->registerHandler(mSender);
+
+            int32_t senderRTPPort;
+            CHECK_EQ((status_t)OK,
+                     mSender->initAsync(
+                         RTPSender::TRANSPORT_UDP,
+                         host.c_str(),
+                         receiverRTPPort,
+                         receiverRTPPort + 1,
+                         &senderRTPPort));
+
+            printf("picked senderRTPPort %d\n", senderRTPPort);
+            break;
+        }
+
+        case kWhatSenderNotify:
+        {
+            ALOGI("kWhatSenderNotify");
+
+            int32_t what;
+            CHECK(msg->findInt32("what", &what));
+
+            switch (what) {
+                case RTPSender::kWhatInitDone:
+                {
+                    int32_t err;
+                    CHECK(msg->findInt32("err", &err));
+
+                    ALOGI("RTPSender::initAsync completed w/ err %d", err);
+
+                    if (err == OK) {
+                        err = readMore();
+
+                        if (err != OK) {
+                            (new AMessage(kWhatStop, id()))->post();
+                        }
+                    }
+                    break;
+                }
+
+                case RTPSender::kWhatError:
+                    break;
+            }
+            break;
+        }
+
+        case kWhatReceiverNotify:
+        {
+            ALOGI("kWhatReceiverNotify");
+
+            int32_t what;
+            CHECK(msg->findInt32("what", &what));
+
+            switch (what) {
+                case RTPReceiver::kWhatInitDone:
+                {
+                    int32_t err;
+                    CHECK(msg->findInt32("err", &err));
+
+                    ALOGI("RTPReceiver::initAsync completed w/ err %d", err);
+                    break;
+                }
+
+                case RTPSender::kWhatError:
+                    break;
+            }
+            break;
+        }
+
+        case kWhatSendMore:
+        {
+            sp<ABuffer> accessUnit;
+            CHECK(msg->findBuffer("accessUnit", &accessUnit));
+
+            CHECK_EQ((status_t)OK,
+                     mSender->queueBuffer(
+                         accessUnit,
+                         33,
+                         RTPSender::PACKETIZATION_H264));
+
+            status_t err = readMore();
+
+            if (err != OK) {
+                (new AMessage(kWhatStop, id()))->post();
+            }
+            break;
+        }
+
+        case kWhatStop:
+        {
+            if (mReceiver != NULL) {
+                looper()->unregisterHandler(mReceiver->id());
+                mReceiver.clear();
+            }
+
+            if (mSender != NULL) {
+                looper()->unregisterHandler(mSender->id());
+                mSender.clear();
+            }
+
+            mExtractor.clear();
+
+            looper()->stop();
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+status_t TestHandler::readMore() {
+    int64_t timeUs;
+    status_t err = mExtractor->getSampleTime(&timeUs);
+
+    if (err != OK) {
+        return err;
+    }
+
+    sp<ABuffer> accessUnit = new ABuffer(mMaxSampleSize);
+    CHECK_EQ((status_t)OK, mExtractor->readSampleData(accessUnit));
+
+    accessUnit->meta()->setInt64("timeUs", timeUs);
+
+    CHECK_EQ((status_t)OK, mExtractor->advance());
+
+    int64_t nowUs = ALooper::GetNowUs();
+    int64_t whenUs;
+
+    if (mFirstTimeRealUs < 0ll) {
+        mFirstTimeRealUs = whenUs = nowUs;
+        mFirstTimeMediaUs = timeUs;
+    } else {
+        whenUs = mFirstTimeRealUs + timeUs - mFirstTimeMediaUs;
+    }
+
+    sp<AMessage> msg = new AMessage(kWhatSendMore, id());
+    msg->setBuffer("accessUnit", accessUnit);
+    msg->post(whenUs - nowUs);
+
+    return OK;
+}
+
+}  // namespace android
+
+static void usage(const char *me) {
+    fprintf(stderr,
+            "usage: %s -c host:port\tconnect to remote host\n"
+            "               -l       \tlisten\n",
+            me);
+}
+
+int main(int argc, char **argv) {
+    using namespace android;
+
+    // srand(time(NULL));
+
+    ProcessState::self()->startThreadPool();
+
+    DataSource::RegisterDefaultSniffers();
+
+    bool listen = false;
+    int32_t connectToPort = -1;
+    AString connectToHost;
+
+    int res;
+    while ((res = getopt(argc, argv, "hc:l")) >= 0) {
+        switch (res) {
+            case 'c':
+            {
+                const char *colonPos = strrchr(optarg, ':');
+
+                if (colonPos == NULL) {
+                    usage(argv[0]);
+                    exit(1);
+                }
+
+                connectToHost.setTo(optarg, colonPos - optarg);
+
+                char *end;
+                connectToPort = strtol(colonPos + 1, &end, 10);
+
+                if (*end != '\0' || end == colonPos + 1
+                        || connectToPort < 1 || connectToPort > 65535) {
+                    fprintf(stderr, "Illegal port specified.\n");
+                    exit(1);
+                }
+                break;
+            }
+
+            case 'l':
+            {
+                listen = true;
+                break;
+            }
+
+            case '?':
+            case 'h':
+                usage(argv[0]);
+                exit(1);
+        }
+    }
+
+    if (!listen && connectToPort < 0) {
+        fprintf(stderr,
+                "You need to select either client or server mode.\n");
+        exit(1);
+    }
+
+    sp<ANetworkSession> netSession = new ANetworkSession;
+    netSession->start();
+
+    sp<ALooper> looper = new ALooper;
+
+    sp<TestHandler> handler = new TestHandler(netSession);
+    looper->registerHandler(handler);
+
+    if (listen) {
+        handler->listen();
+    }
+
+    if (connectToPort >= 0) {
+        handler->connect(connectToHost.c_str(), connectToPort);
+    }
+
+    looper->start(true /* runOnCallingThread */);
+
+    return 0;
+}
+
diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp
index 70369bb..b53252d 100644
--- a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp
+++ b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp
@@ -20,15 +20,13 @@
 
 #include "DirectRenderer.h"
 
-#include "AnotherPacketSource.h"
-#include "ATSParser.h"
-
 #include <gui/SurfaceComposerClient.h>
 #include <gui/Surface.h>
 #include <media/ICrypto.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/MediaCodec.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
@@ -36,30 +34,13 @@
 
 namespace android {
 
-#if 1
-// static
-const int64_t DirectRenderer::kPacketLostDelayUs = 80000ll;
-
-// static
-const int64_t DirectRenderer::kPacketLateDelayUs = 60000ll;
-#else
-// static
-const int64_t DirectRenderer::kPacketLostDelayUs = 1000000ll;
-
-// static
-const int64_t DirectRenderer::kPacketLateDelayUs = -1ll;
-#endif
-
 DirectRenderer::DirectRenderer(
-        const sp<AMessage> &notifyLost,
         const sp<IGraphicBufferProducer> &bufferProducer)
-    : mNotifyLost(notifyLost),
-      mSurfaceTex(bufferProducer),
-      mTSParser(new ATSParser(ATSParser::ALIGNED_VIDEO_DATA)),
+    : mSurfaceTex(bufferProducer),
       mVideoDecoderNotificationPending(false),
-      mAwaitingExtSeqNo(-1),
-      mRequestedRetransmission(false),
-      mPacketLostGeneration(0) {
+      mRenderPending(false),
+      mFirstRenderTimeUs(-1ll),
+      mFirstRenderRealUs(-1ll) {
 }
 
 DirectRenderer::~DirectRenderer() {
@@ -74,263 +55,84 @@
 
 void DirectRenderer::onMessageReceived(const sp<AMessage> &msg) {
     switch (msg->what()) {
-        case kWhatQueueBuffer:
-        {
-            sp<ABuffer> buffer;
-            CHECK(msg->findBuffer("buffer", &buffer));
-
-            onQueueBuffer(buffer);
-
-            dequeueMore();
-            break;
-        }
-
-        case kWhatPacketLate:
-        case kWhatPacketLost:
-        {
-            int32_t generation;
-            CHECK(msg->findInt32("generation", &generation));
-
-            if (generation != mPacketLostGeneration) {
-                // stale.
-                break;
-            }
-
-            if (msg->what() == kWhatPacketLate) {
-                CHECK(!mRequestedRetransmission);
-                CHECK_GE(mAwaitingExtSeqNo, 0);
-
-                ALOGV("packet extSeqNo %d is late, requesting retransmission.",
-                      mAwaitingExtSeqNo);
-
-                sp<AMessage> notify = mNotifyLost->dup();
-                notify->setInt32("seqNo", (mAwaitingExtSeqNo & 0xffff));
-                notify->post();
-
-                mRequestedRetransmission = true;
-                break;
-            }
-
-            ALOGW("lost packet extSeqNo %d", mAwaitingExtSeqNo);
-
-            sp<AMessage> extra;
-            mTSParser->signalDiscontinuity(
-                    ATSParser::DISCONTINUITY_TIME, extra);
-
-            mAwaitingExtSeqNo = -1;
-            mRequestedRetransmission = false;
-            dequeueMore();
-            break;
-        }
-
         case kWhatVideoDecoderNotify:
         {
             onVideoDecoderNotify();
             break;
         }
 
+        case kWhatRender:
+        {
+            onRender();
+            break;
+        }
+
         default:
             TRESPASS();
     }
 }
 
-void DirectRenderer::onQueueBuffer(const sp<ABuffer> &buffer) {
-    int32_t newExtendedSeqNo = buffer->int32Data();
-
-    if (mPackets.empty()) {
-        mPackets.push_back(buffer);
+void DirectRenderer::setFormat(
+        size_t trackIndex, const sp<AMessage> &format) {
+    if (trackIndex == 1) {
+        // Ignore audio for now.
         return;
     }
 
-    if (mAwaitingExtSeqNo > 0 && newExtendedSeqNo < mAwaitingExtSeqNo) {
-        // We're no longer interested in these. They're old.
+    CHECK(mVideoDecoder == NULL);
+
+    AString mime;
+    CHECK(format->findString("mime", &mime));
+
+    mVideoDecoderLooper = new ALooper;
+    mVideoDecoderLooper->setName("video codec looper");
+
+    mVideoDecoderLooper->start(
+            false /* runOnCallingThread */,
+            false /* canCallJava */,
+            PRIORITY_DEFAULT);
+
+    mVideoDecoder = MediaCodec::CreateByType(
+            mVideoDecoderLooper, mime.c_str(), false /* encoder */);
+
+    CHECK(mVideoDecoder != NULL);
+
+    status_t err = mVideoDecoder->configure(
+            format,
+            mSurfaceTex == NULL
+                ? NULL : new Surface(mSurfaceTex),
+            NULL /* crypto */,
+            0 /* flags */);
+    CHECK_EQ(err, (status_t)OK);
+
+    err = mVideoDecoder->start();
+    CHECK_EQ(err, (status_t)OK);
+
+    err = mVideoDecoder->getInputBuffers(
+            &mVideoDecoderInputBuffers);
+    CHECK_EQ(err, (status_t)OK);
+
+    scheduleVideoDecoderNotification();
+}
+
+void DirectRenderer::queueAccessUnit(
+        size_t trackIndex, const sp<ABuffer> &accessUnit) {
+    if (trackIndex == 1) {
+        // Ignore audio for now.
         return;
     }
 
-    List<sp<ABuffer> >::iterator firstIt = mPackets.begin();
-    List<sp<ABuffer> >::iterator it = --mPackets.end();
-    for (;;) {
-        int32_t extendedSeqNo = (*it)->int32Data();
+    if (mVideoDecoder == NULL) {
+        sp<AMessage> format = new AMessage;
+        format->setString("mime", "video/avc");
+        format->setInt32("width", 640);
+        format->setInt32("height", 360);
 
-        if (extendedSeqNo == newExtendedSeqNo) {
-            // Duplicate packet.
-            return;
-        }
-
-        if (extendedSeqNo < newExtendedSeqNo) {
-            // Insert new packet after the one at "it".
-            mPackets.insert(++it, buffer);
-            return;
-        }
-
-        if (it == firstIt) {
-            // Insert new packet before the first existing one.
-            mPackets.insert(it, buffer);
-            return;
-        }
-
-        --it;
-    }
-}
-
-void DirectRenderer::dequeueMore() {
-    if (mAwaitingExtSeqNo >= 0) {
-        // Remove all packets before the one we're looking for, they had
-        // their chance.
-        while (!mPackets.empty()
-                && (*mPackets.begin())->int32Data() < mAwaitingExtSeqNo) {
-            ALOGV("dropping late packet extSeqNo %d",
-                  (*mPackets.begin())->int32Data());
-
-            mPackets.erase(mPackets.begin());
-        }
+        setFormat(0, format);
     }
 
-    bool packetLostScheduled = (mAwaitingExtSeqNo >= 0);
-
-    while (!mPackets.empty()) {
-        sp<ABuffer> buffer = *mPackets.begin();
-        int32_t extSeqNo = buffer->int32Data();
-
-        if (mAwaitingExtSeqNo >= 0 && extSeqNo != mAwaitingExtSeqNo) {
-            break;
-        }
-
-        mPackets.erase(mPackets.begin());
-
-        if (packetLostScheduled) {
-            packetLostScheduled = false;
-            cancelPacketLost();
-        }
-
-        if (mRequestedRetransmission) {
-            ALOGV("recovered after requesting retransmission of extSeqNo %d",
-                  mAwaitingExtSeqNo);
-        }
-
-        CHECK_EQ(buffer->size() % 188, 0u);
-
-        for (size_t offset = 0; offset < buffer->size(); offset += 188) {
-            status_t err = mTSParser->feedTSPacket(
-                    buffer->data() + offset, 188);
-
-            CHECK_EQ(err, (status_t)OK);
-        }
-
-        mAwaitingExtSeqNo = extSeqNo + 1;
-        mRequestedRetransmission = false;
-    }
-
-    if (!packetLostScheduled && mAwaitingExtSeqNo >= 0) {
-        schedulePacketLost();
-    }
-
-    dequeueAccessUnits();
-}
-
-void DirectRenderer::dequeueAccessUnits() {
-    sp<AnotherPacketSource> audioSource =
-        static_cast<AnotherPacketSource *>(
-                mTSParser->getSource(ATSParser::AUDIO).get());
-
-    if (audioSource != NULL) {
-        status_t finalResult;
-        size_t n = 0;
-        while (audioSource->hasBufferAvailable(&finalResult)) {
-            sp<ABuffer> accessUnit;
-            status_t err = audioSource->dequeueAccessUnit(&accessUnit);
-            if (err == OK) {
-                ++n;
-            }
-        }
-
-        if (n > 0) {
-            ALOGV("dequeued %d audio access units.", n);
-        }
-    }
-
-    sp<AnotherPacketSource> videoSource =
-        static_cast<AnotherPacketSource *>(
-                mTSParser->getSource(ATSParser::VIDEO).get());
-
-    if (videoSource != NULL) {
-        if (mVideoDecoder == NULL) {
-            sp<MetaData> meta = videoSource->getFormat();
-            if (meta != NULL) {
-                sp<AMessage> videoFormat;
-                status_t err = convertMetaDataToMessage(meta, &videoFormat);
-                CHECK_EQ(err, (status_t)OK);
-
-                AString mime;
-                CHECK(videoFormat->findString("mime", &mime));
-
-                mVideoDecoderLooper = new ALooper;
-                mVideoDecoderLooper->setName("video codec looper");
-
-                mVideoDecoderLooper->start(
-                        false /* runOnCallingThread */,
-                        false /* canCallJava */,
-                        PRIORITY_DEFAULT);
-
-                mVideoDecoder = MediaCodec::CreateByType(
-                        mVideoDecoderLooper, mime.c_str(), false /* encoder */);
-
-                CHECK(mVideoDecoder != NULL);
-
-                err = mVideoDecoder->configure(
-                        videoFormat,
-                        mSurfaceTex == NULL
-                            ? NULL : new Surface(mSurfaceTex),
-                        NULL /* crypto */,
-                        0 /* flags */);
-
-                CHECK_EQ(err, (status_t)OK);
-
-                err = mVideoDecoder->start();
-                CHECK_EQ(err, (status_t)OK);
-
-                err = mVideoDecoder->getInputBuffers(
-                        &mVideoDecoderInputBuffers);
-                CHECK_EQ(err, (status_t)OK);
-
-                scheduleVideoDecoderNotification();
-            }
-        }
-
-        status_t finalResult;
-        size_t n = 0;
-        while (videoSource->hasBufferAvailable(&finalResult)) {
-            sp<ABuffer> accessUnit;
-            status_t err = videoSource->dequeueAccessUnit(&accessUnit);
-            if (err == OK) {
-                mVideoAccessUnits.push_back(accessUnit);
-                ++n;
-            }
-        }
-
-        if (n > 0) {
-            ALOGV("dequeued %d video access units.", n);
-            queueVideoDecoderInputBuffers();
-        }
-    }
-}
-
-void DirectRenderer::schedulePacketLost() {
-    sp<AMessage> msg;
-
-    if (kPacketLateDelayUs > 0ll) {
-        msg = new AMessage(kWhatPacketLate, id());
-        msg->setInt32("generation", mPacketLostGeneration);
-        msg->post(kPacketLateDelayUs);
-    }
-
-    msg = new AMessage(kWhatPacketLost, id());
-    msg->setInt32("generation", mPacketLostGeneration);
-    msg->post(kPacketLostDelayUs);
-}
-
-void DirectRenderer::cancelPacketLost() {
-    ++mPacketLostGeneration;
+    mVideoAccessUnits.push_back(accessUnit);
+    queueVideoDecoderInputBuffers();
 }
 
 void DirectRenderer::queueVideoDecoderInputBuffers() {
@@ -406,8 +208,7 @@
                 &flags);
 
         if (err == OK) {
-            err = mVideoDecoder->renderOutputBufferAndRelease(index);
-            CHECK_EQ(err, (status_t)OK);
+            queueOutputBuffer(index, timeUs);
         } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) {
             // We don't care.
         } else if (err == INFO_FORMAT_CHANGED) {
@@ -422,6 +223,62 @@
     scheduleVideoDecoderNotification();
 }
 
+void DirectRenderer::queueOutputBuffer(size_t index, int64_t timeUs) {
+#if 0
+    OutputInfo info;
+    info.mIndex = index;
+    info.mTimeUs = timeUs;
+    mOutputBuffers.push_back(info);
+
+    scheduleRenderIfNecessary();
+#else
+    status_t err = mVideoDecoder->renderOutputBufferAndRelease(index);
+    CHECK_EQ(err, (status_t)OK);
+#endif
+}
+
+void DirectRenderer::scheduleRenderIfNecessary() {
+    if (mRenderPending || mOutputBuffers.empty()) {
+        return;
+    }
+
+    mRenderPending = true;
+
+    int64_t timeUs = (*mOutputBuffers.begin()).mTimeUs;
+    int64_t nowUs = ALooper::GetNowUs();
+
+    if (mFirstRenderTimeUs < 0ll) {
+        mFirstRenderTimeUs = timeUs;
+        mFirstRenderRealUs = nowUs;
+    }
+
+    int64_t whenUs = timeUs - mFirstRenderTimeUs + mFirstRenderRealUs;
+    int64_t delayUs = whenUs - nowUs;
+
+    (new AMessage(kWhatRender, id()))->post(delayUs);
+}
+
+void DirectRenderer::onRender() {
+    mRenderPending = false;
+
+    int64_t nowUs = ALooper::GetNowUs();
+
+    while (!mOutputBuffers.empty()) {
+        const OutputInfo &info = *mOutputBuffers.begin();
+
+        if (info.mTimeUs > nowUs) {
+            break;
+        }
+
+        status_t err = mVideoDecoder->renderOutputBufferAndRelease(info.mIndex);
+        CHECK_EQ(err, (status_t)OK);
+
+        mOutputBuffers.erase(mOutputBuffers.begin());
+    }
+
+    scheduleRenderIfNecessary();
+}
+
 void DirectRenderer::scheduleVideoDecoderNotification() {
     if (mVideoDecoderNotificationPending) {
         return;
diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.h b/media/libstagefright/wifi-display/sink/DirectRenderer.h
index 2babcb8..7219080 100644
--- a/media/libstagefright/wifi-display/sink/DirectRenderer.h
+++ b/media/libstagefright/wifi-display/sink/DirectRenderer.h
@@ -23,7 +23,6 @@
 namespace android {
 
 struct ABuffer;
-struct ATSParser;
 struct IGraphicBufferProducer;
 struct MediaCodec;
 
@@ -32,13 +31,10 @@
 // delay. Primarily meant to finetune packet loss discovery and minimize
 // latency.
 struct DirectRenderer : public AHandler {
-    DirectRenderer(
-            const sp<AMessage> &notifyLost,
-            const sp<IGraphicBufferProducer> &bufferProducer);
+    DirectRenderer(const sp<IGraphicBufferProducer> &bufferProducer);
 
-    enum {
-        kWhatQueueBuffer = 'queB',
-    };
+    void setFormat(size_t trackIndex, const sp<AMessage> &format);
+    void queueAccessUnit(size_t trackIndex, const sp<ABuffer> &accessUnit);
 
 protected:
     virtual void onMessageReceived(const sp<AMessage> &msg);
@@ -46,22 +42,17 @@
 
 private:
     enum {
-        kWhatPacketLate,
-        kWhatPacketLost,
         kWhatVideoDecoderNotify,
+        kWhatRender,
     };
 
-    static const int64_t kPacketLateDelayUs;
-    static const int64_t kPacketLostDelayUs;
+    struct OutputInfo {
+        size_t mIndex;
+        int64_t mTimeUs;
+    };
 
-    sp<AMessage> mNotifyLost;
     sp<IGraphicBufferProducer> mSurfaceTex;
 
-    // Ordered by extended seq number.
-    List<sp<ABuffer> > mPackets;
-
-    sp<ATSParser> mTSParser;
-
     sp<ALooper> mVideoDecoderLooper;
     sp<MediaCodec> mVideoDecoder;
     Vector<sp<ABuffer> > mVideoDecoderInputBuffers;
@@ -70,21 +61,19 @@
 
     List<sp<ABuffer> > mVideoAccessUnits;
 
-    int32_t mAwaitingExtSeqNo;
-    bool mRequestedRetransmission;
-    int32_t mPacketLostGeneration;
+    List<OutputInfo> mOutputBuffers;
+    bool mRenderPending;
+    int64_t mFirstRenderTimeUs;
+    int64_t mFirstRenderRealUs;
 
-    void onQueueBuffer(const sp<ABuffer> &buffer);
     void onVideoDecoderNotify();
-
-    void dequeueMore();
-    void dequeueAccessUnits();
-
-    void schedulePacketLost();
-    void cancelPacketLost();
+    void onRender();
 
     void queueVideoDecoderInputBuffers();
     void scheduleVideoDecoderNotification();
+    void scheduleRenderIfNecessary();
+
+    void queueOutputBuffer(size_t index, int64_t timeUs);
 
     DISALLOW_EVIL_CONSTRUCTORS(DirectRenderer);
 };
diff --git a/media/libstagefright/wifi-display/sink/LinearRegression.cpp b/media/libstagefright/wifi-display/sink/LinearRegression.cpp
deleted file mode 100644
index 8cfce37..0000000
--- a/media/libstagefright/wifi-display/sink/LinearRegression.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "LinearRegression"
-#include <utils/Log.h>
-
-#include "LinearRegression.h"
-
-#include <math.h>
-#include <string.h>
-
-namespace android {
-
-LinearRegression::LinearRegression(size_t historySize)
-    : mHistorySize(historySize),
-      mCount(0),
-      mHistory(new Point[mHistorySize]),
-      mSumX(0.0),
-      mSumY(0.0) {
-}
-
-LinearRegression::~LinearRegression() {
-    delete[] mHistory;
-    mHistory = NULL;
-}
-
-void LinearRegression::addPoint(float x, float y) {
-    if (mCount == mHistorySize) {
-        const Point &oldest = mHistory[0];
-
-        mSumX -= oldest.mX;
-        mSumY -= oldest.mY;
-
-        memmove(&mHistory[0], &mHistory[1], (mHistorySize - 1) * sizeof(Point));
-        --mCount;
-    }
-
-    Point *newest = &mHistory[mCount++];
-    newest->mX = x;
-    newest->mY = y;
-
-    mSumX += x;
-    mSumY += y;
-}
-
-bool LinearRegression::approxLine(float *n1, float *n2, float *b) const {
-    static const float kEpsilon = 1.0E-4;
-
-    if (mCount < 2) {
-        return false;
-    }
-
-    float sumX2 = 0.0f;
-    float sumY2 = 0.0f;
-    float sumXY = 0.0f;
-
-    float meanX = mSumX / (float)mCount;
-    float meanY = mSumY / (float)mCount;
-
-    for (size_t i = 0; i < mCount; ++i) {
-        const Point &p = mHistory[i];
-
-        float x = p.mX - meanX;
-        float y = p.mY - meanY;
-
-        sumX2 += x * x;
-        sumY2 += y * y;
-        sumXY += x * y;
-    }
-
-    float T = sumX2 + sumY2;
-    float D = sumX2 * sumY2 - sumXY * sumXY;
-    float root = sqrt(T * T * 0.25 - D);
-
-    float L1 = T * 0.5 - root;
-
-    if (fabs(sumXY) > kEpsilon) {
-        *n1 = 1.0;
-        *n2 = (2.0 * L1 - sumX2) / sumXY;
-
-        float mag = sqrt((*n1) * (*n1) + (*n2) * (*n2));
-
-        *n1 /= mag;
-        *n2 /= mag;
-    } else {
-        *n1 = 0.0;
-        *n2 = 1.0;
-    }
-
-    *b = (*n1) * meanX + (*n2) * meanY;
-
-    return true;
-}
-
-}  // namespace android
-
diff --git a/media/libstagefright/wifi-display/sink/LinearRegression.h b/media/libstagefright/wifi-display/sink/LinearRegression.h
deleted file mode 100644
index ca6f5a1..0000000
--- a/media/libstagefright/wifi-display/sink/LinearRegression.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef LINEAR_REGRESSION_H_
-
-#define LINEAR_REGRESSION_H_
-
-#include <sys/types.h>
-#include <media/stagefright/foundation/ABase.h>
-
-namespace android {
-
-// Helper class to fit a line to a set of points minimizing the sum of
-// squared (orthogonal) distances from line to individual points.
-struct LinearRegression {
-    LinearRegression(size_t historySize);
-    ~LinearRegression();
-
-    void addPoint(float x, float y);
-
-    bool approxLine(float *n1, float *n2, float *b) const;
-
-private:
-    struct Point {
-        float mX, mY;
-    };
-
-    size_t mHistorySize;
-    size_t mCount;
-    Point *mHistory;
-
-    float mSumX, mSumY;
-
-    DISALLOW_EVIL_CONSTRUCTORS(LinearRegression);
-};
-
-}  // namespace android
-
-#endif  // LINEAR_REGRESSION_H_
diff --git a/media/libstagefright/wifi-display/sink/RTPSink.cpp b/media/libstagefright/wifi-display/sink/RTPSink.cpp
deleted file mode 100644
index 3c90a1e..0000000
--- a/media/libstagefright/wifi-display/sink/RTPSink.cpp
+++ /dev/null
@@ -1,870 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "RTPSink"
-#include <utils/Log.h>
-
-#include "RTPSink.h"
-
-#include "ANetworkSession.h"
-
-#if USE_TUNNEL_RENDERER
-#include "TunnelRenderer.h"
-#define RENDERER_CLASS TunnelRenderer
-#else
-#include "DirectRenderer.h"
-#define RENDERER_CLASS DirectRenderer
-#endif
-
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/hexdump.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/Utils.h>
-
-namespace android {
-
-struct RTPSink::Source : public RefBase {
-    Source(uint16_t seq, const sp<ABuffer> &buffer,
-           const sp<AMessage> queueBufferMsg);
-
-    bool updateSeq(uint16_t seq, const sp<ABuffer> &buffer);
-
-    void addReportBlock(uint32_t ssrc, const sp<ABuffer> &buf);
-
-protected:
-    virtual ~Source();
-
-private:
-    static const uint32_t kMinSequential = 2;
-    static const uint32_t kMaxDropout = 3000;
-    static const uint32_t kMaxMisorder = 100;
-    static const uint32_t kRTPSeqMod = 1u << 16;
-
-    sp<AMessage> mQueueBufferMsg;
-
-    uint16_t mMaxSeq;
-    uint32_t mCycles;
-    uint32_t mBaseSeq;
-    uint32_t mBadSeq;
-    uint32_t mProbation;
-    uint32_t mReceived;
-    uint32_t mExpectedPrior;
-    uint32_t mReceivedPrior;
-
-    void initSeq(uint16_t seq);
-    void queuePacket(const sp<ABuffer> &buffer);
-
-    DISALLOW_EVIL_CONSTRUCTORS(Source);
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
-RTPSink::Source::Source(
-        uint16_t seq, const sp<ABuffer> &buffer,
-        const sp<AMessage> queueBufferMsg)
-    : mQueueBufferMsg(queueBufferMsg),
-      mProbation(kMinSequential) {
-    initSeq(seq);
-    mMaxSeq = seq - 1;
-
-    buffer->setInt32Data(mCycles | seq);
-    queuePacket(buffer);
-}
-
-RTPSink::Source::~Source() {
-}
-
-void RTPSink::Source::initSeq(uint16_t seq) {
-    mMaxSeq = seq;
-    mCycles = 0;
-    mBaseSeq = seq;
-    mBadSeq = kRTPSeqMod + 1;
-    mReceived = 0;
-    mExpectedPrior = 0;
-    mReceivedPrior = 0;
-}
-
-bool RTPSink::Source::updateSeq(uint16_t seq, const sp<ABuffer> &buffer) {
-    uint16_t udelta = seq - mMaxSeq;
-
-    if (mProbation) {
-        // Startup phase
-
-        if (seq == mMaxSeq + 1) {
-            buffer->setInt32Data(mCycles | seq);
-            queuePacket(buffer);
-
-            --mProbation;
-            mMaxSeq = seq;
-            if (mProbation == 0) {
-                initSeq(seq);
-                ++mReceived;
-
-                return true;
-            }
-        } else {
-            // Packet out of sequence, restart startup phase
-
-            mProbation = kMinSequential - 1;
-            mMaxSeq = seq;
-
-#if 0
-            mPackets.clear();
-            mTotalBytesQueued = 0;
-            ALOGI("XXX cleared packets");
-#endif
-
-            buffer->setInt32Data(mCycles | seq);
-            queuePacket(buffer);
-        }
-
-        return false;
-    }
-
-    if (udelta < kMaxDropout) {
-        // In order, with permissible gap.
-
-        if (seq < mMaxSeq) {
-            // Sequence number wrapped - count another 64K cycle
-            mCycles += kRTPSeqMod;
-        }
-
-        mMaxSeq = seq;
-    } else if (udelta <= kRTPSeqMod - kMaxMisorder) {
-        // The sequence number made a very large jump
-
-        if (seq == mBadSeq) {
-            // Two sequential packets -- assume that the other side
-            // restarted without telling us so just re-sync
-            // (i.e. pretend this was the first packet)
-
-            initSeq(seq);
-        } else {
-            mBadSeq = (seq + 1) & (kRTPSeqMod - 1);
-
-            return false;
-        }
-    } else {
-        // Duplicate or reordered packet.
-    }
-
-    ++mReceived;
-
-    buffer->setInt32Data(mCycles | seq);
-    queuePacket(buffer);
-
-    return true;
-}
-
-void RTPSink::Source::queuePacket(const sp<ABuffer> &buffer) {
-    sp<AMessage> msg = mQueueBufferMsg->dup();
-    msg->setBuffer("buffer", buffer);
-    msg->post();
-}
-
-void RTPSink::Source::addReportBlock(
-        uint32_t ssrc, const sp<ABuffer> &buf) {
-    uint32_t extMaxSeq = mMaxSeq | mCycles;
-    uint32_t expected = extMaxSeq - mBaseSeq + 1;
-
-    int64_t lost = (int64_t)expected - (int64_t)mReceived;
-    if (lost > 0x7fffff) {
-        lost = 0x7fffff;
-    } else if (lost < -0x800000) {
-        lost = -0x800000;
-    }
-
-    uint32_t expectedInterval = expected - mExpectedPrior;
-    mExpectedPrior = expected;
-
-    uint32_t receivedInterval = mReceived - mReceivedPrior;
-    mReceivedPrior = mReceived;
-
-    int64_t lostInterval = expectedInterval - receivedInterval;
-
-    uint8_t fractionLost;
-    if (expectedInterval == 0 || lostInterval <=0) {
-        fractionLost = 0;
-    } else {
-        fractionLost = (lostInterval << 8) / expectedInterval;
-    }
-
-    uint8_t *ptr = buf->data() + buf->size();
-
-    ptr[0] = ssrc >> 24;
-    ptr[1] = (ssrc >> 16) & 0xff;
-    ptr[2] = (ssrc >> 8) & 0xff;
-    ptr[3] = ssrc & 0xff;
-
-    ptr[4] = fractionLost;
-
-    ptr[5] = (lost >> 16) & 0xff;
-    ptr[6] = (lost >> 8) & 0xff;
-    ptr[7] = lost & 0xff;
-
-    ptr[8] = extMaxSeq >> 24;
-    ptr[9] = (extMaxSeq >> 16) & 0xff;
-    ptr[10] = (extMaxSeq >> 8) & 0xff;
-    ptr[11] = extMaxSeq & 0xff;
-
-    // XXX TODO:
-
-    ptr[12] = 0x00;  // interarrival jitter
-    ptr[13] = 0x00;
-    ptr[14] = 0x00;
-    ptr[15] = 0x00;
-
-    ptr[16] = 0x00;  // last SR
-    ptr[17] = 0x00;
-    ptr[18] = 0x00;
-    ptr[19] = 0x00;
-
-    ptr[20] = 0x00;  // delay since last SR
-    ptr[21] = 0x00;
-    ptr[22] = 0x00;
-    ptr[23] = 0x00;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-RTPSink::RTPSink(
-        const sp<ANetworkSession> &netSession,
-        const sp<IGraphicBufferProducer> &bufferProducer,
-        const sp<AMessage> &notify)
-    : mNetSession(netSession),
-      mSurfaceTex(bufferProducer),
-      mNotify(notify),
-      mUsingTCPTransport(false),
-      mUsingTCPInterleaving(false),
-      mRTPPort(0),
-      mRTPSessionID(0),
-      mRTCPSessionID(0),
-      mRTPClientSessionID(0),
-      mRTCPClientSessionID(0),
-      mFirstArrivalTimeUs(-1ll),
-      mNumPacketsReceived(0ll),
-      mRegression(1000),
-      mMaxDelayMs(-1ll) {
-}
-
-RTPSink::~RTPSink() {
-    if (mRTCPClientSessionID != 0) {
-        mNetSession->destroySession(mRTCPClientSessionID);
-    }
-
-    if (mRTPClientSessionID != 0) {
-        mNetSession->destroySession(mRTPClientSessionID);
-    }
-
-    if (mRTCPSessionID != 0) {
-        mNetSession->destroySession(mRTCPSessionID);
-    }
-
-    if (mRTPSessionID != 0) {
-        mNetSession->destroySession(mRTPSessionID);
-    }
-}
-
-status_t RTPSink::init(bool usingTCPTransport, bool usingTCPInterleaving) {
-    mUsingTCPTransport = usingTCPTransport;
-    mUsingTCPInterleaving = usingTCPInterleaving;
-
-    if (usingTCPInterleaving) {
-        return OK;
-    }
-
-    int clientRtp;
-
-    sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
-    sp<AMessage> rtcpNotify = new AMessage(kWhatRTCPNotify, id());
-    for (clientRtp = 15550;; clientRtp += 2) {
-        int32_t rtpSession;
-        status_t err;
-        struct in_addr ifaceAddr;
-        if (usingTCPTransport) {
-            ifaceAddr.s_addr = INADDR_ANY;
-            err = mNetSession->createTCPDatagramSession(
-                        ifaceAddr, clientRtp, rtpNotify, &rtpSession);
-        } else {
-            err = mNetSession->createUDPSession(
-                        clientRtp, rtpNotify, &rtpSession);
-        }
-
-        if (err != OK) {
-            ALOGI("failed to create RTP socket on port %d", clientRtp);
-            continue;
-        }
-
-        int32_t rtcpSession;
-        if (usingTCPTransport) {
-            err = mNetSession->createTCPDatagramSession(
-                    ifaceAddr, clientRtp + 1, rtcpNotify, &rtcpSession);
-        } else {
-            err = mNetSession->createUDPSession(
-                    clientRtp + 1, rtcpNotify, &rtcpSession);
-        }
-
-        if (err == OK) {
-            mRTPPort = clientRtp;
-            mRTPSessionID = rtpSession;
-            mRTCPSessionID = rtcpSession;
-            break;
-        }
-
-        ALOGI("failed to create RTCP socket on port %d", clientRtp + 1);
-        mNetSession->destroySession(rtpSession);
-    }
-
-    if (mRTPPort == 0) {
-        return UNKNOWN_ERROR;
-    }
-
-    return OK;
-}
-
-int32_t RTPSink::getRTPPort() const {
-    return mRTPPort;
-}
-
-void RTPSink::onMessageReceived(const sp<AMessage> &msg) {
-    switch (msg->what()) {
-        case kWhatRTPNotify:
-        case kWhatRTCPNotify:
-        {
-            int32_t reason;
-            CHECK(msg->findInt32("reason", &reason));
-
-            switch (reason) {
-                case ANetworkSession::kWhatError:
-                {
-                    int32_t sessionID;
-                    CHECK(msg->findInt32("sessionID", &sessionID));
-
-                    int32_t err;
-                    CHECK(msg->findInt32("err", &err));
-
-                    AString detail;
-                    CHECK(msg->findString("detail", &detail));
-
-                    ALOGE("An error occurred in session %d (%d, '%s/%s').",
-                          sessionID,
-                          err,
-                          detail.c_str(),
-                          strerror(-err));
-
-                    mNetSession->destroySession(sessionID);
-
-                    if (sessionID == mRTPSessionID) {
-                        mRTPSessionID = 0;
-                    } else if (sessionID == mRTCPSessionID) {
-                        mRTCPSessionID = 0;
-                    }
-                    break;
-                }
-
-                case ANetworkSession::kWhatDatagram:
-                {
-                    int32_t sessionID;
-                    CHECK(msg->findInt32("sessionID", &sessionID));
-
-                    sp<ABuffer> data;
-                    CHECK(msg->findBuffer("data", &data));
-
-                    status_t err;
-                    if (msg->what() == kWhatRTPNotify) {
-                        err = parseRTP(data);
-                    } else {
-                        err = parseRTCP(data);
-                    }
-                    break;
-                }
-
-                case ANetworkSession::kWhatClientConnected:
-                {
-                    int32_t sessionID;
-                    CHECK(msg->findInt32("sessionID", &sessionID));
-                    ALOGI("TCP session %d now connected", sessionID);
-
-                    int32_t serverPort;
-                    CHECK(msg->findInt32("server-port", &serverPort));
-
-                    if (serverPort == mRTPPort) {
-                        mRTPClientSessionID = sessionID;
-                    } else {
-                        CHECK_EQ(serverPort, mRTPPort + 1);
-                        mRTCPClientSessionID = sessionID;
-                    }
-                    break;
-                }
-
-                default:
-                    TRESPASS();
-            }
-            break;
-        }
-
-        case kWhatSendRR:
-        {
-            onSendRR();
-            break;
-        }
-
-        case kWhatPacketLost:
-        {
-            onPacketLost(msg);
-            break;
-        }
-
-        case kWhatInject:
-        {
-            int32_t isRTP;
-            CHECK(msg->findInt32("isRTP", &isRTP));
-
-            sp<ABuffer> buffer;
-            CHECK(msg->findBuffer("buffer", &buffer));
-
-            status_t err;
-            if (isRTP) {
-                err = parseRTP(buffer);
-            } else {
-                err = parseRTCP(buffer);
-            }
-            break;
-        }
-
-        default:
-            TRESPASS();
-    }
-}
-
-status_t RTPSink::injectPacket(bool isRTP, const sp<ABuffer> &buffer) {
-    sp<AMessage> msg = new AMessage(kWhatInject, id());
-    msg->setInt32("isRTP", isRTP);
-    msg->setBuffer("buffer", buffer);
-    msg->post();
-
-    return OK;
-}
-
-status_t RTPSink::parseRTP(const sp<ABuffer> &buffer) {
-    size_t size = buffer->size();
-    if (size < 12) {
-        // Too short to be a valid RTP header.
-        return ERROR_MALFORMED;
-    }
-
-    const uint8_t *data = buffer->data();
-
-    if ((data[0] >> 6) != 2) {
-        // Unsupported version.
-        return ERROR_UNSUPPORTED;
-    }
-
-    if (data[0] & 0x20) {
-        // Padding present.
-
-        size_t paddingLength = data[size - 1];
-
-        if (paddingLength + 12 > size) {
-            // If we removed this much padding we'd end up with something
-            // that's too short to be a valid RTP header.
-            return ERROR_MALFORMED;
-        }
-
-        size -= paddingLength;
-    }
-
-    int numCSRCs = data[0] & 0x0f;
-
-    size_t payloadOffset = 12 + 4 * numCSRCs;
-
-    if (size < payloadOffset) {
-        // Not enough data to fit the basic header and all the CSRC entries.
-        return ERROR_MALFORMED;
-    }
-
-    if (data[0] & 0x10) {
-        // Header eXtension present.
-
-        if (size < payloadOffset + 4) {
-            // Not enough data to fit the basic header, all CSRC entries
-            // and the first 4 bytes of the extension header.
-
-            return ERROR_MALFORMED;
-        }
-
-        const uint8_t *extensionData = &data[payloadOffset];
-
-        size_t extensionLength =
-            4 * (extensionData[2] << 8 | extensionData[3]);
-
-        if (size < payloadOffset + 4 + extensionLength) {
-            return ERROR_MALFORMED;
-        }
-
-        payloadOffset += 4 + extensionLength;
-    }
-
-    uint32_t srcId = U32_AT(&data[8]);
-    uint32_t rtpTime = U32_AT(&data[4]);
-    uint16_t seqNo = U16_AT(&data[2]);
-
-#if 0
-    int64_t arrivalTimeUs;
-    CHECK(buffer->meta()->findInt64("arrivalTimeUs", &arrivalTimeUs));
-
-    if (mFirstArrivalTimeUs < 0ll) {
-        mFirstArrivalTimeUs = arrivalTimeUs;
-    }
-    arrivalTimeUs -= mFirstArrivalTimeUs;
-
-    int64_t arrivalTimeMedia = (arrivalTimeUs * 9ll) / 100ll;
-
-    ALOGV("seqNo: %d, SSRC 0x%08x, diff %lld",
-            seqNo, srcId, rtpTime - arrivalTimeMedia);
-
-    mRegression.addPoint((float)rtpTime, (float)arrivalTimeMedia);
-
-    ++mNumPacketsReceived;
-
-    float n1, n2, b;
-    if (mRegression.approxLine(&n1, &n2, &b)) {
-        ALOGV("Line %lld: %.2f %.2f %.2f, slope %.2f",
-              mNumPacketsReceived, n1, n2, b, -n1 / n2);
-
-        float expectedArrivalTimeMedia = (b - n1 * (float)rtpTime) / n2;
-        float latenessMs = (arrivalTimeMedia - expectedArrivalTimeMedia) / 90.0;
-
-        if (mMaxDelayMs < 0ll || latenessMs > mMaxDelayMs) {
-            mMaxDelayMs = latenessMs;
-            ALOGI("packet was %.2f ms late", latenessMs);
-        }
-    }
-#endif
-
-    sp<AMessage> meta = buffer->meta();
-    meta->setInt32("ssrc", srcId);
-    meta->setInt32("rtp-time", rtpTime);
-    meta->setInt32("PT", data[1] & 0x7f);
-    meta->setInt32("M", data[1] >> 7);
-
-    buffer->setRange(payloadOffset, size - payloadOffset);
-
-    ssize_t index = mSources.indexOfKey(srcId);
-    if (index < 0) {
-        if (mRenderer == NULL) {
-            sp<AMessage> notifyLost = new AMessage(kWhatPacketLost, id());
-            notifyLost->setInt32("ssrc", srcId);
-
-            mRenderer = new RENDERER_CLASS(notifyLost, mSurfaceTex);
-            looper()->registerHandler(mRenderer);
-        }
-
-        sp<AMessage> queueBufferMsg =
-            new AMessage(RENDERER_CLASS::kWhatQueueBuffer, mRenderer->id());
-
-        sp<Source> source = new Source(seqNo, buffer, queueBufferMsg);
-        mSources.add(srcId, source);
-    } else {
-        mSources.valueAt(index)->updateSeq(seqNo, buffer);
-    }
-
-    return OK;
-}
-
-status_t RTPSink::parseRTCP(const sp<ABuffer> &buffer) {
-    const uint8_t *data = buffer->data();
-    size_t size = buffer->size();
-
-    while (size > 0) {
-        if (size < 8) {
-            // Too short to be a valid RTCP header
-            return ERROR_MALFORMED;
-        }
-
-        if ((data[0] >> 6) != 2) {
-            // Unsupported version.
-            return ERROR_UNSUPPORTED;
-        }
-
-        if (data[0] & 0x20) {
-            // Padding present.
-
-            size_t paddingLength = data[size - 1];
-
-            if (paddingLength + 12 > size) {
-                // If we removed this much padding we'd end up with something
-                // that's too short to be a valid RTP header.
-                return ERROR_MALFORMED;
-            }
-
-            size -= paddingLength;
-        }
-
-        size_t headerLength = 4 * (data[2] << 8 | data[3]) + 4;
-
-        if (size < headerLength) {
-            // Only received a partial packet?
-            return ERROR_MALFORMED;
-        }
-
-        switch (data[1]) {
-            case 200:
-            {
-                parseSR(data, headerLength);
-                break;
-            }
-
-            case 201:  // RR
-            case 202:  // SDES
-            case 204:  // APP
-                break;
-
-            case 205:  // TSFB (transport layer specific feedback)
-            case 206:  // PSFB (payload specific feedback)
-                // hexdump(data, headerLength);
-                break;
-
-            case 203:
-            {
-                parseBYE(data, headerLength);
-                break;
-            }
-
-            default:
-            {
-                ALOGW("Unknown RTCP packet type %u of size %d",
-                     (unsigned)data[1], headerLength);
-                break;
-            }
-        }
-
-        data += headerLength;
-        size -= headerLength;
-    }
-
-    return OK;
-}
-
-status_t RTPSink::parseBYE(const uint8_t *data, size_t size) {
-    size_t SC = data[0] & 0x3f;
-
-    if (SC == 0 || size < (4 + SC * 4)) {
-        // Packet too short for the minimal BYE header.
-        return ERROR_MALFORMED;
-    }
-
-    uint32_t id = U32_AT(&data[4]);
-
-    return OK;
-}
-
-status_t RTPSink::parseSR(const uint8_t *data, size_t size) {
-    size_t RC = data[0] & 0x1f;
-
-    if (size < (7 + RC * 6) * 4) {
-        // Packet too short for the minimal SR header.
-        return ERROR_MALFORMED;
-    }
-
-    uint32_t id = U32_AT(&data[4]);
-    uint64_t ntpTime = U64_AT(&data[8]);
-    uint32_t rtpTime = U32_AT(&data[16]);
-
-    ALOGV("SR: ssrc 0x%08x, ntpTime 0x%016llx, rtpTime 0x%08x",
-          id, ntpTime, rtpTime);
-
-    return OK;
-}
-
-status_t RTPSink::connect(
-        const char *host, int32_t remoteRtpPort, int32_t remoteRtcpPort) {
-    ALOGI("connecting RTP/RTCP sockets to %s:{%d,%d}",
-          host, remoteRtpPort, remoteRtcpPort);
-
-    status_t err =
-        mNetSession->connectUDPSession(mRTPSessionID, host, remoteRtpPort);
-
-    if (err != OK) {
-        return err;
-    }
-
-    err = mNetSession->connectUDPSession(mRTCPSessionID, host, remoteRtcpPort);
-
-    if (err != OK) {
-        return err;
-    }
-
-#if 0
-    sp<ABuffer> buf = new ABuffer(1500);
-    memset(buf->data(), 0, buf->size());
-
-    mNetSession->sendRequest(
-            mRTPSessionID, buf->data(), buf->size());
-
-    mNetSession->sendRequest(
-            mRTCPSessionID, buf->data(), buf->size());
-#endif
-
-    if (!mUsingTCPTransport) {
-        scheduleSendRR();
-    }
-
-    return OK;
-}
-
-void RTPSink::scheduleSendRR() {
-    (new AMessage(kWhatSendRR, id()))->post(2000000ll);
-}
-
-void RTPSink::addSDES(const sp<ABuffer> &buffer) {
-    uint8_t *data = buffer->data() + buffer->size();
-    data[0] = 0x80 | 1;
-    data[1] = 202;  // SDES
-    data[4] = 0xde;  // SSRC
-    data[5] = 0xad;
-    data[6] = 0xbe;
-    data[7] = 0xef;
-
-    size_t offset = 8;
-
-    data[offset++] = 1;  // CNAME
-
-    AString cname = "stagefright@somewhere";
-    data[offset++] = cname.size();
-
-    memcpy(&data[offset], cname.c_str(), cname.size());
-    offset += cname.size();
-
-    data[offset++] = 6;  // TOOL
-
-    AString tool = "stagefright/1.0";
-    data[offset++] = tool.size();
-
-    memcpy(&data[offset], tool.c_str(), tool.size());
-    offset += tool.size();
-
-    data[offset++] = 0;
-
-    if ((offset % 4) > 0) {
-        size_t count = 4 - (offset % 4);
-        switch (count) {
-            case 3:
-                data[offset++] = 0;
-            case 2:
-                data[offset++] = 0;
-            case 1:
-                data[offset++] = 0;
-        }
-    }
-
-    size_t numWords = (offset / 4) - 1;
-    data[2] = numWords >> 8;
-    data[3] = numWords & 0xff;
-
-    buffer->setRange(buffer->offset(), buffer->size() + offset);
-}
-
-void RTPSink::onSendRR() {
-    sp<ABuffer> buf = new ABuffer(1500);
-    buf->setRange(0, 0);
-
-    uint8_t *ptr = buf->data();
-    ptr[0] = 0x80 | 0;
-    ptr[1] = 201;  // RR
-    ptr[2] = 0;
-    ptr[3] = 1;
-    ptr[4] = 0xde;  // SSRC
-    ptr[5] = 0xad;
-    ptr[6] = 0xbe;
-    ptr[7] = 0xef;
-
-    buf->setRange(0, 8);
-
-    size_t numReportBlocks = 0;
-    for (size_t i = 0; i < mSources.size(); ++i) {
-        uint32_t ssrc = mSources.keyAt(i);
-        sp<Source> source = mSources.valueAt(i);
-
-        if (numReportBlocks > 31 || buf->size() + 24 > buf->capacity()) {
-            // Cannot fit another report block.
-            break;
-        }
-
-        source->addReportBlock(ssrc, buf);
-        ++numReportBlocks;
-    }
-
-    ptr[0] |= numReportBlocks;  // 5 bit
-
-    size_t sizeInWordsMinus1 = 1 + 6 * numReportBlocks;
-    ptr[2] = sizeInWordsMinus1 >> 8;
-    ptr[3] = sizeInWordsMinus1 & 0xff;
-
-    buf->setRange(0, (sizeInWordsMinus1 + 1) * 4);
-
-    addSDES(buf);
-
-    mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size());
-
-    scheduleSendRR();
-}
-
-void RTPSink::onPacketLost(const sp<AMessage> &msg) {
-    if (mUsingTCPTransport) {
-        ALOGW("huh? lost a packet even though using reliable transport?");
-        return;
-    }
-
-    uint32_t srcId;
-    CHECK(msg->findInt32("ssrc", (int32_t *)&srcId));
-
-    int32_t seqNo;
-    CHECK(msg->findInt32("seqNo", &seqNo));
-
-    int32_t blp = 0;
-
-    sp<ABuffer> buf = new ABuffer(16);
-    buf->setRange(0, 0);
-
-    uint8_t *ptr = buf->data();
-    ptr[0] = 0x80 | 1;  // generic NACK
-    ptr[1] = 205;  // TSFB
-    ptr[2] = 0;
-    ptr[3] = 3;
-    ptr[4] = 0xde;  // sender SSRC
-    ptr[5] = 0xad;
-    ptr[6] = 0xbe;
-    ptr[7] = 0xef;
-    ptr[8] = (srcId >> 24) & 0xff;
-    ptr[9] = (srcId >> 16) & 0xff;
-    ptr[10] = (srcId >> 8) & 0xff;
-    ptr[11] = (srcId & 0xff);
-    ptr[12] = (seqNo >> 8) & 0xff;
-    ptr[13] = (seqNo & 0xff);
-    ptr[14] = (blp >> 8) & 0xff;
-    ptr[15] = (blp & 0xff);
-
-    buf->setRange(0, 16);
-
-    mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size());
-}
-
-}  // namespace android
-
diff --git a/media/libstagefright/wifi-display/sink/RTPSink.h b/media/libstagefright/wifi-display/sink/RTPSink.h
deleted file mode 100644
index 4706c6d..0000000
--- a/media/libstagefright/wifi-display/sink/RTPSink.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef RTP_SINK_H_
-
-#define RTP_SINK_H_
-
-#include <media/stagefright/foundation/AHandler.h>
-
-#include "LinearRegression.h"
-
-#include <gui/Surface.h>
-
-#define USE_TUNNEL_RENDERER     0
-
-namespace android {
-
-struct ABuffer;
-struct ANetworkSession;
-
-#if USE_TUNNEL_RENDERER
-struct TunnelRenderer;
-#else
-struct DirectRenderer;
-#endif
-
-// Creates a pair of sockets for RTP/RTCP traffic, instantiates a renderer
-// for incoming transport stream data and occasionally sends statistics over
-// the RTCP channel.
-struct RTPSink : public AHandler {
-    RTPSink(const sp<ANetworkSession> &netSession,
-            const sp<IGraphicBufferProducer> &bufferProducer,
-            const sp<AMessage> &notify);
-
-    // If TCP interleaving is used, no UDP sockets are created, instead
-    // incoming RTP/RTCP packets (arriving on the RTSP control connection)
-    // are manually injected by WifiDisplaySink.
-    status_t init(bool usingTCPTransport, bool usingTCPInterleaving);
-
-    status_t connect(
-            const char *host, int32_t remoteRtpPort, int32_t remoteRtcpPort);
-
-    int32_t getRTPPort() const;
-
-    status_t injectPacket(bool isRTP, const sp<ABuffer> &buffer);
-
-protected:
-    virtual void onMessageReceived(const sp<AMessage> &msg);
-    virtual ~RTPSink();
-
-private:
-    enum {
-        kWhatRTPNotify,
-        kWhatRTCPNotify,
-        kWhatSendRR,
-        kWhatPacketLost,
-        kWhatInject,
-    };
-
-    struct Source;
-    struct StreamSource;
-
-    sp<ANetworkSession> mNetSession;
-    sp<IGraphicBufferProducer> mSurfaceTex;
-    sp<AMessage> mNotify;
-    KeyedVector<uint32_t, sp<Source> > mSources;
-
-    bool mUsingTCPTransport;
-    bool mUsingTCPInterleaving;
-
-    int32_t mRTPPort;
-
-    int32_t mRTPSessionID;   // in TCP unicast mode these are just server
-    int32_t mRTCPSessionID;  // sockets. No data is transferred through them.
-
-    int32_t mRTPClientSessionID;  // in TCP unicast mode
-    int32_t mRTCPClientSessionID;
-
-    int64_t mFirstArrivalTimeUs;
-    int64_t mNumPacketsReceived;
-    LinearRegression mRegression;
-    int64_t mMaxDelayMs;
-
-#if USE_TUNNEL_RENDERER
-    sp<TunnelRenderer> mRenderer;
-#else
-    sp<DirectRenderer> mRenderer;
-#endif
-
-    status_t parseRTP(const sp<ABuffer> &buffer);
-    status_t parseRTCP(const sp<ABuffer> &buffer);
-    status_t parseBYE(const uint8_t *data, size_t size);
-    status_t parseSR(const uint8_t *data, size_t size);
-
-    void addSDES(const sp<ABuffer> &buffer);
-    void onSendRR();
-    void onPacketLost(const sp<AMessage> &msg);
-    void scheduleSendRR();
-
-    DISALLOW_EVIL_CONSTRUCTORS(RTPSink);
-};
-
-}  // namespace android
-
-#endif  // RTP_SINK_H_
diff --git a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp
index 75f9d73..d9d8a76 100644
--- a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp
+++ b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp
@@ -158,175 +158,17 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 TunnelRenderer::TunnelRenderer(
-        const sp<AMessage> &notifyLost,
         const sp<IGraphicBufferProducer> &bufferProducer)
-    : mNotifyLost(notifyLost),
-      mSurfaceTex(bufferProducer),
-      mTotalBytesQueued(0ll),
-      mLastDequeuedExtSeqNo(-1),
-      mFirstFailedAttemptUs(-1ll),
-      mRequestedRetransmission(false) {
+    : mSurfaceTex(bufferProducer),
+      mStartup(true) {
 }
 
 TunnelRenderer::~TunnelRenderer() {
     destroyPlayer();
 }
 
-void TunnelRenderer::queueBuffer(const sp<ABuffer> &buffer) {
-    Mutex::Autolock autoLock(mLock);
-
-    mTotalBytesQueued += buffer->size();
-
-    if (mPackets.empty()) {
-        mPackets.push_back(buffer);
-        return;
-    }
-
-    int32_t newExtendedSeqNo = buffer->int32Data();
-
-    List<sp<ABuffer> >::iterator firstIt = mPackets.begin();
-    List<sp<ABuffer> >::iterator it = --mPackets.end();
-    for (;;) {
-        int32_t extendedSeqNo = (*it)->int32Data();
-
-        if (extendedSeqNo == newExtendedSeqNo) {
-            // Duplicate packet.
-            return;
-        }
-
-        if (extendedSeqNo < newExtendedSeqNo) {
-            // Insert new packet after the one at "it".
-            mPackets.insert(++it, buffer);
-            return;
-        }
-
-        if (it == firstIt) {
-            // Insert new packet before the first existing one.
-            mPackets.insert(it, buffer);
-            return;
-        }
-
-        --it;
-    }
-}
-
-sp<ABuffer> TunnelRenderer::dequeueBuffer() {
-    Mutex::Autolock autoLock(mLock);
-
-    sp<ABuffer> buffer;
-    int32_t extSeqNo;
-    while (!mPackets.empty()) {
-        buffer = *mPackets.begin();
-        extSeqNo = buffer->int32Data();
-
-        if (mLastDequeuedExtSeqNo < 0 || extSeqNo > mLastDequeuedExtSeqNo) {
-            break;
-        }
-
-        // This is a retransmission of a packet we've already returned.
-
-        mTotalBytesQueued -= buffer->size();
-        buffer.clear();
-        extSeqNo = -1;
-
-        mPackets.erase(mPackets.begin());
-    }
-
-    if (mPackets.empty()) {
-        if (mFirstFailedAttemptUs < 0ll) {
-            mFirstFailedAttemptUs = ALooper::GetNowUs();
-            mRequestedRetransmission = false;
-        } else {
-            ALOGV("no packets available for %.2f secs",
-                    (ALooper::GetNowUs() - mFirstFailedAttemptUs) / 1E6);
-        }
-
-        return NULL;
-    }
-
-    if (mLastDequeuedExtSeqNo < 0 || extSeqNo == mLastDequeuedExtSeqNo + 1) {
-        if (mRequestedRetransmission) {
-            ALOGI("Recovered after requesting retransmission of %d",
-                  extSeqNo);
-        }
-
-        mLastDequeuedExtSeqNo = extSeqNo;
-        mFirstFailedAttemptUs = -1ll;
-        mRequestedRetransmission = false;
-
-        mPackets.erase(mPackets.begin());
-
-        mTotalBytesQueued -= buffer->size();
-
-        return buffer;
-    }
-
-    if (mFirstFailedAttemptUs < 0ll) {
-        mFirstFailedAttemptUs = ALooper::GetNowUs();
-
-        ALOGV("failed to get the correct packet the first time.");
-        return NULL;
-    }
-
-    if (mFirstFailedAttemptUs + 50000ll > ALooper::GetNowUs()) {
-        // We're willing to wait a little while to get the right packet.
-
-#if 1
-        if (!mRequestedRetransmission) {
-            ALOGI("requesting retransmission of extSeqNo %d (seqNo %d)",
-                  mLastDequeuedExtSeqNo + 1,
-                  (mLastDequeuedExtSeqNo + 1) & 0xffff);
-
-            sp<AMessage> notify = mNotifyLost->dup();
-            notify->setInt32("seqNo", (mLastDequeuedExtSeqNo + 1) & 0xffff);
-            notify->post();
-
-            mRequestedRetransmission = true;
-        } else
-#endif
-        {
-            ALOGV("still waiting for the correct packet to arrive.");
-        }
-
-        return NULL;
-    }
-
-    ALOGI("dropping packet. extSeqNo %d didn't arrive in time",
-            mLastDequeuedExtSeqNo + 1);
-
-    // Permanent failure, we never received the packet.
-    mLastDequeuedExtSeqNo = extSeqNo;
-    mFirstFailedAttemptUs = -1ll;
-    mRequestedRetransmission = false;
-
-    mTotalBytesQueued -= buffer->size();
-
-    mPackets.erase(mPackets.begin());
-
-    return buffer;
-}
-
 void TunnelRenderer::onMessageReceived(const sp<AMessage> &msg) {
     switch (msg->what()) {
-        case kWhatQueueBuffer:
-        {
-            sp<ABuffer> buffer;
-            CHECK(msg->findBuffer("buffer", &buffer));
-
-            queueBuffer(buffer);
-
-            if (mStreamSource == NULL) {
-                if (mTotalBytesQueued > 0ll) {
-                    initPlayer();
-                } else {
-                    ALOGI("Have %lld bytes queued...", mTotalBytesQueued);
-                }
-            } else {
-                mStreamSource->doSomeWork();
-            }
-            break;
-        }
-
         default:
             TRESPASS();
     }
@@ -396,5 +238,31 @@
     }
 }
 
+void TunnelRenderer::queueBuffer(const sp<ABuffer> &buffer) {
+    {
+        Mutex::Autolock autoLock(mLock);
+        mBuffers.push_back(buffer);
+    }
+
+    if (mStartup) {
+        initPlayer();
+        mStartup = false;
+    }
+
+    mStreamSource->doSomeWork();
+}
+
+sp<ABuffer> TunnelRenderer::dequeueBuffer() {
+    Mutex::Autolock autoLock(mLock);
+    if (mBuffers.empty()) {
+        return NULL;
+    }
+
+    sp<ABuffer> buf = *mBuffers.begin();
+    mBuffers.erase(mBuffers.begin());
+
+    return buf;
+}
+
 }  // namespace android
 
diff --git a/media/libstagefright/wifi-display/sink/TunnelRenderer.h b/media/libstagefright/wifi-display/sink/TunnelRenderer.h
index 52e6e66..8e96665 100644
--- a/media/libstagefright/wifi-display/sink/TunnelRenderer.h
+++ b/media/libstagefright/wifi-display/sink/TunnelRenderer.h
@@ -34,16 +34,11 @@
 // and sends the resulting transport stream to a mediaplayer instance
 // for playback.
 struct TunnelRenderer : public AHandler {
-    TunnelRenderer(
-            const sp<AMessage> &notifyLost,
-            const sp<IGraphicBufferProducer> &bufferProducer);
+    TunnelRenderer(const sp<IGraphicBufferProducer> &bufferProducer);
 
+    void queueBuffer(const sp<ABuffer> &buffer);
     sp<ABuffer> dequeueBuffer();
 
-    enum {
-        kWhatQueueBuffer,
-    };
-
 protected:
     virtual void onMessageReceived(const sp<AMessage> &msg);
     virtual ~TunnelRenderer();
@@ -54,11 +49,10 @@
 
     mutable Mutex mLock;
 
-    sp<AMessage> mNotifyLost;
     sp<IGraphicBufferProducer> mSurfaceTex;
 
-    List<sp<ABuffer> > mPackets;
-    int64_t mTotalBytesQueued;
+    bool mStartup;
+    List<sp<ABuffer> > mBuffers;
 
     sp<SurfaceComposerClient> mComposerClient;
     sp<SurfaceControl> mSurfaceControl;
@@ -67,15 +61,9 @@
     sp<IMediaPlayer> mPlayer;
     sp<StreamSource> mStreamSource;
 
-    int32_t mLastDequeuedExtSeqNo;
-    int64_t mFirstFailedAttemptUs;
-    bool mRequestedRetransmission;
-
     void initPlayer();
     void destroyPlayer();
 
-    void queueBuffer(const sp<ABuffer> &buffer);
-
     DISALLOW_EVIL_CONSTRUCTORS(TunnelRenderer);
 };
 
diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
index 55581a6..a6f58cd 100644
--- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
+++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
@@ -19,8 +19,11 @@
 #include <utils/Log.h>
 
 #include "WifiDisplaySink.h"
+
+#include "DirectRenderer.h"
+#include "MediaReceiver.h"
 #include "ParsedMessage.h"
-#include "RTPSink.h"
+#include "TunnelRenderer.h"
 
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -42,7 +45,8 @@
       mUsingTCPTransport(false),
       mUsingTCPInterleaving(false),
       mSessionID(0),
-      mNextCSeq(1) {
+      mNextCSeq(1),
+      mIDRFrameRequestPending(false) {
 #if 1
     // We support any and all resolutions, but prefer 720p30
     mSinkSupportedVideoFormats.setNativeResolution(
@@ -50,11 +54,11 @@
 
     mSinkSupportedVideoFormats.enableAll();
 #else
-    // We only support 800 x 600 p60.
+    // We only support 640 x 360 p30.
     mSinkSupportedVideoFormats.disableAll();
 
     mSinkSupportedVideoFormats.setNativeResolution(
-            VideoFormats::RESOLUTION_VESA, 1);  // 800 x 600 p60
+            VideoFormats::RESOLUTION_HH, 6);  // 640 x 360 p30
 #endif
 }
 
@@ -212,20 +216,6 @@
                     break;
                 }
 
-                case ANetworkSession::kWhatBinaryData:
-                {
-                    CHECK(mUsingTCPInterleaving);
-
-                    int32_t channel;
-                    CHECK(msg->findInt32("channel", &channel));
-
-                    sp<ABuffer> data;
-                    CHECK(msg->findBuffer("data", &data));
-
-                    mRTPSink->injectPacket(channel == 0 /* isRTP */, data);
-                    break;
-                }
-
                 default:
                     TRESPASS();
             }
@@ -238,15 +228,80 @@
             break;
         }
 
-        case kWhatRequestIDRFrame:
+        case kWhatMediaReceiverNotify:
         {
-            ALOGI("requesting IDR frame");
-            sendIDRFrameRequest(mSessionID);
+            onMediaReceiverNotify(msg);
             break;
         }
 
-        case kWhatRTPSinkNotify:
+        default:
+            TRESPASS();
+    }
+}
+
+void WifiDisplaySink::onMediaReceiverNotify(const sp<AMessage> &msg) {
+    int32_t what;
+    CHECK(msg->findInt32("what", &what));
+
+    switch (what) {
+        case MediaReceiver::kWhatInitDone:
         {
+            status_t err;
+            CHECK(msg->findInt32("err", &err));
+
+            ALOGI("MediaReceiver initialization completed w/ err %d", err);
+            break;
+        }
+
+        case MediaReceiver::kWhatError:
+        {
+            status_t err;
+            CHECK(msg->findInt32("err", &err));
+
+            ALOGE("MediaReceiver signaled error %d", err);
+            break;
+        }
+
+        case MediaReceiver::kWhatAccessUnit:
+        {
+            if (mRenderer == NULL) {
+#if USE_TUNNEL_RENDERER
+                mRenderer = new TunnelRenderer(mSurfaceTex);
+#else
+                mRenderer = new DirectRenderer(mSurfaceTex);
+#endif
+
+                looper()->registerHandler(mRenderer);
+            }
+
+            sp<ABuffer> accessUnit;
+            CHECK(msg->findBuffer("accessUnit", &accessUnit));
+
+#if USE_TUNNEL_RENDERER
+            mRenderer->queueBuffer(accessUnit);
+#else
+            size_t trackIndex;
+            CHECK(msg->findSize("trackIndex", &trackIndex));
+
+            sp<AMessage> format;
+            if (msg->findMessage("format", &format)) {
+                mRenderer->setFormat(trackIndex, format);
+            }
+
+            mRenderer->queueAccessUnit(trackIndex, accessUnit);
+#endif
+            break;
+        }
+
+        case MediaReceiver::kWhatPacketLost:
+        {
+#if 0
+            if (!mIDRFrameRequestPending) {
+                ALOGI("requesting IDR frame");
+
+                sendIDRFrameRequest(mSessionID);
+            }
+#endif
             break;
         }
 
@@ -381,7 +436,8 @@
         ALOGW("Server picked an odd numbered RTP port.");
     }
 
-    return mRTPSink->connect(sourceHost.c_str(), rtpPort, rtcpPort);
+    return mMediaReceiver->connectTrack(
+            0 /* trackIndex */, sourceHost.c_str(), rtpPort, rtcpPort);
 }
 
 status_t WifiDisplaySink::onReceivePlayResponse(
@@ -402,6 +458,9 @@
 
 status_t WifiDisplaySink::onReceiveIDRFrameRequestResponse(
         int32_t sessionID, const sp<ParsedMessage> &msg) {
+    CHECK(mIDRFrameRequestPending);
+    mIDRFrameRequestPending = false;
+
     return OK;
 }
 
@@ -539,16 +598,48 @@
 }
 
 status_t WifiDisplaySink::sendSetup(int32_t sessionID, const char *uri) {
-    sp<AMessage> notify = new AMessage(kWhatRTPSinkNotify, id());
+    sp<AMessage> notify = new AMessage(kWhatMediaReceiverNotify, id());
 
-    mRTPSink = new RTPSink(mNetSession, mSurfaceTex, notify);
-    looper()->registerHandler(mRTPSink);
+    mMediaReceiverLooper = new ALooper;
+    mMediaReceiverLooper->setName("media_receiver");
 
-    status_t err = mRTPSink->init(mUsingTCPTransport, mUsingTCPInterleaving);
+    mMediaReceiverLooper->start(
+            false /* runOnCallingThread */,
+            false /* canCallJava */,
+            PRIORITY_AUDIO);
+
+    mMediaReceiver = new MediaReceiver(mNetSession, notify);
+    mMediaReceiverLooper->registerHandler(mMediaReceiver);
+
+    RTPReceiver::TransportMode mode = RTPReceiver::TRANSPORT_UDP;
+    if (mUsingTCPTransport) {
+        if (mUsingTCPInterleaving) {
+            mode = RTPReceiver::TRANSPORT_TCP_INTERLEAVED;
+        } else {
+            mode = RTPReceiver::TRANSPORT_TCP;
+        }
+    }
+
+    int32_t localRTPPort;
+    status_t err = mMediaReceiver->addTrack(mode, &localRTPPort);
+
+    if (err == OK) {
+        err = mMediaReceiver->initAsync(
+#if USE_TUNNEL_RENDERER
+                MediaReceiver::MODE_TRANSPORT_STREAM_RAW
+#else
+                MediaReceiver::MODE_TRANSPORT_STREAM
+#endif
+                );
+    }
 
     if (err != OK) {
-        looper()->unregisterHandler(mRTPSink->id());
-        mRTPSink.clear();
+        mMediaReceiverLooper->unregisterHandler(mMediaReceiver->id());
+        mMediaReceiver.clear();
+
+        mMediaReceiverLooper->stop();
+        mMediaReceiverLooper.clear();
+
         return err;
     }
 
@@ -556,17 +647,19 @@
 
     AppendCommonResponse(&request, mNextCSeq);
 
-    if (mUsingTCPInterleaving) {
+    if (mode == RTPReceiver::TRANSPORT_TCP_INTERLEAVED) {
         request.append("Transport: RTP/AVP/TCP;interleaved=0-1\r\n");
-    } else {
-        int32_t rtpPort = mRTPSink->getRTPPort();
-
+    } else if (mode == RTPReceiver::TRANSPORT_TCP) {
         request.append(
                 StringPrintf(
-                    "Transport: RTP/AVP/%s;unicast;client_port=%d-%d\r\n",
-                    mUsingTCPTransport ? "TCP" : "UDP",
-                    rtpPort,
-                    rtpPort + 1));
+                    "Transport: RTP/AVP/TCP;unicast;client_port=%d\r\n",
+                    localRTPPort));
+    } else {
+        request.append(
+                StringPrintf(
+                    "Transport: RTP/AVP/UDP;unicast;client_port=%d-%d\r\n",
+                    localRTPPort,
+                    localRTPPort + 1));
     }
 
     request.append("\r\n");
@@ -611,6 +704,8 @@
 }
 
 status_t WifiDisplaySink::sendIDRFrameRequest(int32_t sessionID) {
+    CHECK(!mIDRFrameRequestPending);
+
     AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n";
 
     AppendCommonResponse(&request, mNextCSeq);
@@ -636,6 +731,8 @@
 
     ++mNextCSeq;
 
+    mIDRFrameRequestPending = true;
+
     return OK;
 }
 
diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h
index 8b5ff6b..01af58b 100644
--- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h
+++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h
@@ -28,8 +28,12 @@
 namespace android {
 
 struct AMessage;
+struct DirectRenderer;
+struct MediaReceiver;
 struct ParsedMessage;
-struct RTPSink;
+struct TunnelRenderer;
+
+#define USE_TUNNEL_RENDERER     0
 
 // Represents the RTSP client acting as a wifi display sink.
 // Connects to a wifi display source and renders the incoming
@@ -68,8 +72,7 @@
         kWhatStart,
         kWhatRTSPNotify,
         kWhatStop,
-        kWhatRequestIDRFrame,
-        kWhatRTPSinkNotify,
+        kWhatMediaReceiverNotify,
     };
 
     struct ResponseID {
@@ -100,10 +103,20 @@
 
     KeyedVector<ResponseID, HandleRTSPResponseFunc> mResponseHandlers;
 
-    sp<RTPSink> mRTPSink;
+    sp<ALooper> mMediaReceiverLooper;
+    sp<MediaReceiver> mMediaReceiver;
+
+#if USE_TUNNEL_RENDERER
+    sp<TunnelRenderer> mRenderer;
+#else
+    sp<DirectRenderer> mRenderer;
+#endif
+
     AString mPlaybackSessionID;
     int32_t mPlaybackSessionTimeoutSecs;
 
+    bool mIDRFrameRequestPending;
+
     status_t sendM2(int32_t sessionID);
     status_t sendSetup(int32_t sessionID, const char *uri);
     status_t sendPlay(int32_t sessionID, const char *uri);
@@ -143,6 +156,8 @@
             int32_t cseq,
             const sp<ParsedMessage> &data);
 
+    void onMediaReceiverNotify(const sp<AMessage> &msg);
+
     void sendErrorResponse(
             int32_t sessionID,
             const char *errorDetail,
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
index ede4e60..ea195b3 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
@@ -23,8 +23,6 @@
 #include "Converter.h"
 #include "MediaPuller.h"
 #include "RepeaterSource.h"
-#include "Sender.h"
-#include "TSPacketizer.h"
 #include "include/avc_utils.h"
 #include "WifiDisplaySource.h"
 
@@ -65,9 +63,9 @@
     bool isAudio() const;
 
     const sp<Converter> &converter() const;
-    ssize_t packetizerTrackIndex() const;
 
-    void setPacketizerTrackIndex(size_t index);
+    ssize_t mediaSenderTrackIndex() const;
+    void setMediaSenderTrackIndex(size_t index);
 
     status_t start();
     void stopAsync();
@@ -107,7 +105,7 @@
     sp<MediaPuller> mMediaPuller;
     sp<Converter> mConverter;
     bool mStarted;
-    ssize_t mPacketizerTrackIndex;
+    ssize_t mMediaSenderTrackIndex;
     bool mIsAudio;
     List<sp<ABuffer> > mQueuedAccessUnits;
     sp<RepeaterSource> mRepeaterSource;
@@ -131,7 +129,6 @@
       mMediaPuller(mediaPuller),
       mConverter(converter),
       mStarted(false),
-      mPacketizerTrackIndex(-1),
       mIsAudio(IsAudioFormat(mConverter->getOutputFormat())),
       mLastOutputBufferQueuedTimeUs(-1ll) {
 }
@@ -161,13 +158,14 @@
     return mConverter;
 }
 
-ssize_t WifiDisplaySource::PlaybackSession::Track::packetizerTrackIndex() const {
-    return mPacketizerTrackIndex;
+ssize_t WifiDisplaySource::PlaybackSession::Track::mediaSenderTrackIndex() const {
+    CHECK_GE(mMediaSenderTrackIndex, 0);
+    return mMediaSenderTrackIndex;
 }
 
-void WifiDisplaySource::PlaybackSession::Track::setPacketizerTrackIndex(size_t index) {
-    CHECK_LT(mPacketizerTrackIndex, 0);
-    mPacketizerTrackIndex = index;
+void WifiDisplaySource::PlaybackSession::Track::setMediaSenderTrackIndex(
+        size_t index) {
+    mMediaSenderTrackIndex = index;
 }
 
 status_t WifiDisplaySource::PlaybackSession::Track::start() {
@@ -331,22 +329,28 @@
       mNotify(notify),
       mInterfaceAddr(interfaceAddr),
       mHDCP(hdcp),
+      mLocalRTPPort(-1),
       mWeAreDead(false),
       mPaused(false),
       mLastLifesignUs(),
       mVideoTrackIndex(-1),
-      mPrevTimeUs(-1ll),
-      mAllTracksHavePacketizerIndex(false) {
+      mPrevTimeUs(-1ll) {
 }
 
 status_t WifiDisplaySource::PlaybackSession::init(
         const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
-        Sender::TransportMode transportMode,
+        RTPSender::TransportMode transportMode,
         bool enableAudio,
         bool usePCMAudio,
         bool enableVideo,
         VideoFormats::ResolutionType videoResolutionType,
         size_t videoResolutionIndex) {
+    sp<AMessage> notify = new AMessage(kWhatMediaSenderNotify, id());
+    mMediaSender = new MediaSender(mNetSession, notify);
+    looper()->registerHandler(mMediaSender);
+
+    mMediaSender->setHDCP(mHDCP);
+
     status_t err = setupPacketizer(
             enableAudio,
             usePCMAudio,
@@ -354,26 +358,22 @@
             videoResolutionType,
             videoResolutionIndex);
 
-    if (err != OK) {
-        return err;
+    if (err == OK) {
+        err = mMediaSender->initAsync(
+                -1 /* trackIndex */,
+                transportMode,
+                clientIP,
+                clientRtp,
+                clientRtcp,
+                &mLocalRTPPort);
     }
 
-    sp<AMessage> notify = new AMessage(kWhatSenderNotify, id());
-    mSender = new Sender(mNetSession, notify);
-
-    mSenderLooper = new ALooper;
-    mSenderLooper->setName("sender_looper");
-
-    mSenderLooper->start(
-            false /* runOnCallingThread */,
-            false /* canCallJava */,
-            PRIORITY_AUDIO);
-
-    mSenderLooper->registerHandler(mSender);
-
-    err = mSender->init(clientIP, clientRtp, clientRtcp, transportMode);
-
     if (err != OK) {
+        mLocalRTPPort = -1;
+
+        looper()->unregisterHandler(mMediaSender->id());
+        mMediaSender.clear();
+
         return err;
     }
 
@@ -386,7 +386,7 @@
 }
 
 int32_t WifiDisplaySource::PlaybackSession::getRTPPort() const {
-    return mSender->getRTPPort();
+    return mLocalRTPPort;
 }
 
 int64_t WifiDisplaySource::PlaybackSession::getLastLifesignUs() const {
@@ -406,18 +406,10 @@
 }
 
 status_t WifiDisplaySource::PlaybackSession::finishPlay() {
-    // XXX Give the dongle a second to bind its sockets.
-    (new AMessage(kWhatFinishPlay, id()))->post(1000000ll);
     return OK;
 }
 
-status_t WifiDisplaySource::PlaybackSession::onFinishPlay() {
-    return mSender->finishInit();
-}
-
-status_t WifiDisplaySource::PlaybackSession::onFinishPlay2() {
-    mSender->scheduleSendSR();
-
+status_t WifiDisplaySource::PlaybackSession::onMediaSenderInitialized() {
     for (size_t i = 0; i < mTracks.size(); ++i) {
         CHECK_EQ((status_t)OK, mTracks.editValueAt(i)->start());
     }
@@ -464,44 +456,18 @@
             CHECK(msg->findSize("trackIndex", &trackIndex));
 
             if (what == Converter::kWhatAccessUnit) {
-                const sp<Track> &track = mTracks.valueFor(trackIndex);
-
-                ssize_t packetizerTrackIndex = track->packetizerTrackIndex();
-
-                if (packetizerTrackIndex < 0) {
-                    sp<AMessage> trackFormat = track->getFormat()->dup();
-                    if (mHDCP != NULL && !track->isAudio()) {
-                        // HDCP2.0 _and_ HDCP 2.1 specs say to set the version
-                        // inside the HDCP descriptor to 0x20!!!
-                        trackFormat->setInt32("hdcp-version", 0x20);
-                    }
-                    packetizerTrackIndex = mPacketizer->addTrack(trackFormat);
-
-                    CHECK_GE(packetizerTrackIndex, 0);
-
-                    track->setPacketizerTrackIndex(packetizerTrackIndex);
-
-                    if (allTracksHavePacketizerIndex()) {
-                        status_t err = packetizeQueuedAccessUnits();
-
-                        if (err != OK) {
-                            notifySessionDead();
-                            break;
-                        }
-                    }
-                }
-
                 sp<ABuffer> accessUnit;
                 CHECK(msg->findBuffer("accessUnit", &accessUnit));
 
-                if (!allTracksHavePacketizerIndex()) {
-                    track->queueAccessUnit(accessUnit);
-                    break;
+                const sp<Track> &track = mTracks.valueFor(trackIndex);
+
+                status_t err = mMediaSender->queueAccessUnit(
+                        track->mediaSenderTrackIndex(),
+                        accessUnit);
+
+                if (err != OK) {
+                    notifySessionDead();
                 }
-
-                track->queueOutputBuffer(accessUnit);
-
-                drainAccessUnits();
                 break;
             } else if (what == Converter::kWhatEOS) {
                 CHECK_EQ(what, Converter::kWhatEOS);
@@ -533,37 +499,25 @@
             break;
         }
 
-        case kWhatSenderNotify:
+        case kWhatMediaSenderNotify:
         {
             int32_t what;
             CHECK(msg->findInt32("what", &what));
 
-            if (what == Sender::kWhatInitDone) {
-                onFinishPlay2();
-            } else if (what == Sender::kWhatSessionDead) {
+            if (what == MediaSender::kWhatInitDone) {
+                status_t err;
+                CHECK(msg->findInt32("err", &err));
+
+                if (err == OK) {
+                    onMediaSenderInitialized();
+                } else {
+                    notifySessionDead();
+                }
+            } else if (what == MediaSender::kWhatError) {
                 notifySessionDead();
-            } else if (what == Sender::kWhatBinaryData) {
-                sp<AMessage> notify = mNotify->dup();
-                notify->setInt32("what", kWhatBinaryData);
-
-                int32_t channel;
-                CHECK(msg->findInt32("channel", &channel));
-                notify->setInt32("channel", channel);
-
-                sp<ABuffer> data;
-                CHECK(msg->findBuffer("data", &data));
-                notify->setBuffer("data", data);
-                notify->post();
             } else {
                 TRESPASS();
             }
-
-            break;
-        }
-
-        case kWhatFinishPlay:
-        {
-            onFinishPlay();
             break;
         }
 
@@ -588,11 +542,8 @@
                     break;
                 }
 
-                mSenderLooper->unregisterHandler(mSender->id());
-                mSender.clear();
-                mSenderLooper.clear();
-
-                mPacketizer.clear();
+                looper()->unregisterHandler(mMediaSender->id());
+                mMediaSender.clear();
 
                 sp<AMessage> notify = mNotify->dup();
                 notify->setInt32("what", kWhatSessionDestroyed);
@@ -601,28 +552,6 @@
             break;
         }
 
-        case kWhatPacketize:
-        {
-            size_t trackIndex;
-            CHECK(msg->findSize("trackIndex", &trackIndex));
-
-            sp<ABuffer> accessUnit;
-            CHECK(msg->findBuffer("accessUnit", &accessUnit));
-
-#if 0
-            if ((ssize_t)trackIndex == mVideoTrackIndex) {
-                int64_t nowUs = ALooper::GetNowUs();
-                static int64_t prevNowUs = 0ll;
-
-                ALOGI("sending AU, dNowUs=%lld us", nowUs - prevNowUs);
-
-                prevNowUs = nowUs;
-            }
-#endif
-
-            break;
-        }
-
         case kWhatPause:
         {
             if (mPaused) {
@@ -664,8 +593,6 @@
         size_t videoResolutionIndex) {
     CHECK(enableAudio || enableVideo);
 
-    mPacketizer = new TSPacketizer;
-
     if (enableVideo) {
         status_t err = addVideoSource(
                 videoResolutionType, videoResolutionIndex);
@@ -763,6 +690,17 @@
         mVideoTrackIndex = trackIndex;
     }
 
+    uint32_t flags = 0;
+    if (converter->needToManuallyPrependSPSPPS()) {
+        flags |= MediaSender::FLAG_MANUALLY_PREPEND_SPS_PPS;
+    }
+
+    ssize_t mediaSenderTrackIndex =
+        mMediaSender->addTrack(converter->getOutputFormat(), flags);
+    CHECK_GE(mediaSenderTrackIndex, 0);
+
+    track->setMediaSenderTrackIndex(mediaSenderTrackIndex);
+
     return OK;
 }
 
@@ -832,168 +770,6 @@
     }
 }
 
-bool WifiDisplaySource::PlaybackSession::allTracksHavePacketizerIndex() {
-    if (mAllTracksHavePacketizerIndex) {
-        return true;
-    }
-
-    for (size_t i = 0; i < mTracks.size(); ++i) {
-        if (mTracks.valueAt(i)->packetizerTrackIndex() < 0) {
-            return false;
-        }
-    }
-
-    mAllTracksHavePacketizerIndex = true;
-
-    return true;
-}
-
-status_t WifiDisplaySource::PlaybackSession::packetizeAccessUnit(
-        size_t trackIndex, sp<ABuffer> accessUnit,
-        sp<ABuffer> *packets) {
-    const sp<Track> &track = mTracks.valueFor(trackIndex);
-
-    uint32_t flags = 0;
-
-    bool isHDCPEncrypted = false;
-    uint64_t inputCTR;
-    uint8_t HDCP_private_data[16];
-
-    bool manuallyPrependSPSPPS =
-        !track->isAudio()
-        && track->converter()->needToManuallyPrependSPSPPS()
-        && IsIDR(accessUnit);
-
-    if (mHDCP != NULL && !track->isAudio()) {
-        isHDCPEncrypted = true;
-
-        if (manuallyPrependSPSPPS) {
-            accessUnit = mPacketizer->prependCSD(
-                    track->packetizerTrackIndex(), accessUnit);
-        }
-
-        status_t err = mHDCP->encrypt(
-                accessUnit->data(), accessUnit->size(),
-                trackIndex  /* streamCTR */,
-                &inputCTR,
-                accessUnit->data());
-
-        if (err != OK) {
-            ALOGE("Failed to HDCP-encrypt media data (err %d)",
-                  err);
-
-            return err;
-        }
-
-        HDCP_private_data[0] = 0x00;
-
-        HDCP_private_data[1] =
-            (((trackIndex >> 30) & 3) << 1) | 1;
-
-        HDCP_private_data[2] = (trackIndex >> 22) & 0xff;
-
-        HDCP_private_data[3] =
-            (((trackIndex >> 15) & 0x7f) << 1) | 1;
-
-        HDCP_private_data[4] = (trackIndex >> 7) & 0xff;
-
-        HDCP_private_data[5] =
-            ((trackIndex & 0x7f) << 1) | 1;
-
-        HDCP_private_data[6] = 0x00;
-
-        HDCP_private_data[7] =
-            (((inputCTR >> 60) & 0x0f) << 1) | 1;
-
-        HDCP_private_data[8] = (inputCTR >> 52) & 0xff;
-
-        HDCP_private_data[9] =
-            (((inputCTR >> 45) & 0x7f) << 1) | 1;
-
-        HDCP_private_data[10] = (inputCTR >> 37) & 0xff;
-
-        HDCP_private_data[11] =
-            (((inputCTR >> 30) & 0x7f) << 1) | 1;
-
-        HDCP_private_data[12] = (inputCTR >> 22) & 0xff;
-
-        HDCP_private_data[13] =
-            (((inputCTR >> 15) & 0x7f) << 1) | 1;
-
-        HDCP_private_data[14] = (inputCTR >> 7) & 0xff;
-
-        HDCP_private_data[15] =
-            ((inputCTR & 0x7f) << 1) | 1;
-
-#if 0
-        ALOGI("HDCP_private_data:");
-        hexdump(HDCP_private_data, sizeof(HDCP_private_data));
-
-        ABitReader br(HDCP_private_data, sizeof(HDCP_private_data));
-        CHECK_EQ(br.getBits(13), 0);
-        CHECK_EQ(br.getBits(2), (trackIndex >> 30) & 3);
-        CHECK_EQ(br.getBits(1), 1u);
-        CHECK_EQ(br.getBits(15), (trackIndex >> 15) & 0x7fff);
-        CHECK_EQ(br.getBits(1), 1u);
-        CHECK_EQ(br.getBits(15), trackIndex & 0x7fff);
-        CHECK_EQ(br.getBits(1), 1u);
-        CHECK_EQ(br.getBits(11), 0);
-        CHECK_EQ(br.getBits(4), (inputCTR >> 60) & 0xf);
-        CHECK_EQ(br.getBits(1), 1u);
-        CHECK_EQ(br.getBits(15), (inputCTR >> 45) & 0x7fff);
-        CHECK_EQ(br.getBits(1), 1u);
-        CHECK_EQ(br.getBits(15), (inputCTR >> 30) & 0x7fff);
-        CHECK_EQ(br.getBits(1), 1u);
-        CHECK_EQ(br.getBits(15), (inputCTR >> 15) & 0x7fff);
-        CHECK_EQ(br.getBits(1), 1u);
-        CHECK_EQ(br.getBits(15), inputCTR & 0x7fff);
-        CHECK_EQ(br.getBits(1), 1u);
-#endif
-
-        flags |= TSPacketizer::IS_ENCRYPTED;
-    } else if (manuallyPrependSPSPPS) {
-        flags |= TSPacketizer::PREPEND_SPS_PPS_TO_IDR_FRAMES;
-    }
-
-    int64_t timeUs = ALooper::GetNowUs();
-    if (mPrevTimeUs < 0ll || mPrevTimeUs + 100000ll <= timeUs) {
-        flags |= TSPacketizer::EMIT_PCR;
-        flags |= TSPacketizer::EMIT_PAT_AND_PMT;
-
-        mPrevTimeUs = timeUs;
-    }
-
-    mPacketizer->packetize(
-            track->packetizerTrackIndex(), accessUnit, packets, flags,
-            !isHDCPEncrypted ? NULL : HDCP_private_data,
-            !isHDCPEncrypted ? 0 : sizeof(HDCP_private_data),
-            track->isAudio() ? 2 : 0 /* numStuffingBytes */);
-
-    return OK;
-}
-
-status_t WifiDisplaySource::PlaybackSession::packetizeQueuedAccessUnits() {
-    for (;;) {
-        bool gotMoreData = false;
-        for (size_t i = 0; i < mTracks.size(); ++i) {
-            size_t trackIndex = mTracks.keyAt(i);
-            const sp<Track> &track = mTracks.valueAt(i);
-
-            sp<ABuffer> accessUnit = track->dequeueAccessUnit();
-            if (accessUnit != NULL) {
-                track->queueOutputBuffer(accessUnit);
-                gotMoreData = true;
-            }
-        }
-
-        if (!gotMoreData) {
-            break;
-        }
-    }
-
-    return OK;
-}
-
 void WifiDisplaySource::PlaybackSession::notifySessionDead() {
     // Inform WifiDisplaySource of our premature death (wish).
     sp<AMessage> notify = mNotify->dup();
@@ -1003,78 +779,5 @@
     mWeAreDead = true;
 }
 
-void WifiDisplaySource::PlaybackSession::drainAccessUnits() {
-    ALOGV("audio/video has %d/%d buffers ready.",
-            mTracks.valueFor(1)->countQueuedOutputBuffers(),
-            mTracks.valueFor(0)->countQueuedOutputBuffers());
-
-    while (drainAccessUnit()) {
-    }
-}
-
-bool WifiDisplaySource::PlaybackSession::drainAccessUnit() {
-    ssize_t minTrackIndex = -1;
-    int64_t minTimeUs = -1ll;
-
-    for (size_t i = 0; i < mTracks.size(); ++i) {
-        const sp<Track> &track = mTracks.valueAt(i);
-
-        int64_t timeUs;
-        if (track->hasOutputBuffer(&timeUs)) {
-            if (minTrackIndex < 0 || timeUs < minTimeUs) {
-                minTrackIndex = mTracks.keyAt(i);
-                minTimeUs = timeUs;
-            }
-        }
-#if SUSPEND_VIDEO_IF_IDLE
-        else if (!track->isSuspended()) {
-            // We still consider this track "live", so it should keep
-            // delivering output data whose time stamps we'll have to
-            // consider for proper interleaving.
-            return false;
-        }
-#else
-        else {
-            // We need access units available on all tracks to be able to
-            // dequeue the earliest one.
-            return false;
-        }
-#endif
-    }
-
-    if (minTrackIndex < 0) {
-        return false;
-    }
-
-    const sp<Track> &track = mTracks.valueFor(minTrackIndex);
-    sp<ABuffer> accessUnit = track->dequeueOutputBuffer();
-
-    sp<ABuffer> packets;
-    status_t err = packetizeAccessUnit(minTrackIndex, accessUnit, &packets);
-
-    if (err != OK) {
-        notifySessionDead();
-        return false;
-    }
-
-    if ((ssize_t)minTrackIndex == mVideoTrackIndex) {
-        packets->meta()->setInt32("isVideo", 1);
-    }
-    mSender->queuePackets(minTimeUs, packets);
-
-#if 0
-    if (minTrackIndex == mVideoTrackIndex) {
-        int64_t nowUs = ALooper::GetNowUs();
-
-        // Latency from "data acquired" to "ready to send if we wanted to".
-        ALOGI("[%s] latencyUs = %lld ms",
-              minTrackIndex == mVideoTrackIndex ? "video" : "audio",
-              (nowUs - minTimeUs) / 1000ll);
-    }
-#endif
-
-    return true;
-}
-
 }  // namespace android
 
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h
index 7365c78..cd6da85 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.h
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.h
@@ -18,7 +18,7 @@
 
 #define PLAYBACK_SESSION_H_
 
-#include "Sender.h"
+#include "MediaSender.h"
 #include "VideoFormats.h"
 #include "WifiDisplaySource.h"
 
@@ -30,7 +30,7 @@
 struct IGraphicBufferProducer;
 struct MediaPuller;
 struct MediaSource;
-struct TSPacketizer;
+struct MediaSender;
 
 // Encapsulates the state of an RTP/RTCP session in the context of wifi
 // display.
@@ -43,7 +43,7 @@
 
     status_t init(
             const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
-            Sender::TransportMode transportMode,
+            RTPSender::TransportMode transportMode,
             bool enableAudio,
             bool usePCMAudio,
             bool enableVideo,
@@ -83,26 +83,25 @@
         kWhatMediaPullerNotify,
         kWhatConverterNotify,
         kWhatTrackNotify,
-        kWhatSenderNotify,
         kWhatUpdateSurface,
-        kWhatFinishPlay,
-        kWhatPacketize,
         kWhatPause,
         kWhatResume,
+        kWhatMediaSenderNotify,
     };
 
     sp<ANetworkSession> mNetSession;
-    sp<Sender> mSender;
-    sp<ALooper> mSenderLooper;
     sp<AMessage> mNotify;
     in_addr mInterfaceAddr;
     sp<IHDCP> mHDCP;
+
+    sp<MediaSender> mMediaSender;
+    int32_t mLocalRTPPort;
+
     bool mWeAreDead;
     bool mPaused;
 
     int64_t mLastLifesignUs;
 
-    sp<TSPacketizer> mPacketizer;
     sp<BufferQueue> mBufferQueue;
 
     KeyedVector<size_t, sp<Track> > mTracks;
@@ -110,8 +109,6 @@
 
     int64_t mPrevTimeUs;
 
-    bool mAllTracksHavePacketizerIndex;
-
     status_t setupPacketizer(
             bool enableAudio,
             bool usePCMAudio,
@@ -132,27 +129,10 @@
 
     status_t addAudioSource(bool usePCMAudio);
 
-    ssize_t appendTSData(
-            const void *data, size_t size, bool timeDiscontinuity, bool flush);
-
-    status_t onFinishPlay();
-    status_t onFinishPlay2();
-
-    bool allTracksHavePacketizerIndex();
-
-    status_t packetizeAccessUnit(
-            size_t trackIndex, sp<ABuffer> accessUnit,
-            sp<ABuffer> *packets);
-
-    status_t packetizeQueuedAccessUnits();
+    status_t onMediaSenderInitialized();
 
     void notifySessionDead();
 
-    void drainAccessUnits();
-
-    // Returns true iff an access unit was successfully drained.
-    bool drainAccessUnit();
-
     DISALLOW_EVIL_CONSTRUCTORS(PlaybackSession);
 };
 
diff --git a/media/libstagefright/wifi-display/source/RepeaterSource.h b/media/libstagefright/wifi-display/source/RepeaterSource.h
index a13973c..146af32 100644
--- a/media/libstagefright/wifi-display/source/RepeaterSource.h
+++ b/media/libstagefright/wifi-display/source/RepeaterSource.h
@@ -6,7 +6,7 @@
 #include <media/stagefright/foundation/AHandlerReflector.h>
 #include <media/stagefright/MediaSource.h>
 
-#define SUSPEND_VIDEO_IF_IDLE   1
+#define SUSPEND_VIDEO_IF_IDLE   0
 
 namespace android {
 
diff --git a/media/libstagefright/wifi-display/source/Sender.cpp b/media/libstagefright/wifi-display/source/Sender.cpp
deleted file mode 100644
index 8b7d93f..0000000
--- a/media/libstagefright/wifi-display/source/Sender.cpp
+++ /dev/null
@@ -1,878 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "Sender"
-#include <utils/Log.h>
-
-#include "Sender.h"
-
-#include "ANetworkSession.h"
-#include "TimeSeries.h"
-
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/hexdump.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/Utils.h>
-
-namespace android {
-
-static size_t kMaxRTPPacketSize = 1500;
-static size_t kMaxNumTSPacketsPerRTPPacket = (kMaxRTPPacketSize - 12) / 188;
-
-Sender::Sender(
-        const sp<ANetworkSession> &netSession,
-        const sp<AMessage> &notify)
-    : mNetSession(netSession),
-      mNotify(notify),
-      mTransportMode(TRANSPORT_UDP),
-      mRTPChannel(0),
-      mRTCPChannel(0),
-      mRTPPort(0),
-      mRTPSessionID(0),
-      mRTCPSessionID(0),
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-      mRTPRetransmissionSessionID(0),
-      mRTCPRetransmissionSessionID(0),
-#endif
-      mClientRTPPort(0),
-      mClientRTCPPort(0),
-      mRTPConnected(false),
-      mRTCPConnected(false),
-      mFirstOutputBufferReadyTimeUs(-1ll),
-      mFirstOutputBufferSentTimeUs(-1ll),
-      mRTPSeqNo(0),
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-      mRTPRetransmissionSeqNo(0),
-#endif
-      mLastNTPTime(0),
-      mLastRTPTime(0),
-      mNumRTPSent(0),
-      mNumRTPOctetsSent(0),
-      mNumSRsSent(0),
-      mSendSRPending(false)
-#if ENABLE_RETRANSMISSION
-      ,mHistoryLength(0)
-#endif
-#if TRACK_BANDWIDTH
-      ,mFirstPacketTimeUs(-1ll)
-      ,mTotalBytesSent(0ll)
-#endif
-#if LOG_TRANSPORT_STREAM
-    ,mLogFile(NULL)
-#endif
-{
-#if LOG_TRANSPORT_STREAM
-    mLogFile = fopen("/system/etc/log.ts", "wb");
-#endif
-}
-
-Sender::~Sender() {
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-    if (mRTCPRetransmissionSessionID != 0) {
-        mNetSession->destroySession(mRTCPRetransmissionSessionID);
-    }
-
-    if (mRTPRetransmissionSessionID != 0) {
-        mNetSession->destroySession(mRTPRetransmissionSessionID);
-    }
-#endif
-
-    if (mRTCPSessionID != 0) {
-        mNetSession->destroySession(mRTCPSessionID);
-    }
-
-    if (mRTPSessionID != 0) {
-        mNetSession->destroySession(mRTPSessionID);
-    }
-
-#if LOG_TRANSPORT_STREAM
-    if (mLogFile != NULL) {
-        fclose(mLogFile);
-        mLogFile = NULL;
-    }
-#endif
-}
-
-status_t Sender::init(
-        const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
-        TransportMode transportMode) {
-    mClientIP = clientIP;
-    mTransportMode = transportMode;
-
-    if (transportMode == TRANSPORT_TCP_INTERLEAVED) {
-        mRTPChannel = clientRtp;
-        mRTCPChannel = clientRtcp;
-        mRTPPort = 0;
-        mRTPSessionID = 0;
-        mRTCPSessionID = 0;
-        return OK;
-    }
-
-    mRTPChannel = 0;
-    mRTCPChannel = 0;
-
-    if (mTransportMode == TRANSPORT_TCP) {
-        // XXX This is wrong, we need to allocate sockets here, we only
-        // need to do this because the dongles are not establishing their
-        // end until after PLAY instead of before SETUP.
-        mRTPPort = 20000;
-        mRTPSessionID = 0;
-        mRTCPSessionID = 0;
-        mClientRTPPort = clientRtp;
-        mClientRTCPPort = clientRtcp;
-        return OK;
-    }
-
-    int serverRtp;
-
-    sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
-    sp<AMessage> rtcpNotify = new AMessage(kWhatRTCPNotify, id());
-
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-    sp<AMessage> rtpRetransmissionNotify =
-        new AMessage(kWhatRTPRetransmissionNotify, id());
-
-    sp<AMessage> rtcpRetransmissionNotify =
-        new AMessage(kWhatRTCPRetransmissionNotify, id());
-#endif
-
-    status_t err;
-    for (serverRtp = 15550;; serverRtp += 2) {
-        int32_t rtpSession;
-        if (mTransportMode == TRANSPORT_UDP) {
-            err = mNetSession->createUDPSession(
-                        serverRtp, clientIP, clientRtp,
-                        rtpNotify, &rtpSession);
-        } else {
-            err = mNetSession->createTCPDatagramSession(
-                        serverRtp, clientIP, clientRtp,
-                        rtpNotify, &rtpSession);
-        }
-
-        if (err != OK) {
-            ALOGI("failed to create RTP socket on port %d", serverRtp);
-            continue;
-        }
-
-        int32_t rtcpSession = 0;
-
-        if (clientRtcp >= 0) {
-            if (mTransportMode == TRANSPORT_UDP) {
-                err = mNetSession->createUDPSession(
-                        serverRtp + 1, clientIP, clientRtcp,
-                        rtcpNotify, &rtcpSession);
-            } else {
-                err = mNetSession->createTCPDatagramSession(
-                        serverRtp + 1, clientIP, clientRtcp,
-                        rtcpNotify, &rtcpSession);
-            }
-
-            if (err != OK) {
-                ALOGI("failed to create RTCP socket on port %d", serverRtp + 1);
-
-                mNetSession->destroySession(rtpSession);
-                continue;
-            }
-        }
-
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-        if (mTransportMode == TRANSPORT_UDP) {
-            int32_t rtpRetransmissionSession;
-
-            err = mNetSession->createUDPSession(
-                        serverRtp + kRetransmissionPortOffset,
-                        clientIP,
-                        clientRtp + kRetransmissionPortOffset,
-                        rtpRetransmissionNotify,
-                        &rtpRetransmissionSession);
-
-            if (err != OK) {
-                mNetSession->destroySession(rtcpSession);
-                mNetSession->destroySession(rtpSession);
-                continue;
-            }
-
-            CHECK_GE(clientRtcp, 0);
-
-            int32_t rtcpRetransmissionSession;
-            err = mNetSession->createUDPSession(
-                        serverRtp + 1 + kRetransmissionPortOffset,
-                        clientIP,
-                        clientRtp + 1 + kRetransmissionPortOffset,
-                        rtcpRetransmissionNotify,
-                        &rtcpRetransmissionSession);
-
-            if (err != OK) {
-                mNetSession->destroySession(rtpRetransmissionSession);
-                mNetSession->destroySession(rtcpSession);
-                mNetSession->destroySession(rtpSession);
-                continue;
-            }
-
-            mRTPRetransmissionSessionID = rtpRetransmissionSession;
-            mRTCPRetransmissionSessionID = rtcpRetransmissionSession;
-
-            ALOGI("rtpRetransmissionSessionID = %d, "
-                  "rtcpRetransmissionSessionID = %d",
-                  rtpRetransmissionSession, rtcpRetransmissionSession);
-        }
-#endif
-
-        mRTPPort = serverRtp;
-        mRTPSessionID = rtpSession;
-        mRTCPSessionID = rtcpSession;
-
-        ALOGI("rtpSessionID = %d, rtcpSessionID = %d", rtpSession, rtcpSession);
-        break;
-    }
-
-    if (mRTPPort == 0) {
-        return UNKNOWN_ERROR;
-    }
-
-    return OK;
-}
-
-status_t Sender::finishInit() {
-    if (mTransportMode != TRANSPORT_TCP) {
-        notifyInitDone();
-        return OK;
-    }
-
-    sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
-
-    status_t err = mNetSession->createTCPDatagramSession(
-                mRTPPort, mClientIP.c_str(), mClientRTPPort,
-                rtpNotify, &mRTPSessionID);
-
-    if (err != OK) {
-        return err;
-    }
-
-    if (mClientRTCPPort >= 0) {
-        sp<AMessage> rtcpNotify = new AMessage(kWhatRTCPNotify, id());
-
-        err = mNetSession->createTCPDatagramSession(
-                mRTPPort + 1, mClientIP.c_str(), mClientRTCPPort,
-                rtcpNotify, &mRTCPSessionID);
-
-        if (err != OK) {
-            return err;
-        }
-    }
-
-    return OK;
-}
-
-int32_t Sender::getRTPPort() const {
-    return mRTPPort;
-}
-
-void Sender::queuePackets(
-        int64_t timeUs, const sp<ABuffer> &tsPackets) {
-    const size_t numTSPackets = tsPackets->size() / 188;
-
-    const size_t numRTPPackets =
-        (numTSPackets + kMaxNumTSPacketsPerRTPPacket - 1)
-            / kMaxNumTSPacketsPerRTPPacket;
-
-    sp<ABuffer> udpPackets = new ABuffer(
-            numRTPPackets * (12 + kMaxNumTSPacketsPerRTPPacket * 188));
-
-    udpPackets->meta()->setInt64("timeUs", timeUs);
-
-    size_t dstOffset = 0;
-    for (size_t i = 0; i < numTSPackets; ++i) {
-        if ((i % kMaxNumTSPacketsPerRTPPacket) == 0) {
-            static const bool kMarkerBit = false;
-
-            uint8_t *rtp = udpPackets->data() + dstOffset;
-            rtp[0] = 0x80;
-            rtp[1] = 33 | (kMarkerBit ? (1 << 7) : 0);  // M-bit
-            rtp[2] = (mRTPSeqNo >> 8) & 0xff;
-            rtp[3] = mRTPSeqNo & 0xff;
-            rtp[4] = 0x00;  // rtp time to be filled in later.
-            rtp[5] = 0x00;
-            rtp[6] = 0x00;
-            rtp[7] = 0x00;
-            rtp[8] = kSourceID >> 24;
-            rtp[9] = (kSourceID >> 16) & 0xff;
-            rtp[10] = (kSourceID >> 8) & 0xff;
-            rtp[11] = kSourceID & 0xff;
-
-            ++mRTPSeqNo;
-
-            dstOffset += 12;
-        }
-
-        memcpy(udpPackets->data() + dstOffset,
-               tsPackets->data() + 188 * i,
-               188);
-
-        dstOffset += 188;
-    }
-
-    udpPackets->setRange(0, dstOffset);
-
-    sp<AMessage> msg = new AMessage(kWhatDrainQueue, id());
-    msg->setBuffer("udpPackets", udpPackets);
-    msg->post();
-
-#if LOG_TRANSPORT_STREAM
-    if (mLogFile != NULL) {
-        fwrite(tsPackets->data(), 1, tsPackets->size(), mLogFile);
-    }
-#endif
-}
-
-void Sender::onMessageReceived(const sp<AMessage> &msg) {
-    switch (msg->what()) {
-        case kWhatRTPNotify:
-        case kWhatRTCPNotify:
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-        case kWhatRTPRetransmissionNotify:
-        case kWhatRTCPRetransmissionNotify:
-#endif
-        {
-            int32_t reason;
-            CHECK(msg->findInt32("reason", &reason));
-
-            switch (reason) {
-                case ANetworkSession::kWhatError:
-                {
-                    int32_t sessionID;
-                    CHECK(msg->findInt32("sessionID", &sessionID));
-
-                    int32_t err;
-                    CHECK(msg->findInt32("err", &err));
-
-                    int32_t errorOccuredDuringSend;
-                    CHECK(msg->findInt32("send", &errorOccuredDuringSend));
-
-                    AString detail;
-                    CHECK(msg->findString("detail", &detail));
-
-                    if ((msg->what() == kWhatRTPNotify
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-                            || msg->what() == kWhatRTPRetransmissionNotify
-#endif
-                        ) && !errorOccuredDuringSend) {
-                        // This is ok, we don't expect to receive anything on
-                        // the RTP socket.
-                        break;
-                    }
-
-                    ALOGE("An error occurred during %s in session %d "
-                          "(%d, '%s' (%s)).",
-                          errorOccuredDuringSend ? "send" : "receive",
-                          sessionID,
-                          err,
-                          detail.c_str(),
-                          strerror(-err));
-
-                    mNetSession->destroySession(sessionID);
-
-                    if (sessionID == mRTPSessionID) {
-                        mRTPSessionID = 0;
-                    } else if (sessionID == mRTCPSessionID) {
-                        mRTCPSessionID = 0;
-                    }
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-                    else if (sessionID == mRTPRetransmissionSessionID) {
-                        mRTPRetransmissionSessionID = 0;
-                    } else if (sessionID == mRTCPRetransmissionSessionID) {
-                        mRTCPRetransmissionSessionID = 0;
-                    }
-#endif
-
-                    notifySessionDead();
-                    break;
-                }
-
-                case ANetworkSession::kWhatDatagram:
-                {
-                    int32_t sessionID;
-                    CHECK(msg->findInt32("sessionID", &sessionID));
-
-                    sp<ABuffer> data;
-                    CHECK(msg->findBuffer("data", &data));
-
-                    status_t err;
-                    if (msg->what() == kWhatRTCPNotify
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-                            || msg->what() == kWhatRTCPRetransmissionNotify
-#endif
-                       )
-                    {
-                        err = parseRTCP(data);
-                    }
-                    break;
-                }
-
-                case ANetworkSession::kWhatConnected:
-                {
-                    CHECK_EQ(mTransportMode, TRANSPORT_TCP);
-
-                    int32_t sessionID;
-                    CHECK(msg->findInt32("sessionID", &sessionID));
-
-                    if (sessionID == mRTPSessionID) {
-                        CHECK(!mRTPConnected);
-                        mRTPConnected = true;
-                        ALOGI("RTP Session now connected.");
-                    } else if (sessionID == mRTCPSessionID) {
-                        CHECK(!mRTCPConnected);
-                        mRTCPConnected = true;
-                        ALOGI("RTCP Session now connected.");
-                    } else {
-                        TRESPASS();
-                    }
-
-                    if (mRTPConnected
-                            && (mClientRTCPPort < 0 || mRTCPConnected)) {
-                        notifyInitDone();
-                    }
-                    break;
-                }
-
-                default:
-                    TRESPASS();
-            }
-            break;
-        }
-
-        case kWhatDrainQueue:
-        {
-            sp<ABuffer> udpPackets;
-            CHECK(msg->findBuffer("udpPackets", &udpPackets));
-
-            onDrainQueue(udpPackets);
-            break;
-        }
-
-        case kWhatSendSR:
-        {
-            mSendSRPending = false;
-
-            if (mRTCPSessionID == 0) {
-                break;
-            }
-
-            onSendSR();
-
-            scheduleSendSR();
-            break;
-        }
-    }
-}
-
-void Sender::scheduleSendSR() {
-    if (mSendSRPending || mRTCPSessionID == 0) {
-        return;
-    }
-
-    mSendSRPending = true;
-    (new AMessage(kWhatSendSR, id()))->post(kSendSRIntervalUs);
-}
-
-void Sender::addSR(const sp<ABuffer> &buffer) {
-    uint8_t *data = buffer->data() + buffer->size();
-
-    // TODO: Use macros/utility functions to clean up all the bitshifts below.
-
-    data[0] = 0x80 | 0;
-    data[1] = 200;  // SR
-    data[2] = 0;
-    data[3] = 6;
-    data[4] = kSourceID >> 24;
-    data[5] = (kSourceID >> 16) & 0xff;
-    data[6] = (kSourceID >> 8) & 0xff;
-    data[7] = kSourceID & 0xff;
-
-    data[8] = mLastNTPTime >> (64 - 8);
-    data[9] = (mLastNTPTime >> (64 - 16)) & 0xff;
-    data[10] = (mLastNTPTime >> (64 - 24)) & 0xff;
-    data[11] = (mLastNTPTime >> 32) & 0xff;
-    data[12] = (mLastNTPTime >> 24) & 0xff;
-    data[13] = (mLastNTPTime >> 16) & 0xff;
-    data[14] = (mLastNTPTime >> 8) & 0xff;
-    data[15] = mLastNTPTime & 0xff;
-
-    data[16] = (mLastRTPTime >> 24) & 0xff;
-    data[17] = (mLastRTPTime >> 16) & 0xff;
-    data[18] = (mLastRTPTime >> 8) & 0xff;
-    data[19] = mLastRTPTime & 0xff;
-
-    data[20] = mNumRTPSent >> 24;
-    data[21] = (mNumRTPSent >> 16) & 0xff;
-    data[22] = (mNumRTPSent >> 8) & 0xff;
-    data[23] = mNumRTPSent & 0xff;
-
-    data[24] = mNumRTPOctetsSent >> 24;
-    data[25] = (mNumRTPOctetsSent >> 16) & 0xff;
-    data[26] = (mNumRTPOctetsSent >> 8) & 0xff;
-    data[27] = mNumRTPOctetsSent & 0xff;
-
-    buffer->setRange(buffer->offset(), buffer->size() + 28);
-}
-
-void Sender::addSDES(const sp<ABuffer> &buffer) {
-    uint8_t *data = buffer->data() + buffer->size();
-    data[0] = 0x80 | 1;
-    data[1] = 202;  // SDES
-    data[4] = kSourceID >> 24;
-    data[5] = (kSourceID >> 16) & 0xff;
-    data[6] = (kSourceID >> 8) & 0xff;
-    data[7] = kSourceID & 0xff;
-
-    size_t offset = 8;
-
-    data[offset++] = 1;  // CNAME
-
-    static const char *kCNAME = "someone@somewhere";
-    data[offset++] = strlen(kCNAME);
-
-    memcpy(&data[offset], kCNAME, strlen(kCNAME));
-    offset += strlen(kCNAME);
-
-    data[offset++] = 7;  // NOTE
-
-    static const char *kNOTE = "Hell's frozen over.";
-    data[offset++] = strlen(kNOTE);
-
-    memcpy(&data[offset], kNOTE, strlen(kNOTE));
-    offset += strlen(kNOTE);
-
-    data[offset++] = 0;
-
-    if ((offset % 4) > 0) {
-        size_t count = 4 - (offset % 4);
-        switch (count) {
-            case 3:
-                data[offset++] = 0;
-            case 2:
-                data[offset++] = 0;
-            case 1:
-                data[offset++] = 0;
-        }
-    }
-
-    size_t numWords = (offset / 4) - 1;
-    data[2] = numWords >> 8;
-    data[3] = numWords & 0xff;
-
-    buffer->setRange(buffer->offset(), buffer->size() + offset);
-}
-
-// static
-uint64_t Sender::GetNowNTP() {
-    uint64_t nowUs = ALooper::GetNowUs();
-
-    nowUs += ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll;
-
-    uint64_t hi = nowUs / 1000000ll;
-    uint64_t lo = ((1ll << 32) * (nowUs % 1000000ll)) / 1000000ll;
-
-    return (hi << 32) | lo;
-}
-
-void Sender::onSendSR() {
-    sp<ABuffer> buffer = new ABuffer(1500);
-    buffer->setRange(0, 0);
-
-    addSR(buffer);
-    addSDES(buffer);
-
-    if (mTransportMode == TRANSPORT_TCP_INTERLEAVED) {
-        sp<AMessage> notify = mNotify->dup();
-        notify->setInt32("what", kWhatBinaryData);
-        notify->setInt32("channel", mRTCPChannel);
-        notify->setBuffer("data", buffer);
-        notify->post();
-    } else {
-        sendPacket(mRTCPSessionID, buffer->data(), buffer->size());
-    }
-
-    ++mNumSRsSent;
-}
-
-#if ENABLE_RETRANSMISSION
-status_t Sender::parseTSFB(
-        const uint8_t *data, size_t size) {
-    if ((data[0] & 0x1f) != 1) {
-        return ERROR_UNSUPPORTED;  // We only support NACK for now.
-    }
-
-    uint32_t srcId = U32_AT(&data[8]);
-    if (srcId != kSourceID) {
-        return ERROR_MALFORMED;
-    }
-
-    for (size_t i = 12; i < size; i += 4) {
-        uint16_t seqNo = U16_AT(&data[i]);
-        uint16_t blp = U16_AT(&data[i + 2]);
-
-        List<sp<ABuffer> >::iterator it = mHistory.begin();
-        bool foundSeqNo = false;
-        while (it != mHistory.end()) {
-            const sp<ABuffer> &buffer = *it;
-
-            uint16_t bufferSeqNo = buffer->int32Data() & 0xffff;
-
-            bool retransmit = false;
-            if (bufferSeqNo == seqNo) {
-                retransmit = true;
-            } else if (blp != 0) {
-                for (size_t i = 0; i < 16; ++i) {
-                    if ((blp & (1 << i))
-                        && (bufferSeqNo == ((seqNo + i + 1) & 0xffff))) {
-                        blp &= ~(1 << i);
-                        retransmit = true;
-                    }
-                }
-            }
-
-            if (retransmit) {
-                ALOGI("retransmitting seqNo %d", bufferSeqNo);
-
-#if RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-                sp<ABuffer> retransRTP = new ABuffer(2 + buffer->size());
-                uint8_t *rtp = retransRTP->data();
-                memcpy(rtp, buffer->data(), 12);
-                rtp[2] = (mRTPRetransmissionSeqNo >> 8) & 0xff;
-                rtp[3] = mRTPRetransmissionSeqNo & 0xff;
-                rtp[12] = (bufferSeqNo >> 8) & 0xff;
-                rtp[13] = bufferSeqNo & 0xff;
-                memcpy(&rtp[14], buffer->data() + 12, buffer->size() - 12);
-
-                ++mRTPRetransmissionSeqNo;
-
-                sendPacket(
-                        mRTPRetransmissionSessionID,
-                        retransRTP->data(), retransRTP->size());
-#else
-                sendPacket(
-                        mRTPSessionID, buffer->data(), buffer->size());
-#endif
-
-                if (bufferSeqNo == seqNo) {
-                    foundSeqNo = true;
-                }
-
-                if (foundSeqNo && blp == 0) {
-                    break;
-                }
-            }
-
-            ++it;
-        }
-
-        if (!foundSeqNo || blp != 0) {
-            ALOGI("Some sequence numbers were no longer available for "
-                  "retransmission (seqNo = %d, foundSeqNo = %d, blp = 0x%04x)",
-                  seqNo, foundSeqNo, blp);
-
-            if (!mHistory.empty()) {
-                int32_t earliest = (*mHistory.begin())->int32Data() & 0xffff;
-                int32_t latest = (*--mHistory.end())->int32Data() & 0xffff;
-
-                ALOGI("have seq numbers from %d - %d", earliest, latest);
-            }
-        }
-    }
-
-    return OK;
-}
-#endif
-
-status_t Sender::parseRTCP(
-        const sp<ABuffer> &buffer) {
-    const uint8_t *data = buffer->data();
-    size_t size = buffer->size();
-
-    while (size > 0) {
-        if (size < 8) {
-            // Too short to be a valid RTCP header
-            return ERROR_MALFORMED;
-        }
-
-        if ((data[0] >> 6) != 2) {
-            // Unsupported version.
-            return ERROR_UNSUPPORTED;
-        }
-
-        if (data[0] & 0x20) {
-            // Padding present.
-
-            size_t paddingLength = data[size - 1];
-
-            if (paddingLength + 12 > size) {
-                // If we removed this much padding we'd end up with something
-                // that's too short to be a valid RTP header.
-                return ERROR_MALFORMED;
-            }
-
-            size -= paddingLength;
-        }
-
-        size_t headerLength = 4 * (data[2] << 8 | data[3]) + 4;
-
-        if (size < headerLength) {
-            // Only received a partial packet?
-            return ERROR_MALFORMED;
-        }
-
-        switch (data[1]) {
-            case 200:
-            case 201:  // RR
-            case 202:  // SDES
-            case 203:
-            case 204:  // APP
-                break;
-
-#if ENABLE_RETRANSMISSION
-            case 205:  // TSFB (transport layer specific feedback)
-                parseTSFB(data, headerLength);
-                break;
-#endif
-
-            case 206:  // PSFB (payload specific feedback)
-                hexdump(data, headerLength);
-                break;
-
-            default:
-            {
-                ALOGW("Unknown RTCP packet type %u of size %d",
-                     (unsigned)data[1], headerLength);
-                break;
-            }
-        }
-
-        data += headerLength;
-        size -= headerLength;
-    }
-
-    return OK;
-}
-
-status_t Sender::sendPacket(
-        int32_t sessionID, const void *data, size_t size) {
-    return mNetSession->sendRequest(sessionID, data, size);
-}
-
-void Sender::notifyInitDone() {
-    sp<AMessage> notify = mNotify->dup();
-    notify->setInt32("what", kWhatInitDone);
-    notify->post();
-}
-
-void Sender::notifySessionDead() {
-    sp<AMessage> notify = mNotify->dup();
-    notify->setInt32("what", kWhatSessionDead);
-    notify->post();
-}
-
-void Sender::onDrainQueue(const sp<ABuffer> &udpPackets) {
-    static const size_t kFullRTPPacketSize =
-        12 + 188 * kMaxNumTSPacketsPerRTPPacket;
-
-    size_t srcOffset = 0;
-    while (srcOffset < udpPackets->size()) {
-        uint8_t *rtp = udpPackets->data() + srcOffset;
-
-        size_t rtpPacketSize = udpPackets->size() - srcOffset;
-        if (rtpPacketSize > kFullRTPPacketSize) {
-            rtpPacketSize = kFullRTPPacketSize;
-        }
-
-        int64_t nowUs = ALooper::GetNowUs();
-        mLastNTPTime = GetNowNTP();
-
-        // 90kHz time scale
-        uint32_t rtpTime = (nowUs * 9ll) / 100ll;
-
-        rtp[4] = rtpTime >> 24;
-        rtp[5] = (rtpTime >> 16) & 0xff;
-        rtp[6] = (rtpTime >> 8) & 0xff;
-        rtp[7] = rtpTime & 0xff;
-
-        ++mNumRTPSent;
-        mNumRTPOctetsSent += rtpPacketSize - 12;
-
-        mLastRTPTime = rtpTime;
-
-        if (mTransportMode == TRANSPORT_TCP_INTERLEAVED) {
-            sp<AMessage> notify = mNotify->dup();
-            notify->setInt32("what", kWhatBinaryData);
-
-            sp<ABuffer> data = new ABuffer(rtpPacketSize);
-            memcpy(data->data(), rtp, rtpPacketSize);
-
-            notify->setInt32("channel", mRTPChannel);
-            notify->setBuffer("data", data);
-            notify->post();
-        } else {
-            sendPacket(mRTPSessionID, rtp, rtpPacketSize);
-
-#if TRACK_BANDWIDTH
-            mTotalBytesSent += rtpPacketSize->size();
-            int64_t delayUs = ALooper::GetNowUs() - mFirstPacketTimeUs;
-
-            if (delayUs > 0ll) {
-                ALOGI("approx. net bandwidth used: %.2f Mbit/sec",
-                        mTotalBytesSent * 8.0 / delayUs);
-            }
-#endif
-        }
-
-#if ENABLE_RETRANSMISSION
-        addToHistory(rtp, rtpPacketSize);
-#endif
-
-        srcOffset += rtpPacketSize;
-    }
-
-#if 0
-    int64_t timeUs;
-    CHECK(udpPackets->meta()->findInt64("timeUs", &timeUs));
-
-    ALOGI("dTimeUs = %lld us", ALooper::GetNowUs() - timeUs);
-#endif
-}
-
-#if ENABLE_RETRANSMISSION
-void Sender::addToHistory(const uint8_t *rtp, size_t rtpPacketSize) {
-    sp<ABuffer> packet = new ABuffer(rtpPacketSize);
-    memcpy(packet->data(), rtp, rtpPacketSize);
-
-    unsigned rtpSeqNo = U16_AT(&rtp[2]);
-    packet->setInt32Data(rtpSeqNo);
-
-    mHistory.push_back(packet);
-    ++mHistoryLength;
-
-    if (mHistoryLength > kMaxHistoryLength) {
-        mHistory.erase(mHistory.begin());
-        --mHistoryLength;
-    }
-}
-#endif
-
-}  // namespace android
-
diff --git a/media/libstagefright/wifi-display/source/Sender.h b/media/libstagefright/wifi-display/source/Sender.h
deleted file mode 100644
index 66951f7..0000000
--- a/media/libstagefright/wifi-display/source/Sender.h
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SENDER_H_
-
-#define SENDER_H_
-
-#include <media/stagefright/foundation/AHandler.h>
-
-namespace android {
-
-#define LOG_TRANSPORT_STREAM            0
-#define TRACK_BANDWIDTH                 0
-
-#define ENABLE_RETRANSMISSION                   1
-
-// If retransmission is enabled the following define determines what
-// kind we support, if RETRANSMISSION_ACCORDING_TO_RFC_XXXX is 0
-// we'll send NACKs on the original RTCP channel and retransmit packets
-// on the original RTP channel, otherwise a separate channel pair is used
-// for this purpose.
-#define RETRANSMISSION_ACCORDING_TO_RFC_XXXX    0
-
-struct ABuffer;
-struct ANetworkSession;
-
-struct Sender : public AHandler {
-    Sender(const sp<ANetworkSession> &netSession, const sp<AMessage> &notify);
-
-    enum {
-        kWhatInitDone,
-        kWhatSessionDead,
-        kWhatBinaryData,
-    };
-
-    enum TransportMode {
-        TRANSPORT_UDP,
-        TRANSPORT_TCP_INTERLEAVED,
-        TRANSPORT_TCP,
-    };
-    status_t init(
-            const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
-            TransportMode transportMode);
-
-    status_t finishInit();
-
-    int32_t getRTPPort() const;
-
-    void queuePackets(int64_t timeUs, const sp<ABuffer> &tsPackets);
-    void scheduleSendSR();
-
-protected:
-    virtual ~Sender();
-    virtual void onMessageReceived(const sp<AMessage> &msg);
-
-private:
-    enum {
-        kWhatDrainQueue,
-        kWhatSendSR,
-        kWhatRTPNotify,
-        kWhatRTCPNotify,
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-        kWhatRTPRetransmissionNotify,
-        kWhatRTCPRetransmissionNotify,
-#endif
-    };
-
-    static const int64_t kSendSRIntervalUs = 10000000ll;
-
-    static const uint32_t kSourceID = 0xdeadbeef;
-    static const size_t kMaxHistoryLength = 128;
-
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-    static const size_t kRetransmissionPortOffset = 120;
-#endif
-
-    sp<ANetworkSession> mNetSession;
-    sp<AMessage> mNotify;
-
-    TransportMode mTransportMode;
-    AString mClientIP;
-
-    // in TCP mode
-    int32_t mRTPChannel;
-    int32_t mRTCPChannel;
-
-    // in UDP mode
-    int32_t mRTPPort;
-    int32_t mRTPSessionID;
-    int32_t mRTCPSessionID;
-
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-    int32_t mRTPRetransmissionSessionID;
-    int32_t mRTCPRetransmissionSessionID;
-#endif
-
-    int32_t mClientRTPPort;
-    int32_t mClientRTCPPort;
-    bool mRTPConnected;
-    bool mRTCPConnected;
-
-    int64_t mFirstOutputBufferReadyTimeUs;
-    int64_t mFirstOutputBufferSentTimeUs;
-
-    uint32_t mRTPSeqNo;
-#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
-    uint32_t mRTPRetransmissionSeqNo;
-#endif
-
-    uint64_t mLastNTPTime;
-    uint32_t mLastRTPTime;
-    uint32_t mNumRTPSent;
-    uint32_t mNumRTPOctetsSent;
-    uint32_t mNumSRsSent;
-
-    bool mSendSRPending;
-
-#if ENABLE_RETRANSMISSION
-    List<sp<ABuffer> > mHistory;
-    size_t mHistoryLength;
-#endif
-
-#if TRACK_BANDWIDTH
-    int64_t mFirstPacketTimeUs;
-    uint64_t mTotalBytesSent;
-#endif
-
-#if LOG_TRANSPORT_STREAM
-    FILE *mLogFile;
-#endif
-
-    void onSendSR();
-    void addSR(const sp<ABuffer> &buffer);
-    void addSDES(const sp<ABuffer> &buffer);
-    static uint64_t GetNowNTP();
-
-#if ENABLE_RETRANSMISSION
-    status_t parseTSFB(const uint8_t *data, size_t size);
-    void addToHistory(const uint8_t *rtp, size_t rtpPacketSize);
-#endif
-
-    status_t parseRTCP(const sp<ABuffer> &buffer);
-
-    status_t sendPacket(int32_t sessionID, const void *data, size_t size);
-
-    void notifyInitDone();
-    void notifySessionDead();
-
-    void onDrainQueue(const sp<ABuffer> &udpPackets);
-
-    DISALLOW_EVIL_CONSTRUCTORS(Sender);
-};
-
-}  // namespace android
-
-#endif  // SENDER_H_
diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.cpp b/media/libstagefright/wifi-display/source/TSPacketizer.cpp
index ef57a4d..8420529 100644
--- a/media/libstagefright/wifi-display/source/TSPacketizer.cpp
+++ b/media/libstagefright/wifi-display/source/TSPacketizer.cpp
@@ -58,6 +58,7 @@
     sp<ABuffer> descriptorAt(size_t index) const;
 
     void finalize();
+    void extractCSDIfNecessary();
 
 protected:
     virtual ~Track();
@@ -77,6 +78,7 @@
 
     bool mAudioLacksATDSHeaders;
     bool mFinalized;
+    bool mExtractedCSD;
 
     DISALLOW_EVIL_CONSTRUCTORS(Track);
 };
@@ -90,14 +92,21 @@
       mStreamID(streamID),
       mContinuityCounter(0),
       mAudioLacksATDSHeaders(false),
-      mFinalized(false) {
+      mFinalized(false),
+      mExtractedCSD(false) {
     CHECK(format->findString("mime", &mMIME));
+}
+
+void TSPacketizer::Track::extractCSDIfNecessary() {
+    if (mExtractedCSD) {
+        return;
+    }
 
     if (!strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_VIDEO_AVC)
             || !strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) {
         for (size_t i = 0;; ++i) {
             sp<ABuffer> csd;
-            if (!format->findBuffer(StringPrintf("csd-%d", i).c_str(), &csd)) {
+            if (!mFormat->findBuffer(StringPrintf("csd-%d", i).c_str(), &csd)) {
                 break;
             }
 
@@ -111,6 +120,8 @@
             }
         }
     }
+
+    mExtractedCSD = true;
 }
 
 TSPacketizer::Track::~Track() {
@@ -407,6 +418,17 @@
     return mTracks.add(track);
 }
 
+status_t TSPacketizer::extractCSDIfNecessary(size_t trackIndex) {
+    if (trackIndex >= mTracks.size()) {
+        return -ERANGE;
+    }
+
+    const sp<Track> &track = mTracks.itemAt(trackIndex);
+    track->extractCSDIfNecessary();
+
+    return OK;
+}
+
 status_t TSPacketizer::packetize(
         size_t trackIndex,
         const sp<ABuffer> &_accessUnit,
diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.h b/media/libstagefright/wifi-display/source/TSPacketizer.h
index a37917d..5d1d70e 100644
--- a/media/libstagefright/wifi-display/source/TSPacketizer.h
+++ b/media/libstagefright/wifi-display/source/TSPacketizer.h
@@ -50,6 +50,8 @@
             const uint8_t *PES_private_data, size_t PES_private_data_len,
             size_t numStuffingBytes = 0);
 
+    status_t extractCSDIfNecessary(size_t trackIndex);
+
     // XXX to be removed once encoder config option takes care of this for
     // encrypted mode.
     sp<ABuffer> prependCSD(
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
index 825ebc6..07eb237 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
@@ -22,7 +22,7 @@
 #include "PlaybackSession.h"
 #include "Parameters.h"
 #include "ParsedMessage.h"
-#include "Sender.h"
+#include "rtp/RTPSender.h"
 
 #include <binder/IServiceManager.h>
 #include <gui/IGraphicBufferProducer.h>
@@ -1140,7 +1140,7 @@
         return ERROR_MALFORMED;
     }
 
-    Sender::TransportMode transportMode = Sender::TRANSPORT_UDP;
+    RTPSender::TransportMode transportMode = RTPSender::TRANSPORT_UDP;
 
     int clientRtp, clientRtcp;
     if (transport.startsWith("RTP/AVP/TCP;")) {
@@ -1149,7 +1149,7 @@
                     transport.c_str(), "interleaved", &interleaved)
                 && sscanf(interleaved.c_str(), "%d-%d",
                           &clientRtp, &clientRtcp) == 2) {
-            transportMode = Sender::TRANSPORT_TCP_INTERLEAVED;
+            transportMode = RTPSender::TRANSPORT_TCP_INTERLEAVED;
         } else {
             bool badRequest = false;
 
@@ -1171,7 +1171,7 @@
                 return ERROR_MALFORMED;
             }
 
-            transportMode = Sender::TRANSPORT_TCP;
+            transportMode = RTPSender::TRANSPORT_TCP;
         }
     } else if (transport.startsWith("RTP/AVP;unicast;")
             || transport.startsWith("RTP/AVP/UDP;unicast;")) {
@@ -1263,7 +1263,7 @@
     AString response = "RTSP/1.0 200 OK\r\n";
     AppendCommonResponse(&response, cseq, playbackSessionID);
 
-    if (transportMode == Sender::TRANSPORT_TCP_INTERLEAVED) {
+    if (transportMode == RTPSender::TRANSPORT_TCP_INTERLEAVED) {
         response.append(
                 StringPrintf(
                     "Transport: RTP/AVP/TCP;interleaved=%d-%d;",
@@ -1272,7 +1272,7 @@
         int32_t serverRtp = playbackSession->getRTPPort();
 
         AString transportString = "UDP";
-        if (transportMode == Sender::TRANSPORT_TCP) {
+        if (transportMode == RTPSender::TRANSPORT_TCP) {
             transportString = "TCP";
         }
 
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))