Bug 881512 - Allow a SourceBuffer to own multiple subdecoders. Switch decoders on abort(). r=cajbir
authorMatthew Gregan <kinetik@flim.org>
Mon, 14 Apr 2014 23:24:00 +1200
changeset 199950 d978fe16633a715172f74b08a0feb8f522e83c2b
parent 199949 c570bcfd29b4d6df0b3ea5d92ec7e12e8f2e0ad3
child 199951 f8ece490919dd18b8f736e910d771ec3f7573fe3
push id486
push userasasaki@mozilla.com
push dateMon, 14 Jul 2014 18:39:42 +0000
treeherdermozilla-release@d33428174ff1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscajbir
bugs881512
milestone31.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 881512 - Allow a SourceBuffer to own multiple subdecoders. Switch decoders on abort(). r=cajbir
content/media/mediasource/MediaSource.cpp
content/media/mediasource/SourceBuffer.cpp
content/media/mediasource/SourceBuffer.h
--- a/content/media/mediasource/MediaSource.cpp
+++ b/content/media/mediasource/MediaSource.cpp
@@ -160,16 +160,20 @@ MediaSource::AddSourceBuffer(const nsASt
   nsContentTypeParser parser(aType);
   nsAutoString mimeType;
   rv = parser.GetType(mimeType);
   if (NS_FAILED(rv)) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return nullptr;
   }
   nsRefPtr<SourceBuffer> sourceBuffer = new SourceBuffer(this, NS_ConvertUTF16toUTF8(mimeType));
+  if (!sourceBuffer->Init()) {
+    aRv.Throw(NS_ERROR_FAILURE); // XXX need a better error here
+    return nullptr;
+  }
   mSourceBuffers->Append(sourceBuffer);
   MSE_DEBUG("%p AddSourceBuffer(Type=%s) -> %p", this,
             NS_ConvertUTF16toUTF8(mimeType).get(), sourceBuffer.get());
   return sourceBuffer.forget();
 }
 
 void
 MediaSource::RemoveSourceBuffer(SourceBuffer& aSourceBuffer, ErrorResult& aRv)
--- a/content/media/mediasource/SourceBuffer.cpp
+++ b/content/media/mediasource/SourceBuffer.cpp
@@ -150,17 +150,30 @@ SourceBuffer::SetTimestampOffset(double 
 already_AddRefed<TimeRanges>
 SourceBuffer::GetBuffered(ErrorResult& aRv)
 {
   if (!IsAttached()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
   nsRefPtr<TimeRanges> ranges = new TimeRanges();
-  mDecoder->GetBuffered(ranges);
+  for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
+    nsRefPtr<TimeRanges> r = new TimeRanges();
+    mDecoders[i]->GetBuffered(r);
+    if (r->Length() > 0) {
+      MSE_DEBUG("%p GetBuffered decoder=%u Length=%u Start=%f End=%f", this, i, r->Length(),
+                r->GetStartTime(), r->GetEndTime());
+      ranges->Add(r->GetStartTime(), r->GetEndTime());
+    } else {
+      MSE_DEBUG("%p GetBuffered decoder=%u Length=%u", this, i, r->Length());
+    }
+  }
+  ranges->Normalize();
+  MSE_DEBUG("%p GetBuffered Length=%u Start=%f End=%f", this, ranges->Length(),
+            ranges->GetStartTime(), ranges->GetEndTime());
   return ranges.forget();
 }
 
 void
 SourceBuffer::SetAppendWindowStart(double aAppendWindowStart, ErrorResult& aRv)
 {
   if (!IsAttached() || mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
@@ -198,36 +211,44 @@ void
 SourceBuffer::AppendBuffer(const ArrayBufferView& aData, ErrorResult& aRv)
 {
   AppendData(aData.Data(), aData.Length(), aRv);
 }
 
 void
 SourceBuffer::Abort(ErrorResult& aRv)
 {
+  MSE_DEBUG("%p Abort()", this);
   if (!IsAttached()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (mMediaSource->ReadyState() != MediaSourceReadyState::Open) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (mUpdating) {
     // TODO: Abort segment parser loop, buffer append, and stream append loop algorithms.
     AbortUpdating();
   }
   // TODO: Run reset parser algorithm.
   mAppendWindowStart = 0;
   mAppendWindowEnd = PositiveInfinity<double>();
+
+  MSE_DEBUG("%p Abort: Switching decoders.", this);
+  mCurrentDecoder->GetResource()->Ended();
+  if (!InitNewDecoder()) {
+    aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling.
+  }
 }
 
 void
 SourceBuffer::Remove(double aStart, double aEnd, ErrorResult& aRv)
 {
+  MSE_DEBUG("%p Remove(Start=%f End=%f)", this, aStart, aEnd);
   if (!IsAttached() || mUpdating ||
       mMediaSource->ReadyState() != MediaSourceReadyState::Open) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (aStart < 0 || aStart > mMediaSource->Duration() ||
       aEnd <= aStart) {
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
@@ -237,45 +258,46 @@ SourceBuffer::Remove(double aStart, doub
   /// TODO: Run coded frame removal algorithm asynchronously (would call StopUpdating()).
   StopUpdating();
 }
 
 void
 SourceBuffer::Detach()
 {
   Ended();
-  mDecoder = nullptr;
+  mDecoders.Clear();
+  mCurrentDecoder = nullptr;
   mMediaSource = nullptr;
 }
 
 void
 SourceBuffer::Ended()
 {
-  mDecoder->GetResource()->Ended();
+  for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
+    mDecoders[i]->GetResource()->Ended();
+  }
 }
 
 SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
   : DOMEventTargetHelper(aMediaSource->GetParentObject())
   , mMediaSource(aMediaSource)
+  , mType(aType)
   , mAppendWindowStart(0)
   , mAppendWindowEnd(PositiveInfinity<double>())
   , mTimestampOffset(0)
   , mAppendMode(SourceBufferAppendMode::Segments)
   , mUpdating(false)
 {
   MOZ_ASSERT(aMediaSource);
-  MediaSourceDecoder* parentDecoder = aMediaSource->GetDecoder();
-  mDecoder = parentDecoder->CreateSubDecoder(aType);
-  MOZ_ASSERT(mDecoder);
 }
 
 SourceBuffer::~SourceBuffer()
 {
-  if (mDecoder) {
-    mDecoder->GetResource()->Ended();
+  for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
+    mDecoders[i]->GetResource()->Ended();
   }
 }
 
 MediaSource*
 SourceBuffer::GetParentObject() const
 {
   return mMediaSource;
 }
@@ -296,16 +318,32 @@ SourceBuffer::DispatchSimpleEvent(const 
 void
 SourceBuffer::QueueAsyncSimpleEvent(const char* aName)
 {
   MSE_DEBUG("%p Queuing event %s to SourceBuffer", this, aName);
   nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<SourceBuffer>(this, aName);
   NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
 }
 
+bool
+SourceBuffer::InitNewDecoder()
+{
+  MediaSourceDecoder* parentDecoder = mMediaSource->GetDecoder();
+  nsRefPtr<SubBufferDecoder> decoder = parentDecoder->CreateSubDecoder(mType);
+  if (!decoder) {
+    return false;
+  }
+  mDecoders.AppendElement(decoder);
+  // XXX: At this point, we really want to push through any remaining
+  // processing for the old decoder and discard it, rather than hanging on
+  // to all of them in mDecoders.
+  mCurrentDecoder = decoder;
+  return true;
+}
+
 void
 SourceBuffer::StartUpdating()
 {
   MOZ_ASSERT(!mUpdating);
   mUpdating = true;
   QueueAsyncSimpleEvent("updatestart");
 }
 
@@ -325,41 +363,41 @@ SourceBuffer::AbortUpdating()
   mUpdating = false;
   QueueAsyncSimpleEvent("abort");
   QueueAsyncSimpleEvent("updateend");
 }
 
 void
 SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
 {
+  MSE_DEBUG("%p AppendBuffer(Data=%u bytes)", this, aLength);
   if (!IsAttached() || mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
     mMediaSource->SetReadyState(MediaSourceReadyState::Open);
   }
   // TODO: Run coded frame eviction algorithm.
   // TODO: Test buffer full flag.
-  MSE_DEBUG("%p Append(ArrayBuffer=%u)", this, aLength);
   StartUpdating();
   // XXX: For future reference: NDA call must run on the main thread.
-  mDecoder->NotifyDataArrived(reinterpret_cast<const char*>(aData),
-                              aLength,
-                              mDecoder->GetResource()->GetLength());
+  mCurrentDecoder->NotifyDataArrived(reinterpret_cast<const char*>(aData),
+                                     aLength,
+                                     mCurrentDecoder->GetResource()->GetLength());
   // TODO: Run buffer append algorithm asynchronously (would call StopUpdating()).
-  mDecoder->GetResource()->AppendData(aData, aLength);
+  mCurrentDecoder->GetResource()->AppendData(aData, aLength);
 
   // Eviction uses a byte threshold. If the buffer is greater than the
   // number of bytes then data is evicted. The time range for this
   // eviction is reported back to the media source. It will then
   // evict data before that range across all SourceBuffer's it knows
   // about.
   const int evict_threshold = 1000000;
-  bool evicted = mDecoder->GetResource()->EvictData(evict_threshold);
+  bool evicted = mCurrentDecoder->GetResource()->EvictData(evict_threshold);
   if (evicted) {
     double start = 0.0;
     double end = 0.0;
     GetBufferedStartEndTime(&start, &end);
 
     // We notify that we've evicted from the time range 0 through to
     // the current start point.
     mMediaSource->NotifyEvicted(0.0, start);
@@ -369,38 +407,37 @@ SourceBuffer::AppendData(const uint8_t* 
   // Schedule the state machine thread to ensure playback starts
   // if required when data is appended.
   mMediaSource->GetDecoder()->ScheduleStateMachineThread();
 }
 
 void
 SourceBuffer::GetBufferedStartEndTime(double* aStart, double* aEnd)
 {
-  nsRefPtr<TimeRanges> ranges = new TimeRanges();
-  mDecoder->GetBuffered(ranges);
-  ranges->Normalize();
-  int length = ranges->Length();
-  ErrorResult rv;
-
-  if (aStart) {
-    *aStart = length > 0 ? ranges->Start(0, rv) : 0.0;
+  ErrorResult dummy;
+  nsRefPtr<TimeRanges> ranges = GetBuffered(dummy);
+  if (!ranges || ranges->Length() == 0) {
+    *aStart = *aEnd = 0.0;
+    return;
   }
-
-  if (aEnd) {
-    *aEnd = length > 0 ? ranges->End(length - 1, rv) : 0.0;
-  }
+  *aStart = ranges->Start(0, dummy);
+  *aEnd = ranges->End(ranges->Length() - 1, dummy);
 }
 
 void
 SourceBuffer::Evict(double aStart, double aEnd)
 {
-  // Need to map time to byte offset then evict
-  int64_t end = mDecoder->ConvertToByteOffset(aEnd);
-  if (end > 0) {
-    mDecoder->GetResource()->EvictBefore(end);
+  for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
+    // Need to map time to byte offset then evict
+    int64_t end = mDecoders[i]->ConvertToByteOffset(aEnd);
+    if (end <= 0) {
+      NS_WARNING("SourceBuffer::Evict failed");
+      continue;
+    }
+    mDecoders[i]->GetResource()->EvictBefore(end);
   }
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_1(SourceBuffer, DOMEventTargetHelper,
                                      mMediaSource)
 
 NS_IMPL_ADDREF_INHERITED(SourceBuffer, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(SourceBuffer, DOMEventTargetHelper)
--- a/content/media/mediasource/SourceBuffer.h
+++ b/content/media/mediasource/SourceBuffer.h
@@ -103,36 +103,51 @@ public:
     return mMediaSource != nullptr;
   }
 
   void Ended();
 
   // Evict data in the source buffer in the given time range.
   void Evict(double aStart, double aEnd);
 
+  // Initialize this SourceBuffer.  Must be called before use.
+  bool Init()
+  {
+    MOZ_ASSERT(!mCurrentDecoder);
+    return InitNewDecoder();
+  }
+
 private:
   friend class AsyncEventRunner<SourceBuffer>;
   void DispatchSimpleEvent(const char* aName);
   void QueueAsyncSimpleEvent(const char* aName);
 
+  // Create a new decoder for mType, add it to mDecoders and update mCurrentDecoder.
+  bool InitNewDecoder();
+
   // Update mUpdating and fire the appropriate events.
   void StartUpdating();
   void StopUpdating();
   void AbortUpdating();
 
   // Shared implementation of AppendBuffer overloads.
   void AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv);
 
   // Provide the minimum start time and maximum end time that is available
   // in the data buffered by this SourceBuffer.
   void GetBufferedStartEndTime(double* aStart, double* aEnd);
 
   nsRefPtr<MediaSource> mMediaSource;
 
-  nsRefPtr<SubBufferDecoder> mDecoder;
+  const nsAutoCString mType;
+
+  // XXX: We only want to keep the current decoder alive, but need a way to
+  // query @buffered for everything this SourceBuffer is responsible for.
+  nsTArray<nsRefPtr<SubBufferDecoder>> mDecoders;
+  nsRefPtr<SubBufferDecoder> mCurrentDecoder;
 
   double mAppendWindowStart;
   double mAppendWindowEnd;
 
   double mTimestampOffset;
 
   SourceBufferAppendMode mAppendMode;
   bool mUpdating;