Reconcile with jb-mr1-factory-release jb-mr1-release - do not merge

Change-Id: Idf4c25115d89397ba668fc290344b2e7c1ea1993
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index d27f463..cba8a6b 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -242,7 +242,10 @@
     status_t setupAVCEncoderParameters(const sp<AMessage> &msg);
 
     status_t verifySupportForProfileAndLevel(int32_t profile, int32_t level);
-    status_t configureBitrate(int32_t bitrate);
+
+    status_t configureBitrate(
+            int32_t bitrate, OMX_VIDEO_CONTROLRATETYPE bitrateMode);
+
     status_t setupErrorCorrectionParameters();
 
     status_t initNativeWindow();
diff --git a/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.c b/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.c
index 3b3c07c..32c4ce0 100644
--- a/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.c
+++ b/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.c
@@ -232,6 +232,10 @@
     pInstance->pData->AGCInstance.AGC_Gain = pInstance->pData->AGCInstance.AGC_MaxGain;
                                                 /* Default to the bass boost setting */
 
+    // initialize the mixer with some fixes values since otherwise LVDBE_SetVolume ends up
+    // reading uninitialized data
+    pMixer_Instance = &pInstance->pData->BypassVolume;
+    LVC_Mixer_Init(&pMixer_Instance->MixerStream[0],0x00007FFF,0x00007FFF);
 
     /*
      * Initialise the volume
@@ -242,7 +246,6 @@
     pInstance->pData->AGCInstance.Volume = pInstance->pData->AGCInstance.Target;
                                                 /* Initialise as the target */
 
-    pMixer_Instance = &pInstance->pData->BypassVolume;
     MixGain = LVC_Mixer_GetTarget(&pMixer_Instance->MixerStream[0]);
     LVC_Mixer_Init(&pMixer_Instance->MixerStream[0],MixGain,MixGain);
 
diff --git a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.c b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.c
index e83e515..c4767a8 100644
--- a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.c
+++ b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.c
@@ -264,6 +264,9 @@
     MemSize = (pCapabilities->MaxBands * sizeof(LVEQNB_BandDef_t));
     pInstance->pBandDefinitions  = (LVEQNB_BandDef_t *)InstAlloc_AddMember(&AllocMem,
                                                                            MemSize);
+    // clear all the bands, setting their gain to 0, otherwise when applying new params,
+    // it will compare against uninitialized values
+    memset(pInstance->pBandDefinitions, 0, MemSize);
     MemSize = (pCapabilities->MaxBands * sizeof(LVEQNB_BiquadType_en));
     pInstance->pBiquadType = (LVEQNB_BiquadType_en *)InstAlloc_AddMember(&AllocMem,
                                                                          MemSize);
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index 2eae7df..d706c2d 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -2737,7 +2737,7 @@
                 outBuffer->s16[i] =
                         clamp16((LVM_INT32)outBuffer->s16[i] + (LVM_INT32)inBuffer->s16[i]);
             }
-        } else {
+        } else if (outBuffer->raw != inBuffer->raw) {
             memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount*sizeof(LVM_INT16)*2);
         }
     }
diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp
index 48bbf8f..8319cd7 100644
--- a/media/libmedia/MediaProfiles.cpp
+++ b/media/libmedia/MediaProfiles.cpp
@@ -373,7 +373,7 @@
 
 void MediaProfiles::addStartTimeOffset(int cameraId, const char** atts)
 {
-    int offsetTimeMs = 700;
+    int offsetTimeMs = 1000;
     if (atts[2]) {
         CHECK(!strcmp("startOffsetMs", atts[2]));
         offsetTimeMs = atoi(atts[3]);
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 2b4220f..0ca027b 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -1636,6 +1636,15 @@
     return ret;
 }
 
+static OMX_VIDEO_CONTROLRATETYPE getBitrateMode(const sp<AMessage> &msg) {
+    int32_t tmp;
+    if (!msg->findInt32("bitrate-mode", &tmp)) {
+        return OMX_Video_ControlRateVariable;
+    }
+
+    return static_cast<OMX_VIDEO_CONTROLRATETYPE>(tmp);
+}
+
 status_t ACodec::setupMPEG4EncoderParameters(const sp<AMessage> &msg) {
     int32_t bitrate, iFrameInterval;
     if (!msg->findInt32("bitrate", &bitrate)
@@ -1643,6 +1652,8 @@
         return INVALID_OPERATION;
     }
 
+    OMX_VIDEO_CONTROLRATETYPE bitrateMode = getBitrateMode(msg);
+
     float frameRate;
     if (!msg->findFloat("frame-rate", &frameRate)) {
         int32_t tmp;
@@ -1706,7 +1717,7 @@
         return err;
     }
 
-    err = configureBitrate(bitrate);
+    err = configureBitrate(bitrate, bitrateMode);
 
     if (err != OK) {
         return err;
@@ -1722,6 +1733,8 @@
         return INVALID_OPERATION;
     }
 
+    OMX_VIDEO_CONTROLRATETYPE bitrateMode = getBitrateMode(msg);
+
     float frameRate;
     if (!msg->findFloat("frame-rate", &frameRate)) {
         int32_t tmp;
@@ -1780,7 +1793,7 @@
         return err;
     }
 
-    err = configureBitrate(bitrate);
+    err = configureBitrate(bitrate, bitrateMode);
 
     if (err != OK) {
         return err;
@@ -1796,6 +1809,8 @@
         return INVALID_OPERATION;
     }
 
+    OMX_VIDEO_CONTROLRATETYPE bitrateMode = getBitrateMode(msg);
+
     float frameRate;
     if (!msg->findFloat("frame-rate", &frameRate)) {
         int32_t tmp;
@@ -1881,7 +1896,7 @@
         return err;
     }
 
-    return configureBitrate(bitrate);
+    return configureBitrate(bitrate, bitrateMode);
 }
 
 status_t ACodec::verifySupportForProfileAndLevel(
@@ -1910,7 +1925,8 @@
     }
 }
 
-status_t ACodec::configureBitrate(int32_t bitrate) {
+status_t ACodec::configureBitrate(
+        int32_t bitrate, OMX_VIDEO_CONTROLRATETYPE bitrateMode) {
     OMX_VIDEO_PARAM_BITRATETYPE bitrateType;
     InitOMXParams(&bitrateType);
     bitrateType.nPortIndex = kPortIndexOutput;
@@ -1923,7 +1939,7 @@
         return err;
     }
 
-    bitrateType.eControlRate = OMX_Video_ControlRateVariable;
+    bitrateType.eControlRate = bitrateMode;
     bitrateType.nTargetBitrate = bitrate;
 
     return mOMX->setParameter(
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 0cb378d..1e2625a 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -1504,6 +1504,7 @@
         status_t err = mVideoSource->start();
 
         if (err != OK) {
+            ALOGE("failed to start video source");
             mVideoSource.clear();
             return err;
         }
@@ -1706,7 +1707,8 @@
         }
     }
 
-    if ((mFlags & TEXTPLAYER_INITIALIZED) && !(mFlags & (TEXT_RUNNING | SEEK_PREVIEW))) {
+    if ((mFlags & TEXTPLAYER_INITIALIZED)
+            && !(mFlags & (TEXT_RUNNING | SEEK_PREVIEW))) {
         mTextDriver->start();
         modifyFlags(TEXT_RUNNING, SET);
     }
@@ -1752,17 +1754,24 @@
                 && mAudioPlayer != NULL
                 && mAudioPlayer->getMediaTimeMapping(
                     &realTimeUs, &mediaTimeUs)) {
-            ALOGI("we're much too late (%.2f secs), video skipping ahead",
-                 latenessUs / 1E6);
+            if (mWVMExtractor == NULL) {
+                ALOGI("we're much too late (%.2f secs), video skipping ahead",
+                     latenessUs / 1E6);
 
-            mVideoBuffer->release();
-            mVideoBuffer = NULL;
+                mVideoBuffer->release();
+                mVideoBuffer = NULL;
 
-            mSeeking = SEEK_VIDEO_ONLY;
-            mSeekTimeUs = mediaTimeUs;
+                mSeeking = SEEK_VIDEO_ONLY;
+                mSeekTimeUs = mediaTimeUs;
 
-            postVideoEvent_l();
-            return;
+                postVideoEvent_l();
+                return;
+            } else {
+                // The widevine extractor doesn't deal well with seeking
+                // audio and video independently. We'll just have to wait
+                // until the decoder catches up, which won't be long at all.
+                ALOGI("we're very late (%.2f secs)", latenessUs / 1E6);
+            }
         }
 
         if (latenessUs > 40000) {
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 07f92c7..70de174 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -1689,6 +1689,8 @@
     if (transform) {
         err = native_window_set_buffers_transform(
                 mNativeWindow.get(), transform);
+        ALOGE("native_window_set_buffers_transform failed: %s (%d)",
+                strerror(-err), -err);
     }
 
     return err;
@@ -1703,6 +1705,7 @@
     status_t err = mOMX->getParameter(
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
     if (err != OK) {
+        CODEC_LOGE("getParameter failed: %d", err);
         return err;
     }
 
@@ -3606,6 +3609,7 @@
     Mutex::Autolock autoLock(mLock);
 
     if (mState != LOADED) {
+        CODEC_LOGE("called start in the unexpected state: %d", mState);
         return UNKNOWN_ERROR;
     }
 
@@ -3639,12 +3643,14 @@
         // if supported, the source to use exactly the same number of input
         // buffers as requested by the encoder.
         if ((err = init()) != OK) {
+            CODEC_LOGE("init failed: %d", err);
             return err;
         }
 
         params->setInt32(kKeyNumBuffers, mPortBuffers[kPortIndexInput].size());
         err = mSource->start(params.get());
         if (err != OK) {
+            CODEC_LOGE("source failed to start: %d", err);
             stopOmxComponent_l();
         }
         return err;
@@ -3652,6 +3658,7 @@
 
     // Decoder case
     if ((err = mSource->start(params.get())) != OK) {
+        CODEC_LOGE("source failed to start: %d", err);
         return err;
     }
     return init();
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index ff8cc3e..d88813e 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -27,7 +27,7 @@
 
 #define FILEREAD_MAX_LAYERS 2
 
-#define DRC_DEFAULT_MOBILE_REF_LEVEL 48  /* 48*-0.25dB = -12 dB below full scale for mobile conf */
+#define DRC_DEFAULT_MOBILE_REF_LEVEL 64  /* 64*-0.25dB = -16 dB below full scale for mobile conf */
 #define DRC_DEFAULT_MOBILE_DRC_CUT   127 /* maximum compression of dynamic range for mobile conf */
 #define MAX_CHANNEL_COUNT            6  /* maximum number of audio channels that can be decoded */
 // names of properties that can be used to override the default DRC settings
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 27c7bf4..9faa6bc 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -131,6 +131,8 @@
     sp<AnotherPacketSource> mSource;
     bool mPayloadStarted;
 
+    uint64_t mPrevPTS;
+
     ElementaryStreamQueue *mQueue;
 
     status_t flush();
@@ -458,6 +460,7 @@
       mPCR_PID(PCR_PID),
       mExpectedContinuityCounter(-1),
       mPayloadStarted(false),
+      mPrevPTS(0),
       mQueue(NULL) {
     switch (mStreamType) {
         case STREAMTYPE_H264:
@@ -486,6 +489,11 @@
                     ElementaryStreamQueue::MPEG4_VIDEO);
             break;
 
+        case STREAMTYPE_PCM_AUDIO:
+            mQueue = new ElementaryStreamQueue(
+                    ElementaryStreamQueue::PCM_AUDIO);
+            break;
+
         default:
             break;
     }
@@ -583,6 +591,7 @@
         case STREAMTYPE_MPEG1_AUDIO:
         case STREAMTYPE_MPEG2_AUDIO:
         case STREAMTYPE_MPEG2_AUDIO_ADTS:
+        case STREAMTYPE_PCM_AUDIO:
             return true;
 
         default:
@@ -827,6 +836,14 @@
 void ATSParser::Stream::onPayloadData(
         unsigned PTS_DTS_flags, uint64_t PTS, uint64_t DTS,
         const uint8_t *data, size_t size) {
+#if 0
+    ALOGI("payload streamType 0x%02x, PTS = 0x%016llx, dPTS = %lld",
+          mStreamType,
+          PTS,
+          (int64_t)PTS - mPrevPTS);
+    mPrevPTS = PTS;
+#endif
+
     ALOGV("onPayloadData mStreamType=0x%02x", mStreamType);
 
     int64_t timeUs = 0ll;  // no presentation timestamp available.
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 5ccbab7..46edc45 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -87,6 +87,7 @@
         STREAMTYPE_MPEG2_AUDIO_ADTS     = 0x0f,
         STREAMTYPE_MPEG4_VIDEO          = 0x10,
         STREAMTYPE_H264                 = 0x1b,
+        STREAMTYPE_PCM_AUDIO            = 0x83,
     };
 
 protected:
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index e58e9bf..82fb637 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -31,6 +31,8 @@
 
 #include "include/avc_utils.h"
 
+#include <netinet/in.h>
+
 namespace android {
 
 ElementaryStreamQueue::ElementaryStreamQueue(Mode mode, uint32_t flags)
@@ -248,6 +250,11 @@
                 break;
             }
 
+            case PCM_AUDIO:
+            {
+                break;
+            }
+
             default:
                 TRESPASS();
                 break;
@@ -324,12 +331,68 @@
             return dequeueAccessUnitMPEGVideo();
         case MPEG4_VIDEO:
             return dequeueAccessUnitMPEG4Video();
+        case PCM_AUDIO:
+            return dequeueAccessUnitPCMAudio();
         default:
             CHECK_EQ((unsigned)mMode, (unsigned)MPEG_AUDIO);
             return dequeueAccessUnitMPEGAudio();
     }
 }
 
+sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitPCMAudio() {
+    if (mBuffer->size() < 4) {
+        return NULL;
+    }
+
+    ABitReader bits(mBuffer->data(), 4);
+    CHECK_EQ(bits.getBits(8), 0xa0);
+    unsigned numAUs = bits.getBits(8);
+    bits.skipBits(8);
+    unsigned quantization_word_length = bits.getBits(2);
+    unsigned audio_sampling_frequency = bits.getBits(3);
+    unsigned num_channels = bits.getBits(3);
+
+    CHECK_EQ(audio_sampling_frequency, 2);  // 48kHz
+    CHECK_EQ(num_channels, 1u);  // stereo!
+
+    if (mFormat == NULL) {
+        mFormat = new MetaData;
+        mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+        mFormat->setInt32(kKeyChannelCount, 2);
+        mFormat->setInt32(kKeySampleRate, 48000);
+    }
+
+    static const size_t kFramesPerAU = 80;
+    size_t frameSize = 2 /* numChannels */ * sizeof(int16_t);
+
+    size_t payloadSize = numAUs * frameSize * kFramesPerAU;
+
+    if (mBuffer->size() < 4 + payloadSize) {
+        return NULL;
+    }
+
+    sp<ABuffer> accessUnit = new ABuffer(payloadSize);
+    memcpy(accessUnit->data(), mBuffer->data() + 4, payloadSize);
+
+    int64_t timeUs = fetchTimestamp(payloadSize + 4);
+    CHECK_GE(timeUs, 0ll);
+    accessUnit->meta()->setInt64("timeUs", timeUs);
+
+    int16_t *ptr = (int16_t *)accessUnit->data();
+    for (size_t i = 0; i < payloadSize / sizeof(int16_t); ++i) {
+        ptr[i] = ntohs(ptr[i]);
+    }
+
+    memmove(
+            mBuffer->data(),
+            mBuffer->data() + 4 + payloadSize,
+            mBuffer->size() - 4 - payloadSize);
+
+    mBuffer->setRange(0, mBuffer->size() - 4 - payloadSize);
+
+    return accessUnit;
+}
+
 sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAAC() {
     int64_t timeUs;
 
diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h
index 72aa2e7..66a8087 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.h
+++ b/media/libstagefright/mpeg2ts/ESQueue.h
@@ -35,6 +35,7 @@
         MPEG_AUDIO,
         MPEG_VIDEO,
         MPEG4_VIDEO,
+        PCM_AUDIO,
     };
 
     enum Flags {
@@ -69,6 +70,7 @@
     sp<ABuffer> dequeueAccessUnitMPEGAudio();
     sp<ABuffer> dequeueAccessUnitMPEGVideo();
     sp<ABuffer> dequeueAccessUnitMPEG4Video();
+    sp<ABuffer> dequeueAccessUnitPCMAudio();
 
     // consume a logical (compressed) access unit of size "size",
     // returns its timestamp in us (or -1 if no time information).
diff --git a/media/libstagefright/wifi-display/ANetworkSession.cpp b/media/libstagefright/wifi-display/ANetworkSession.cpp
index 0279c34..819cd62 100644
--- a/media/libstagefright/wifi-display/ANetworkSession.cpp
+++ b/media/libstagefright/wifi-display/ANetworkSession.cpp
@@ -94,8 +94,11 @@
     sp<AMessage> mNotify;
     bool mSawReceiveFailure, mSawSendFailure;
 
+    // for TCP / stream data
     AString mOutBuffer;
-    List<size_t> mOutBufferSizes;
+
+    // for UDP / datagrams
+    List<sp<ABuffer> > mOutDatagrams;
 
     AString mInBuffer;
 
@@ -213,8 +216,8 @@
 bool ANetworkSession::Session::wantsToWrite() {
     return !mSawSendFailure
         && (mState == CONNECTING
-            || ((mState == CONNECTED || mState == DATAGRAM)
-                    && !mOutBuffer.empty()));
+            || (mState == CONNECTED && !mOutBuffer.empty())
+            || (mState == DATAGRAM && !mOutDatagrams.empty()));
 }
 
 status_t ANetworkSession::Session::readMore() {
@@ -398,30 +401,27 @@
 
 status_t ANetworkSession::Session::writeMore() {
     if (mState == DATAGRAM) {
-        CHECK(!mOutBufferSizes.empty());
+        CHECK(!mOutDatagrams.empty());
 
         status_t err;
         do {
-            size_t size = *mOutBufferSizes.begin();
-
-            CHECK_GE(mOutBuffer.size(), size);
+            const sp<ABuffer> &datagram = *mOutDatagrams.begin();
 
             int n;
             do {
-                n = send(mSocket, mOutBuffer.c_str(), size, 0);
+                n = send(mSocket, datagram->data(), datagram->size(), 0);
             } while (n < 0 && errno == EINTR);
 
             err = OK;
 
             if (n > 0) {
-                mOutBufferSizes.erase(mOutBufferSizes.begin());
-                mOutBuffer.erase(0, n);
+                mOutDatagrams.erase(mOutDatagrams.begin());
             } else if (n < 0) {
                 err = -errno;
             } else if (n == 0) {
                 err = -ECONNRESET;
             }
-        } while (err == OK && !mOutBufferSizes.empty());
+        } while (err == OK && !mOutDatagrams.empty());
 
         if (err == -EAGAIN) {
             err = OK;
@@ -488,6 +488,16 @@
 status_t ANetworkSession::Session::sendRequest(const void *data, ssize_t size) {
     CHECK(mState == CONNECTED || mState == DATAGRAM);
 
+    if (mState == DATAGRAM) {
+        CHECK_GE(size, 0);
+
+        sp<ABuffer> datagram = new ABuffer(size);
+        memcpy(datagram->data(), data, size);
+
+        mOutDatagrams.push_back(datagram);
+        return OK;
+    }
+
     if (mState == CONNECTED && !mIsRTSPConnection) {
         CHECK_LE(size, 65535);
 
@@ -502,11 +512,6 @@
             (const char *)data,
             (size >= 0) ? size : strlen((const char *)data));
 
-    if (mState == DATAGRAM) {
-        CHECK_GE(size, 0);
-        mOutBufferSizes.push_back(size);
-    }
-
     return OK;
 }
 
diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk
index f8e4219..611bfff 100644
--- a/media/libstagefright/wifi-display/Android.mk
+++ b/media/libstagefright/wifi-display/Android.mk
@@ -14,6 +14,7 @@
         source/MediaPuller.cpp          \
         source/PlaybackSession.cpp      \
         source/RepeaterSource.cpp       \
+        source/Sender.cpp               \
         source/TSPacketizer.cpp         \
         source/WifiDisplaySource.cpp    \
 
diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp
index a4a6f07..01a394f 100644
--- a/media/libstagefright/wifi-display/source/Converter.cpp
+++ b/media/libstagefright/wifi-display/source/Converter.cpp
@@ -33,6 +33,8 @@
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 
+#include <OMX_Video.h>
+
 namespace android {
 
 Converter::Converter(
@@ -143,7 +145,7 @@
     mOutputFormat->setString("mime", outputMIME.c_str());
 
     int32_t audioBitrate = getBitrate("media.wfd.audio-bitrate", 128000);
-    int32_t videoBitrate = getBitrate("media.wfd.video-bitrate", 2500000);
+    int32_t videoBitrate = getBitrate("media.wfd.video-bitrate", 5000000);
 
     ALOGI("using audio bitrate of %d bps, video bitrate of %d bps",
           audioBitrate, videoBitrate);
@@ -152,6 +154,7 @@
         mOutputFormat->setInt32("bitrate", audioBitrate);
     } else {
         mOutputFormat->setInt32("bitrate", videoBitrate);
+        mOutputFormat->setInt32("bitrate-mode", OMX_Video_ControlRateConstant);
         mOutputFormat->setInt32("frame-rate", 30);
         mOutputFormat->setInt32("i-frame-interval", 1);  // Iframes every 1 secs
         mOutputFormat->setInt32("prepend-sps-pps-to-idr-frames", 1);
@@ -465,7 +468,7 @@
             timeUs += copyUs;
             buffer->meta()->setInt64("timeUs", timeUs);
 
-            if (copy == partialAudioAU->size() - 4) {
+            if (copy == partialAudioAU->capacity() - 4) {
                 sp<AMessage> notify = mNotify->dup();
                 notify->setInt32("what", kWhatAccessUnit);
                 notify->setBuffer("accessUnit", partialAudioAU);
diff --git a/media/libstagefright/wifi-display/source/Converter.h b/media/libstagefright/wifi-display/source/Converter.h
index 8dfff3d..2cdeda3 100644
--- a/media/libstagefright/wifi-display/source/Converter.h
+++ b/media/libstagefright/wifi-display/source/Converter.h
@@ -27,7 +27,7 @@
 struct ABuffer;
 struct MediaCodec;
 
-#define ENABLE_SILENCE_DETECTION        1
+#define ENABLE_SILENCE_DETECTION        0
 
 // Utility class that receives media access units and converts them into
 // media access unit of a different format.
@@ -120,4 +120,3 @@
 }  // namespace android
 
 #endif  // CONVERTER_H_
-
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
index 6ef5e40..f1e7140 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
@@ -23,6 +23,7 @@
 #include "Converter.h"
 #include "MediaPuller.h"
 #include "RepeaterSource.h"
+#include "Sender.h"
 #include "TSPacketizer.h"
 #include "include/avc_utils.h"
 
@@ -50,9 +51,6 @@
 
 namespace android {
 
-static size_t kMaxRTPPacketSize = 1500;
-static size_t kMaxNumTSPacketsPerRTPPacket = (kMaxRTPPacketSize - 12) / 188;
-
 struct WifiDisplaySource::PlaybackSession::Track : public AHandler {
     enum {
         kWhatStopped,
@@ -80,6 +78,15 @@
     void queueAccessUnit(const sp<ABuffer> &accessUnit);
     sp<ABuffer> dequeueAccessUnit();
 
+    bool hasOutputBuffer(int64_t *timeUs) const;
+    void queueOutputBuffer(const sp<ABuffer> &accessUnit);
+    sp<ABuffer> dequeueOutputBuffer();
+    bool isSuspended() const;
+
+    size_t countQueuedOutputBuffers() const {
+        return mQueuedOutputBuffers.size();
+    }
+
     void requestIDRFrame();
 
 protected:
@@ -101,6 +108,8 @@
     bool mIsAudio;
     List<sp<ABuffer> > mQueuedAccessUnits;
     sp<RepeaterSource> mRepeaterSource;
+    List<sp<ABuffer> > mQueuedOutputBuffers;
+    int64_t mLastOutputBufferQueuedTimeUs;
 
     static bool IsAudioFormat(const sp<AMessage> &format);
 
@@ -120,7 +129,8 @@
       mConverter(converter),
       mStarted(false),
       mPacketizerTrackIndex(-1),
-      mIsAudio(IsAudioFormat(mConverter->getOutputFormat())) {
+      mIsAudio(IsAudioFormat(mConverter->getOutputFormat())),
+      mLastOutputBufferQueuedTimeUs(-1ll) {
 }
 
 WifiDisplaySource::PlaybackSession::Track::~Track() {
@@ -251,6 +261,53 @@
     mConverter->requestIDRFrame();
 }
 
+bool WifiDisplaySource::PlaybackSession::Track::hasOutputBuffer(
+        int64_t *timeUs) const {
+    *timeUs = 0ll;
+
+    if (mQueuedOutputBuffers.empty()) {
+        return false;
+    }
+
+    const sp<ABuffer> &outputBuffer = *mQueuedOutputBuffers.begin();
+
+    CHECK(outputBuffer->meta()->findInt64("timeUs", timeUs));
+
+    return true;
+}
+
+void WifiDisplaySource::PlaybackSession::Track::queueOutputBuffer(
+        const sp<ABuffer> &accessUnit) {
+    mQueuedOutputBuffers.push_back(accessUnit);
+
+    mLastOutputBufferQueuedTimeUs = ALooper::GetNowUs();
+}
+
+sp<ABuffer> WifiDisplaySource::PlaybackSession::Track::dequeueOutputBuffer() {
+    CHECK(!mQueuedOutputBuffers.empty());
+
+    sp<ABuffer> outputBuffer = *mQueuedOutputBuffers.begin();
+    mQueuedOutputBuffers.erase(mQueuedOutputBuffers.begin());
+
+    return outputBuffer;
+}
+
+bool WifiDisplaySource::PlaybackSession::Track::isSuspended() const {
+    if (!mQueuedOutputBuffers.empty()) {
+        return false;
+    }
+
+    if (mLastOutputBufferQueuedTimeUs < 0ll) {
+        // We've never seen an output buffer queued, but tracks start
+        // out live, not suspended.
+        return false;
+    }
+
+    // If we've not seen new output data for 60ms or more, we consider
+    // this track suspended for the time being.
+    return (ALooper::GetNowUs() - mLastOutputBufferQueuedTimeUs) > 60000ll;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 WifiDisplaySource::PlaybackSession::PlaybackSession(
@@ -265,197 +322,37 @@
       mWeAreDead(false),
       mLastLifesignUs(),
       mVideoTrackIndex(-1),
-      mTSQueue(new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188)),
       mPrevTimeUs(-1ll),
-      mTransportMode(TRANSPORT_UDP),
-      mAllTracksHavePacketizerIndex(false),
-      mRTPChannel(0),
-      mRTCPChannel(0),
-      mRTPPort(0),
-      mRTPSessionID(0),
-      mRTCPSessionID(0),
-#if ENABLE_RETRANSMISSION
-      mRTPRetransmissionSessionID(0),
-      mRTCPRetransmissionSessionID(0),
-#endif
-      mClientRTPPort(0),
-      mClientRTCPPort(0),
-      mRTPConnected(false),
-      mRTCPConnected(false),
-      mRTPSeqNo(0),
-#if ENABLE_RETRANSMISSION
-      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
-{
-    mTSQueue->setRange(0, 12);
-
-#if LOG_TRANSPORT_STREAM
-    mLogFile = fopen("/system/etc/log.ts", "wb");
-#endif
+      mAllTracksHavePacketizerIndex(false) {
 }
 
 status_t WifiDisplaySource::PlaybackSession::init(
         const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
-        TransportMode transportMode,
+        Sender::TransportMode transportMode,
         bool usePCMAudio) {
-    mClientIP = clientIP;
-
     status_t err = setupPacketizer(usePCMAudio);
 
     if (err != OK) {
         return err;
     }
 
-    mTransportMode = transportMode;
+    sp<AMessage> notify = new AMessage(kWhatSenderNotify, id());
+    mSender = new Sender(mNetSession, notify);
 
-    if (transportMode == TRANSPORT_TCP_INTERLEAVED) {
-        mRTPChannel = clientRtp;
-        mRTCPChannel = clientRtcp;
-        mRTPPort = 0;
-        mRTPSessionID = 0;
-        mRTCPSessionID = 0;
+    mSenderLooper = new ALooper;
+    mSenderLooper->setName("sender_looper");
 
-        updateLiveness();
-        return OK;
-    }
+    mSenderLooper->start(
+            false /* runOnCallingThread */,
+            false /* canCallJava */,
+            PRIORITY_AUDIO);
 
-    mRTPChannel = 0;
-    mRTCPChannel = 0;
+    mSenderLooper->registerHandler(mSender);
 
-    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;
+    err = mSender->init(clientIP, clientRtp, clientRtcp, transportMode);
 
-        updateLiveness();
-        return OK;
-    }
-
-    int serverRtp;
-
-    sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
-    sp<AMessage> rtcpNotify = new AMessage(kWhatRTCPNotify, id());
-
-#if ENABLE_RETRANSMISSION
-    sp<AMessage> rtpRetransmissionNotify =
-        new AMessage(kWhatRTPRetransmissionNotify, id());
-
-    sp<AMessage> rtcpRetransmissionNotify =
-        new AMessage(kWhatRTCPRetransmissionNotify, id());
-#endif
-
-    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
-        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;
+    if (err != OK) {
+        return err;
     }
 
     updateLiveness();
@@ -464,16 +361,10 @@
 }
 
 WifiDisplaySource::PlaybackSession::~PlaybackSession() {
-#if LOG_TRANSPORT_STREAM
-    if (mLogFile != NULL) {
-        fclose(mLogFile);
-        mLogFile = NULL;
-    }
-#endif
 }
 
 int32_t WifiDisplaySource::PlaybackSession::getRTPPort() const {
-    return mRTPPort;
+    return mSender->getRTPPort();
 }
 
 int64_t WifiDisplaySource::PlaybackSession::getLastLifesignUs() const {
@@ -497,35 +388,11 @@
 }
 
 status_t WifiDisplaySource::PlaybackSession::onFinishPlay() {
-    if (mTransportMode != TRANSPORT_TCP) {
-        return onFinishPlay2();
-    }
-
-    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);
-    }
-
-    return err;
+    return mSender->finishInit();
 }
 
 status_t WifiDisplaySource::PlaybackSession::onFinishPlay2() {
-    if (mRTCPSessionID != 0) {
-        scheduleSendSR();
-    }
+    mSender->scheduleSendSR();
 
     for (size_t i = 0; i < mTracks.size(); ++i) {
         CHECK_EQ((status_t)OK, mTracks.editValueAt(i)->start());
@@ -555,134 +422,6 @@
 void WifiDisplaySource::PlaybackSession::onMessageReceived(
         const sp<AMessage> &msg) {
     switch (msg->what()) {
-        case kWhatRTPNotify:
-        case kWhatRTCPNotify:
-#if ENABLE_RETRANSMISSION
-        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
-                            || 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
-                    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
-                            || 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)) {
-                        onFinishPlay2();
-                    }
-                    break;
-                }
-
-                default:
-                    TRESPASS();
-            }
-            break;
-        }
-
-        case kWhatSendSR:
-        {
-            mSendSRPending = false;
-
-            if (mRTCPSessionID == 0) {
-                break;
-            }
-
-            onSendSR();
-
-            scheduleSendSR();
-            break;
-        }
-
         case kWhatConverterNotify:
         {
             if (mWeAreDead) {
@@ -729,11 +468,9 @@
                     break;
                 }
 
-                status_t err = packetizeAccessUnit(trackIndex, accessUnit);
+                track->queueOutputBuffer(accessUnit);
 
-                if (err != OK) {
-                    notifySessionDead();
-                }
+                drainAccessUnits();
                 break;
             } else if (what == Converter::kWhatEOS) {
                 CHECK_EQ(what, Converter::kWhatEOS);
@@ -765,6 +502,22 @@
             break;
         }
 
+        case kWhatSenderNotify:
+        {
+            int32_t what;
+            CHECK(msg->findInt32("what", &what));
+
+            if (what == Sender::kWhatInitDone) {
+                onFinishPlay2();
+            } else if (what == Sender::kWhatSessionDead) {
+                notifySessionDead();
+            } else {
+                TRESPASS();
+            }
+
+            break;
+        }
+
         case kWhatFinishPlay:
         {
             onFinishPlay();
@@ -792,26 +545,12 @@
                     break;
                 }
 
+                mSenderLooper->unregisterHandler(mSender->id());
+                mSender.clear();
+                mSenderLooper.clear();
+
                 mPacketizer.clear();
 
-#if ENABLE_RETRANSMISSION
-                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);
-                }
-
                 sp<AMessage> notify = mNotify->dup();
                 notify->setInt32("what", kWhatSessionDestroyed);
                 notify->post();
@@ -819,6 +558,28 @@
             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;
+        }
+
         default:
             TRESPASS();
     }
@@ -981,372 +742,6 @@
     return 720;
 }
 
-void WifiDisplaySource::PlaybackSession::scheduleSendSR() {
-    if (mSendSRPending) {
-        return;
-    }
-
-    mSendSRPending = true;
-    (new AMessage(kWhatSendSR, id()))->post(kSendSRIntervalUs);
-}
-
-void WifiDisplaySource::PlaybackSession::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 WifiDisplaySource::PlaybackSession::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 WifiDisplaySource::PlaybackSession::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 WifiDisplaySource::PlaybackSession::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;
-}
-
-ssize_t WifiDisplaySource::PlaybackSession::appendTSData(
-        const void *data, size_t size, bool timeDiscontinuity, bool flush) {
-    CHECK_EQ(size, 188);
-
-    CHECK_LE(mTSQueue->size() + size, mTSQueue->capacity());
-
-    memcpy(mTSQueue->data() + mTSQueue->size(), data, size);
-    mTSQueue->setRange(0, mTSQueue->size() + size);
-
-    if (flush || mTSQueue->size() == mTSQueue->capacity()) {
-        // flush
-
-        int64_t nowUs = ALooper::GetNowUs();
-
-#if TRACK_BANDWIDTH
-        if (mFirstPacketTimeUs < 0ll) {
-            mFirstPacketTimeUs = nowUs;
-        }
-#endif
-
-        // 90kHz time scale
-        uint32_t rtpTime = (nowUs * 9ll) / 100ll;
-
-        uint8_t *rtp = mTSQueue->data();
-        rtp[0] = 0x80;
-        rtp[1] = 33 | (timeDiscontinuity ? (1 << 7) : 0);  // M-bit
-        rtp[2] = (mRTPSeqNo >> 8) & 0xff;
-        rtp[3] = mRTPSeqNo & 0xff;
-        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;
-
-        ++mRTPSeqNo;
-        ++mNumRTPSent;
-        mNumRTPOctetsSent += mTSQueue->size() - 12;
-
-        mLastRTPTime = rtpTime;
-        mLastNTPTime = GetNowNTP();
-
-        if (mTransportMode == TRANSPORT_TCP_INTERLEAVED) {
-            sp<AMessage> notify = mNotify->dup();
-            notify->setInt32("what", kWhatBinaryData);
-
-            sp<ABuffer> data = new ABuffer(mTSQueue->size());
-            memcpy(data->data(), rtp, mTSQueue->size());
-
-            notify->setInt32("channel", mRTPChannel);
-            notify->setBuffer("data", data);
-            notify->post();
-        } else {
-            sendPacket(mRTPSessionID, rtp, mTSQueue->size());
-
-#if TRACK_BANDWIDTH
-            mTotalBytesSent += mTSQueue->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
-        mTSQueue->setInt32Data(mRTPSeqNo - 1);
-
-        mHistory.push_back(mTSQueue);
-        ++mHistoryLength;
-
-        if (mHistoryLength > kMaxHistoryLength) {
-            mTSQueue = *mHistory.begin();
-            mHistory.erase(mHistory.begin());
-
-            --mHistoryLength;
-        } else {
-            mTSQueue = new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188);
-        }
-#endif
-
-        mTSQueue->setRange(0, 12);
-    }
-
-    return size;
-}
-
-status_t WifiDisplaySource::PlaybackSession::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;
-}
-
-#if ENABLE_RETRANSMISSION
-status_t WifiDisplaySource::PlaybackSession::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);
-
-                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());
-
-                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");
-        }
-    }
-
-    return OK;
-}
-#endif
-
 void WifiDisplaySource::PlaybackSession::requestIDRFrame() {
     for (size_t i = 0; i < mTracks.size(); ++i) {
         const sp<Track> &track = mTracks.valueAt(i);
@@ -1355,11 +750,6 @@
     }
 }
 
-status_t WifiDisplaySource::PlaybackSession::sendPacket(
-        int32_t sessionID, const void *data, size_t size) {
-    return mNetSession->sendRequest(sessionID, data, size);
-}
-
 bool WifiDisplaySource::PlaybackSession::allTracksHavePacketizerIndex() {
     if (mAllTracksHavePacketizerIndex) {
         return true;
@@ -1377,7 +767,8 @@
 }
 
 status_t WifiDisplaySource::PlaybackSession::packetizeAccessUnit(
-        size_t trackIndex, const sp<ABuffer> &accessUnit) {
+        size_t trackIndex, const sp<ABuffer> &accessUnit,
+        sp<ABuffer> *packets) {
     const sp<Track> &track = mTracks.valueFor(trackIndex);
 
     uint32_t flags = 0;
@@ -1477,35 +868,11 @@
         mPrevTimeUs = timeUs;
     }
 
-    sp<ABuffer> packets;
     mPacketizer->packetize(
-            track->packetizerTrackIndex(), accessUnit, &packets, flags,
+            track->packetizerTrackIndex(), accessUnit, packets, flags,
             !isHDCPEncrypted ? NULL : HDCP_private_data,
-            !isHDCPEncrypted ? 0 : sizeof(HDCP_private_data));
-
-    for (size_t offset = 0;
-            offset < packets->size(); offset += 188) {
-        bool lastTSPacket = (offset + 188 >= packets->size());
-
-        // We're only going to flush video, audio packets are
-        // much more frequent and would waste all that space
-        // available in a full sized UDP packet.
-        bool flush =
-            lastTSPacket
-                && ((ssize_t)trackIndex == mVideoTrackIndex);
-
-        appendTSData(
-                packets->data() + offset,
-                188,
-                true /* timeDiscontinuity */,
-                flush);
-    }
-
-#if LOG_TRANSPORT_STREAM
-    if (mLogFile != NULL) {
-        fwrite(packets->data(), 1, packets->size(), mLogFile);
-    }
-#endif
+            !isHDCPEncrypted ? 0 : sizeof(HDCP_private_data),
+            track->isAudio() ? 2 : 0 /* numStuffingBytes */);
 
     return OK;
 }
@@ -1519,12 +886,7 @@
 
             sp<ABuffer> accessUnit = track->dequeueAccessUnit();
             if (accessUnit != NULL) {
-                status_t err = packetizeAccessUnit(trackIndex, accessUnit);
-
-                if (err != OK) {
-                    return err;
-                }
-
+                track->queueOutputBuffer(accessUnit);
                 gotMoreData = true;
             }
         }
@@ -1546,5 +908,57 @@
     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;
+            }
+        } 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;
+        }
+    }
+
+    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();
+    }
+
+    if ((ssize_t)minTrackIndex == mVideoTrackIndex) {
+        packets->meta()->setInt32("isVideo", 1);
+    }
+    mSender->queuePackets(minTimeUs, packets);
+
+    return true;
+}
+
 }  // namespace android
 
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h
index 4bbc3f0..cc8b244 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.h
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.h
@@ -18,6 +18,7 @@
 
 #define PLAYBACK_SESSION_H_
 
+#include "Sender.h"
 #include "WifiDisplaySource.h"
 
 namespace android {
@@ -30,10 +31,6 @@
 struct MediaSource;
 struct TSPacketizer;
 
-#define LOG_TRANSPORT_STREAM            0
-#define ENABLE_RETRANSMISSION           0
-#define TRACK_BANDWIDTH                 0
-
 // Encapsulates the state of an RTP/RTCP session in the context of wifi
 // display.
 struct WifiDisplaySource::PlaybackSession : public AHandler {
@@ -43,14 +40,9 @@
             const struct in_addr &interfaceAddr,
             const sp<IHDCP> &hdcp);
 
-    enum TransportMode {
-        TRANSPORT_UDP,
-        TRANSPORT_TCP_INTERLEAVED,
-        TRANSPORT_TCP,
-    };
     status_t init(
             const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
-            TransportMode transportMode,
+            Sender::TransportMode transportMode,
             bool usePCMAudio);
 
     void destroyAsync();
@@ -85,29 +77,18 @@
     struct Track;
 
     enum {
-        kWhatSendSR,
-        kWhatRTPNotify,
-        kWhatRTCPNotify,
-#if ENABLE_RETRANSMISSION
-        kWhatRTPRetransmissionNotify,
-        kWhatRTCPRetransmissionNotify,
-#endif
         kWhatMediaPullerNotify,
         kWhatConverterNotify,
         kWhatTrackNotify,
+        kWhatSenderNotify,
         kWhatUpdateSurface,
         kWhatFinishPlay,
+        kWhatPacketize,
     };
 
-    static const int64_t kSendSRIntervalUs = 10000000ll;
-    static const uint32_t kSourceID = 0xdeadbeef;
-    static const size_t kMaxHistoryLength = 128;
-
-#if ENABLE_RETRANSMISSION
-    static const size_t kRetransmissionPortOffset = 120;
-#endif
-
     sp<ANetworkSession> mNetSession;
+    sp<Sender> mSender;
+    sp<ALooper> mSenderLooper;
     sp<AMessage> mNotify;
     in_addr mInterfaceAddr;
     sp<IHDCP> mHDCP;
@@ -121,66 +102,10 @@
     KeyedVector<size_t, sp<Track> > mTracks;
     ssize_t mVideoTrackIndex;
 
-    sp<ABuffer> mTSQueue;
     int64_t mPrevTimeUs;
 
-    TransportMode mTransportMode;
-
-    AString mClientIP;
-
     bool mAllTracksHavePacketizerIndex;
 
-    // in TCP mode
-    int32_t mRTPChannel;
-    int32_t mRTCPChannel;
-
-    // in UDP mode
-    int32_t mRTPPort;
-    int32_t mRTPSessionID;
-    int32_t mRTCPSessionID;
-
-#if ENABLE_RETRANSMISSION
-    int32_t mRTPRetransmissionSessionID;
-    int32_t mRTCPRetransmissionSessionID;
-#endif
-
-    int32_t mClientRTPPort;
-    int32_t mClientRTCPPort;
-    bool mRTPConnected;
-    bool mRTCPConnected;
-
-    uint32_t mRTPSeqNo;
-#if ENABLE_RETRANSMISSION
-    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();
-
     status_t setupPacketizer(bool usePCMAudio);
 
     status_t addSource(
@@ -196,27 +121,24 @@
     ssize_t appendTSData(
             const void *data, size_t size, bool timeDiscontinuity, bool flush);
 
-    void scheduleSendSR();
-
-    status_t parseRTCP(const sp<ABuffer> &buffer);
-
-#if ENABLE_RETRANSMISSION
-    status_t parseTSFB(const uint8_t *data, size_t size);
-#endif
-
-    status_t sendPacket(int32_t sessionID, const void *data, size_t size);
     status_t onFinishPlay();
     status_t onFinishPlay2();
 
     bool allTracksHavePacketizerIndex();
 
     status_t packetizeAccessUnit(
-            size_t trackIndex, const sp<ABuffer> &accessUnit);
+            size_t trackIndex, const sp<ABuffer> &accessUnit,
+            sp<ABuffer> *packets);
 
     status_t packetizeQueuedAccessUnits();
 
     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/Sender.cpp b/media/libstagefright/wifi-display/source/Sender.cpp
new file mode 100644
index 0000000..ea12424
--- /dev/null
+++ b/media/libstagefright/wifi-display/source/Sender.cpp
@@ -0,0 +1,979 @@
+/*
+ * 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 <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 <math.h>
+
+#define DEBUG_JITTER    0
+
+namespace android {
+
+////////////////////////////////////////////////////////////////////////////////
+
+#if DEBUG_JITTER
+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;
+};
+
+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);
+}
+#endif  // DEBUG_JITTER
+
+////////////////////////////////////////////////////////////////////////////////
+
+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),
+      mTSQueue(new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188)),
+      mTransportMode(TRANSPORT_UDP),
+      mRTPChannel(0),
+      mRTCPChannel(0),
+      mRTPPort(0),
+      mRTPSessionID(0),
+      mRTCPSessionID(0),
+#if ENABLE_RETRANSMISSION
+      mRTPRetransmissionSessionID(0),
+      mRTCPRetransmissionSessionID(0),
+#endif
+      mClientRTPPort(0),
+      mClientRTCPPort(0),
+      mRTPConnected(false),
+      mRTCPConnected(false),
+      mFirstOutputBufferReadyTimeUs(-1ll),
+      mFirstOutputBufferSentTimeUs(-1ll),
+      mRTPSeqNo(0),
+#if ENABLE_RETRANSMISSION
+      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
+{
+    mTSQueue->setRange(0, 12);
+
+#if LOG_TRANSPORT_STREAM
+    mLogFile = fopen("/system/etc/log.ts", "wb");
+#endif
+}
+
+Sender::~Sender() {
+#if ENABLE_RETRANSMISSION
+    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
+    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
+        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> &packets) {
+    bool isVideo = false;
+
+    int32_t dummy;
+    if (packets->meta()->findInt32("isVideo", &dummy)) {
+        isVideo = true;
+    }
+
+    int64_t delayUs;
+    int64_t whenUs;
+
+    if (mFirstOutputBufferReadyTimeUs < 0ll) {
+        mFirstOutputBufferReadyTimeUs = timeUs;
+        mFirstOutputBufferSentTimeUs = whenUs = ALooper::GetNowUs();
+        delayUs = 0ll;
+    } else {
+        int64_t nowUs = ALooper::GetNowUs();
+
+        whenUs = (timeUs - mFirstOutputBufferReadyTimeUs)
+                + mFirstOutputBufferSentTimeUs;
+
+        delayUs = whenUs - nowUs;
+    }
+
+    sp<AMessage> msg = new AMessage(kWhatQueuePackets, id());
+    msg->setBuffer("packets", packets);
+
+    packets->meta()->setInt64("timeUs", timeUs);
+    packets->meta()->setInt64("whenUs", whenUs);
+    packets->meta()->setInt64("delayUs", delayUs);
+    msg->post(delayUs > 0 ? delayUs : 0);
+}
+
+void Sender::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatRTPNotify:
+        case kWhatRTCPNotify:
+#if ENABLE_RETRANSMISSION
+        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
+                            || 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
+                    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
+                            || 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 kWhatQueuePackets:
+        {
+            sp<ABuffer> packets;
+            CHECK(msg->findBuffer("packets", &packets));
+
+            onQueuePackets(packets);
+            break;
+        }
+
+        case kWhatSendSR:
+        {
+            mSendSRPending = false;
+
+            if (mRTCPSessionID == 0) {
+                break;
+            }
+
+            onSendSR();
+
+            scheduleSendSR();
+            break;
+        }
+    }
+}
+
+void Sender::onQueuePackets(const sp<ABuffer> &packets) {
+#if DEBUG_JITTER
+    int32_t dummy;
+    if (packets->meta()->findInt32("isVideo", &dummy)) {
+        static int64_t lastTimeUs = 0ll;
+        int64_t nowUs = ALooper::GetNowUs();
+
+        static TimeSeries series;
+        series.add((double)(nowUs - lastTimeUs));
+
+        ALOGI("deltaTimeUs = %lld us, mean %.2f, sdev %.2f",
+              nowUs - lastTimeUs, series.mean(), series.sdev());
+
+        lastTimeUs = nowUs;
+    }
+#endif
+
+    int64_t startTimeUs = ALooper::GetNowUs();
+
+    for (size_t offset = 0;
+            offset < packets->size(); offset += 188) {
+        bool lastTSPacket = (offset + 188 >= packets->size());
+
+        appendTSData(
+                packets->data() + offset,
+                188,
+                true /* timeDiscontinuity */,
+                lastTSPacket /* flush */);
+    }
+
+#if 0
+    int64_t netTimeUs = ALooper::GetNowUs() - startTimeUs;
+
+    int64_t whenUs;
+    CHECK(packets->meta()->findInt64("whenUs", &whenUs));
+
+    int64_t delayUs;
+    CHECK(packets->meta()->findInt64("delayUs", &delayUs));
+
+    bool isVideo = false;
+    int32_t dummy;
+    if (packets->meta()->findInt32("isVideo", &dummy)) {
+        isVideo = true;
+    }
+
+    int64_t nowUs = ALooper::GetNowUs();
+
+    if (nowUs - whenUs > 2000) {
+        ALOGI("[%s] delayUs = %lld us, delta = %lld us",
+              isVideo ? "video" : "audio", delayUs, nowUs - netTimeUs - whenUs);
+    }
+#endif
+
+#if LOG_TRANSPORT_STREAM
+    if (mLogFile != NULL) {
+        fwrite(packets->data(), 1, packets->size(), mLogFile);
+    }
+#endif
+}
+
+ssize_t Sender::appendTSData(
+        const void *data, size_t size, bool timeDiscontinuity, bool flush) {
+    CHECK_EQ(size, 188);
+
+    CHECK_LE(mTSQueue->size() + size, mTSQueue->capacity());
+
+    memcpy(mTSQueue->data() + mTSQueue->size(), data, size);
+    mTSQueue->setRange(0, mTSQueue->size() + size);
+
+    if (flush || mTSQueue->size() == mTSQueue->capacity()) {
+        // flush
+
+        int64_t nowUs = ALooper::GetNowUs();
+
+#if TRACK_BANDWIDTH
+        if (mFirstPacketTimeUs < 0ll) {
+            mFirstPacketTimeUs = nowUs;
+        }
+#endif
+
+        // 90kHz time scale
+        uint32_t rtpTime = (nowUs * 9ll) / 100ll;
+
+        uint8_t *rtp = mTSQueue->data();
+        rtp[0] = 0x80;
+        rtp[1] = 33 | (timeDiscontinuity ? (1 << 7) : 0);  // M-bit
+        rtp[2] = (mRTPSeqNo >> 8) & 0xff;
+        rtp[3] = mRTPSeqNo & 0xff;
+        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;
+
+        ++mRTPSeqNo;
+        ++mNumRTPSent;
+        mNumRTPOctetsSent += mTSQueue->size() - 12;
+
+        mLastRTPTime = rtpTime;
+        mLastNTPTime = GetNowNTP();
+
+        if (mTransportMode == TRANSPORT_TCP_INTERLEAVED) {
+            sp<AMessage> notify = mNotify->dup();
+            notify->setInt32("what", kWhatBinaryData);
+
+            sp<ABuffer> data = new ABuffer(mTSQueue->size());
+            memcpy(data->data(), rtp, mTSQueue->size());
+
+            notify->setInt32("channel", mRTPChannel);
+            notify->setBuffer("data", data);
+            notify->post();
+        } else {
+            sendPacket(mRTPSessionID, rtp, mTSQueue->size());
+
+#if TRACK_BANDWIDTH
+            mTotalBytesSent += mTSQueue->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
+        mTSQueue->setInt32Data(mRTPSeqNo - 1);
+
+        mHistory.push_back(mTSQueue);
+        ++mHistoryLength;
+
+        if (mHistoryLength > kMaxHistoryLength) {
+            mTSQueue = *mHistory.begin();
+            mHistory.erase(mHistory.begin());
+
+            --mHistoryLength;
+        } else {
+            mTSQueue = new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188);
+        }
+#endif
+
+        mTSQueue->setRange(0, 12);
+    }
+
+    return size;
+}
+
+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);
+
+                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());
+
+                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");
+        }
+    }
+
+    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();
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/wifi-display/source/Sender.h b/media/libstagefright/wifi-display/source/Sender.h
new file mode 100644
index 0000000..e476e84
--- /dev/null
+++ b/media/libstagefright/wifi-display/source/Sender.h
@@ -0,0 +1,166 @@
+/*
+ * 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 ENABLE_RETRANSMISSION           0
+#define TRACK_BANDWIDTH                 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> &packets);
+    void scheduleSendSR();
+
+protected:
+    virtual ~Sender();
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    enum {
+        kWhatQueuePackets,
+        kWhatSendSR,
+        kWhatRTPNotify,
+        kWhatRTCPNotify,
+#if ENABLE_RETRANSMISSION
+        kWhatRTPRetransmissionNotify,
+        kWhatRTCPRetransmissionNotify
+#endif
+    };
+
+    static const int64_t kSendSRIntervalUs = 10000000ll;
+
+    static const uint32_t kSourceID = 0xdeadbeef;
+    static const size_t kMaxHistoryLength = 128;
+
+#if ENABLE_RETRANSMISSION
+    static const size_t kRetransmissionPortOffset = 120;
+#endif
+
+    sp<ANetworkSession> mNetSession;
+    sp<AMessage> mNotify;
+
+    sp<ABuffer> mTSQueue;
+
+    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
+    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
+    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
+
+    void onSendSR();
+    void addSR(const sp<ABuffer> &buffer);
+    void addSDES(const sp<ABuffer> &buffer);
+    static uint64_t GetNowNTP();
+
+#if LOG_TRANSPORT_STREAM
+    FILE *mLogFile;
+#endif
+
+    ssize_t appendTSData(
+            const void *data, size_t size, bool timeDiscontinuity, bool flush);
+
+    void onQueuePackets(const sp<ABuffer> &packets);
+
+#if ENABLE_RETRANSMISSION
+    status_t parseTSFB(const uint8_t *data, size_t size);
+#endif
+
+    status_t parseRTCP(const sp<ABuffer> &buffer);
+
+    status_t sendPacket(int32_t sessionID, const void *data, size_t size);
+
+    void notifyInitDone();
+    void notifySessionDead();
+
+    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 7e66072..a5679ad 100644
--- a/media/libstagefright/wifi-display/source/TSPacketizer.cpp
+++ b/media/libstagefright/wifi-display/source/TSPacketizer.cpp
@@ -49,10 +49,16 @@
     bool isH264() const;
     bool isAAC() const;
     bool lacksADTSHeader() const;
+    bool isPCMAudio() const;
 
     sp<ABuffer> prependCSD(const sp<ABuffer> &accessUnit) const;
     sp<ABuffer> prependADTSHeader(const sp<ABuffer> &accessUnit) const;
 
+    size_t countDescriptors() const;
+    sp<ABuffer> descriptorAt(size_t index) const;
+
+    void finalize();
+
 protected:
     virtual ~Track();
 
@@ -67,7 +73,10 @@
     AString mMIME;
     Vector<sp<ABuffer> > mCSD;
 
+    Vector<sp<ABuffer> > mDescriptors;
+
     bool mAudioLacksATDSHeaders;
+    bool mFinalized;
 
     DISALLOW_EVIL_CONSTRUCTORS(Track);
 };
@@ -80,7 +89,8 @@
       mStreamType(streamType),
       mStreamID(streamID),
       mContinuityCounter(0),
-      mAudioLacksATDSHeaders(false) {
+      mAudioLacksATDSHeaders(false),
+      mFinalized(false) {
     CHECK(format->findString("mime", &mMIME));
 
     if (!strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_VIDEO_AVC)
@@ -144,6 +154,10 @@
     return !strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_AUDIO_AAC);
 }
 
+bool TSPacketizer::Track::isPCMAudio() const {
+    return !strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_AUDIO_RAW);
+}
+
 bool TSPacketizer::Track::lacksADTSHeader() const {
     return mAudioLacksATDSHeaders;
 }
@@ -213,6 +227,96 @@
     return dup;
 }
 
+size_t TSPacketizer::Track::countDescriptors() const {
+    return mDescriptors.size();
+}
+
+sp<ABuffer> TSPacketizer::Track::descriptorAt(size_t index) const {
+    CHECK_LT(index, mDescriptors.size());
+    return mDescriptors.itemAt(index);
+}
+
+void TSPacketizer::Track::finalize() {
+    if (mFinalized) {
+        return;
+    }
+
+    if (isH264()) {
+        {
+            // AVC video descriptor (40)
+
+            sp<ABuffer> descriptor = new ABuffer(6);
+            uint8_t *data = descriptor->data();
+            data[0] = 40;  // descriptor_tag
+            data[1] = 4;  // descriptor_length
+
+            CHECK_EQ(mCSD.size(), 1u);
+            const sp<ABuffer> &sps = mCSD.itemAt(0);
+            CHECK(!memcmp("\x00\x00\x00\x01", sps->data(), 4));
+            CHECK_GE(sps->size(), 7u);
+            // profile_idc, constraint_set*, level_idc
+            memcpy(&data[2], sps->data() + 4, 3);
+
+            // AVC_still_present=0, AVC_24_hour_picture_flag=0, reserved
+            data[5] = 0x3f;
+
+            mDescriptors.push_back(descriptor);
+        }
+
+        {
+            // AVC timing and HRD descriptor (42)
+
+            sp<ABuffer> descriptor = new ABuffer(4);
+            uint8_t *data = descriptor->data();
+            data[0] = 42;  // descriptor_tag
+            data[1] = 2;  // descriptor_length
+
+            // hrd_management_valid_flag = 0
+            // reserved = 111111b
+            // picture_and_timing_info_present = 0
+
+            data[2] = 0x7e;
+
+            // fixed_frame_rate_flag = 0
+            // temporal_poc_flag = 0
+            // picture_to_display_conversion_flag = 0
+            // reserved = 11111b
+            data[3] = 0x1f;
+
+            mDescriptors.push_back(descriptor);
+        }
+    } else if (isPCMAudio()) {
+        // LPCM audio stream descriptor (0x83)
+
+        int32_t channelCount;
+        CHECK(mFormat->findInt32("channel-count", &channelCount));
+        CHECK_EQ(channelCount, 2);
+
+        int32_t sampleRate;
+        CHECK(mFormat->findInt32("sample-rate", &sampleRate));
+        CHECK(sampleRate == 44100 || sampleRate == 48000);
+
+        sp<ABuffer> descriptor = new ABuffer(4);
+        uint8_t *data = descriptor->data();
+        data[0] = 0x83;  // descriptor_tag
+        data[1] = 2;  // descriptor_length
+
+        unsigned sampling_frequency = (sampleRate == 44100) ? 1 : 2;
+
+        data[2] = (sampling_frequency << 5)
+                    | (3 /* reserved */ << 1)
+                    | 0 /* emphasis_flag */;
+
+        data[3] =
+            (1 /* number_of_channels = stereo */ << 5)
+            | 0xf /* reserved */;
+
+        mDescriptors.push_back(descriptor);
+    }
+
+    mFinalized = true;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 TSPacketizer::TSPacketizer()
@@ -289,7 +393,8 @@
         const sp<ABuffer> &_accessUnit,
         sp<ABuffer> *packets,
         uint32_t flags,
-        const uint8_t *PES_private_data, size_t PES_private_data_len) {
+        const uint8_t *PES_private_data, size_t PES_private_data_len,
+        size_t numStuffingBytes) {
     sp<ABuffer> accessUnit = _accessUnit;
 
     int64_t timeUs;
@@ -347,7 +452,7 @@
     // reserved = b1
     // the first fragment of "buffer" follows
 
-    size_t PES_packet_length = accessUnit->size() + 8;
+    size_t PES_packet_length = accessUnit->size() + 8 + numStuffingBytes;
     if (PES_private_data_len > 0) {
         PES_packet_length += PES_private_data_len + 1;
     }
@@ -410,7 +515,7 @@
         *ptr++ = 0x10 | mPATContinuityCounter;
         *ptr++ = 0x00;
 
-        const uint8_t *crcDataStart = ptr;
+        uint8_t *crcDataStart = ptr;
         *ptr++ = 0x00;
         *ptr++ = 0xb0;
         *ptr++ = 0x0d;
@@ -472,8 +577,6 @@
             mPMTContinuityCounter = 0;
         }
 
-        size_t section_length = 5 * mTracks.size() + 4 + 9;
-
         ptr = packetDataStart;
         *ptr++ = 0x47;
         *ptr++ = 0x40 | (kPID_PMT >> 8);
@@ -483,8 +586,10 @@
 
         crcDataStart = ptr;
         *ptr++ = 0x02;
-        *ptr++ = 0xb0 | (section_length >> 8);
-        *ptr++ = section_length & 0xff;
+
+        *ptr++ = 0x00;  // section_length to be filled in below.
+        *ptr++ = 0x00;
+
         *ptr++ = 0x00;
         *ptr++ = 0x01;
         *ptr++ = 0xc3;
@@ -498,14 +603,34 @@
         for (size_t i = 0; i < mTracks.size(); ++i) {
             const sp<Track> &track = mTracks.itemAt(i);
 
+            // Make sure all the decriptors have been added.
+            track->finalize();
+
             *ptr++ = track->streamType();
             *ptr++ = 0xe0 | (track->PID() >> 8);
             *ptr++ = track->PID() & 0xff;
-            *ptr++ = 0xf0;
-            *ptr++ = 0x00;
+
+            size_t ES_info_length = 0;
+            for (size_t i = 0; i < track->countDescriptors(); ++i) {
+                ES_info_length += track->descriptorAt(i)->size();
+            }
+            CHECK_LE(ES_info_length, 0xfff);
+
+            *ptr++ = 0xf0 | (ES_info_length >> 8);
+            *ptr++ = (ES_info_length & 0xff);
+
+            for (size_t i = 0; i < track->countDescriptors(); ++i) {
+                const sp<ABuffer> &descriptor = track->descriptorAt(i);
+                memcpy(ptr, descriptor->data(), descriptor->size());
+                ptr += descriptor->size();
+            }
         }
 
-        CHECK_EQ(ptr - crcDataStart, 12 + mTracks.size() * 5);
+        size_t section_length = ptr - (crcDataStart + 3) + 4 /* CRC */;
+
+        crcDataStart[1] = 0xb0 | (section_length >> 8);
+        crcDataStart[2] = section_length & 0xff;
+
         crc = htonl(crc32(crcDataStart, ptr - crcDataStart));
         memcpy(ptr, &crc, 4);
         ptr += 4;
@@ -601,8 +726,12 @@
     *ptr++ = 0x84;
     *ptr++ = (PES_private_data_len > 0) ? 0x81 : 0x80;
 
-    *ptr++ = (PES_private_data_len > 0)
-        ? (1 + PES_private_data_len + 0x05) : 0x05;
+    size_t headerLength = 0x05 + numStuffingBytes;
+    if (PES_private_data_len > 0) {
+        headerLength += 1 + PES_private_data_len;
+    }
+
+    *ptr++ = headerLength;
 
     *ptr++ = 0x20 | (((PTS >> 30) & 7) << 1) | 1;
     *ptr++ = (PTS >> 22) & 0xff;
@@ -616,6 +745,10 @@
         ptr += PES_private_data_len;
     }
 
+    for (size_t i = 0; i < numStuffingBytes; ++i) {
+        *ptr++ = 0xff;
+    }
+
     // 18 bytes of TS/PES header leave 188 - 18 = 170 bytes for the payload
 
     size_t sizeLeft = packetDataStart + 188 - ptr;
diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.h b/media/libstagefright/wifi-display/source/TSPacketizer.h
index 0733c06..a37917d 100644
--- a/media/libstagefright/wifi-display/source/TSPacketizer.h
+++ b/media/libstagefright/wifi-display/source/TSPacketizer.h
@@ -47,7 +47,8 @@
             size_t trackIndex, const sp<ABuffer> &accessUnit,
             sp<ABuffer> *packets,
             uint32_t flags,
-            const uint8_t *PES_private_data, size_t PES_private_data_len);
+            const uint8_t *PES_private_data, size_t PES_private_data_len,
+            size_t numStuffingBytes = 0);
 
     // XXX to be removed once encoder config option takes care of this for
     // encrypted mode.
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
index 8ab9abe..b16c5d0 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
@@ -22,6 +22,7 @@
 #include "PlaybackSession.h"
 #include "Parameters.h"
 #include "ParsedMessage.h"
+#include "Sender.h"
 
 #include <binder/IServiceManager.h>
 #include <gui/ISurfaceTexture.h>
@@ -52,13 +53,12 @@
       mUsingPCMAudio(false),
       mClientSessionID(0),
       mReaperPending(false),
-      mNextCSeq(1)
-#if REQUIRE_HDCP
-      ,mIsHDCP2_0(false)
-      ,mHDCPPort(0)
-      ,mHDCPInitializationComplete(false)
-      ,mSetupTriggerDeferred(false)
-#endif
+      mNextCSeq(1),
+      mUsingHDCP(false),
+      mIsHDCP2_0(false),
+      mHDCPPort(0),
+      mHDCPInitializationComplete(false),
+      mSetupTriggerDeferred(false)
 {
 }
 
@@ -316,12 +316,9 @@
                             mClientInfo.mPlaybackSession->getSurfaceTexture(),
                             mClientInfo.mPlaybackSession->width(),
                             mClientInfo.mPlaybackSession->height(),
-#if REQUIRE_HDCP
-                            IRemoteDisplayClient::kDisplayFlagSecure
-#else
-                            0 /* flags */
-#endif
-                            );
+                            mUsingHDCP
+                                ? IRemoteDisplayClient::kDisplayFlagSecure
+                                : 0);
                 }
 
                 if (mState == ABOUT_TO_PLAY) {
@@ -385,7 +382,6 @@
             break;
         }
 
-#if REQUIRE_HDCP
         case kWhatHDCPNotify:
         {
             int32_t msgCode, ext1, ext2;
@@ -437,7 +433,6 @@
             finishStop2();
             break;
         }
-#endif
 
         default:
             TRESPASS();
@@ -477,9 +472,7 @@
 
 status_t WifiDisplaySource::sendM3(int32_t sessionID) {
     AString body =
-#if REQUIRE_HDCP
         "wfd_content_protection\r\n"
-#endif
         "wfd_video_formats\r\n"
         "wfd_audio_codecs\r\n"
         "wfd_client_rtp_ports\r\n";
@@ -751,39 +744,39 @@
         return ERROR_UNSUPPORTED;
     }
 
-#if REQUIRE_HDCP
+    mUsingHDCP = false;
     if (!params->findParameter("wfd_content_protection", &value)) {
-        ALOGE("Sink doesn't appear to support content protection.");
-        return -EACCES;
-    }
+        ALOGI("Sink doesn't appear to support content protection.");
+    } else if (value == "none") {
+        ALOGI("Sink does not support content protection.");
+    } else {
+        mUsingHDCP = true;
 
-    if (value == "none") {
-        ALOGE("Sink does not support content protection.");
-        return -EACCES;
-    }
+        bool isHDCP2_0 = false;
+        if (value.startsWith("HDCP2.0 ")) {
+            isHDCP2_0 = true;
+        } else if (!value.startsWith("HDCP2.1 ")) {
+            ALOGE("malformed wfd_content_protection: '%s'", value.c_str());
 
-    bool isHDCP2_0 = false;
-    if (value.startsWith("HDCP2.0 ")) {
-        isHDCP2_0 = true;
-    } else if (!value.startsWith("HDCP2.1 ")) {
-        return ERROR_MALFORMED;
-    }
+            return ERROR_MALFORMED;
+        }
 
-    int32_t hdcpPort;
-    if (!ParsedMessage::GetInt32Attribute(value.c_str() + 8, "port", &hdcpPort)
-            || hdcpPort < 1 || hdcpPort > 65535) {
-        return ERROR_MALFORMED;
-    }
+        int32_t hdcpPort;
+        if (!ParsedMessage::GetInt32Attribute(
+                    value.c_str() + 8, "port", &hdcpPort)
+                || hdcpPort < 1 || hdcpPort > 65535) {
+            return ERROR_MALFORMED;
+        }
 
-    mIsHDCP2_0 = isHDCP2_0;
-    mHDCPPort = hdcpPort;
+        mIsHDCP2_0 = isHDCP2_0;
+        mHDCPPort = hdcpPort;
 
-    status_t err = makeHDCP();
-    if (err != OK) {
-        ALOGE("Unable to instantiate HDCP component.");
-        return err;
+        status_t err = makeHDCP();
+        if (err != OK) {
+            ALOGE("Unable to instantiate HDCP component.");
+            return err;
+        }
     }
-#endif
 
     return sendM4(sessionID);
 }
@@ -799,14 +792,12 @@
         return ERROR_UNSUPPORTED;
     }
 
-#if REQUIRE_HDCP
-    if (!mHDCPInitializationComplete) {
+    if (mUsingHDCP && !mHDCPInitializationComplete) {
         ALOGI("Deferring SETUP trigger until HDCP initialization completes.");
 
         mSetupTriggerDeferred = true;
         return OK;
     }
-#endif
 
     return sendM5(sessionID, false /* requestShutdown */);
 }
@@ -991,8 +982,7 @@
         return ERROR_MALFORMED;
     }
 
-    PlaybackSession::TransportMode transportMode =
-        PlaybackSession::TRANSPORT_UDP;
+    Sender::TransportMode transportMode = Sender::TRANSPORT_UDP;
 
     int clientRtp, clientRtcp;
     if (transport.startsWith("RTP/AVP/TCP;")) {
@@ -1001,7 +991,7 @@
                     transport.c_str(), "interleaved", &interleaved)
                 && sscanf(interleaved.c_str(), "%d-%d",
                           &clientRtp, &clientRtcp) == 2) {
-            transportMode = PlaybackSession::TRANSPORT_TCP_INTERLEAVED;
+            transportMode = Sender::TRANSPORT_TCP_INTERLEAVED;
         } else {
             bool badRequest = false;
 
@@ -1023,7 +1013,7 @@
                 return ERROR_MALFORMED;
             }
 
-            transportMode = PlaybackSession::TRANSPORT_TCP;
+            transportMode = Sender::TRANSPORT_TCP;
         }
     } else if (transport.startsWith("RTP/AVP;unicast;")
             || transport.startsWith("RTP/AVP/UDP;unicast;")) {
@@ -1065,13 +1055,7 @@
 
     sp<PlaybackSession> playbackSession =
         new PlaybackSession(
-                mNetSession, notify, mInterfaceAddr,
-#if REQUIRE_HDCP
-                mHDCP
-#else
-                NULL
-#endif
-                );
+                mNetSession, notify, mInterfaceAddr, mHDCP);
 
     looper()->registerHandler(playbackSession);
 
@@ -1117,7 +1101,7 @@
     AString response = "RTSP/1.0 200 OK\r\n";
     AppendCommonResponse(&response, cseq, playbackSessionID);
 
-    if (transportMode == PlaybackSession::TRANSPORT_TCP_INTERLEAVED) {
+    if (transportMode == Sender::TRANSPORT_TCP_INTERLEAVED) {
         response.append(
                 StringPrintf(
                     "Transport: RTP/AVP/TCP;interleaved=%d-%d;",
@@ -1126,7 +1110,7 @@
         int32_t serverRtp = playbackSession->getRTPPort();
 
         AString transportString = "UDP";
-        if (transportMode == PlaybackSession::TRANSPORT_TCP) {
+        if (transportMode == Sender::TRANSPORT_TCP) {
             transportString = "TCP";
         }
 
@@ -1268,13 +1252,11 @@
 void WifiDisplaySource::finishStopAfterDisconnectingClient() {
     ALOGV("finishStopAfterDisconnectingClient");
 
-#if REQUIRE_HDCP
     if (mHDCP != NULL) {
         ALOGI("Initiating HDCP shutdown.");
         mHDCP->shutdownAsync();
         return;
     }
-#endif
 
     finishStop2();
 }
@@ -1282,13 +1264,11 @@
 void WifiDisplaySource::finishStop2() {
     ALOGV("finishStop2");
 
-#if REQUIRE_HDCP
     if (mHDCP != NULL) {
         mHDCP->setObserver(NULL);
         mHDCPObserver.clear();
         mHDCP.clear();
     }
-#endif
 
     if (mSessionID != 0) {
         mNetSession->destroySession(mSessionID);
@@ -1448,7 +1428,6 @@
     finishStopAfterDisconnectingClient();
 }
 
-#if REQUIRE_HDCP
 struct WifiDisplaySource::HDCPObserver : public BnHDCPObserver {
     HDCPObserver(const sp<AMessage> &notify);
 
@@ -1512,7 +1491,6 @@
 
     return OK;
 }
-#endif
 
 }  // namespace android
 
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
index 9e748dd..02fa0a6 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
@@ -26,8 +26,6 @@
 
 namespace android {
 
-#define REQUIRE_HDCP            1
-
 struct IHDCP;
 struct IRemoteDisplayClient;
 struct ParsedMessage;
@@ -50,10 +48,7 @@
 
 private:
     struct PlaybackSession;
-
-#if REQUIRE_HDCP
     struct HDCPObserver;
-#endif
 
     enum State {
         INITIALIZED,
@@ -134,7 +129,8 @@
 
     KeyedVector<ResponseID, HandleRTSPResponseFunc> mResponseHandlers;
 
-#if REQUIRE_HDCP
+    // HDCP specific section >>>>
+    bool mUsingHDCP;
     bool mIsHDCP2_0;
     int32_t mHDCPPort;
     sp<IHDCP> mHDCP;
@@ -144,7 +140,7 @@
     bool mSetupTriggerDeferred;
 
     status_t makeHDCP();
-#endif
+    // <<<< HDCP specific section
 
     status_t sendM1(int32_t sessionID);
     status_t sendM3(int32_t sessionID);
diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp
index 6b1abb1..ddd5b84 100644
--- a/media/mediaserver/main_mediaserver.cpp
+++ b/media/mediaserver/main_mediaserver.cpp
@@ -33,6 +33,7 @@
 
 int main(int argc, char** argv)
 {
+    signal(SIGPIPE, SIG_IGN);
     sp<ProcessState> proc(ProcessState::self());
     sp<IServiceManager> sm = defaultServiceManager();
     ALOGI("ServiceManager: %p", sm.get());
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index aa30864..76d6447 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -3033,7 +3033,7 @@
                             (mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000;
                     size_t framesWritten =
                             mBytesWritten / audio_stream_frame_size(&mOutput->stream->common);
-                    if (!track->presentationComplete(framesWritten, audioHALFrames)) {
+                    if (!(mStandby || track->presentationComplete(framesWritten, audioHALFrames))) {
                         // track stays in active list until presentation is complete
                         break;
                     }
@@ -3275,11 +3275,10 @@
                 // Remove it from the list of active tracks.
                 // TODO: use actual buffer filling status instead of latency when available from
                 // audio HAL
-                size_t audioHALFrames =
-                        (mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000;
+                size_t audioHALFrames = (latency_l() * mSampleRate) / 1000;
                 size_t framesWritten =
                         mBytesWritten / audio_stream_frame_size(&mOutput->stream->common);
-                if (track->presentationComplete(framesWritten, audioHALFrames)) {
+                if (mStandby || track->presentationComplete(framesWritten, audioHALFrames)) {
                     if (track->isStopped()) {
                         track->reset();
                     }
@@ -3799,11 +3798,10 @@
                 // We have consumed all the buffers of this track.
                 // Remove it from the list of active tracks.
                 // TODO: implement behavior for compressed audio
-                size_t audioHALFrames =
-                        (mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000;
+                size_t audioHALFrames = (latency_l() * mSampleRate) / 1000;
                 size_t framesWritten =
                         mBytesWritten / audio_stream_frame_size(&mOutput->stream->common);
-                if (track->presentationComplete(framesWritten, audioHALFrames)) {
+                if (mStandby || track->presentationComplete(framesWritten, audioHALFrames)) {
                     if (track->isStopped()) {
                         track->reset();
                     }
diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp
index 96293e3..ffea9b9 100644
--- a/services/audioflinger/AudioResampler.cpp
+++ b/services/audioflinger/AudioResampler.cpp
@@ -130,7 +130,7 @@
     }
 }
 
-static const uint32_t maxMHz = 75;  // an arbitrary number that permits 2 VHQ, should be tunable
+static const uint32_t maxMHz = 130; // an arbitrary number that permits 3 VHQ, should be tunable
 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 static uint32_t currentMHz = 0;
 
diff --git a/services/audioflinger/AudioResamplerSinc.cpp b/services/audioflinger/AudioResamplerSinc.cpp
index 90651f1..9e8447a 100644
--- a/services/audioflinger/AudioResamplerSinc.cpp
+++ b/services/audioflinger/AudioResamplerSinc.cpp
@@ -252,7 +252,7 @@
     // FIXME store current state (up or down sample) and only load the coefs when the state
     // changes. Or load two pointers one for up and one for down in the init function.
     // Not critical now since the read functions are fast, but would be important if read was slow.
-    if (readResampleCoefficients) {
+    if (mConstants == &veryHighQualityConstants && readResampleCoefficients) {
         ALOGV("get coefficient from libmm-audio resampler library");
         mFirCoefs = (mInSampleRate <= mSampleRate) ? readResampleCoefficients(true) :
                 readResampleCoefficients(false);
diff --git a/services/camera/libcameraservice/Camera2Client.cpp b/services/camera/libcameraservice/Camera2Client.cpp
index 7290663..e59a240 100644
--- a/services/camera/libcameraservice/Camera2Client.cpp
+++ b/services/camera/libcameraservice/Camera2Client.cpp
@@ -267,6 +267,17 @@
         default: result.append("UNKNOWN\n");
     }
 
+    result.append("   Focus state: ");
+    switch (p.focusState) {
+        CASE_APPEND_ENUM(ANDROID_CONTROL_AF_STATE_INACTIVE)
+        CASE_APPEND_ENUM(ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN)
+        CASE_APPEND_ENUM(ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED)
+        CASE_APPEND_ENUM(ANDROID_CONTROL_AF_STATE_ACTIVE_SCAN)
+        CASE_APPEND_ENUM(ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED)
+        CASE_APPEND_ENUM(ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED)
+        default: result.append("UNKNOWN\n");
+    }
+
     result.append("    Focusing areas:\n");
     for (size_t i = 0; i < p.focusingAreas.size(); i++) {
         result.appendFormat("      [ (%d, %d, %d, %d), weight %d ]\n",
@@ -518,15 +529,19 @@
         return NO_ERROR;
     }
 
-    SharedParameters::Lock l(mParameters);
-    switch (l.mParameters.state) {
+    Parameters::State state;
+    {
+        SharedParameters::Lock l(mParameters);
+        state = l.mParameters.state;
+    }
+    switch (state) {
         case Parameters::DISCONNECTED:
         case Parameters::RECORD:
         case Parameters::STILL_CAPTURE:
         case Parameters::VIDEO_SNAPSHOT:
             ALOGE("%s: Camera %d: Cannot set preview display while in state %s",
                     __FUNCTION__, mCameraId,
-                    Parameters::getStateName(l.mParameters.state));
+                    Parameters::getStateName(state));
             return INVALID_OPERATION;
         case Parameters::STOPPED:
         case Parameters::WAITING_FOR_PREVIEW_WINDOW:
@@ -534,10 +549,8 @@
             break;
         case Parameters::PREVIEW:
             // Already running preview - need to stop and create a new stream
-            // TODO: Optimize this so that we don't wait for old stream to drain
-            // before spinning up new stream
             mStreamingProcessor->stopStream();
-            l.mParameters.state = Parameters::WAITING_FOR_PREVIEW_WINDOW;
+            state = Parameters::WAITING_FOR_PREVIEW_WINDOW;
             break;
     }
 
@@ -549,7 +562,9 @@
         return res;
     }
 
-    if (l.mParameters.state == Parameters::WAITING_FOR_PREVIEW_WINDOW) {
+    if (state == Parameters::WAITING_FOR_PREVIEW_WINDOW) {
+        SharedParameters::Lock l(mParameters);
+        l.mParameters.state = state;
         return startPreviewL(l.mParameters, false);
     }
 
@@ -952,6 +967,8 @@
     if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
 
     int triggerId;
+    bool notifyImmediately = false;
+    bool notifySuccess = false;
     {
         SharedParameters::Lock l(mParameters);
         if (l.mParameters.state < Parameters::PREVIEW) {
@@ -964,18 +981,38 @@
           * with a fake value of success set to true.
           */
         if (l.mParameters.focusMode == Parameters::FOCUS_MODE_FIXED) {
+            notifyImmediately = true;
+            notifySuccess = true;
+        }
+        /**
+         * If we're in CAF mode, and AF has already been locked, just fire back
+         * the callback right away; the HAL would not send a notification since
+         * no state change would happen on a AF trigger.
+         */
+        if ( (l.mParameters.focusMode == Parameters::FOCUS_MODE_CONTINUOUS_PICTURE ||
+                l.mParameters.focusMode == Parameters::FOCUS_MODE_CONTINUOUS_VIDEO) &&
+                l.mParameters.focusState == ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED ) {
+            notifyImmediately = true;
+            notifySuccess = true;
+        }
+        /**
+         * Send immediate notification back to client
+         */
+        if (notifyImmediately) {
             SharedCameraClient::Lock l(mSharedCameraClient);
             if (l.mCameraClient != 0) {
                 l.mCameraClient->notifyCallback(CAMERA_MSG_FOCUS,
-                    /*success*/1, 0);
+                        notifySuccess ? 1 : 0, 0);
             }
-
             return OK;
         }
-
+        /**
+         * Handle quirk mode for AF in scene modes
+         */
         if (l.mParameters.quirks.triggerAfWithAuto &&
                 l.mParameters.sceneMode != ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED &&
-                l.mParameters.focusMode != Parameters::FOCUS_MODE_AUTO) {
+                l.mParameters.focusMode != Parameters::FOCUS_MODE_AUTO &&
+                !l.mParameters.focusingAreas[0].isEmpty()) {
             ALOGV("%s: Quirk: Switching from focusMode %d to AUTO",
                     __FUNCTION__, l.mParameters.focusMode);
             l.mParameters.shadowFocusMode = l.mParameters.focusMode;
@@ -1006,13 +1043,16 @@
         triggerId = ++l.mParameters.afTriggerCounter;
 
         // When using triggerAfWithAuto quirk, may need to reset focus mode to
-        // the real state at this point.
+        // the real state at this point. No need to cancel explicitly if
+        // changing the AF mode.
         if (l.mParameters.shadowFocusMode != Parameters::FOCUS_MODE_INVALID) {
             ALOGV("%s: Quirk: Restoring focus mode to %d", __FUNCTION__,
                     l.mParameters.shadowFocusMode);
             l.mParameters.focusMode = l.mParameters.shadowFocusMode;
             l.mParameters.shadowFocusMode = Parameters::FOCUS_MODE_INVALID;
             updateRequests(l.mParameters);
+
+            return OK;
         }
     }
     syncWithDevice();
@@ -1028,42 +1068,44 @@
     status_t res;
     if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
 
-    SharedParameters::Lock l(mParameters);
-    switch (l.mParameters.state) {
-        case Parameters::DISCONNECTED:
-        case Parameters::STOPPED:
-        case Parameters::WAITING_FOR_PREVIEW_WINDOW:
-            ALOGE("%s: Camera %d: Cannot take picture without preview enabled",
-                    __FUNCTION__, mCameraId);
-            return INVALID_OPERATION;
-        case Parameters::PREVIEW:
-            // Good to go for takePicture
-            res = commandStopFaceDetectionL(l.mParameters);
-            if (res != OK) {
-                ALOGE("%s: Camera %d: Unable to stop face detection for still capture",
+    {
+        SharedParameters::Lock l(mParameters);
+        switch (l.mParameters.state) {
+            case Parameters::DISCONNECTED:
+            case Parameters::STOPPED:
+            case Parameters::WAITING_FOR_PREVIEW_WINDOW:
+                ALOGE("%s: Camera %d: Cannot take picture without preview enabled",
                         __FUNCTION__, mCameraId);
-                return res;
-            }
-            l.mParameters.state = Parameters::STILL_CAPTURE;
-            break;
-        case Parameters::RECORD:
-            // Good to go for video snapshot
-            l.mParameters.state = Parameters::VIDEO_SNAPSHOT;
-            break;
-        case Parameters::STILL_CAPTURE:
-        case Parameters::VIDEO_SNAPSHOT:
-            ALOGE("%s: Camera %d: Already taking a picture",
-                    __FUNCTION__, mCameraId);
-            return INVALID_OPERATION;
-    }
+                return INVALID_OPERATION;
+            case Parameters::PREVIEW:
+                // Good to go for takePicture
+                res = commandStopFaceDetectionL(l.mParameters);
+                if (res != OK) {
+                    ALOGE("%s: Camera %d: Unable to stop face detection for still capture",
+                            __FUNCTION__, mCameraId);
+                    return res;
+                }
+                l.mParameters.state = Parameters::STILL_CAPTURE;
+                break;
+            case Parameters::RECORD:
+                // Good to go for video snapshot
+                l.mParameters.state = Parameters::VIDEO_SNAPSHOT;
+                break;
+            case Parameters::STILL_CAPTURE:
+            case Parameters::VIDEO_SNAPSHOT:
+                ALOGE("%s: Camera %d: Already taking a picture",
+                        __FUNCTION__, mCameraId);
+                return INVALID_OPERATION;
+        }
 
-    ALOGV("%s: Camera %d: Starting picture capture", __FUNCTION__, mCameraId);
+        ALOGV("%s: Camera %d: Starting picture capture", __FUNCTION__, mCameraId);
 
-    res = mJpegProcessor->updateStream(l.mParameters);
-    if (res != OK) {
-        ALOGE("%s: Camera %d: Can't set up still image stream: %s (%d)",
-                __FUNCTION__, mCameraId, strerror(-res), res);
-        return res;
+        res = mJpegProcessor->updateStream(l.mParameters);
+        if (res != OK) {
+            ALOGE("%s: Camera %d: Can't set up still image stream: %s (%d)",
+                    __FUNCTION__, mCameraId, strerror(-res), res);
+            return res;
+        }
     }
 
     // Need HAL to have correct settings before (possibly) triggering precapture
@@ -1303,6 +1345,7 @@
     bool afInMotion = false;
     {
         SharedParameters::Lock l(mParameters);
+        l.mParameters.focusState = newState;
         switch (l.mParameters.focusMode) {
             case Parameters::FOCUS_MODE_AUTO:
             case Parameters::FOCUS_MODE_MACRO:
@@ -1573,7 +1616,7 @@
 
 status_t Camera2Client::syncWithDevice() {
     ATRACE_CALL();
-    const nsecs_t kMaxSyncTimeout = 100000000; // 100 ms
+    const nsecs_t kMaxSyncTimeout = 500000000; // 500 ms
     status_t res;
 
     int32_t activeRequestId = mStreamingProcessor->getActiveRequestId();
diff --git a/services/camera/libcameraservice/Camera2Device.h b/services/camera/libcameraservice/Camera2Device.h
index 29830bd..41df2e4 100644
--- a/services/camera/libcameraservice/Camera2Device.h
+++ b/services/camera/libcameraservice/Camera2Device.h
@@ -230,7 +230,7 @@
         // Real interfaces. On enqueue, queue takes ownership of buffer pointer
         // On dequeue, user takes ownership of buffer pointer.
         status_t enqueue(camera_metadata_t *buf);
-        status_t dequeue(camera_metadata_t **buf, bool incrementCount = true);
+        status_t dequeue(camera_metadata_t **buf, bool incrementCount = false);
         int      getBufferCount();
         status_t waitForBuffer(nsecs_t timeout);
         // Wait until a buffer with the given ID is dequeued. Will return
diff --git a/services/camera/libcameraservice/camera2/Parameters.cpp b/services/camera/libcameraservice/camera2/Parameters.cpp
index 3c679da..9a0083a 100644
--- a/services/camera/libcameraservice/camera2/Parameters.cpp
+++ b/services/camera/libcameraservice/camera2/Parameters.cpp
@@ -633,6 +633,7 @@
         params.set(CameraParameters::KEY_SUPPORTED_FOCUS_MODES,
                 supportedFocusModes);
     }
+    focusState = ANDROID_CONTROL_AF_STATE_INACTIVE;
     shadowFocusMode = FOCUS_MODE_INVALID;
 
     camera_metadata_ro_entry_t max3aRegions =
@@ -1462,8 +1463,9 @@
                 }
             }
         }
+        validatedParams.focusState = ANDROID_CONTROL_AF_STATE_INACTIVE;
         // Always reset shadow focus mode to avoid reverting settings
-        shadowFocusMode = FOCUS_MODE_INVALID;
+        validatedParams.shadowFocusMode = FOCUS_MODE_INVALID;
         // Update in case of override
         newParams.set(CameraParameters::KEY_FOCUS_MODE,
                 focusModeEnumToString(validatedParams.focusMode));
diff --git a/services/camera/libcameraservice/camera2/Parameters.h b/services/camera/libcameraservice/camera2/Parameters.h
index fd02744..54b1e8c 100644
--- a/services/camera/libcameraservice/camera2/Parameters.h
+++ b/services/camera/libcameraservice/camera2/Parameters.h
@@ -88,6 +88,8 @@
         FOCUS_MODE_INVALID = -1
     } focusMode;
 
+    uint8_t focusState; // Latest focus state from HAL
+
     // For use with triggerAfWithAuto quirk
     focusMode_t shadowFocusMode;
 
@@ -98,6 +100,9 @@
         Area(int left, int top, int right, int bottom, int weight):
                 left(left), top(top), right(right), bottom(bottom),
                 weight(weight) {}
+        bool isEmpty() const {
+            return (left == 0) && (top == 0) && (right == 0) && (bottom == 0);
+        }
     };
     Vector<Area> focusingAreas;