Bug 1058364 - MediaSource streams don't correctly handle the end of sub decoders - r=kinetik
authorChris Double <chris.double@double.co.nz>
Tue, 26 Aug 2014 19:25:09 +1200
changeset 223248 bfa272c7141e636291fc2771c5853bce6f2cde97
parent 223247 aabb54f965b747fef1a214a72170718e5ad77b7b
child 223249 2fc1d14dee3eb51b26da5375a092b04967f3b167
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik
bugs1058364
milestone34.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 1058364 - MediaSource streams don't correctly handle the end of sub decoders - r=kinetik
content/media/mediasource/MediaSource.cpp
content/media/mediasource/MediaSourceDecoder.cpp
content/media/mediasource/MediaSourceDecoder.h
content/media/mediasource/MediaSourceReader.cpp
content/media/mediasource/MediaSourceReader.h
--- a/content/media/mediasource/MediaSource.cpp
+++ b/content/media/mediasource/MediaSource.cpp
@@ -261,16 +261,17 @@ MediaSource::EndOfStream(const Optional<
   if (mReadyState != MediaSourceReadyState::Open ||
       mSourceBuffers->AnyUpdating()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   SetReadyState(MediaSourceReadyState::Ended);
   mSourceBuffers->Ended();
+  mDecoder->Ended();
   if (!aError.WasPassed()) {
     DurationChange(mSourceBuffers->GetHighestBufferedEndTime(), aRv);
     if (aRv.Failed()) {
       return;
     }
     // TODO:
     //   Notify media element that all data is now available.
     return;
--- a/content/media/mediasource/MediaSourceDecoder.cpp
+++ b/content/media/mediasource/MediaSourceDecoder.cpp
@@ -124,16 +124,24 @@ MediaSourceDecoder::DetachMediaSource()
 already_AddRefed<SourceBufferDecoder>
 MediaSourceDecoder::CreateSubDecoder(const nsACString& aType)
 {
   MOZ_ASSERT(mReader);
   return mReader->CreateSubDecoder(aType);
 }
 
 void
+MediaSourceDecoder::Ended()
+{
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  mReader->Ended();
+  mon.NotifyAll();
+}
+
+void
 MediaSourceDecoder::SetMediaSourceDuration(double aDuration)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!mMediaSource) {
     return;
   }
   ErrorResult dummy;
   mMediaSource->SetDuration(aDuration, dummy);
--- a/content/media/mediasource/MediaSourceDecoder.h
+++ b/content/media/mediasource/MediaSourceDecoder.h
@@ -42,16 +42,18 @@ public:
 
   static already_AddRefed<MediaResource> CreateResource();
 
   void AttachMediaSource(dom::MediaSource* aMediaSource);
   void DetachMediaSource();
 
   already_AddRefed<SourceBufferDecoder> CreateSubDecoder(const nsACString& aType);
 
+  void Ended();
+
   void SetMediaSourceDuration(double aDuration);
 
   // Provide a mechanism for MediaSourceReader to block waiting on data from a SourceBuffer.
   void WaitForData();
 
   // Called whenever a SourceBuffer has new data appended.
   void NotifyGotData();
 
--- a/content/media/mediasource/MediaSourceReader.cpp
+++ b/content/media/mediasource/MediaSourceReader.cpp
@@ -35,16 +35,17 @@ extern PRLogModuleInfo* GetMediaSourceAP
 
 namespace mozilla {
 
 MediaSourceReader::MediaSourceReader(MediaSourceDecoder* aDecoder)
   : MediaDecoderReader(aDecoder)
   , mTimeThreshold(-1)
   , mDropAudioBeforeThreshold(false)
   , mDropVideoBeforeThreshold(false)
+  , mEnded(false)
 {
 }
 
 bool
 MediaSourceReader::IsWaitingMediaResources()
 {
   return mDecoders.IsEmpty() && mPendingDecoders.IsEmpty();
 }
@@ -128,21 +129,26 @@ void
 MediaSourceReader::OnVideoEOS()
 {
   // End of stream. See if we can switch to another video decoder.
   MSE_DEBUG("MediaSourceReader(%p)::OnVideoEOS reader=%p (readers=%u)",
             this, mVideoReader.get(), mDecoders.Length());
   if (SwitchReaders(SWITCH_FORCED)) {
     // Success! Resume decoding with next video decoder.
     RequestVideoData(false, mTimeThreshold);
-  } else {
+  } else if (IsEnded()) {
     // End of stream.
     MSE_DEBUG("MediaSourceReader(%p)::OnVideoEOS reader=%p EOS (readers=%u)",
               this, mVideoReader.get(), mDecoders.Length());
     GetCallback()->OnVideoEOS();
+  } else {
+    // If a new decoder isn't ready to respond with frames yet, we're going to
+    // keep hitting this path at 1/frame_duration Hz. Bug 1058422 is raised to
+    // address this issue.
+    RequestVideoData(false, mTimeThreshold);
   }
 }
 
 void
 MediaSourceReader::OnDecodeError()
 {
   GetCallback()->OnDecodeError();
 }
@@ -407,24 +413,24 @@ MediaSourceReader::Seek(int64_t aTime, i
     MSE_DEBUG("MediaSourceReader(%p)::Seek no active buffer contains target=%f", this, target);
     NS_DispatchToMainThread(new ChangeToHaveMetadata(mDecoder));
   }
 
   // Loop until we have the requested time range in the source buffers.
   // This is a workaround for our lack of async functionality in the
   // MediaDecoderStateMachine. Bug 979104 implements what we need and
   // we'll remove this for an async approach based on that in bug XXXXXXX.
-  while (!DecodersContainTime(target) && !IsShutdown()) {
+  while (!DecodersContainTime(target) && !IsShutdown() && !IsEnded()) {
     MSE_DEBUG("MediaSourceReader(%p)::Seek waiting for target=%f", this, target);
     static_cast<MediaSourceDecoder*>(mDecoder)->WaitForData();
     SwitchReaders(SWITCH_FORCED);
   }
 
   if (IsShutdown()) {
-    return NS_OK;
+    return NS_ERROR_FAILURE;
   }
 
   ResetDecode();
   if (mAudioReader) {
     nsresult rv = mAudioReader->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
     MSE_DEBUG("MediaSourceReader(%p)::Seek audio reader=%p rv=%xf", this, mAudioReader.get(), rv);
     if (NS_FAILED(rv)) {
       return rv;
@@ -489,9 +495,23 @@ MediaSourceReader::ReadMetadata(MediaInf
   }
 
   *aInfo = mInfo;
   *aTags = nullptr; // TODO: Handle metadata.
 
   return NS_OK;
 }
 
+void
+MediaSourceReader::Ended()
+{
+  mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
+  mEnded = true;
+}
+
+bool
+MediaSourceReader::IsEnded()
+{
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+  return mEnded;
+}
+
 } // namespace mozilla
--- a/content/media/mediasource/MediaSourceReader.h
+++ b/content/media/mediasource/MediaSourceReader.h
@@ -82,16 +82,22 @@ public:
   {
     ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
     return mDecoder->IsShutdown();
   }
 
   // Return true if any of the active decoders contain data for the given time
   bool DecodersContainTime(double aTime);
 
+  // Mark the reader to indicate that EndOfStream has been called on our MediaSource
+  void Ended();
+
+  // Return true if the Ended method has been called
+  bool IsEnded();
+
 private:
   enum SwitchType {
     SWITCH_OPTIONAL,
     SWITCH_FORCED
   };
 
   bool SwitchReaders(SwitchType aType);
 
@@ -103,13 +109,15 @@ private:
   bool mDropAudioBeforeThreshold;
   bool mDropVideoBeforeThreshold;
 
   nsTArray<nsRefPtr<SourceBufferDecoder>> mPendingDecoders;
   nsTArray<nsRefPtr<SourceBufferDecoder>> mDecoders;
 
   nsRefPtr<MediaDecoderReader> mAudioReader;
   nsRefPtr<MediaDecoderReader> mVideoReader;
+
+  bool mEnded;
 };
 
 } // namespace mozilla
 
 #endif /* MOZILLA_MEDIASOURCEREADER_H_ */