Bug 1062664 - Refactor reader switching. r=cajbir
authorMatthew Gregan <kinetik@flim.org>
Mon, 08 Sep 2014 12:07:56 +1200
changeset 204121 5cf7d475651b01c495770778786e293904c313fe
parent 204120 d742663d733d0de48707fb9bdff6482c245e33af
child 204122 b8eaf4b01762717a0f2a17bcec7cfcf5dbffccf3
push id48846
push usermgregan@mozilla.com
push dateMon, 08 Sep 2014 23:20:45 +0000
treeherdermozilla-inbound@d911ec123f2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscajbir
bugs1062664
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 1062664 - Refactor reader switching. r=cajbir
content/media/mediasource/MediaSourceReader.cpp
content/media/mediasource/MediaSourceReader.h
--- a/content/media/mediasource/MediaSourceReader.cpp
+++ b/content/media/mediasource/MediaSourceReader.cpp
@@ -229,90 +229,115 @@ MediaSourceReader::BreakCycles()
   mVideoReader = nullptr;
   for (uint32_t i = 0; i < mTrackBuffers.Length(); ++i) {
     mTrackBuffers[i]->BreakCycles();
   }
   mTrackBuffers.Clear();
 }
 
 bool
+MediaSourceReader::CanSelectAudioReader(MediaDecoderReader* aNewReader)
+{
+  AudioInfo currentInfo = mAudioReader->GetMediaInfo().mAudio;
+  AudioInfo newInfo = aNewReader->GetMediaInfo().mAudio;
+
+  // TODO: We can't handle switching audio formats yet.
+  if (currentInfo.mRate != newInfo.mRate ||
+      currentInfo.mChannels != newInfo.mChannels) {
+    MSE_DEBUGV("MediaSourceReader(%p)::CanSelectAudioReader(%p) skip reader due to format mismatch",
+               this, aNewReader);
+    return false;
+  }
+
+  if (aNewReader->AudioQueue().AtEndOfStream()) {
+    MSE_DEBUGV("MediaSourceReader(%p)::CanSelectAudioReader(%p) skip reader due to queue EOS",
+               this, aNewReader);
+    return false;
+  }
+
+  return true;
+}
+
+bool
+MediaSourceReader::CanSelectVideoReader(MediaDecoderReader* aNewReader)
+{
+  if (aNewReader->VideoQueue().AtEndOfStream()) {
+    MSE_DEBUGV("MediaSourceReader(%p)::CanSelectVideoReader(%p) skip reader due to queue EOS",
+               this, aNewReader);
+    return false;
+  }
+
+  return true;
+}
+
+already_AddRefed<MediaDecoderReader>
+MediaSourceReader::SelectReader(double aTarget,
+                                bool (MediaSourceReader::*aCanSelectReader)(MediaDecoderReader*),
+                                const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders)
+{
+  mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
+
+  for (uint32_t i = 0; i < aTrackDecoders.Length(); ++i) {
+    nsRefPtr<MediaDecoderReader> newReader = aTrackDecoders[i]->GetReader();
+
+    // Check the track-type-specific aspects first, as it's assumed these
+    // are cheaper than a buffered range comparison, which seems worthwhile
+    // to avoid on any reader we'd subsequently reject.
+    if (!(this->*aCanSelectReader)(newReader)) {
+      continue;
+    }
+
+    nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
+    aTrackDecoders[i]->GetBuffered(ranges);
+    if (ranges->Find(aTarget) == dom::TimeRanges::NoIndex) {
+      MSE_DEBUGV("MediaSourceReader(%p)::SelectReader(%f) newReader=%p target not in ranges=%s",
+                 this, aTarget, newReader.get(), DumpTimeRanges(ranges).get());
+      continue;
+    }
+
+    return newReader.forget();
+  }
+
+  return nullptr;
+}
+
+bool
 MediaSourceReader::SwitchAudioReader(double aTarget)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  // XXX: Can't handle adding an audio track after ReadMetadata yet.
+  // XXX: Can't handle adding an audio track after ReadMetadata.
   if (!mAudioTrack) {
     return false;
   }
-  auto& decoders = mAudioTrack->Decoders();
-  for (uint32_t i = 0; i < decoders.Length(); ++i) {
-    nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
-    decoders[i]->GetBuffered(ranges);
-
-    MediaDecoderReader* newReader = decoders[i]->GetReader();
-    MSE_DEBUGV("MediaDecoderReader(%p)::SwitchAudioReader(%f) audioReader=%p reader=%p ranges=%s",
-               this, aTarget, mAudioReader.get(), newReader, DumpTimeRanges(ranges).get());
-
-    AudioInfo targetInfo = newReader->GetMediaInfo().mAudio;
-    AudioInfo currentInfo = mAudioReader->GetMediaInfo().mAudio;
-
-    // TODO: We can't handle switching audio formats yet.
-    if (currentInfo.mRate != targetInfo.mRate ||
-        currentInfo.mChannels != targetInfo.mChannels) {
-      continue;
-    }
-
-    if (ranges->Find(aTarget) != dom::TimeRanges::NoIndex) {
-      if (newReader->AudioQueue().AtEndOfStream()) {
-        continue;
-      }
-      if (mAudioReader) {
-        mAudioReader->SetIdle();
-      }
-      mAudioReader = newReader;
-      MSE_DEBUG("MediaDecoderReader(%p)::SwitchAudioReader(%f) switching to audio reader %p",
-                this, aTarget, mAudioReader.get());
-      return true;
-    }
+  nsRefPtr<MediaDecoderReader> newReader = SelectReader(aTarget,
+                                                        &MediaSourceReader::CanSelectAudioReader,
+                                                        mAudioTrack->Decoders());
+  if (newReader) {
+    mAudioReader->SetIdle();
+    mAudioReader = newReader;
   }
-
-  return false;
+  return newReader != nullptr;
 }
 
 bool
 MediaSourceReader::SwitchVideoReader(double aTarget)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  // XXX: Can't handle adding a video track after ReadMetadata yet.
+  // XXX: Can't handle adding a video track after ReadMetadata.
   if (!mVideoTrack) {
     return false;
   }
-  auto& decoders = mVideoTrack->Decoders();
-  for (uint32_t i = 0; i < decoders.Length(); ++i) {
-    nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
-    decoders[i]->GetBuffered(ranges);
-
-    MediaDecoderReader* newReader = decoders[i]->GetReader();
-    MSE_DEBUGV("MediaDecoderReader(%p)::SwitchVideoReader(%f) videoReader=%p reader=%p ranges=%s",
-               this, aTarget, mVideoReader.get(), newReader, DumpTimeRanges(ranges).get());
-
-    if (ranges->Find(aTarget) != dom::TimeRanges::NoIndex) {
-      if (newReader->VideoQueue().AtEndOfStream()) {
-        continue;
-      }
-      if (mVideoReader) {
-        mVideoReader->SetIdle();
-      }
-      mVideoReader = newReader;
-      MSE_DEBUG("MediaDecoderReader(%p)::SwitchVideoReader(%f) switching to video reader %p",
-                this, aTarget, mVideoReader.get());
-      return true;
-    }
+  nsRefPtr<MediaDecoderReader> newReader = SelectReader(aTarget,
+                                                        &MediaSourceReader::CanSelectVideoReader,
+                                                        mVideoTrack->Decoders());
+  if (newReader) {
+    mVideoReader->SetIdle();
+    mVideoReader = newReader;
   }
-
-  return false;
+  return newReader != nullptr;
 }
 
 MediaDecoderReader*
 CreateReaderForType(const nsACString& aType, AbstractMediaDecoder* aDecoder)
 {
 #ifdef MOZ_FMP4
   // The MP4Reader that supports fragmented MP4 and uses
   // PlatformDecoderModules is hidden behind prefs for regular video
--- a/content/media/mediasource/MediaSourceReader.h
+++ b/content/media/mediasource/MediaSourceReader.h
@@ -100,16 +100,32 @@ public:
 
   // Return true if the Ended method has been called
   bool IsEnded();
 
 private:
   bool SwitchAudioReader(double aTarget);
   bool SwitchVideoReader(double aTarget);
 
+  // Return a reader from the set available in aTrackDecoders that is considered
+  // usable by the aCanUserReader callback and has data available in the range
+  // requested by aTarget.
+  // aCanSelectReader is passed each reader available in aTrackDecoders and is
+  // expected to return true if the reader is considerable selectable.
+  already_AddRefed<MediaDecoderReader> SelectReader(double aTarget,
+                                                    bool (MediaSourceReader::*aCanSelectReader)(MediaDecoderReader*),
+                                                    const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders);
+
+  // Passed to SelectReader to enforce any track format specific requirements.
+  // In the case of CanSelectAudioReader, verifies that aNewReader has a
+  // matching audio format to the existing reader, as format switching is not
+  // yet supported.
+  bool CanSelectAudioReader(MediaDecoderReader* aNewReader);
+  bool CanSelectVideoReader(MediaDecoderReader* aNewReader);
+
   nsRefPtr<MediaDecoderReader> mAudioReader;
   nsRefPtr<MediaDecoderReader> mVideoReader;
 
   nsTArray<nsRefPtr<TrackBuffer>> mTrackBuffers;
   nsTArray<nsRefPtr<TrackBuffer>> mEssentialTrackBuffers;
   nsRefPtr<TrackBuffer> mAudioTrack;
   nsRefPtr<TrackBuffer> mVideoTrack;