Various improvements to the wifi display implementation.

Using a MediaPuller now, audio and video on their separate threads.
No more flushing the RTP stream on audio tracks, since it wastes too much
of a full size UDP packet.

Change-Id: I53346b4aea739c3142da13bd179428503a3c98b0
diff --git a/media/libmediaplayerservice/RemoteDisplay.cpp b/media/libmediaplayerservice/RemoteDisplay.cpp
index 5542bb5..5baa3ad 100644
--- a/media/libmediaplayerservice/RemoteDisplay.cpp
+++ b/media/libmediaplayerservice/RemoteDisplay.cpp
@@ -28,6 +28,7 @@
     : mLooper(new ALooper),
       mNetSession(new ANetworkSession),
       mSource(new WifiDisplaySource(mNetSession, client)) {
+    mLooper->setName("wfd_looper");
     mLooper->registerHandler(mSource);
 
     mNetSession->start();
diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk
index 0e59b9e..681ba4f 100644
--- a/media/libstagefright/wifi-display/Android.mk
+++ b/media/libstagefright/wifi-display/Android.mk
@@ -10,6 +10,7 @@
         sink/TunnelRenderer.cpp         \
         sink/WifiDisplaySink.cpp        \
         source/Converter.cpp            \
+        source/MediaPuller.cpp          \
         source/PlaybackSession.cpp      \
         source/RepeaterSource.cpp       \
         source/Serializer.cpp           \
diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp
index b8b8688..0b29df9 100644
--- a/media/libstagefright/wifi-display/source/Converter.cpp
+++ b/media/libstagefright/wifi-display/source/Converter.cpp
@@ -90,7 +90,7 @@
     if (isAudio) {
         mOutputFormat->setInt32("bitrate", 64000);      // 64 kBit/sec
     } else {
-        mOutputFormat->setInt32("bitrate", 5000000);    // 5Mbit/sec
+        mOutputFormat->setInt32("bitrate", 10000000);    // 10Mbit/sec
         mOutputFormat->setInt32("frame-rate", 60);
         mOutputFormat->setInt32("i-frame-interval", 3);  // Iframes every 3 secs
     }
diff --git a/media/libstagefright/wifi-display/source/MediaPuller.cpp b/media/libstagefright/wifi-display/source/MediaPuller.cpp
new file mode 100644
index 0000000..786029a
--- /dev/null
+++ b/media/libstagefright/wifi-display/source/MediaPuller.cpp
@@ -0,0 +1,152 @@
+/*
+ * 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 "MediaPuller"
+#include <utils/Log.h>
+
+#include "MediaPuller.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+MediaPuller::MediaPuller(
+        const sp<MediaSource> &source, const sp<AMessage> &notify)
+    : mSource(source),
+      mNotify(notify),
+      mPullGeneration(0) {
+}
+
+MediaPuller::~MediaPuller() {
+}
+
+status_t MediaPuller::postSynchronouslyAndReturnError(
+        const sp<AMessage> &msg) {
+    sp<AMessage> response;
+    status_t err = msg->postAndAwaitResponse(&response);
+
+    if (err != OK) {
+        return err;
+    }
+
+    if (!response->findInt32("err", &err)) {
+        err = OK;
+    }
+
+    return err;
+}
+
+status_t MediaPuller::start() {
+    return postSynchronouslyAndReturnError(new AMessage(kWhatStart, id()));
+}
+
+status_t MediaPuller::stop() {
+    return postSynchronouslyAndReturnError(new AMessage(kWhatStop, id()));
+}
+
+void MediaPuller::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatStart:
+        case kWhatStop:
+        {
+            status_t err;
+
+            if (msg->what() == kWhatStart) {
+                err = mSource->start();
+
+                if (err == OK) {
+                    schedulePull();
+                }
+            } else {
+                err = mSource->stop();
+                ++mPullGeneration;
+            }
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatPull:
+        {
+            int32_t generation;
+            CHECK(msg->findInt32("generation", &generation));
+
+            if (generation != mPullGeneration) {
+                break;
+            }
+
+            MediaBuffer *mbuf;
+            status_t err = mSource->read(&mbuf);
+
+            if (err != OK) {
+                if (err == ERROR_END_OF_STREAM) {
+                    ALOGI("stream ended.");
+                } else {
+                    ALOGE("error %d reading stream.", err);
+                }
+
+                sp<AMessage> notify = mNotify->dup();
+                notify->setInt32("what", kWhatEOS);
+                notify->post();
+            } else {
+                int64_t timeUs;
+                CHECK(mbuf->meta_data()->findInt64(kKeyTime, &timeUs));
+
+                sp<ABuffer> accessUnit = new ABuffer(mbuf->range_length());
+
+                memcpy(accessUnit->data(),
+                       (const uint8_t *)mbuf->data() + mbuf->range_offset(),
+                       mbuf->range_length());
+
+                accessUnit->meta()->setInt64("timeUs", timeUs);
+                accessUnit->meta()->setPointer("mediaBuffer", mbuf);
+
+                sp<AMessage> notify = mNotify->dup();
+
+                notify->setInt32("what", kWhatAccessUnit);
+                notify->setBuffer("accessUnit", accessUnit);
+                notify->post();
+
+                schedulePull();
+            }
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+void MediaPuller::schedulePull() {
+    sp<AMessage> msg = new AMessage(kWhatPull, id());
+    msg->setInt32("generation", mPullGeneration);
+    msg->post();
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/wifi-display/source/MediaPuller.h b/media/libstagefright/wifi-display/source/MediaPuller.h
new file mode 100644
index 0000000..5297501
--- /dev/null
+++ b/media/libstagefright/wifi-display/source/MediaPuller.h
@@ -0,0 +1,61 @@
+/*
+ * 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 MEDIA_PULLER_H_
+
+#define MEDIA_PULLER_H_
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+struct MediaSource;
+
+struct MediaPuller : public AHandler {
+    enum {
+        kWhatEOS,
+        kWhatAccessUnit
+    };
+
+    MediaPuller(const sp<MediaSource> &source, const sp<AMessage> &notify);
+
+    status_t start();
+    status_t stop();
+
+protected:
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+    virtual ~MediaPuller();
+
+private:
+    enum {
+        kWhatStart,
+        kWhatStop,
+        kWhatPull,
+    };
+
+    sp<MediaSource> mSource;
+    sp<AMessage> mNotify;
+    int32_t mPullGeneration;
+
+    status_t postSynchronouslyAndReturnError(const sp<AMessage> &msg);
+    void schedulePull();
+
+    DISALLOW_EVIL_CONSTRUCTORS(MediaPuller);
+};
+
+}  // namespace android
+
+#endif  // MEDIA_PULLER_H_
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
index ab16407..5d0ddf1 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
@@ -21,6 +21,7 @@
 #include "PlaybackSession.h"
 
 #include "Converter.h"
+#include "MediaPuller.h"
 #include "RepeaterSource.h"
 #include "Serializer.h"
 #include "TSPacketizer.h"
@@ -45,7 +46,8 @@
 
 #include <OMX_IVCommon.h>
 
-#define FAKE_VIDEO      0
+//#define FAKE_VIDEO      1
+#define USE_SERIALIZER  0
 
 namespace android {
 
@@ -53,7 +55,11 @@
 static size_t kMaxNumTSPacketsPerRTPPacket = (kMaxRTPPacketSize - 12) / 188;
 
 struct WifiDisplaySource::PlaybackSession::Track : public RefBase {
-    Track(const sp<Converter> &converter);
+    Track(const sp<ALooper> &pullLooper,
+          const sp<ALooper> &codecLooper,
+          const sp<MediaPuller> &mediaPuller,
+          const sp<Converter> &converter);
+
     Track(const sp<AMessage> &format);
 
     sp<AMessage> getFormat();
@@ -63,19 +69,34 @@
 
     void setPacketizerTrackIndex(size_t index);
 
+    status_t start();
+    status_t stop();
+
 protected:
     virtual ~Track();
 
 private:
+    sp<ALooper> mPullLooper;
+    sp<ALooper> mCodecLooper;
+    sp<MediaPuller> mMediaPuller;
     sp<Converter> mConverter;
     sp<AMessage> mFormat;
+    bool mStarted;
     ssize_t mPacketizerTrackIndex;
 
     DISALLOW_EVIL_CONSTRUCTORS(Track);
 };
 
-WifiDisplaySource::PlaybackSession::Track::Track(const sp<Converter> &converter)
-    : mConverter(converter),
+WifiDisplaySource::PlaybackSession::Track::Track(
+        const sp<ALooper> &pullLooper,
+        const sp<ALooper> &codecLooper,
+        const sp<MediaPuller> &mediaPuller,
+        const sp<Converter> &converter)
+    : mPullLooper(pullLooper),
+      mCodecLooper(codecLooper),
+      mMediaPuller(mediaPuller),
+      mConverter(converter),
+      mStarted(false),
       mPacketizerTrackIndex(-1) {
 }
 
@@ -85,6 +106,7 @@
 }
 
 WifiDisplaySource::PlaybackSession::Track::~Track() {
+    stop();
 }
 
 sp<AMessage> WifiDisplaySource::PlaybackSession::Track::getFormat() {
@@ -108,6 +130,40 @@
     mPacketizerTrackIndex = index;
 }
 
+status_t WifiDisplaySource::PlaybackSession::Track::start() {
+    if (mStarted) {
+        return INVALID_OPERATION;
+    }
+
+    status_t err = OK;
+
+    if (mMediaPuller != NULL) {
+        err = mMediaPuller->start();
+    }
+
+    if (err == OK) {
+        mStarted = true;
+    }
+
+    return err;
+}
+
+status_t WifiDisplaySource::PlaybackSession::Track::stop() {
+    if (!mStarted) {
+        return INVALID_OPERATION;
+    }
+
+    status_t err = OK;
+
+    if (mMediaPuller != NULL) {
+        err = mMediaPuller->stop();
+    }
+
+    mStarted = false;
+
+    return err;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 WifiDisplaySource::PlaybackSession::PlaybackSession(
@@ -118,6 +174,7 @@
       mNotify(notify),
       mLegacyMode(legacyMode),
       mLastLifesignUs(),
+      mVideoTrackIndex(-1),
       mTSQueue(new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188)),
       mPrevTimeUs(-1ll),
       mUseInterleavedTCP(false),
@@ -234,11 +291,6 @@
 
     mTracks.clear();
 
-    if (mCodecLooper != NULL) {
-        mCodecLooper->stop();
-        mCodecLooper.clear();
-    }
-
     mPacketizer.clear();
 
     if (mSerializer != NULL) {
@@ -248,6 +300,8 @@
         mSerializer.clear();
     }
 
+    mTracks.clear();
+
     if (mSerializerLooper != NULL) {
         mSerializerLooper->stop();
         mSerializerLooper.clear();
@@ -290,7 +344,23 @@
         scheduleSendSR();
     }
 
-    return mSerializer->start();
+    if (mSerializer != NULL) {
+        return mSerializer->start();
+    }
+
+    for (size_t i = 0; i < mTracks.size(); ++i) {
+        status_t err = mTracks.editValueAt(i)->start();
+
+        if (err != OK) {
+            for (size_t j = 0; j < i; ++j) {
+                mTracks.editValueAt(j)->stop();
+            }
+
+            return err;
+        }
+    }
+
+    return OK;
 }
 
 status_t WifiDisplaySource::PlaybackSession::pause() {
@@ -483,11 +553,18 @@
                             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 */,
-                                lastTSPacket /* flush */);
+                                flush);
                     }
 
 #if LOG_TRANSPORT_STREAM
@@ -534,21 +611,33 @@
 status_t WifiDisplaySource::PlaybackSession::setupPacketizer() {
     sp<AMessage> msg = new AMessage(kWhatSerializerNotify, id());
 
-    mSerializerLooper = new ALooper;
-    mSerializerLooper->start();
-
-    mSerializer = new Serializer(
-#if FAKE_VIDEO
-            true /* throttled */
-#else
-            false /* throttled */
-#endif
-            , msg);
-    mSerializerLooper->registerHandler(mSerializer);
-
     mPacketizer = new TSPacketizer;
 
 #if FAKE_VIDEO
+    return addFakeSources();
+#else
+    status_t err = addVideoSource();
+
+    if (err != OK) {
+        return err;
+    }
+
+    return addAudioSource();
+#endif
+}
+
+status_t WifiDisplaySource::PlaybackSession::addFakeSources() {
+#if FAKE_VIDEO
+    mSerializerLooper = new ALooper;
+    mSerializerLooper->setName("serializer_looper");
+    mSerializerLooper->start();
+
+    sp<AMessage> msg = new AMessage(kWhatSerializerNotify, id());
+    mSerializer = new Serializer(
+            true /* throttled */, msg);
+
+    mSerializerLooper->registerHandler(mSerializer);
+
     DataSource::RegisterDefaultSniffers();
 
     sp<DataSource> dataSource =
@@ -593,60 +682,128 @@
         mTracks.add(index, new Track(format));
     }
     CHECK(haveAudio || haveVideo);
-#else
-    mCodecLooper = new ALooper;
-    mCodecLooper->start();
-
-    sp<SurfaceMediaSource> source = new SurfaceMediaSource(width(), height());
-
-#if 0
-    ssize_t index = mSerializer->addSource(source);
-#else
-    ssize_t index = mSerializer->addSource(
-            new RepeaterSource(source, 30.0 /* rateHz */));
 #endif
 
-    CHECK_GE(index, 0);
+    return OK;
+}
+
+status_t WifiDisplaySource::PlaybackSession::addSource(
+        bool isVideo, const sp<MediaSource> &source, size_t *numInputBuffers) {
+#if USE_SERIALIZER
+    if (mSerializer == NULL) {
+        mSerializerLooper = new ALooper;
+        mSerializerLooper->setName("serializer_looper");
+        mSerializerLooper->start();
+
+        sp<AMessage> msg = new AMessage(kWhatSerializerNotify, id());
+        mSerializer = new Serializer(
+                false /* throttled */, msg);
+
+        mSerializerLooper->registerHandler(mSerializer);
+    }
+#else
+    sp<ALooper> pullLooper = new ALooper;
+    pullLooper->setName("pull_looper");
+
+    pullLooper->start(
+            false /* runOnCallingThread */,
+            false /* canCallJava */,
+            PRIORITY_DEFAULT);
+#endif
+
+    sp<ALooper> codecLooper = new ALooper;
+    codecLooper->setName("codec_looper");
+
+    codecLooper->start(
+            false /* runOnCallingThread */,
+            false /* canCallJava */,
+            PRIORITY_DEFAULT);
+
+    size_t trackIndex;
+
+    sp<AMessage> notify;
+
+#if USE_SERIALIZER
+    trackIndex = mSerializer->addSource(source);
+#else
+    trackIndex = mTracks.size();
+
+    notify = new AMessage(kWhatSerializerNotify, id());
+    notify->setSize("trackIndex", trackIndex);
+    sp<MediaPuller> puller = new MediaPuller(source, notify);
+    pullLooper->registerHandler(puller);
+#endif
 
     sp<AMessage> format;
     status_t err = convertMetaDataToMessage(source->getFormat(), &format);
     CHECK_EQ(err, (status_t)OK);
 
-    format->setInt32("store-metadata-in-buffers", true);
+    if (isVideo) {
+        format->setInt32("store-metadata-in-buffers", true);
 
-    format->setInt32(
-            "color-format", OMX_COLOR_FormatAndroidOpaque);
+        format->setInt32(
+                "color-format", OMX_COLOR_FormatAndroidOpaque);
+    }
 
-    sp<AMessage> notify = new AMessage(kWhatConverterNotify, id());
-    notify->setSize("trackIndex", index);
+    notify = new AMessage(kWhatConverterNotify, id());
+    notify->setSize("trackIndex", trackIndex);
 
     sp<Converter> converter =
-        new Converter(notify, mCodecLooper, format);
+        new Converter(notify, codecLooper, format);
     CHECK_EQ(converter->initCheck(), (status_t)OK);
 
-    size_t numInputBuffers = converter->getInputBufferCount();
-    ALOGI("numInputBuffers to the encoder is %d", numInputBuffers);
-
     looper()->registerHandler(converter);
 
-    mTracks.add(index, new Track(converter));
+    if (numInputBuffers != NULL) {
+        *numInputBuffers = converter->getInputBufferCount();
+    }
 
-    sp<IServiceManager> sm = defaultServiceManager();
-    sp<IBinder> binder = sm->getService(String16("SurfaceFlinger"));
-    sp<ISurfaceComposer> service = interface_cast<ISurfaceComposer>(binder);
-    CHECK(service != NULL);
+#if USE_SERIALIZER
+    mTracks.add(trackIndex, new Track(NULL, codecLooper, NULL, converter));
+#else
+    mTracks.add(trackIndex, new Track(pullLooper, codecLooper, puller, converter));
+#endif
+
+    if (isVideo) {
+        mVideoTrackIndex = trackIndex;
+    }
+
+    return OK;
+}
+
+status_t WifiDisplaySource::PlaybackSession::addVideoSource() {
+    sp<SurfaceMediaSource> source = new SurfaceMediaSource(width(), height());
+
+    sp<MediaSource> videoSource =
+            new RepeaterSource(source, 30.0 /* rateHz */);
+
+    size_t numInputBuffers;
+    status_t err = addSource(true /* isVideo */, videoSource, &numInputBuffers);
+
+    if (err != OK) {
+        return err;
+    }
 
     // Add one reference to account for the serializer.
     // Add another two for unknown reasons.
-    err = source->setMaxAcquiredBufferCount(numInputBuffers + 2);
+    err = source->setMaxAcquiredBufferCount(15);  // XXX numInputBuffers + 2);
     CHECK_EQ(err, (status_t)OK);
 
     mBufferQueue = source->getBufferQueue();
 
     if (mLegacyMode) {
+        sp<IServiceManager> sm = defaultServiceManager();
+        sp<IBinder> binder = sm->getService(String16("SurfaceFlinger"));
+        sp<ISurfaceComposer> service = interface_cast<ISurfaceComposer>(binder);
+        CHECK(service != NULL);
+
         service->connectDisplay(mBufferQueue);
     }
 
+    return OK;
+}
+
+status_t WifiDisplaySource::PlaybackSession::addAudioSource() {
     sp<AudioSource> audioSource = new AudioSource(
             AUDIO_SOURCE_REMOTE_SUBMIX,
             48000 /* sampleRate */,
@@ -655,26 +812,11 @@
     if (audioSource->initCheck() == OK) {
         audioSource->setUseLooperTime(true);
 
-        index = mSerializer->addSource(audioSource);
-        CHECK_GE(index, 0);
-
-        sp<AMessage> audioFormat;
-        err = convertMetaDataToMessage(audioSource->getFormat(), &audioFormat);
-        CHECK_EQ(err, (status_t)OK);
-
-        sp<AMessage> audioNotify = new AMessage(kWhatConverterNotify, id());
-        audioNotify->setSize("trackIndex", index);
-
-        converter = new Converter(audioNotify, mCodecLooper, audioFormat);
-        looper()->registerHandler(converter);
-
-        mTracks.add(index, new Track(converter));
-
-        ALOGI("Successfully instantiated audio source.");
-    } else {
-        ALOGW("Unable to instantiate audio source");
+        return addSource(
+                false /* isVideo */, audioSource, NULL /* numInputBuffers */);
     }
-#endif
+
+    ALOGW("Unable to instantiate audio source");
 
     return OK;
 }
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h
index 5c228f6..528a039 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.h
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.h
@@ -25,6 +25,8 @@
 struct ABuffer;
 struct BufferQueue;
 struct ISurfaceTexture;
+struct MediaPuller;
+struct MediaSource;
 struct Serializer;
 struct TSPacketizer;
 
@@ -88,10 +90,10 @@
     sp<ALooper> mSerializerLooper;
     sp<Serializer> mSerializer;
     sp<TSPacketizer> mPacketizer;
-    sp<ALooper> mCodecLooper;
     sp<BufferQueue> mBufferQueue;
 
     KeyedVector<size_t, sp<Track> > mTracks;
+    ssize_t mVideoTrackIndex;
 
     sp<ABuffer> mTSQueue;
     int64_t mPrevTimeUs;
@@ -134,6 +136,16 @@
 
     status_t setupPacketizer();
 
+    status_t addFakeSources();
+
+    status_t addSource(
+            bool isVideo,
+            const sp<MediaSource> &source,
+            size_t *numInputBuffers);
+
+    status_t addVideoSource();
+    status_t addAudioSource();
+
     ssize_t appendTSData(
             const void *data, size_t size, bool timeDiscontinuity, bool flush);
 
diff --git a/media/libstagefright/wifi-display/source/RepeaterSource.cpp b/media/libstagefright/wifi-display/source/RepeaterSource.cpp
index 8af4fdf..56e8860 100644
--- a/media/libstagefright/wifi-display/source/RepeaterSource.cpp
+++ b/media/libstagefright/wifi-display/source/RepeaterSource.cpp
@@ -38,6 +38,7 @@
     mFrameCount = 0;
 
     mLooper = new ALooper;
+    mLooper->setName("repeater_looper");
     mLooper->start();
 
     mReflector = new AHandlerReflector<RepeaterSource>(this);
diff --git a/media/libstagefright/wifi-display/source/Serializer.cpp b/media/libstagefright/wifi-display/source/Serializer.cpp
index 0c54bf7..78dc4e6 100644
--- a/media/libstagefright/wifi-display/source/Serializer.cpp
+++ b/media/libstagefright/wifi-display/source/Serializer.cpp
@@ -47,6 +47,7 @@
 
 private:
     sp<MediaSource> mSource;
+    AString mMIME;
 
     bool mStarted;
     status_t mFinalResult;
@@ -62,6 +63,11 @@
       mFinalResult(OK),
       mBuffer(NULL),
       mBufferTimeUs(-1ll) {
+    const char *mime;
+    sp<MetaData> meta = mSource->getFormat();
+    CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+    mMIME = mime;
 }
 
 Serializer::Track::~Track() {
@@ -106,7 +112,12 @@
         return;
     }
 
+    int64_t nowUs = ALooper::GetNowUs();
     mFinalResult = mSource->read(&mBuffer);
+    int64_t delayUs = ALooper::GetNowUs() - nowUs;
+
+    ALOGV("read on track %s took %lld us, got %d bytes",
+          mMIME.c_str(), delayUs, mBuffer->range_length());
 
     if (mFinalResult != OK) {
         ALOGI("read failed w/ err %d", mFinalResult);
@@ -275,7 +286,8 @@
     }
 
     if (err == OK) {
-        schedulePoll();
+        // XXX add a 5 second delay for the client to get ready.
+        schedulePoll(5000000ll);
     }
 
     return err;