Merge "Documentation on pipes" into jb-mr1-dev
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 26a25b0..34108b3 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -547,7 +547,7 @@
     status_t allocateTimedBuffer(size_t size, sp<IMemory>* buffer);
 
     /* queue a buffer obtained via allocateTimedBuffer for playback at the
-       given timestamp.  PTS units a microseconds on the media time timeline.
+       given timestamp.  PTS units are microseconds on the media time timeline.
        The media time transform (set with setMediaTimeTransform) set by the
        audio producer will handle converting from media time to local time
        (perhaps going through the common time timeline in the case of
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 73d396e..362d022 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -1144,7 +1144,7 @@
 
     // If the track is not invalid already, try to allocate a buffer.  alloc
     // fails indicating that the server is dead, flag the track as invalid so
-    // we can attempt to restore in in just a bit.
+    // we can attempt to restore in just a bit.
     if (!(mCblk->flags & CBLK_INVALID_MSK)) {
         result = mAudioTrack->allocateTimedBuffer(size, buffer);
         if (result == DEAD_OBJECT) {
diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp
index f5c8c93..c478b28 100644
--- a/media/libstagefright/SurfaceMediaSource.cpp
+++ b/media/libstagefright/SurfaceMediaSource.cpp
@@ -52,7 +52,7 @@
         ALOGE("Invalid dimensions %dx%d", bufferWidth, bufferHeight);
     }
 
-    mBufferQueue = new BufferQueue(true, MIN_UNDEQUEUED_BUFFERS);
+    mBufferQueue = new BufferQueue(true);
     mBufferQueue->setDefaultBufferSize(bufferWidth, bufferHeight);
     mBufferQueue->setSynchronousMode(true);
     mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER |
@@ -136,10 +136,32 @@
 {
     ALOGV("start");
 
+    Mutex::Autolock lock(mMutex);
+
     mStartTimeNs = 0;
     int64_t startTimeUs;
-    if (params && params->findInt64(kKeyTime, &startTimeUs)) {
-        mStartTimeNs = startTimeUs * 1000;
+    int32_t bufferCount = 0;
+    if (params) {
+        if (params->findInt64(kKeyTime, &startTimeUs)) {
+            mStartTimeNs = startTimeUs * 1000;
+        }
+
+        if (!params->findInt32(kKeyNumBuffers, &bufferCount)) {
+            ALOGE("Failed to find the advertised buffer count");
+            return UNKNOWN_ERROR;
+        }
+
+        if (bufferCount <= 1) {
+            ALOGE("bufferCount %d is too small", bufferCount);
+            return BAD_VALUE;
+        }
+    }
+
+    if (bufferCount != 0) {
+        status_t err = mBufferQueue->setMaxAcquiredBufferCount(bufferCount);
+        if (err != OK) {
+            return err;
+        }
     }
 
     return OK;
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
index 35d4414..a998dcd 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
@@ -116,8 +116,6 @@
 
                     err = mNetSession->createRTSPServer(
                             addr, port, notify, &mSessionID);
-
-                    ALOGI("createRTSPServer returned err %d", err);
                 } else {
                     err = -EINVAL;
                 }
@@ -154,7 +152,7 @@
 
                     mNetSession->destroySession(sessionID);
 
-                    mClientIPs.removeItem(sessionID);
+                    mClientInfos.removeItem(sessionID);
                     break;
                 }
 
@@ -167,10 +165,11 @@
                     CHECK(msg->findString("client-ip", &info.mRemoteIP));
                     CHECK(msg->findString("server-ip", &info.mLocalIP));
                     CHECK(msg->findInt32("server-port", &info.mLocalPort));
+                    info.mPlaybackSessionID = -1;
 
                     ALOGI("We now have a client (%d) connected.", sessionID);
 
-                    mClientIPs.add(sessionID, info);
+                    mClientInfos.add(sessionID, info);
 
                     status_t err = sendM1(sessionID);
                     CHECK_EQ(err, (status_t)OK);
@@ -284,6 +283,20 @@
             break;
         }
 
+        case kWhatKeepAlive:
+        {
+            int32_t sessionID;
+            CHECK(msg->findInt32("sessionID", &sessionID));
+
+            if (mClientInfos.indexOfKey(sessionID) < 0) {
+                // Obsolete event, client is already gone.
+                break;
+            }
+
+            sendM16(sessionID);
+            break;
+        }
+
         default:
             TRESPASS();
     }
@@ -366,7 +379,7 @@
     //   max-hres (none or 2 byte)
     //   max-vres (none or 2 byte)
 
-    const ClientInfo &info = mClientIPs.valueFor(sessionID);
+    const ClientInfo &info = mClientInfos.valueFor(sessionID);
 
     AString body = StringPrintf(
         "wfd_video_formats: "
@@ -425,6 +438,31 @@
     return OK;
 }
 
+status_t WifiDisplaySource::sendM16(int32_t sessionID) {
+    AString request = "GET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n";
+    AppendCommonResponse(&request, mNextCSeq);
+
+    const ClientInfo &info = mClientInfos.valueFor(sessionID);
+    request.append(StringPrintf("Session: %d\r\n", info.mPlaybackSessionID));
+
+    request.append("Content-Length: 0\r\n");
+    request.append("\r\n");
+
+    status_t err =
+        mNetSession->sendRequest(sessionID, request.c_str(), request.size());
+
+    if (err != OK) {
+        return err;
+    }
+
+    registerResponseHandler(
+            sessionID, mNextCSeq, &WifiDisplaySource::onReceiveM16Response);
+
+    ++mNextCSeq;
+
+    return OK;
+}
+
 status_t WifiDisplaySource::onReceiveM1Response(
         int32_t sessionID, const sp<ParsedMessage> &msg) {
     int32_t statusCode;
@@ -481,6 +519,22 @@
     return OK;
 }
 
+status_t WifiDisplaySource::onReceiveM16Response(
+        int32_t sessionID, const sp<ParsedMessage> &msg) {
+    // If only the response was required to include a "Session:" header...
+
+    const ClientInfo &info = mClientInfos.valueFor(sessionID);
+
+    ssize_t index = mPlaybackSessions.indexOfKey(info.mPlaybackSessionID);
+    if (index >= 0) {
+        mPlaybackSessions.valueAt(index)->updateLiveness();
+
+        scheduleKeepAlive(sessionID);
+    }
+
+    return OK;
+}
+
 void WifiDisplaySource::scheduleReaper() {
     if (mReaperPending) {
         return;
@@ -490,6 +544,16 @@
     (new AMessage(kWhatReapDeadClients, id()))->post(kReaperIntervalUs);
 }
 
+void WifiDisplaySource::scheduleKeepAlive(int32_t sessionID) {
+    // We need to send updates at least 5 secs before the timeout is set to
+    // expire, make sure the timeout is greater than 5 secs to begin with.
+    CHECK_GT(kPlaybackSessionTimeoutUs, 5000000ll);
+
+    sp<AMessage> msg = new AMessage(kWhatKeepAlive, id());
+    msg->setInt32("sessionID", sessionID);
+    msg->post(kPlaybackSessionTimeoutUs - 5000000ll);
+}
+
 void WifiDisplaySource::onReceiveClientData(const sp<AMessage> &msg) {
     int32_t sessionID;
     CHECK(msg->findInt32("sessionID", &sessionID));
@@ -637,6 +701,14 @@
         int32_t sessionID,
         int32_t cseq,
         const sp<ParsedMessage> &data) {
+    ClientInfo *info = &mClientInfos.editValueFor(sessionID);
+    if (info->mPlaybackSessionID != -1) {
+        // We only support a single playback session per client.
+        // This is due to the reversed keep-alive design in the wfd specs...
+        sendErrorResponse(sessionID, "400 Bad Request", cseq);
+        return;
+    }
+
     AString transport;
     if (!data->findString("transport", &transport)) {
         sendErrorResponse(sessionID, "400 Bad Request", cseq);
@@ -713,10 +785,8 @@
         return;
     }
 
-    const ClientInfo &info = mClientIPs.valueFor(sessionID);
-
     status_t err = playbackSession->init(
-            info.mRemoteIP.c_str(),
+            info->mRemoteIP.c_str(),
             clientRtp,
             clientRtcp,
             useInterleavedTCP);
@@ -739,6 +809,8 @@
 
     mPlaybackSessions.add(playbackSessionID, playbackSession);
 
+    info->mPlaybackSessionID = playbackSessionID;
+
     AString response = "RTSP/1.0 200 OK\r\n";
     AppendCommonResponse(&response, cseq, playbackSessionID);
 
@@ -770,10 +842,8 @@
     err = mNetSession->sendRequest(sessionID, response.c_str());
     CHECK_EQ(err, (status_t)OK);
 
-#if 0
-    // XXX the dongle does not currently send keep-alives.
     scheduleReaper();
-#endif
+    scheduleKeepAlive(sessionID);
 }
 
 void WifiDisplaySource::onPlayRequest(
@@ -949,6 +1019,11 @@
     for (;;) {
         int32_t playbackSessionID = rand();
 
+        if (playbackSessionID == -1) {
+            // reserved.
+            continue;
+        }
+
         for (size_t i = 0; i < mPlaybackSessions.size(); ++i) {
             if (mPlaybackSessions.keyAt(i) == playbackSessionID) {
                 continue;
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
index cd9939b..f56347d 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
@@ -49,6 +49,7 @@
         kWhatStop,
         kWhatReapDeadClients,
         kWhatPlaybackSessionNotify,
+        kWhatKeepAlive,
     };
 
     struct ResponseID {
@@ -79,8 +80,10 @@
         AString mRemoteIP;
         AString mLocalIP;
         int32_t mLocalPort;
+        int32_t mPlaybackSessionID;
     };
-    KeyedVector<int32_t, ClientInfo> mClientIPs;
+    // by sessionID.
+    KeyedVector<int32_t, ClientInfo> mClientInfos;
 
     bool mReaperPending;
 
@@ -94,6 +97,7 @@
     status_t sendM3(int32_t sessionID);
     status_t sendM4(int32_t sessionID);
     status_t sendM5(int32_t sessionID);
+    status_t sendM16(int32_t sessionID);
 
     status_t onReceiveM1Response(
             int32_t sessionID, const sp<ParsedMessage> &msg);
@@ -107,6 +111,9 @@
     status_t onReceiveM5Response(
             int32_t sessionID, const sp<ParsedMessage> &msg);
 
+    status_t onReceiveM16Response(
+            int32_t sessionID, const sp<ParsedMessage> &msg);
+
     void registerResponseHandler(
             int32_t sessionID, int32_t cseq, HandleRTSPResponseFunc func);
 
@@ -161,6 +168,7 @@
             AString *response, int32_t cseq, int32_t playbackSessionID = -1ll);
 
     void scheduleReaper();
+    void scheduleKeepAlive(int32_t sessionID);
 
     int32_t makeUniquePlaybackSessionID() const;
 
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 1a37f4f..14f74b5 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -169,8 +169,8 @@
 // for the track.  The client then sub-divides this into smaller buffers for its use.
 // Currently the client uses double-buffering by default, but doesn't tell us about that.
 // So for now we just assume that client is double-buffered.
-// FIXME It would be better for client to tell us whether it wants double-buffering or N-buffering,
-// so we could allocate the right amount of memory.
+// FIXME It would be better for client to tell AudioFlinger whether it wants double-buffering or
+// N-buffering, so AudioFlinger could allocate the right amount of memory.
 // See the client's minBufCount and mNotificationFramesAct calculations for details.
 static const int kFastTrackMultiplier = 2;
 
@@ -258,11 +258,11 @@
 AudioFlinger::~AudioFlinger()
 {
     while (!mRecordThreads.isEmpty()) {
-        // closeInput() will remove first entry from mRecordThreads
+        // closeInput_nonvirtual() will remove specified entry from mRecordThreads
         closeInput_nonvirtual(mRecordThreads.keyAt(0));
     }
     while (!mPlaybackThreads.isEmpty()) {
-        // closeOutput() will remove first entry from mPlaybackThreads
+        // closeOutput_nonvirtual() will remove specified entry from mPlaybackThreads
         closeOutput_nonvirtual(mPlaybackThreads.keyAt(0));
     }
 
@@ -1134,7 +1134,7 @@
 
 AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id,
         audio_devices_t device, type_t type)
-    :   Thread(false),
+    :   Thread(false /*canCallJava*/),
         mType(type),
         mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mNormalFrameCount(0),
         // mChannelMask
@@ -1142,6 +1142,7 @@
         mFrameSize(1), mFormat(AUDIO_FORMAT_INVALID),
         mParamStatus(NO_ERROR),
         mStandby(false), mDevice(device), mId(id),
+        // mName will be set by concrete (non-virtual) subclass
         mDeathRecipient(new PMDeathRecipient(this))
 {
 }
@@ -6097,7 +6098,7 @@
                     if (mChannelCount == 1 && mReqChannelCount == 1) {
                         framesOut >>= 1;
                     }
-                    mResampler->resample(mRsmpOutBuffer, framesOut, this);
+                    mResampler->resample(mRsmpOutBuffer, framesOut, this /* AudioBufferProvider* */);
                     // ditherAndClamp() works as long as all buffers returned by mActiveTrack->getNextBuffer()
                     // are 32 bit aligned which should be always true.
                     if (mChannelCount == 2 && mReqChannelCount == 1) {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index b4aefc1..4723cd9 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -454,8 +454,9 @@
             /*const*/ sp<Client> mClient;   // see explanation at ~TrackBase() why not const
             sp<IMemory>         mCblkMemory;
             audio_track_cblk_t* mCblk;
-            void*               mBuffer;
-            void*               mBufferEnd;
+            void*               mBuffer;    // start of track buffer, typically in shared memory
+            void*               mBufferEnd; // &mBuffer[mFrameCount * frameSize], where frameSize
+                                            //   is based on mChannelCount and 16-bit samples
             uint32_t            mFrameCount;
             // we don't really need a lock for these
             track_state         mState;
@@ -1364,6 +1365,7 @@
 
     // record thread
     class RecordThread : public ThreadBase, public AudioBufferProvider
+                            // derives from AudioBufferProvider interface for use by resampler
     {
     public:
 
@@ -1420,7 +1422,7 @@
         void        dumpInternals(int fd, const Vector<String16>& args);
         void        dumpTracks(int fd, const Vector<String16>& args);
 
-        // Thread
+        // Thread virtuals
         virtual bool        threadLoop();
         virtual status_t    readyToRun();
 
@@ -1968,9 +1970,10 @@
                 DefaultKeyedVector< audio_io_handle_t, sp<PlaybackThread> >  mPlaybackThreads;
                 stream_type_t                       mStreamTypes[AUDIO_STREAM_CNT];
 
-                // both are protected by mLock
+                // member variables below are protected by mLock
                 float                               mMasterVolume;
                 bool                                mMasterMute;
+                // end of variables protected by mLock
 
                 DefaultKeyedVector< audio_io_handle_t, sp<RecordThread> >    mRecordThreads;