Bug 1218311 - Port the fix of bug 1193614 to VideoSink. r=cpearce.
authorJW Wang <jwwang@mozilla.com>
Wed, 25 Nov 2015 15:15:05 +0800
changeset 274101 0288a0a7003ffba272dc8040567e225d7115d9bc
parent 274100 51ce619d52f5a4272125fb539033afa71c30f4e0
child 274102 9e87a89f401dd2e998b456be8bdad5b8ee8db9f6
push id68478
push userjwwang@mozilla.com
push dateWed, 25 Nov 2015 07:17:01 +0000
treeherdermozilla-inbound@0288a0a7003f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs1218311, 1193614
milestone45.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 1218311 - Port the fix of bug 1193614 to VideoSink. r=cpearce.
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaQueue.h
dom/media/mediasink/VideoSink.cpp
dom/media/mediasink/VideoSink.h
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -387,17 +387,17 @@ MediaDecoderStateMachine::CreateMediaSin
   //       is extracted. It is tricky that the implementation of DecodedStream
   //       happens to allow reuse after shutdown without creating a new one.
   RefPtr<media::MediaSink> audioSink = aAudioCaptured ?
     mStreamSink : CreateAudioSink();
 
   RefPtr<media::MediaSink> mediaSink =
     new VideoSink(mTaskQueue, audioSink, mVideoQueue,
                   mDecoder->GetVideoFrameContainer(), mRealTime,
-                  mDecoder->GetFrameStatistics(), AUDIO_DURATION_USECS,
+                  mDecoder->GetFrameStatistics(),
                   sVideoQueueSendToCompositorSize);
   return mediaSink.forget();
 }
 
 bool MediaDecoderStateMachine::HasFutureAudio()
 {
   MOZ_ASSERT(OnTaskQueue());
   NS_ASSERTION(HasAudio(), "Should only call HasFutureAudio() when we have audio");
@@ -889,29 +889,16 @@ MediaDecoderStateMachine::OnVideoDecoded
       Push(video, MediaData::VIDEO_DATA);
       if (MaybeFinishDecodeFirstFrame()) {
         return;
       }
       if (mIsVideoPrerolling && DonePrerollingVideo()) {
         StopPrerollingVideo();
       }
 
-      // Schedule the state machine to send stream data as soon as possible if
-      // the VideoQueue() is empty or contains one frame before the Push().
-      //
-      // The state machine threads requires a frame in VideoQueue() that is `in
-      // the future` to gather precise timing information. The head of
-      // VideoQueue() is always `in the past`.
-      //
-      // Schedule the state machine as soon as possible to render the video
-      // frame or delay the state machine thread accurately.
-      if (VideoQueue().GetSize() <= 2) {
-        ScheduleStateMachine();
-      }
-
       // For non async readers, if the requested video sample was slow to
       // arrive, increase the amount of audio we buffer to ensure that we
       // don't run out of audio. This is unnecessary for async readers,
       // since they decode audio and video on different threads so they
       // are unlikely to run out of decoded audio.
       if (mReader->IsAsync()) {
         return;
       }
--- a/dom/media/MediaQueue.h
+++ b/dom/media/MediaQueue.h
@@ -41,25 +41,25 @@ public:
     return nsDeque::GetSize();
   }
 
   inline void Push(T* aItem) {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     MOZ_ASSERT(aItem);
     NS_ADDREF(aItem);
     nsDeque::Push(aItem);
-    mPushEvent.Notify();
+    mPushEvent.Notify(RefPtr<T>(aItem));
   }
 
   inline void PushFront(T* aItem) {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     MOZ_ASSERT(aItem);
     NS_ADDREF(aItem);
     nsDeque::PushFront(aItem);
-    mPushEvent.Notify();
+    mPushEvent.Notify(RefPtr<T>(aItem));
   }
 
   inline already_AddRefed<T> PopFront() {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     RefPtr<T> rv = dont_AddRef(static_cast<T*>(nsDeque::PopFront()));
     if (rv) {
       mPopEvent.Notify(rv);
     }
@@ -156,28 +156,28 @@ public:
     }
     return frames;
   }
 
   MediaEventSource<RefPtr<T>>& PopEvent() {
     return mPopEvent;
   }
 
-  MediaEventSource<void>& PushEvent() {
+  MediaEventSource<RefPtr<T>>& PushEvent() {
     return mPushEvent;
   }
 
   MediaEventSource<void>& FinishEvent() {
     return mFinishEvent;
   }
 
 private:
   mutable ReentrantMonitor mReentrantMonitor;
   MediaEventProducer<RefPtr<T>> mPopEvent;
-  MediaEventProducer<void> mPushEvent;
+  MediaEventProducer<RefPtr<T>> mPushEvent;
   MediaEventProducer<void> mFinishEvent;
   // True when we've decoded the last frame of data in the
   // bitstream for which we're queueing frame data.
   bool mEndOfStream;
 };
 
 } // namespace mozilla
 
--- a/dom/media/mediasink/VideoSink.cpp
+++ b/dom/media/mediasink/VideoSink.cpp
@@ -21,29 +21,27 @@ using namespace mozilla::layers;
 namespace media {
 
 VideoSink::VideoSink(AbstractThread* aThread,
                      MediaSink* aAudioSink,
                      MediaQueue<MediaData>& aVideoQueue,
                      VideoFrameContainer* aContainer,
                      bool aRealTime,
                      FrameStatistics& aFrameStats,
-                     int aDelayDuration,
                      uint32_t aVQueueSentToCompositerSize)
   : mOwnerThread(aThread)
   , mAudioSink(aAudioSink)
   , mVideoQueue(aVideoQueue)
   , mContainer(aContainer)
   , mProducerID(ImageContainer::AllocateProducerID())
   , mRealTime(aRealTime)
   , mFrameStats(aFrameStats)
   , mVideoFrameEndTime(-1)
   , mHasVideo(false)
   , mUpdateScheduler(aThread)
-  , mDelayDuration(aDelayDuration)
   , mVideoQueueSendToCompositorSize(aVQueueSentToCompositerSize)
 {
   MOZ_ASSERT(mAudioSink, "AudioSink should exist.");
 }
 
 VideoSink::~VideoSink()
 {
 }
@@ -213,22 +211,28 @@ VideoSink::Shutdown()
   AssertOwnerThread();
   MOZ_ASSERT(!mAudioSink->IsStarted(), "must be called after playback stops.");
   VSINK_LOG("[%s]", __func__);
 
   mAudioSink->Shutdown();
 }
 
 void
-VideoSink::OnVideoQueueEvent()
+VideoSink::OnVideoQueueEvent(RefPtr<MediaData>&& aSample)
 {
   AssertOwnerThread();
   // Listen to push event, VideoSink should try rendering ASAP if first frame
   // arrives but update scheduler is not triggered yet.
-  TryUpdateRenderedVideoFrames();
+  VideoData* v = aSample->As<VideoData>();
+  if (!v->mSentToCompositor) {
+    // Since we push rendered frames back to the queue, we will receive
+    // push events for them. We only need to trigger render loop
+    // when this frame is not rendered yet.
+    TryUpdateRenderedVideoFrames();
+  }
 }
 
 void
 VideoSink::Redraw()
 {
   AssertOwnerThread();
   RenderVideoFrames(1);
 }
@@ -333,17 +337,17 @@ VideoSink::UpdateRenderedVideoFrames()
 
   TimeStamp nowTime;
   const int64_t clockTime = mAudioSink->GetPosition(&nowTime);
   // Skip frames up to the frame at the playback position, and figure out
   // the time remaining until it's time to display the next frame and drop
   // the current frame.
   NS_ASSERTION(clockTime >= 0, "Should have positive clock time.");
 
-  int64_t remainingTime = mDelayDuration;
+  int64_t remainingTime = -1;
   if (VideoQueue().GetSize() > 0) {
     RefPtr<MediaData> currentFrame = VideoQueue().PopFront();
     int32_t framesRemoved = 0;
     while (VideoQueue().GetSize() > 0) {
       MediaData* nextFrame = VideoQueue().PeekFront();
       if (!mRealTime && nextFrame->mTime > clockTime) {
         remainingTime = nextFrame->mTime - clockTime;
         break;
@@ -360,17 +364,24 @@ VideoSink::UpdateRenderedVideoFrames()
     if (framesRemoved > 0) {
       mVideoFrameEndTime = currentFrame->GetEndTime();
       mFrameStats.NotifyPresentedFrame();
     }
   }
 
   RenderVideoFrames(mVideoQueueSendToCompositorSize, clockTime, nowTime);
 
-  TimeStamp target = nowTime + TimeDuration::FromMicroseconds(remainingTime);
+  // No next fame to render. There is no need to schedule next render
+  // loop. We will run render loops again upon incoming frames.
+  if (remainingTime < 0) {
+    return;
+  }
+
+  TimeStamp target = nowTime + TimeDuration::FromMicroseconds(
+    remainingTime / mAudioSink->GetPlaybackParams().mPlaybackRate);
 
   RefPtr<VideoSink> self = this;
   mUpdateScheduler.Ensure(target, [self] () {
     self->UpdateRenderedVideoFramesByTimer();
   }, [self] () {
     self->UpdateRenderedVideoFramesByTimer();
   });
 }
--- a/dom/media/mediasink/VideoSink.h
+++ b/dom/media/mediasink/VideoSink.h
@@ -30,17 +30,16 @@ class VideoSink : public MediaSink
   typedef mozilla::layers::ImageContainer::ProducerID ProducerID;
 public:
   VideoSink(AbstractThread* aThread,
             MediaSink* aAudioSink,
             MediaQueue<MediaData>& aVideoQueue,
             VideoFrameContainer* aContainer,
             bool aRealTime,
             FrameStatistics& aFrameStats,
-            int aDelayDuration,
             uint32_t aVQueueSentToCompositerSize);
 
   const PlaybackParams& GetPlaybackParams() const override;
 
   void SetPlaybackParams(const PlaybackParams& aParams) override;
 
   RefPtr<GenericPromise> OnEnded(TrackType aType) override;
 
@@ -69,17 +68,17 @@ public:
   bool IsPlaying() const override;
 
   void Shutdown() override;
 
 private:
   virtual ~VideoSink();
 
   // VideoQueue listener related.
-  void OnVideoQueueEvent();
+  void OnVideoQueueEvent(RefPtr<MediaData>&& aSample);
   void ConnectListener();
   void DisconnectListener();
 
   // Sets VideoQueue images into the VideoFrameContainer. Called on the shared
   // state machine thread. The first aMaxFrames (at most) are set.
   // aClockTime and aClockTimeStamp are used as the baseline for deriving
   // timestamps for the frames; when omitted, aMaxFrames must be 1 and
   // a null timestamp is passed to the VideoFrameContainer.
@@ -134,20 +133,16 @@ private:
   MediaEventListener mPushListener;
 
   // True if this sink is going to handle video track.
   bool mHasVideo;
 
   // Used to trigger another update of rendered frames in next round.
   DelayedScheduler mUpdateScheduler;
 
-  // A delay duration to trigger next time UpdateRenderedVideoFrames().
-  // Based on the default value in MDSM.
-  const int mDelayDuration;
-
   // Max frame number sent to compositor at a time.
   // Based on the pref value obtained in MDSM.
   const uint32_t mVideoQueueSendToCompositorSize;
 };
 
 } // namespace media
 } // namespace mozilla