Bug 1074791 - [RTSP] Avoid unnecessary play/pause requests to RTSP server. r=sworkman, r=bechen
authorEthan Tseng <ettseng@mozilla.com>
Wed, 08 Oct 2014 14:23:13 +0800
changeset 232632 8a6c8de66a9e8413fbea09be7849d1185157e131
parent 232631 abff1c80ccea7987c792e9c437f5f9334bb6587b
child 232633 3dfd3d80188f89049b14f05f823799b12c222a64
push id4187
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:29:12 +0000
treeherdermozilla-beta@f23cc6a30c11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssworkman, bechen
bugs1074791
milestone35.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1074791 - [RTSP] Avoid unnecessary play/pause requests to RTSP server. r=sworkman, r=bechen
netwerk/protocol/rtsp/controller/RtspController.cpp
netwerk/protocol/rtsp/controller/RtspController.h
--- a/netwerk/protocol/rtsp/controller/RtspController.cpp
+++ b/netwerk/protocol/rtsp/controller/RtspController.cpp
@@ -42,36 +42,47 @@
 #include "zlib.h"
 #include <algorithm>
 #include "nsDebug.h"
 
 extern PRLogModuleInfo* gRtspLog;
 #undef LOG
 #define LOG(args) PR_LOG(gRtspLog, PR_LOG_DEBUG, args)
 
+const unsigned long kCommandDelayMs = 200;
+
 namespace mozilla {
 namespace net {
 
+//-----------------------------------------------------------------------------
+// RtspController
+//-----------------------------------------------------------------------------
 NS_IMPL_ISUPPORTS(RtspController,
                   nsIStreamingProtocolController)
 
 RtspController::RtspController(nsIChannel *channel)
-  : mState(INIT)
+  : mState(INIT),
+    mTimerLock("RtspController.mTimerLock"),
+    mPlayTimer(nullptr),
+    mPauseTimer(nullptr)
 {
   LOG(("RtspController::RtspController()"));
 }
 
 RtspController::~RtspController()
 {
   LOG(("RtspController::~RtspController()"));
   if (mRtspSource.get()) {
     mRtspSource.clear();
   }
 }
 
+//-----------------------------------------------------------------------------
+// nsIStreamingProtocolController
+//-----------------------------------------------------------------------------
 NS_IMETHODIMP
 RtspController::GetTrackMetaData(uint8_t index,
                                  nsIStreamingProtocolMetaData * *_retval)
 {
   LOG(("RtspController::GetTrackMetaData()"));
   return NS_OK;
 }
 
@@ -83,69 +94,85 @@ RtspController::Play(void)
     MOZ_ASSERT(mRtspSource.get(), "mRtspSource should not be null!");
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   if (mState != CONNECTED) {
     return NS_ERROR_NOT_CONNECTED;
   }
 
-  mRtspSource->play();
+  MutexAutoLock lock(mTimerLock);
+  // Cancel the pause timer if it is active because successive pause-play in a
+  // short duration is unnecessary but could impair playback smoothing.
+  if (mPauseTimer) {
+    mPauseTimer->Cancel();
+    mPauseTimer = nullptr;
+  }
+
+  // Start a timer to delay the play operation for a short duration.
+  if (!mPlayTimer) {
+    mPlayTimer = do_CreateInstance("@mozilla.org/timer;1");
+    if (!mPlayTimer) {
+      return NS_ERROR_NOT_INITIALIZED;
+    }
+    mPlayTimer->InitWithFuncCallback(
+                  RtspController::PlayTimerCallback,
+                  this, kCommandDelayMs,
+                  nsITimer::TYPE_ONE_SHOT);
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 RtspController::Pause(void)
 {
   LOG(("RtspController::Pause()"));
   if (!mRtspSource.get()) {
     MOZ_ASSERT(mRtspSource.get(), "mRtspSource should not be null!");
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   if (mState != CONNECTED) {
     return NS_ERROR_NOT_CONNECTED;
   }
 
-  mRtspSource->pause();
+  MutexAutoLock lock(mTimerLock);
+  // Cancel the play timer if it is active because successive play-pause in a
+  // short duration is unnecessary but could impair playback smoothing.
+  if (mPlayTimer) {
+    mPlayTimer->Cancel();
+    mPlayTimer = nullptr;
+  }
+
+  // Start a timer to delay the pause operation for a short duration.
+  if (!mPauseTimer) {
+    mPauseTimer = do_CreateInstance("@mozilla.org/timer;1");
+    if (!mPauseTimer) {
+      return NS_ERROR_NOT_INITIALIZED;
+    }
+    mPauseTimer->InitWithFuncCallback(
+                   RtspController::PauseTimerCallback,
+                   this, kCommandDelayMs,
+                   nsITimer::TYPE_ONE_SHOT);
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 RtspController::Resume(void)
 {
-  LOG(("RtspController::Resume()"));
-  if (!mRtspSource.get()) {
-    MOZ_ASSERT(mRtspSource.get(), "mRtspSource should not be null!");
-    return NS_ERROR_NOT_INITIALIZED;
-  }
-
-  if (mState != CONNECTED) {
-    return NS_ERROR_NOT_CONNECTED;
-  }
-
-  mRtspSource->play();
-  return NS_OK;
+  return Play();
 }
 
 NS_IMETHODIMP
 RtspController::Suspend(void)
 {
-  LOG(("RtspController::Suspend()"));
-  if (!mRtspSource.get()) {
-    MOZ_ASSERT(mRtspSource.get(), "mRtspSource should not be null!");
-    return NS_ERROR_NOT_INITIALIZED;
-  }
-
-  if (mState != CONNECTED) {
-    return NS_ERROR_NOT_CONNECTED;
-  }
-
-  mRtspSource->pause();
-  return NS_OK;
+  return Pause();
 }
 
 NS_IMETHODIMP
 RtspController::Seek(uint64_t seekTimeUs)
 {
   LOG(("RtspController::Seek() %llu", seekTimeUs));
   if (!mRtspSource.get()) {
     MOZ_ASSERT(mRtspSource.get(), "mRtspSource should not be null!");
@@ -205,16 +232,19 @@ RtspController::AsyncOpen(nsIStreamingPr
                                           mUserAgent.get(), false, 0);
   }
   // Connect to Rtsp Server.
   mRtspSource->start();
 
   return NS_OK;
 }
 
+//-----------------------------------------------------------------------------
+// nsIStreamingProtocolListener
+//-----------------------------------------------------------------------------
 class SendMediaDataTask : public nsRunnable
 {
 public:
   SendMediaDataTask(nsIStreamingProtocolListener *listener,
                     uint8_t index,
                     const nsACString & data,
                     uint32_t length,
                     uint32_t offset,
@@ -324,16 +354,30 @@ private:
 };
 
 NS_IMETHODIMP
 RtspController::OnDisconnected(uint8_t index,
                                nsresult reason)
 {
   LOG(("RtspController::OnDisconnected() for track %d reason = 0x%x", index, reason));
   mState = DISCONNECTED;
+
+  // Ensure play and pause timer are stopped.
+  {
+    MutexAutoLock lock(mTimerLock);
+    if (mPlayTimer) {
+      mPlayTimer->Cancel();
+      mPlayTimer = nullptr;
+    }
+    if (mPauseTimer) {
+      mPauseTimer->Cancel();
+      mPauseTimer = nullptr;
+    }
+  }
+
   if (mListener) {
     nsRefPtr<SendOnDisconnectedTask> task =
       new SendOnDisconnectedTask(mListener, index, reason);
     // Break the cycle reference between the Listener (RtspControllerParent) and
     // us.
     mListener = nullptr;
     return NS_DispatchToMainThread(task);
   }
@@ -387,10 +431,45 @@ RtspController::PlaybackEnded()
     MOZ_ASSERT(mRtspSource.get(), "mRtspSource should not be null!");
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   mRtspSource->playbackEnded();
   return NS_OK;
 }
 
+//-----------------------------------------------------------------------------
+// RtspController static member methods
+//-----------------------------------------------------------------------------
+//static
+void RtspController::PlayTimerCallback(nsITimer *aTimer, void *aClosure)
+{
+  MOZ_ASSERT(aTimer);
+  MOZ_ASSERT(aClosure);
+
+  RtspController *self = static_cast<RtspController*>(aClosure);
+  MOZ_ASSERT(self->mRtspSource.get());
+
+  MutexAutoLock lock(self->mTimerLock);
+  if (self->mPlayTimer) {
+    self->mRtspSource->play();
+    self->mPlayTimer = nullptr;
+  }
+}
+
+//static
+void RtspController::PauseTimerCallback(nsITimer *aTimer, void *aClosure)
+{
+  MOZ_ASSERT(aTimer);
+  MOZ_ASSERT(aClosure);
+
+  RtspController *self = static_cast<RtspController*>(aClosure);
+  MOZ_ASSERT(self->mRtspSource.get());
+
+  MutexAutoLock lock(self->mTimerLock);
+  if (self->mPauseTimer) {
+    self->mRtspSource->pause();
+    self->mPauseTimer = nullptr;
+  }
+}
+
 } // namespace mozilla::net
 } // namespace mozilla
--- a/netwerk/protocol/rtsp/controller/RtspController.h
+++ b/netwerk/protocol/rtsp/controller/RtspController.h
@@ -2,36 +2,42 @@
 /* vim: set sw=2 ts=8 et tw=80 : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef RtspController_h
 #define RtspController_h
 
+#include "mozilla/Mutex.h"
 #include "nsIStreamingProtocolController.h"
 #include "nsIChannel.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
+#include "nsITimer.h"
 #include "RTSPSource.h"
 
 namespace mozilla {
 namespace net {
 
 class RtspController : public nsIStreamingProtocolController
                      , public nsIStreamingProtocolListener
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSISTREAMINGPROTOCOLCONTROLLER
   NS_DECL_NSISTREAMINGPROTOCOLLISTENER
 
   RtspController(nsIChannel *channel);
   ~RtspController();
 
+  // These callbacks will be called when mPlayTimer/mPauseTimer fires.
+  static void PlayTimerCallback(nsITimer *aTimer, void *aClosure);
+  static void PauseTimerCallback(nsITimer *aTimer, void *aClosure);
+
 private:
   enum State {
     INIT,
     CONNECTED,
     DISCONNECTED
   };
 
   // RTSP URL refer to a stream or an aggregate of streams.
@@ -42,13 +48,20 @@ private:
   nsCString mSpec;
   // UserAgent string.
   nsCString mUserAgent;
   // Indicate the connection state between the
   // media streaming server and the Rtsp client.
   State mState;
   // Rtsp Streaming source.
   android::sp<android::RTSPSource> mRtspSource;
+  // This lock protects mPlayTimer and mPauseTimer.
+  Mutex mTimerLock;
+  // Timers to delay the play and pause operations.
+  // They are used for optimization and avoid sending unnecessary requests to
+  // the server.
+  nsCOMPtr<nsITimer> mPlayTimer;
+  nsCOMPtr<nsITimer> mPauseTimer;
 };
 
 }
 } // namespace mozilla::net
 #endif