Bug 1264199: P9. Include pending frames in HasUnplayedFrames calculation. r?jwwang draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Tue, 26 Apr 2016 00:13:01 +1000
changeset 356288 a28e014db660c5d2d886a0aa90b9c1640fc2fefc
parent 356287 4130d3707ef8f207b63ff78b241fb76a8c7c2838
child 356289 9b05b23eb1948cf4c1c317c07addc95cfb79b742
push id16486
push userbmo:jyavenard@mozilla.com
push dateTue, 26 Apr 2016 02:36:37 +0000
reviewersjwwang
bugs1264199
milestone49.0a1
Bug 1264199: P9. Include pending frames in HasUnplayedFrames calculation. r?jwwang MozReview-Commit-ID: 5m8zCe4cB2s
dom/media/mediasink/DecodedAudioDataSink.cpp
dom/media/mediasink/DecodedAudioDataSink.h
--- a/dom/media/mediasink/DecodedAudioDataSink.cpp
+++ b/dom/media/mediasink/DecodedAudioDataSink.cpp
@@ -34,21 +34,22 @@ static const int32_t LOW_AUDIO_USECS = 3
 
 DecodedAudioDataSink::DecodedAudioDataSink(AbstractThread* aThread,
                                            MediaQueue<MediaData>& aAudioQueue,
                                            int64_t aStartTime,
                                            const AudioInfo& aInfo,
                                            dom::AudioChannel aChannel)
   : AudioSink(aAudioQueue)
   , mStartTime(aStartTime)
-  , mWritten(0)
   , mLastGoodPosition(0)
   , mInfo(aInfo)
   , mChannel(aChannel)
   , mPlaying(true)
+  , mMonitor("DecodedAudioDataSink")
+  , mWritten(0)
   , mErrored(false)
   , mPlaybackComplete(false)
   , mOwnerThread(aThread)
   , mProcessedQueueLength(0)
   , mFramesParsed(0)
   , mLastEndTime(0)
 {
   bool resampling = gfxPrefs::AudioSinkResampling();
@@ -117,18 +118,23 @@ DecodedAudioDataSink::GetPosition()
   return mStartTime + mLastGoodPosition;
 }
 
 bool
 DecodedAudioDataSink::HasUnplayedFrames()
 {
   // Experimentation suggests that GetPositionInFrames() is zero-indexed,
   // so we need to add 1 here before comparing it to mWritten.
+  int64_t total;
+  {
+    MonitorAutoLock mon(mMonitor);
+    total = mWritten + (mCursor.get() ? mCursor->Available() : 0);
+  }
   return mProcessedQueue.GetSize() ||
-         (mAudioStream && mAudioStream->GetPositionInFrames() + 1 < mWritten);
+         (mAudioStream && mAudioStream->GetPositionInFrames() + 1 < total);
 }
 
 void
 DecodedAudioDataSink::Shutdown()
 {
   MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
 
   mAudioQueueListener.Disconnect();
@@ -203,17 +209,22 @@ DecodedAudioDataSink::InitializeAudioStr
   mAudioStream->Start();
 
   return NS_OK;
 }
 
 int64_t
 DecodedAudioDataSink::GetEndTime() const
 {
-  CheckedInt64 playedUsecs = FramesToUsecs(mWritten, mOutputRate) + mStartTime;
+  int64_t written;
+  {
+    MonitorAutoLock mon(mMonitor);
+    written = mWritten;
+  }
+  CheckedInt64 playedUsecs = FramesToUsecs(written, mOutputRate) + mStartTime;
   if (!playedUsecs.isValid()) {
     NS_WARNING("Int overflow calculating audio end time");
     return -1;
   }
   // As we may be resampling, rounding errors may occur. Ensure we never get
   // past the original end time.
   return std::min<int64_t>(mLastEndTime, playedUsecs.value());
 }
@@ -267,34 +278,40 @@ DecodedAudioDataSink::PopFrames(uint32_t
 
     // We need to update our values prior popping the processed queue in
     // order to prevent the pop event to fire too early (prior
     // mProcessedQueueLength being updated) or prevent HasUnplayedFrames
     // to incorrectly return true during the time interval betweeen the
     // when mProcessedQueue is read and mWritten is updated.
     needPopping = true;
     mCurrentData = mProcessedQueue.PeekFront();
-    mCursor = MakeUnique<AudioBufferCursor>(mCurrentData->mAudioData.get(),
-                                            mCurrentData->mChannels,
-                                            mCurrentData->mFrames);
+    {
+      MonitorAutoLock mon(mMonitor);
+      mCursor = MakeUnique<AudioBufferCursor>(mCurrentData->mAudioData.get(),
+                                              mCurrentData->mChannels,
+                                              mCurrentData->mFrames);
+    }
     MOZ_ASSERT(mCurrentData->mFrames > 0);
     mProcessedQueueLength -=
       FramesToUsecs(mCurrentData->mFrames, mOutputRate).value();
   }
 
   auto framesToPop = std::min(aFrames, mCursor->Available());
 
   SINK_LOG_V("playing audio at time=%lld offset=%u length=%u",
              mCurrentData->mTime, mCurrentData->mFrames - mCursor->Available(), framesToPop);
 
   UniquePtr<AudioStream::Chunk> chunk =
     MakeUnique<Chunk>(mCurrentData, framesToPop, mCursor->Ptr());
 
-  mWritten += framesToPop;
-  mCursor->Advance(framesToPop);
+  {
+    MonitorAutoLock mon(mMonitor);
+    mWritten += framesToPop;
+    mCursor->Advance(framesToPop);
+  }
 
   // All frames are popped. Reset mCurrentData so we can pop new elements from
   // the audio queue in next calls to PopFrames().
   if (!mCursor->Available()) {
     mCurrentData = nullptr;
   }
 
   if (needPopping) {
--- a/dom/media/mediasink/DecodedAudioDataSink.h
+++ b/dom/media/mediasink/DecodedAudioDataSink.h
@@ -12,17 +12,17 @@
 #include "MediaInfo.h"
 #include "mozilla/RefPtr.h"
 #include "nsISupportsImpl.h"
 
 #include "mozilla/dom/AudioChannelBinding.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MozPromise.h"
-#include "mozilla/ReentrantMonitor.h"
+#include "mozilla/Monitor.h"
 
 namespace mozilla {
 
 class AudioConverter;
 
 namespace media {
 
 class DecodedAudioDataSink : public AudioSink,
@@ -72,19 +72,16 @@ private:
   // The audio stream resource. Used on the task queue of MDSM only.
   RefPtr<AudioStream> mAudioStream;
 
   // The presentation time of the first audio frame that was played in
   // microseconds. We can add this to the audio stream position to determine
   // the current audio time.
   const int64_t mStartTime;
 
-  // PCM frames written to the stream so far.
-  Atomic<int64_t> mWritten;
-
   // Keep the last good position returned from the audio stream. Used to ensure
   // position returned by GetPosition() is mono-increasing in spite of audio
   // stream error. Used on the task queue of MDSM only.
   int64_t mLastGoodPosition;
 
   const AudioInfo mInfo;
 
   const dom::AudioChannel mChannel;
@@ -95,18 +92,28 @@ private:
   MozPromiseHolder<GenericPromise> mEndPromise;
 
   /*
    * Members to implement AudioStream::DataSource.
    * Used on the callback thread of cubeb.
    */
   // The AudioData at which AudioStream::DataSource is reading.
   RefPtr<AudioData> mCurrentData;
+
+  // Monitor protecting access to mCursor and mWritten.
+  // mCursor is created/destroyed on the cubeb thread, while we must also
+  // ensure that mWritten and mCursor::Available() get modified simultaneously.
+  // (written on cubeb thread, and read on MDSM task queue).
+  mutable Monitor mMonitor;
   // Keep track of the read position of mCurrentData.
   UniquePtr<AudioBufferCursor> mCursor;
+
+  // PCM frames written to the stream so far.
+  int64_t mWritten;
+
   // True if there is any error in processing audio data like overflow.
   Atomic<bool> mErrored;
 
   // Set on the callback thread of cubeb once the stream has drained.
   Atomic<bool> mPlaybackComplete;
 
   const RefPtr<AbstractThread> mOwnerThread;