Bug 1264199: P9. Include pending frames in HasUnplayedFrames calculation. r?jwwang
MozReview-Commit-ID: 5m8zCe4cB2s
--- 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;