Bug 1259274: [MSE] P3. Refactor handling of tasks so they only ever run concurrently. r=gerald
authorJean-Yves Avenard <jyavenard@mozilla.com>
Sun, 27 Mar 2016 20:20:40 +1100
changeset 290587 99c24e7caa30e609cce70a31db481beb232cb7df
parent 290586 8498ca83977b7e846a64136ada2a9425c3b0b69e
child 290588 75a3df878ca62c11c080e1101339d6c982b451d4
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgerald
bugs1259274
milestone48.0a1
Bug 1259274: [MSE] P3. Refactor handling of tasks so they only ever run concurrently. r=gerald MozReview-Commit-ID: 1U8r82kTR0t
dom/media/mediasource/SourceBuffer.cpp
dom/media/mediasource/SourceBuffer.h
dom/media/mediasource/SourceBufferAttributes.h
dom/media/mediasource/SourceBufferTask.h
dom/media/mediasource/TrackBuffersManager.cpp
dom/media/mediasource/TrackBuffersManager.h
dom/media/mediasource/moz.build
--- a/dom/media/mediasource/SourceBuffer.cpp
+++ b/dom/media/mediasource/SourceBuffer.cpp
@@ -37,74 +37,71 @@ extern mozilla::LogModule* GetMediaSourc
 #define MP4_READER_DORMANT_HEURISTIC
 #else
 #undef MP4_READER_DORMANT_HEURISTIC
 #endif
 
 namespace mozilla {
 
 using media::TimeUnit;
+typedef SourceBufferAttributes::AppendState AppendState;
 
 namespace dom {
 
 void
 SourceBuffer::SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv)
 {
-  typedef mozilla::TrackBuffersManager::AppendState AppendState;
-
   MOZ_ASSERT(NS_IsMainThread());
   MSE_API("SetMode(aMode=%d)", aMode);
   if (!IsAttached() || mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
-  if (mAttributes->mGenerateTimestamps &&
+  if (mCurrentAttributes.mGenerateTimestamps &&
       aMode == SourceBufferAppendMode::Segments) {
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return;
   }
   MOZ_ASSERT(mMediaSource->ReadyState() != MediaSourceReadyState::Closed);
   if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
     mMediaSource->SetReadyState(MediaSourceReadyState::Open);
   }
-  if (mTrackBuffersManager->GetAppendState() == AppendState::PARSING_MEDIA_SEGMENT){
+  if (mCurrentAttributes.GetAppendState() == AppendState::PARSING_MEDIA_SEGMENT){
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   if (aMode == SourceBufferAppendMode::Sequence) {
     // Will set GroupStartTimestamp to GroupEndTimestamp.
-    mTrackBuffersManager->RestartGroupStartTimestamp();
+    mCurrentAttributes.RestartGroupStartTimestamp();
   }
 
-  mAttributes->SetAppendMode(aMode);
+  mCurrentAttributes.SetAppendMode(aMode);
 }
 
 void
 SourceBuffer::SetTimestampOffset(double aTimestampOffset, ErrorResult& aRv)
 {
-  typedef mozilla::TrackBuffersManager::AppendState AppendState;
-
   MOZ_ASSERT(NS_IsMainThread());
   MSE_API("SetTimestampOffset(aTimestampOffset=%f)", aTimestampOffset);
   if (!IsAttached() || mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   MOZ_ASSERT(mMediaSource->ReadyState() != MediaSourceReadyState::Closed);
   if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
     mMediaSource->SetReadyState(MediaSourceReadyState::Open);
   }
-  if (mTrackBuffersManager->GetAppendState() == AppendState::PARSING_MEDIA_SEGMENT){
+  if (mCurrentAttributes.GetAppendState() == AppendState::PARSING_MEDIA_SEGMENT){
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
-  mAttributes->SetApparentTimestampOffset(aTimestampOffset);
-  if (mAttributes->GetAppendMode() == SourceBufferAppendMode::Sequence) {
-    mTrackBuffersManager->SetGroupStartTimestamp(mAttributes->GetTimestampOffset());
+  mCurrentAttributes.SetApparentTimestampOffset(aTimestampOffset);
+  if (mCurrentAttributes.GetAppendMode() == SourceBufferAppendMode::Sequence) {
+    mCurrentAttributes.SetGroupStartTimestamp(mCurrentAttributes.GetTimestampOffset());
   }
 }
 
 TimeRanges*
 SourceBuffer::GetBuffered(ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   // http://w3c.github.io/media-source/index.html#widl-SourceBuffer-buffered
@@ -141,38 +138,38 @@ SourceBuffer::SetAppendWindowStart(doubl
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_API("SetAppendWindowStart(aAppendWindowStart=%f)", aAppendWindowStart);
   if (!IsAttached() || mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (aAppendWindowStart < 0 ||
-      aAppendWindowStart >= mAttributes->GetAppendWindowEnd()) {
+      aAppendWindowStart >= mCurrentAttributes.GetAppendWindowEnd()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return;
   }
-  mAttributes->SetAppendWindowStart(aAppendWindowStart);
+  mCurrentAttributes.SetAppendWindowStart(aAppendWindowStart);
 }
 
 void
 SourceBuffer::SetAppendWindowEnd(double aAppendWindowEnd, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_API("SetAppendWindowEnd(aAppendWindowEnd=%f)", aAppendWindowEnd);
   if (!IsAttached() || mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (IsNaN(aAppendWindowEnd) ||
-      aAppendWindowEnd <= mAttributes->GetAppendWindowStart()) {
+      aAppendWindowEnd <= mCurrentAttributes.GetAppendWindowStart()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return;
   }
-  mAttributes->SetAppendWindowEnd(aAppendWindowEnd);
+  mCurrentAttributes.SetAppendWindowEnd(aAppendWindowEnd);
 }
 
 void
 SourceBuffer::AppendBuffer(const ArrayBuffer& aData, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_API("AppendBuffer(ArrayBuffer)");
   aData.ComputeLengthAndData();
@@ -197,37 +194,40 @@ SourceBuffer::Abort(ErrorResult& aRv)
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (mMediaSource->ReadyState() != MediaSourceReadyState::Open) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   AbortBufferAppend();
-  mTrackBuffersManager->ResetParserState();
-  mAttributes->SetAppendWindowStart(0);
-  mAttributes->SetAppendWindowEnd(PositiveInfinity<double>());
+  ResetParserState();
+  mCurrentAttributes.SetAppendWindowStart(0);
+  mCurrentAttributes.SetAppendWindowEnd(PositiveInfinity<double>());
 }
 
 void
 SourceBuffer::AbortBufferAppend()
 {
   if (mUpdating) {
     if (mPendingAppend.Exists()) {
       mPendingAppend.Disconnect();
       mTrackBuffersManager->AbortAppendData();
-      // Some data may have been added by the Segment Parser Loop.
-      // Check if we need to update the duration.
-      CheckEndTime();
     }
     AbortUpdating();
   }
 }
 
 void
+SourceBuffer::ResetParserState()
+{
+  mTrackBuffersManager->ResetParserState(mCurrentAttributes);
+}
+
+void
 SourceBuffer::Remove(double aStart, double aEnd, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_API("Remove(aStart=%f, aEnd=%f)", aStart, aEnd);
   if (!IsAttached()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
@@ -290,44 +290,38 @@ SourceBuffer::Ended()
   // We want the MediaSourceReader to refresh its buffered range as it may
   // have been modified (end lined up).
   mMediaSource->GetDecoder()->NotifyDataArrived();
 }
 
 SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
   : DOMEventTargetHelper(aMediaSource->GetParentObject())
   , mMediaSource(aMediaSource)
+  , mCurrentAttributes(aType.LowerCaseEqualsLiteral("audio/mpeg") ||
+                       aType.LowerCaseEqualsLiteral("audio/aac"))
   , mUpdating(false)
   , mActive(false)
   , mType(aType)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aMediaSource);
-  bool generateTimestamps = false;
-  if (aType.LowerCaseEqualsLiteral("audio/mpeg") ||
-      aType.LowerCaseEqualsLiteral("audio/aac")) {
-    generateTimestamps = true;
-  }
-  mAttributes = new SourceBufferAttributes(generateTimestamps);
 
   mTrackBuffersManager =
-    new TrackBuffersManager(mAttributes,
-                            aMediaSource->GetDecoder(),
-                            aType);
+    new TrackBuffersManager(aMediaSource->GetDecoder(), aType);
 
   // Now that we know what type we're dealing with, enable dormant as needed.
 #if defined(MP4_READER_DORMANT_HEURISTIC)
   aMediaSource->GetDecoder()->NotifyDormantSupported(Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false));
 #endif
 
   MSE_DEBUG("Create mTrackBuffersManager=%p",
             mTrackBuffersManager.get());
 
   ErrorResult dummy;
-  if (mAttributes->mGenerateTimestamps) {
+  if (mCurrentAttributes.mGenerateTimestamps) {
     SetMode(SourceBufferAppendMode::Sequence, dummy);
   } else {
     SetMode(SourceBufferAppendMode::Segments, dummy);
   }
   mMediaSource->GetDecoder()->GetDemuxer()->AttachSourceBuffer(
     mTrackBuffersManager.get());
 }
 
@@ -398,71 +392,61 @@ SourceBuffer::AbortUpdating()
   QueueAsyncSimpleEvent("updateend");
 }
 
 void
 SourceBuffer::CheckEndTime()
 {
   MOZ_ASSERT(NS_IsMainThread());
   // Check if we need to update mMediaSource duration
-  double endTime = mTrackBuffersManager->GroupEndTimestamp().ToSeconds();
+  double endTime = mCurrentAttributes.GetGroupEndTimestamp().ToSeconds();
   double duration = mMediaSource->Duration();
   if (endTime > duration) {
     mMediaSource->SetDuration(endTime, MSRangeRemovalAction::SKIP);
   }
 }
 
 void
 SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
 {
   MSE_DEBUG("AppendData(aLength=%u)", aLength);
 
   RefPtr<MediaByteBuffer> data = PrepareAppend(aData, aLength, aRv);
   if (!data) {
     return;
   }
-  mTrackBuffersManager->AppendData(data, mAttributes->GetTimestampOffset());
-
   StartUpdating();
 
-  BufferAppend();
-}
-
-void
-SourceBuffer::BufferAppend()
-{
-  MOZ_ASSERT(mUpdating);
-  MOZ_ASSERT(mMediaSource);
-  MOZ_ASSERT(!mPendingAppend.Exists());
-
-  mPendingAppend.Begin(mTrackBuffersManager->BufferAppend()
+  mPendingAppend.Begin(mTrackBuffersManager->AppendData(data, mCurrentAttributes)
                        ->Then(AbstractThread::MainThread(), __func__, this,
                               &SourceBuffer::AppendDataCompletedWithSuccess,
                               &SourceBuffer::AppendDataErrored));
 }
 
 void
-SourceBuffer::AppendDataCompletedWithSuccess(bool aHasActiveTracks)
+SourceBuffer::AppendDataCompletedWithSuccess(SourceBufferTask::AppendBufferResult aResult)
 {
   MOZ_ASSERT(mUpdating);
   mPendingAppend.Complete();
 
-  if (aHasActiveTracks) {
+  if (aResult.first()) {
     if (!mActive) {
       mActive = true;
       mMediaSource->SourceBufferIsActive(this);
     }
   }
   if (mActive) {
     // Tell our parent decoder that we have received new data.
     mMediaSource->GetDecoder()->NotifyDataArrived();
     // Send progress event.
     mMediaSource->GetDecoder()->NotifyBytesDownloaded();
   }
 
+  mCurrentAttributes = aResult.second();
+
   CheckEndTime();
 
   StopUpdating();
 }
 
 void
 SourceBuffer::AppendDataErrored(nsresult aError)
 {
@@ -480,17 +464,17 @@ SourceBuffer::AppendDataErrored(nsresult
   }
 }
 
 void
 SourceBuffer::AppendError(bool aDecoderError)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  mTrackBuffersManager->ResetParserState();
+  ResetParserState();
 
   mUpdating = false;
 
   QueueAsyncSimpleEvent("error");
   QueueAsyncSimpleEvent("updateend");
 
   if (aDecoderError) {
     Optional<MediaSourceEndOfStreamError> decodeError(
--- a/dom/media/mediasource/SourceBuffer.h
+++ b/dom/media/mediasource/SourceBuffer.h
@@ -9,168 +9,79 @@
 
 #include "mozilla/MozPromise.h"
 #include "MediaSource.h"
 #include "js/RootingAPI.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/UniquePtr.h"
 #include "mozilla/dom/SourceBufferBinding.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/mozalloc.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionNoteChild.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsISupports.h"
 #include "nsString.h"
 #include "nscore.h"
 #include "TrackBuffersManager.h"
-#include "mozilla/Monitor.h"
+#include "SourceBufferTask.h"
 
 class JSObject;
 struct JSContext;
 
 namespace mozilla {
 
 class ErrorResult;
 class MediaByteBuffer;
 template <typename T> class AsyncEventRunner;
 
 namespace dom {
 
 class TimeRanges;
 
-class SourceBufferAttributes {
-public:
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SourceBufferAttributes);
-  explicit SourceBufferAttributes(bool aGenerateTimestamp)
-    : mGenerateTimestamps(aGenerateTimestamp)
-    , mMonitor("SourceBufferAttributes")
-    , mAppendWindowStart(0)
-    , mAppendWindowEnd(PositiveInfinity<double>())
-    , mAppendMode(SourceBufferAppendMode::Segments)
-    , mApparentTimestampOffset(0)
-  {}
-
-  double GetAppendWindowStart()
-  {
-    MonitorAutoLock mon(mMonitor);
-    return mAppendWindowStart;
-  }
-
-  double GetAppendWindowEnd()
-  {
-    MonitorAutoLock mon(mMonitor);
-    return mAppendWindowEnd;
-  }
-
-  void SetAppendWindowStart(double aWindowStart)
-  {
-    MonitorAutoLock mon(mMonitor);
-    mAppendWindowStart = aWindowStart;
-  }
-
-  void SetAppendWindowEnd(double aWindowEnd)
-  {
-    MonitorAutoLock mon(mMonitor);
-    mAppendWindowEnd = aWindowEnd;
-  }
-
-  double GetApparentTimestampOffset()
-  {
-    MonitorAutoLock mon(mMonitor);
-    return mApparentTimestampOffset;
-  }
-
-  void SetApparentTimestampOffset(double aTimestampOffset)
-  {
-    MonitorAutoLock mon(mMonitor);
-    mApparentTimestampOffset = aTimestampOffset;
-    mTimestampOffset = media::TimeUnit::FromSeconds(aTimestampOffset);
-  }
-
-  media::TimeUnit GetTimestampOffset()
-  {
-    MonitorAutoLock mon(mMonitor);
-    return mTimestampOffset;
-  }
-
-  void SetTimestampOffset(media::TimeUnit& aTimestampOffset)
-  {
-    MonitorAutoLock mon(mMonitor);
-    mTimestampOffset = aTimestampOffset;
-    mApparentTimestampOffset = aTimestampOffset.ToSeconds();
-  }
-
-  SourceBufferAppendMode GetAppendMode()
-  {
-    MonitorAutoLock mon(mMonitor);
-    return mAppendMode;
-  }
-
-  void SetAppendMode(SourceBufferAppendMode aAppendMode)
-  {
-    MonitorAutoLock mon(mMonitor);
-    mAppendMode = aAppendMode;
-  }
-
-  // mGenerateTimestamp isn't mutable once the source buffer has been constructed
-  // We don't need a monitor to protect it across threads.
-  const bool mGenerateTimestamps;
-
-private:
-  ~SourceBufferAttributes() {};
-
-  // Monitor protecting all members below.
-  Monitor mMonitor;
-  double mAppendWindowStart;
-  double mAppendWindowEnd;
-  SourceBufferAppendMode mAppendMode;
-  double mApparentTimestampOffset;
-  media::TimeUnit mTimestampOffset;
-};
-
 class SourceBuffer final : public DOMEventTargetHelper
 {
 public:
   /** WebIDL Methods. */
   SourceBufferAppendMode Mode() const
   {
-    return mAttributes->GetAppendMode();
+    return mCurrentAttributes.GetAppendMode();
   }
 
   void SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv);
 
   bool Updating() const
   {
     return mUpdating;
   }
 
   TimeRanges* GetBuffered(ErrorResult& aRv);
   media::TimeIntervals GetTimeIntervals();
 
   double TimestampOffset() const
   {
-    return mAttributes->GetApparentTimestampOffset();
+    return mCurrentAttributes.GetApparentTimestampOffset();
   }
 
   void SetTimestampOffset(double aTimestampOffset, ErrorResult& aRv);
 
   double AppendWindowStart() const
   {
-    return mAttributes->GetAppendWindowStart();
+    return mCurrentAttributes.GetAppendWindowStart();
   }
 
   void SetAppendWindowStart(double aAppendWindowStart, ErrorResult& aRv);
 
   double AppendWindowEnd() const
   {
-    return mAttributes->GetAppendWindowEnd();
+    return mCurrentAttributes.GetAppendWindowEnd();
   }
 
   void SetAppendWindowEnd(double aAppendWindowEnd, ErrorResult& aRv);
 
   void AppendBuffer(const ArrayBuffer& aData, ErrorResult& aRv);
   void AppendBuffer(const ArrayBufferView& aData, ErrorResult& aRv);
 
   void Abort(ErrorResult& aRv);
@@ -217,51 +128,51 @@ private:
   friend class mozilla::TrackBuffersManager;
   void DispatchSimpleEvent(const char* aName);
   void QueueAsyncSimpleEvent(const char* aName);
 
   // Update mUpdating and fire the appropriate events.
   void StartUpdating();
   void StopUpdating();
   void AbortUpdating();
+  void ResetParserState();
 
   // If the media segment contains data beyond the current duration,
   // then run the duration change algorithm with new duration set to the
   // maximum of the current duration and the group end timestamp.
   void CheckEndTime();
 
   // Shared implementation of AppendBuffer overloads.
   void AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv);
-  void BufferAppend();
 
   // Implement the "Append Error Algorithm".
   // Will call endOfStream() with "decode" error if aDecodeError is true.
   // 3.5.3 Append Error Algorithm
   // http://w3c.github.io/media-source/#sourcebuffer-append-error
   void AppendError(bool aDecoderError);
 
   // Implements the "Prepare Append Algorithm". Returns MediaByteBuffer object
   // on success or nullptr (with aRv set) on error.
   already_AddRefed<MediaByteBuffer> PrepareAppend(const uint8_t* aData,
                                                   uint32_t aLength,
                                                   ErrorResult& aRv);
 
-  void AppendDataCompletedWithSuccess(bool aHasActiveTracks);
+  void AppendDataCompletedWithSuccess(SourceBufferTask::AppendBufferResult aResult);
   void AppendDataErrored(nsresult aError);
 
   RefPtr<MediaSource> mMediaSource;
 
   RefPtr<TrackBuffersManager> mTrackBuffersManager;
-  RefPtr<SourceBufferAttributes> mAttributes;
+  SourceBufferAttributes mCurrentAttributes;
 
   bool mUpdating;
 
   mozilla::Atomic<bool> mActive;
 
-  MozPromiseRequestHolder<TrackBuffersManager::AppendPromise> mPendingAppend;
+  MozPromiseRequestHolder<SourceBufferTask::AppendPromise> mPendingAppend;
   const nsCString mType;
 
   RefPtr<TimeRanges> mBuffered;
 };
 
 } // namespace dom
 
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/mediasource/SourceBufferAttributes.h
@@ -0,0 +1,157 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_SourceBufferAttributes_h_
+#define mozilla_SourceBufferAttributes_h_
+
+#include "TimeUnits.h"
+#include "mozilla/dom/SourceBufferBinding.h"
+#include "mozilla/Maybe.h"
+
+namespace mozilla {
+
+class SourceBufferAttributes {
+public:
+
+  // Current state as per Segment Parser Loop Algorithm
+  // http://w3c.github.io/media-source/index.html#sourcebuffer-segment-parser-loop
+  enum class AppendState
+  {
+    WAITING_FOR_SEGMENT,
+    PARSING_INIT_SEGMENT,
+    PARSING_MEDIA_SEGMENT,
+  };
+
+  explicit SourceBufferAttributes(bool aGenerateTimestamp)
+  : mGenerateTimestamps(aGenerateTimestamp)
+  , mAppendWindowStart(0)
+  , mAppendWindowEnd(PositiveInfinity<double>())
+  , mAppendMode(dom::SourceBufferAppendMode::Segments)
+  , mApparentTimestampOffset(0)
+  , mAppendState(AppendState::WAITING_FOR_SEGMENT)
+  {}
+
+  SourceBufferAttributes(const SourceBufferAttributes& aOther) = default;
+
+  double GetAppendWindowStart() const
+  {
+    return mAppendWindowStart;
+  }
+
+  double GetAppendWindowEnd() const
+  {
+    return mAppendWindowEnd;
+  }
+
+  void SetAppendWindowStart(double aWindowStart)
+  {
+    mAppendWindowStart = aWindowStart;
+  }
+
+  void SetAppendWindowEnd(double aWindowEnd)
+  {
+    mAppendWindowEnd = aWindowEnd;
+  }
+
+  double GetApparentTimestampOffset() const
+  {
+    return mApparentTimestampOffset;
+  }
+
+  void SetApparentTimestampOffset(double aTimestampOffset)
+  {
+    mApparentTimestampOffset = aTimestampOffset;
+    mTimestampOffset = media::TimeUnit::FromSeconds(aTimestampOffset);
+  }
+
+  media::TimeUnit GetTimestampOffset() const
+  {
+    return mTimestampOffset;
+  }
+
+  void SetTimestampOffset(const media::TimeUnit& aTimestampOffset)
+  {
+    mTimestampOffset = aTimestampOffset;
+    mApparentTimestampOffset = aTimestampOffset.ToSeconds();
+  }
+
+  dom::SourceBufferAppendMode GetAppendMode() const
+  {
+    return mAppendMode;
+  }
+
+  void SetAppendMode(dom::SourceBufferAppendMode aAppendMode)
+  {
+    mAppendMode = aAppendMode;
+  }
+
+  void SetGroupStartTimestamp(const media::TimeUnit& aGroupStartTimestamp)
+  {
+    mGroupStartTimestamp = Some(aGroupStartTimestamp);
+  }
+
+  media::TimeUnit GetGroupStartTimestamp() const
+  {
+    return mGroupStartTimestamp.ref();
+  }
+
+  bool HaveGroupStartTimestamp() const
+  {
+    return mGroupStartTimestamp.isSome();
+  }
+
+  void ResetGroupStartTimestamp()
+  {
+    mGroupStartTimestamp.reset();
+  }
+
+  void RestartGroupStartTimestamp()
+  {
+    mGroupStartTimestamp = Some(mGroupEndTimestamp);
+  }
+
+  media::TimeUnit GetGroupEndTimestamp() const
+  {
+    return mGroupEndTimestamp;
+  }
+
+  void SetGroupEndTimestamp(const media::TimeUnit& aGroupEndTimestamp)
+  {
+    mGroupEndTimestamp = aGroupEndTimestamp;
+  }
+
+  AppendState GetAppendState() const
+  {
+    return mAppendState;
+  }
+
+  void SetAppendState(AppendState aState)
+  {
+    mAppendState = aState;
+  }
+
+  // mGenerateTimestamp isn't mutable once the source buffer has been constructed
+  bool mGenerateTimestamps;
+
+  SourceBufferAttributes& operator=(const SourceBufferAttributes& aOther) = default;
+
+private:
+  SourceBufferAttributes() = delete;
+
+  double mAppendWindowStart;
+  double mAppendWindowEnd;
+  dom::SourceBufferAppendMode mAppendMode;
+  double mApparentTimestampOffset;
+  media::TimeUnit mTimestampOffset;
+  Maybe<media::TimeUnit> mGroupStartTimestamp;
+  media::TimeUnit mGroupEndTimestamp;
+  // The current append state as per https://w3c.github.io/media-source/#sourcebuffer-append-state
+  AppendState mAppendState;
+};
+
+} // end namespace mozilla
+
+#endif /* mozilla_SourceBufferAttributes_h_ */
new file mode 100644
--- /dev/null
+++ b/dom/media/mediasource/SourceBufferTask.h
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_SOURCEBUFFERTASK_H_
+#define MOZILLA_SOURCEBUFFERTASK_H_
+
+#include "mozilla/MozPromise.h"
+#include "mozilla/Pair.h"
+#include "mozilla/RefPtr.h"
+#include "SourceBufferAttributes.h"
+#include "TimeUnits.h"
+
+namespace mozilla {
+
+class SourceBufferTask {
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SourceBufferTask);
+  enum class Type  {
+    AppendBuffer,
+    Abort,
+    Reset,
+    RangeRemoval,
+    EvictData
+  };
+
+  typedef Pair<bool, SourceBufferAttributes> AppendBufferResult;
+  typedef MozPromise<AppendBufferResult, nsresult, /* IsExclusive = */ true> AppendPromise;
+  typedef MozPromise<bool, nsresult, /* IsExclusive = */ true> RangeRemovalPromise;
+
+  virtual Type GetType() const = 0;
+
+  template<typename ReturnType>
+  ReturnType* As()
+  {
+    MOZ_ASSERT(this->GetType() == ReturnType::sType);
+    return static_cast<ReturnType*>(this);
+  }
+
+protected:
+  virtual ~SourceBufferTask() {}
+};
+
+class AppendBufferTask : public SourceBufferTask {
+public:
+  AppendBufferTask(MediaByteBuffer* aData,
+                   SourceBufferAttributes aAttributes)
+  : mBuffer(aData)
+  , mAttributes(aAttributes)
+  {}
+
+  static const Type sType = Type::AppendBuffer;
+  Type GetType() const override { return Type::AppendBuffer; }
+
+  RefPtr<MediaByteBuffer> mBuffer;
+  SourceBufferAttributes mAttributes;
+  MozPromiseHolder<AppendPromise> mPromise;
+};
+
+class AbortTask : public SourceBufferTask {
+public:
+  static const Type sType = Type::Abort;
+  Type GetType() const override { return Type::Abort; }
+};
+
+class ResetTask : public SourceBufferTask {
+public:
+  static const Type sType = Type::Reset;
+  Type GetType() const override { return Type::Reset; }
+};
+
+class RangeRemovalTask : public SourceBufferTask {
+public:
+  explicit RangeRemovalTask(const media::TimeInterval& aRange)
+  : mRange(aRange)
+  {}
+
+  static const Type sType = Type::RangeRemoval;
+  Type GetType() const override { return Type::RangeRemoval; }
+
+  media::TimeInterval mRange;
+  MozPromiseHolder<RangeRemovalPromise> mPromise;
+};
+
+class EvictDataTask : public SourceBufferTask {
+public:
+  EvictDataTask(const media::TimeUnit& aPlaybackTime, int64_t aSizetoEvict)
+  : mPlaybackTime(aPlaybackTime)
+  , mSizeToEvict(aSizetoEvict)
+  {}
+
+  static const Type sType = Type::EvictData;
+  Type GetType() const override { return Type::EvictData; }
+
+  media::TimeUnit mPlaybackTime;
+  int64_t mSizeToEvict;
+};
+
+} // end mozilla namespace
+
+#endif
\ No newline at end of file
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -8,16 +8,17 @@
 #include "ContainerParser.h"
 #include "MediaSourceDemuxer.h"
 #include "MediaSourceUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/StateMirroring.h"
 #include "SourceBufferResource.h"
 #include "SourceBuffer.h"
 #include "WebMDemuxer.h"
+#include "SourceBufferTask.h"
 
 #ifdef MOZ_FMP4
 #include "MP4Demuxer.h"
 #endif
 
 #include <limits>
 
 extern mozilla::LogModule* GetMediaSourceLog();
@@ -33,26 +34,27 @@ mozilla::LogModule* GetMediaSourceSample
 #define SAMPLE_DEBUG(arg, ...) MOZ_LOG(GetMediaSourceSamplesLog(), mozilla::LogLevel::Debug, ("TrackBuffersManager(%p:%s)::%s: " arg, this, mType.get(), __func__, ##__VA_ARGS__))
 
 namespace mozilla {
 
 using dom::SourceBufferAppendMode;
 using media::TimeUnit;
 using media::TimeInterval;
 using media::TimeIntervals;
+typedef SourceBufferTask::AppendBufferResult AppendBufferResult;
 
 static const char*
-AppendStateToStr(TrackBuffersManager::AppendState aState)
+AppendStateToStr(SourceBufferAttributes::AppendState aState)
 {
   switch (aState) {
-    case TrackBuffersManager::AppendState::WAITING_FOR_SEGMENT:
+    case SourceBufferAttributes::AppendState::WAITING_FOR_SEGMENT:
       return "WAITING_FOR_SEGMENT";
-    case TrackBuffersManager::AppendState::PARSING_INIT_SEGMENT:
+    case SourceBufferAttributes::AppendState::PARSING_INIT_SEGMENT:
       return "PARSING_INIT_SEGMENT";
-    case TrackBuffersManager::AppendState::PARSING_MEDIA_SEGMENT:
+    case SourceBufferAttributes::AppendState::PARSING_MEDIA_SEGMENT:
       return "PARSING_MEDIA_SEGMENT";
     default:
       return "IMPOSSIBLE";
   }
 }
 
 static Atomic<uint32_t> sStreamSourceID(0u);
 
@@ -79,121 +81,219 @@ public:
   }
 private:
   RefPtr<AbstractMediaDecoder> mDecoder;
   nsTArray<uint8_t> mInitData;
   nsString mInitDataType;
 };
 #endif // MOZ_EME
 
-TrackBuffersManager::TrackBuffersManager(dom::SourceBufferAttributes* aAttributes,
-                                         MediaSourceDecoder* aParentDecoder,
+TrackBuffersManager::TrackBuffersManager(MediaSourceDecoder* aParentDecoder,
                                          const nsACString& aType)
   : mInputBuffer(new MediaByteBuffer)
-  , mAppendState(AppendState::WAITING_FOR_SEGMENT)
   , mBufferFull(false)
   , mFirstInitializationSegmentReceived(false)
   , mNewMediaSegmentStarted(false)
   , mActiveTrack(false)
   , mType(aType)
   , mParser(ContainerParser::CreateForMIMEType(aType))
   , mProcessedInput(0)
   , mTaskQueue(aParentDecoder->GetDemuxer()->GetTaskQueue())
-  , mSourceBufferAttributes(aAttributes)
   , mParentDecoder(new nsMainThreadPtrHolder<MediaSourceDecoder>(aParentDecoder, false /* strict */))
+  , mEnded(false)
+  , mDetached(false)
   , mVideoEvictionThreshold(Preferences::GetUint("media.mediasource.eviction_threshold.video",
                                                  100 * 1024 * 1024))
   , mAudioEvictionThreshold(Preferences::GetUint("media.mediasource.eviction_threshold.audio",
                                                  15 * 1024 * 1024))
   , mEvictionOccurred(false)
   , mMonitor("TrackBuffersManager")
-  , mAppendRunning(false)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be instanciated on the main thread");
 }
 
 TrackBuffersManager::~TrackBuffersManager()
 {
+  CancelAllTasks();
   ShutdownDemuxers();
 }
 
-bool
+RefPtr<TrackBuffersManager::AppendPromise>
 TrackBuffersManager::AppendData(MediaByteBuffer* aData,
-                                TimeUnit aTimestampOffset)
+                                const SourceBufferAttributes& aAttributes)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_DEBUG("Appending %lld bytes", aData->Length());
 
   mEnded = false;
-  nsCOMPtr<nsIRunnable> task =
-    NS_NewRunnableMethodWithArg<IncomingBuffer>(
-      this, &TrackBuffersManager::AppendIncomingBuffer,
-      IncomingBuffer(aData, aTimestampOffset));
-  GetTaskQueue()->Dispatch(task.forget());
-  return true;
+
+  RefPtr<MediaByteBuffer> buffer = aData;
+
+  return InvokeAsync(GetTaskQueue(), this,
+                     __func__, &TrackBuffersManager::DoAppendData,
+                     buffer, aAttributes);
+}
+
+RefPtr<TrackBuffersManager::AppendPromise>
+TrackBuffersManager::DoAppendData(RefPtr<MediaByteBuffer> aData,
+                                  SourceBufferAttributes aAttributes)
+{
+  RefPtr<AppendBufferTask> task = new AppendBufferTask(aData, aAttributes);
+  RefPtr<AppendPromise> p = task->mPromise.Ensure(__func__);
+  mQueue.Push(task);
+
+  ProcessTasks();
+
+  return p;
 }
 
 void
-TrackBuffersManager::AppendIncomingBuffer(IncomingBuffer aData)
+TrackBuffersManager::ProcessTasks()
 {
-  MOZ_ASSERT(OnTaskQueue());
-  mIncomingBuffers.AppendElement(aData);
+  typedef SourceBufferTask::Type Type;
+
+  if (mDetached) {
+    return;
+  }
+
+  if (OnTaskQueue()) {
+    if (mCurrentTask) {
+      // Already have a task pending. ProcessTask will be scheduled once the
+      // current task complete.
+      return;
+    }
+    RefPtr<SourceBufferTask> task = mQueue.Pop();
+    if (!task) {
+      // nothing to do.
+      return;
+    }
+    switch (task->GetType()) {
+      case Type::AppendBuffer:
+        mCurrentTask = task;
+        if (!mInputBuffer) {
+          mInputBuffer = task->As<AppendBufferTask>()->mBuffer;
+        } else if (!mInputBuffer->AppendElements(*task->As<AppendBufferTask>()->mBuffer, fallible)) {
+          RejectAppend(NS_ERROR_OUT_OF_MEMORY, __func__);
+          return;
+        }
+        mSourceBufferAttributes =
+          MakeUnique<SourceBufferAttributes>(task->As<AppendBufferTask>()->mAttributes);
+        mAppendWindow =
+          TimeInterval(TimeUnit::FromSeconds(mSourceBufferAttributes->GetAppendWindowStart()),
+                       TimeUnit::FromSeconds(mSourceBufferAttributes->GetAppendWindowEnd()));
+        ScheduleSegmentParserLoop();
+        break;
+      case Type::RangeRemoval:
+      {
+        bool rv = CodedFrameRemoval(task->As<RangeRemovalTask>()->mRange);
+        task->As<RangeRemovalTask>()->mPromise.Resolve(rv, __func__);
+        break;
+      }
+      case Type::EvictData:
+        DoEvictData(task->As<EvictDataTask>()->mPlaybackTime,
+                    task->As<EvictDataTask>()->mSizeToEvict);
+        break;
+      case Type::Abort:
+        // not handled yet, and probably never.
+        break;
+      case Type::Reset:
+        CompleteResetParserState();
+        break;
+      default:
+        NS_WARNING("Invalid Task");
+    }
+  }
+  nsCOMPtr<nsIRunnable> task =
+    NS_NewRunnableMethod(this, &TrackBuffersManager::ProcessTasks);
+  GetTaskQueue()->Dispatch(task.forget());
 }
 
-RefPtr<TrackBuffersManager::AppendPromise>
-TrackBuffersManager::BufferAppend()
+// A PromiseHolder will assert upon destruction if it has a pending promise
+// that hasn't been completed. It is possible that a task didn't get processed
+// due to the owning SourceBuffer having shutdown.
+// We resolve/reject all pending promises and remove all pending tasks from the
+// queue.
+void
+TrackBuffersManager::CancelAllTasks()
 {
-  MOZ_ASSERT(NS_IsMainThread());
-  MSE_DEBUG("");
+  typedef SourceBufferTask::Type Type;
+  MOZ_DIAGNOSTIC_ASSERT(mDetached);
+
+  if (mCurrentTask) {
+    mQueue.Push(mCurrentTask);
+    mCurrentTask = nullptr;
+  }
 
-  mAppendRunning = true;
-  return InvokeAsync(GetTaskQueue(), this,
-                     __func__, &TrackBuffersManager::InitSegmentParserLoop);
+  RefPtr<SourceBufferTask> task;
+  while ((task = mQueue.Pop())) {
+    switch (task->GetType()) {
+      case Type::AppendBuffer:
+        task->As<AppendBufferTask>()->mPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
+        break;
+      case Type::RangeRemoval:
+        task->As<RangeRemovalTask>()->mPromise.ResolveIfExists(false, __func__);
+        break;
+      case Type::EvictData:
+        break;
+      case Type::Abort:
+        // not handled yet, and probably never.
+        break;
+      case Type::Reset:
+        break;
+      default:
+        NS_WARNING("Invalid Task");
+    }
+  }
 }
 
 // The MSE spec requires that we abort the current SegmentParserLoop
 // which is then followed by a call to ResetParserState.
-// However due to our asynchronous design this causes inherent difficulities.
-// As the spec behaviour is non deterministic anyway, we instead wait until the
-// current AppendData has completed its run.
+// However due to our asynchronous design this causes inherent difficulties.
+// As the spec behaviour is non deterministic anyway, we instead process all
+// pending frames found in the input buffer.
 void
 TrackBuffersManager::AbortAppendData()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_DEBUG("");
 
-  MonitorAutoLock mon(mMonitor);
-  while (mAppendRunning) {
-    mon.Wait();
-  }
+  RefPtr<AbortTask> task = new AbortTask();
+  mQueue.Push(task);
+  ProcessTasks();
 }
 
 void
-TrackBuffersManager::ResetParserState()
+TrackBuffersManager::ResetParserState(SourceBufferAttributes& aAttributes)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_DIAGNOSTIC_ASSERT(!mAppendRunning, "Append is running, abort must have been called");
   MSE_DEBUG("");
 
+  // Spec states:
   // 1. If the append state equals PARSING_MEDIA_SEGMENT and the input buffer contains some complete coded frames, then run the coded frame processing algorithm until all of these complete coded frames have been processed.
-  // SourceBuffer.abort() has ensured that all complete coded frames have been
-  // processed. As such, we don't need to check for the value of mAppendState.
-  nsCOMPtr<nsIRunnable> task =
-    NS_NewRunnableMethod(this, &TrackBuffersManager::CompleteResetParserState);
-  GetTaskQueue()->Dispatch(task.forget());
+  // However, we will wait until all coded frames have been processed regardless
+  // of the value of append state.
+  RefPtr<ResetTask> task = new ResetTask();
+  mQueue.Push(task);
+  ProcessTasks();
 
-  // 7. Set append state to WAITING_FOR_SEGMENT.
-  SetAppendState(AppendState::WAITING_FOR_SEGMENT);
+  // ResetParserState has some synchronous steps that much be performed now.
+  // The remaining steps will be performed once the ResetTask gets executed.
+
+  // 6. If the mode attribute equals "sequence", then set the group start timestamp to the group end timestamp
+  if (aAttributes.GetAppendMode() == SourceBufferAppendMode::Sequence) {
+    aAttributes.SetGroupStartTimestamp(aAttributes.GetGroupEndTimestamp());
+  }
+  // 8. Set append state to WAITING_FOR_SEGMENT.
+  aAttributes.SetAppendState(AppendState::WAITING_FOR_SEGMENT);
 }
 
 RefPtr<TrackBuffersManager::RangeRemovalPromise>
 TrackBuffersManager::RangeRemoval(TimeUnit aStart, TimeUnit aEnd)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_DIAGNOSTIC_ASSERT(!mAppendRunning, "Append is running");
   MSE_DEBUG("From %.2f to %.2f", aStart.ToSeconds(), aEnd.ToSeconds());
 
   mEnded = false;
 
   return InvokeAsync(GetTaskQueue(), this, __func__,
                      &TrackBuffersManager::CodedFrameRemovalWithPromise,
                      TimeInterval(aStart, aEnd));
 }
@@ -220,21 +320,19 @@ TrackBuffersManager::EvictData(TimeUnit 
   }
 
   if (mBufferFull && mEvictionOccurred) {
     return EvictDataResult::BUFFER_FULL;
   }
 
   MSE_DEBUG("Reaching our size limit, schedule eviction of %lld bytes", toEvict);
 
-  nsCOMPtr<nsIRunnable> task =
-    NS_NewRunnableMethodWithArgs<TimeUnit, uint32_t>(
-      this, &TrackBuffersManager::DoEvictData,
-      aPlaybackTime, toEvict);
-  GetTaskQueue()->Dispatch(task.forget());
+  RefPtr<EvictDataTask> task = new EvictDataTask(aPlaybackTime, toEvict);
+  mQueue.Push(task);
+  ProcessTasks();
 
   return EvictDataResult::NO_DATA_EVICTED;
 }
 
 TimeIntervals
 TrackBuffersManager::Buffered()
 {
   MSE_DEBUG("");
@@ -282,50 +380,42 @@ TrackBuffersManager::Ended()
   mEnded = true;
 }
 
 void
 TrackBuffersManager::Detach()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_DEBUG("");
+  mDetached = true;
 }
 
 void
 TrackBuffersManager::CompleteResetParserState()
 {
   MOZ_ASSERT(OnTaskQueue());
   MSE_DEBUG("");
 
   // We shouldn't change mInputDemuxer while a demuxer init/reset request is
   // being processed. See bug 1239983.
-  NS_ASSERTION(!mDemuxerInitRequest.Exists(), "Previous AppendBuffer didn't complete");
-  if (mDemuxerInitRequest.Exists()) {
-    mDemuxerInitRequest.Disconnect();
-  }
+  MOZ_DIAGNOSTIC_ASSERT(!mDemuxerInitRequest.Exists(), "Previous AppendBuffer didn't complete");
 
   for (auto& track : GetTracksList()) {
     // 2. Unset the last decode timestamp on all track buffers.
     // 3. Unset the last frame duration on all track buffers.
     // 4. Unset the highest end timestamp on all track buffers.
     // 5. Set the need random access point flag on all track buffers to true.
     track->ResetAppendState();
 
     // if we have been aborted, we may have pending frames that we are going
     // to discard now.
     track->mQueuedSamples.Clear();
   }
 
-  // 6. If the mode attribute equals "sequence", then set the group start timestamp to the group end timestamp
-  if (mSourceBufferAttributes->GetAppendMode() == SourceBufferAppendMode::Sequence) {
-    mGroupStartTimestamp = Some(mGroupEndTimestamp);
-  }
-
   // 7. Remove all bytes from the input buffer.
-  mIncomingBuffers.Clear();
   mInputBuffer = nullptr;
   if (mCurrentInputBuffer) {
     mCurrentInputBuffer->EvictAll();
     // The demuxer will be recreated during the next run of SegmentParserLoop.
     // As such we don't need to notify it that data has been removed.
     mCurrentInputBuffer = new SourceBufferResource(mType);
   }
 
@@ -338,22 +428,16 @@ TrackBuffersManager::CompleteResetParser
     // The aim here is really to destroy our current demuxer.
     CreateDemuxerforMIMEType();
     // Recreate our input buffer. We can't directly assign the initData buffer
     // to mInputBuffer as it will get modified in the Segment Parser Loop.
     mInputBuffer = new MediaByteBuffer;
     mInputBuffer->AppendElements(*mInitData);
   }
   RecreateParser(true);
-
-  // 8. Set append state to WAITING_FOR_SEGMENT.
-  SetAppendState(AppendState::WAITING_FOR_SEGMENT);
-
-  // Reject our promise immediately.
-  mAppendPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
 }
 
 int64_t
 TrackBuffersManager::EvictionThreshold() const
 {
   if (HasVideo()) {
     return mVideoEvictionThreshold;
   }
@@ -430,18 +514,22 @@ TrackBuffersManager::DoEvictData(const T
                    TimeUnit::FromInfinity()));
   }
 }
 
 RefPtr<TrackBuffersManager::RangeRemovalPromise>
 TrackBuffersManager::CodedFrameRemovalWithPromise(TimeInterval aInterval)
 {
   MOZ_ASSERT(OnTaskQueue());
-  bool rv = CodedFrameRemoval(aInterval);
-  return RangeRemovalPromise::CreateAndResolve(rv, __func__);
+
+  RefPtr<RangeRemovalTask> task = new RangeRemovalTask(aInterval);
+  RefPtr<RangeRemovalPromise> p = task->mPromise.Ensure(__func__);
+  mQueue.Push(task);
+  ProcessTasks();
+  return p;
 }
 
 bool
 TrackBuffersManager::CodedFrameRemoval(TimeInterval aInterval)
 {
   MOZ_ASSERT(OnTaskQueue());
   MSE_DEBUG("From %.2fs to %.2f",
             aInterval.mStart.ToSeconds(), aInterval.mEnd.ToSeconds());
@@ -531,54 +619,16 @@ TrackBuffersManager::UpdateBufferedRange
     MSE_DEBUG("after video ranges=%s",
               DumpTimeRanges(mVideoTracks.mBufferedRanges).get());
   }
   if (HasAudio()) {
     MSE_DEBUG("after audio ranges=%s",
               DumpTimeRanges(mAudioTracks.mBufferedRanges).get());
   }
 #endif
-
-  mOfficialGroupEndTimestamp = mGroupEndTimestamp;
-}
-
-RefPtr<TrackBuffersManager::AppendPromise>
-TrackBuffersManager::InitSegmentParserLoop()
-{
-  MOZ_ASSERT(OnTaskQueue());
-  MOZ_DIAGNOSTIC_ASSERT(mAppendPromise.IsEmpty());
-  MSE_DEBUG("");
-
-  RefPtr<AppendPromise> p = mAppendPromise.Ensure(__func__);
-
-  AppendIncomingBuffers();
-  SegmentParserLoop();
-
-  return p;
-}
-
-void
-TrackBuffersManager::AppendIncomingBuffers()
-{
-  MOZ_ASSERT(OnTaskQueue());
-  MonitorAutoLock mon(mMonitor);
-  for (auto& incomingBuffer : mIncomingBuffers) {
-    if (!mInputBuffer) {
-      mInputBuffer = incomingBuffer.first();
-    } else if (!mInputBuffer->AppendElements(*incomingBuffer.first(), fallible)) {
-      RejectAppend(NS_ERROR_OUT_OF_MEMORY, __func__);
-    }
-    mTimestampOffset = incomingBuffer.second();
-    mLastTimestampOffset = mTimestampOffset;
-  }
-  mIncomingBuffers.Clear();
-
-  mAppendWindow =
-    TimeInterval(TimeUnit::FromSeconds(mSourceBufferAttributes->GetAppendWindowStart()),
-                 TimeUnit::FromSeconds(mSourceBufferAttributes->GetAppendWindowEnd()));
 }
 
 void
 TrackBuffersManager::SegmentParserLoop()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   while (true) {
@@ -595,17 +645,17 @@ TrackBuffersManager::SegmentParserLoop()
     // 3. Remove any bytes that the byte stream format specifications say must be
     // ignored from the start of the input buffer.
     // We do not remove bytes from our input buffer. Instead we enforce that
     // our ContainerParser is able to skip over all data that is supposed to be
     // ignored.
 
     // 4. If the append state equals WAITING_FOR_SEGMENT, then run the following
     // steps:
-    if (mAppendState == AppendState::WAITING_FOR_SEGMENT) {
+    if (mSourceBufferAttributes->GetAppendState() == AppendState::WAITING_FOR_SEGMENT) {
       if (mParser->IsInitSegmentPresent(mInputBuffer)) {
         SetAppendState(AppendState::PARSING_INIT_SEGMENT);
         if (mFirstInitializationSegmentReceived) {
           // This is a new initialization segment. Obsolete the old one.
           RecreateParser(false);
         }
         continue;
       }
@@ -622,26 +672,26 @@ TrackBuffersManager::SegmentParserLoop()
     }
 
     int64_t start, end;
     bool newData = mParser->ParseStartAndEndTimestamps(mInputBuffer, start, end);
     mProcessedInput += mInputBuffer->Length();
 
     // 5. If the append state equals PARSING_INIT_SEGMENT, then run the
     // following steps:
-    if (mAppendState == AppendState::PARSING_INIT_SEGMENT) {
+    if (mSourceBufferAttributes->GetAppendState() == AppendState::PARSING_INIT_SEGMENT) {
       if (mParser->InitSegmentRange().IsEmpty()) {
         mInputBuffer = nullptr;
         NeedMoreData();
         return;
       }
       InitializationSegmentReceived();
       return;
     }
-    if (mAppendState == AppendState::PARSING_MEDIA_SEGMENT) {
+    if (mSourceBufferAttributes->GetAppendState() == AppendState::PARSING_MEDIA_SEGMENT) {
       // 1. If the first initialization segment received flag is false, then run the append error algorithm with the decode error parameter set to true and abort this algorithm.
       if (!mFirstInitializationSegmentReceived) {
         RejectAppend(NS_ERROR_FAILURE, __func__);
         return;
       }
 
       // We can't feed some demuxers (WebMDemuxer) with data that do not have
       // monotonizally increasing timestamps. So we check if we have a
@@ -698,42 +748,55 @@ TrackBuffersManager::SegmentParserLoop()
     }
   }
 }
 
 void
 TrackBuffersManager::NeedMoreData()
 {
   MSE_DEBUG("");
-  RestoreCachedVariables();
-  mAppendRunning = false;
-  {
-    // Wake-up any pending Abort()
-    MonitorAutoLock mon(mMonitor);
-    mon.NotifyAll();
+  if (mDetached) {
+    // We've been detached.
+    return;
   }
-  mAppendPromise.ResolveIfExists(mActiveTrack, __func__);
+  MOZ_DIAGNOSTIC_ASSERT(mCurrentTask && mCurrentTask->GetType() == SourceBufferTask::Type::AppendBuffer);
+  MOZ_DIAGNOSTIC_ASSERT(mSourceBufferAttributes);
+
+  mCurrentTask->As<AppendBufferTask>()->mPromise.Resolve(
+    SourceBufferTask::AppendBufferResult(mActiveTrack,
+                                         *mSourceBufferAttributes),
+                                         __func__);
+  mSourceBufferAttributes = nullptr;
+  mCurrentTask = nullptr;
+  ProcessTasks();
 }
 
 void
 TrackBuffersManager::RejectAppend(nsresult aRejectValue, const char* aName)
 {
   MSE_DEBUG("rv=%d", aRejectValue);
-  mAppendRunning = false;
-  {
-    // Wake-up any pending Abort()
-    MonitorAutoLock mon(mMonitor);
-    mon.NotifyAll();
+  if (mDetached) {
+    // We've been detached.
+    return;
   }
-  mAppendPromise.RejectIfExists(aRejectValue, aName);
+  MOZ_DIAGNOSTIC_ASSERT(mCurrentTask && mCurrentTask->GetType() == SourceBufferTask::Type::AppendBuffer);
+  MOZ_DIAGNOSTIC_ASSERT(mSourceBufferAttributes);
+
+  mCurrentTask->As<AppendBufferTask>()->mPromise.Reject(aRejectValue, __func__);
+  mSourceBufferAttributes = nullptr;
+  mCurrentTask = nullptr;
+  ProcessTasks();
 }
 
 void
 TrackBuffersManager::ScheduleSegmentParserLoop()
 {
+  if (mDetached) {
+    return;
+  }
   nsCOMPtr<nsIRunnable> task =
     NS_NewRunnableMethod(this, &TrackBuffersManager::SegmentParserLoop);
   GetTaskQueue()->Dispatch(task.forget());
 }
 
 void
 TrackBuffersManager::ShutdownDemuxers()
 {
@@ -752,23 +815,25 @@ TrackBuffersManager::ShutdownDemuxers()
   mLastParsedEndTime.reset();
 }
 
 void
 TrackBuffersManager::CreateDemuxerforMIMEType()
 {
   ShutdownDemuxers();
 
-  if (mType.LowerCaseEqualsLiteral("video/webm") || mType.LowerCaseEqualsLiteral("audio/webm")) {
+  if (mType.LowerCaseEqualsLiteral("video/webm") ||
+      mType.LowerCaseEqualsLiteral("audio/webm")) {
     mInputDemuxer = new WebMDemuxer(mCurrentInputBuffer, true /* IsMediaSource*/ );
     return;
   }
 
 #ifdef MOZ_FMP4
-  if (mType.LowerCaseEqualsLiteral("video/mp4") || mType.LowerCaseEqualsLiteral("audio/mp4")) {
+  if (mType.LowerCaseEqualsLiteral("video/mp4") ||
+      mType.LowerCaseEqualsLiteral("audio/mp4")) {
     mInputDemuxer = new MP4Demuxer(mCurrentInputBuffer);
     return;
   }
 #endif
   NS_WARNING("Not supported (yet)");
   return;
 }
 
@@ -803,24 +868,26 @@ TrackBuffersManager::OnDemuxerResetDone(
   // mInputDemuxer shouldn't have been destroyed while a demuxer init/reset
   // request was being processed. See bug 1239983.
   MOZ_DIAGNOSTIC_ASSERT(mInputDemuxer);
 
   // Recreate track demuxers.
   uint32_t numVideos = mInputDemuxer->GetNumberTracks(TrackInfo::kVideoTrack);
   if (numVideos) {
     // We currently only handle the first video track.
-    mVideoTracks.mDemuxer = mInputDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
+    mVideoTracks.mDemuxer =
+      mInputDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
     MOZ_ASSERT(mVideoTracks.mDemuxer);
   }
 
   uint32_t numAudios = mInputDemuxer->GetNumberTracks(TrackInfo::kAudioTrack);
   if (numAudios) {
     // We currently only handle the first audio track.
-    mAudioTracks.mDemuxer = mInputDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0);
+    mAudioTracks.mDemuxer =
+      mInputDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0);
     MOZ_ASSERT(mAudioTracks.mDemuxer);
   }
 
   if (mPendingInputBuffer) {
     // We had a partial media segment header stashed aside.
     // Reparse its content so we can continue parsing the current input buffer.
     int64_t start, end;
     mParser->ParseStartAndEndTimestamps(mPendingInputBuffer, start, end);
@@ -866,41 +933,37 @@ TrackBuffersManager::InitializationSegme
                              &TrackBuffersManager::OnDemuxerInitDone,
                              &TrackBuffersManager::OnDemuxerInitFailed));
 }
 
 void
 TrackBuffersManager::OnDemuxerInitDone(nsresult)
 {
   MOZ_ASSERT(OnTaskQueue());
-  mDemuxerInitRequest.Complete();
+  MOZ_DIAGNOSTIC_ASSERT(mInputDemuxer, "mInputDemuxer has been destroyed");
 
-  if (!mInputDemuxer) {
-    // mInputDemuxer shouldn't have been destroyed while a demuxer init/reset
-    // request was being processed. See bug 1239983.
-    NS_ASSERTION(false, "mInputDemuxer has been destroyed");
-    RejectAppend(NS_ERROR_ABORT, __func__);
-    return;
-  }
+  mDemuxerInitRequest.Complete();
 
   MediaInfo info;
 
   uint32_t numVideos = mInputDemuxer->GetNumberTracks(TrackInfo::kVideoTrack);
   if (numVideos) {
     // We currently only handle the first video track.
-    mVideoTracks.mDemuxer = mInputDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
+    mVideoTracks.mDemuxer =
+      mInputDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
     MOZ_ASSERT(mVideoTracks.mDemuxer);
     info.mVideo = *mVideoTracks.mDemuxer->GetInfo()->GetAsVideoInfo();
     info.mVideo.mTrackId = 2;
   }
 
   uint32_t numAudios = mInputDemuxer->GetNumberTracks(TrackInfo::kAudioTrack);
   if (numAudios) {
     // We currently only handle the first audio track.
-    mAudioTracks.mDemuxer = mInputDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0);
+    mAudioTracks.mDemuxer =
+      mInputDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0);
     MOZ_ASSERT(mAudioTracks.mDemuxer);
     info.mAudio = *mAudioTracks.mDemuxer->GetInfo()->GetAsAudioInfo();
     info.mAudio.mTrackId = 1;
   }
 
   int64_t videoDuration = numVideos ? info.mVideo.mDuration : 0;
   int64_t audioDuration = numAudios ? info.mAudio.mDuration : 0;
 
@@ -1031,16 +1094,17 @@ TrackBuffersManager::OnDemuxerInitDone(n
     mFirstInitializationSegmentReceived = true;
   } else {
     // Check that audio configuration hasn't changed as this is something
     // we do not support yet (bug 1185827).
     if (mAudioTracks.mNumTracks &&
         (info.mAudio.mChannels != mAudioTracks.mInfo->GetAsAudioInfo()->mChannels ||
          info.mAudio.mRate != mAudioTracks.mInfo->GetAsAudioInfo()->mRate)) {
       RejectAppend(NS_ERROR_FAILURE, __func__);
+      return;
     }
     mAudioTracks.mLastInfo = new SharedTrackInfo(info.mAudio, streamID);
     mVideoTracks.mLastInfo = new SharedTrackInfo(info.mVideo, streamID);
   }
 
   UniquePtr<EncryptionInfo> crypto = mInputDemuxer->GetCrypto();
   if (crypto && crypto->IsEncrypted()) {
 #ifdef MOZ_EME
@@ -1290,22 +1354,24 @@ TrackBuffersManager::ResolveProcessing(b
 {
   mProcessingPromise.ResolveIfExists(aResolveValue, __func__);
 }
 
 void
 TrackBuffersManager::CheckSequenceDiscontinuity(const TimeUnit& aPresentationTime)
 {
   if (mSourceBufferAttributes->GetAppendMode() == SourceBufferAppendMode::Sequence &&
-      mGroupStartTimestamp.isSome()) {
-    mTimestampOffset = mGroupStartTimestamp.ref() - aPresentationTime;
-    mGroupEndTimestamp = mGroupStartTimestamp.ref();
+      mSourceBufferAttributes->HaveGroupStartTimestamp()) {
+    mSourceBufferAttributes->SetTimestampOffset(
+      mSourceBufferAttributes->GetGroupStartTimestamp() - aPresentationTime);
+    mSourceBufferAttributes->SetGroupEndTimestamp(
+      mSourceBufferAttributes->GetGroupStartTimestamp());
     mVideoTracks.mNeedRandomAccessPoint = true;
     mAudioTracks.mNeedRandomAccessPoint = true;
-    mGroupStartTimestamp.reset();
+    mSourceBufferAttributes->ResetGroupStartTimestamp();
   }
 }
 
 void
 TrackBuffersManager::ProcessFrames(TrackBuffer& aSamples, TrackData& aTrackData)
 {
   if (!aSamples.Length()) {
     return;
@@ -1386,44 +1452,45 @@ TrackBuffersManager::ProcessFrames(Track
     //   Let decode timestamp be a double precision floating point representation of the coded frame's decode timestamp in seconds.
 
     // 2. Let frame duration be a double precision floating point representation of the coded frame's duration in seconds.
     // Step 3 is performed earlier or when a discontinuity has been detected.
     // 4. If timestampOffset is not 0, then run the following steps:
 
     TimeInterval sampleInterval =
       mSourceBufferAttributes->mGenerateTimestamps
-        ? TimeInterval(mTimestampOffset,
-                       mTimestampOffset + TimeUnit::FromMicroseconds(sample->mDuration))
-        : TimeInterval(TimeUnit::FromMicroseconds(sample->mTime) + mTimestampOffset,
-                       TimeUnit::FromMicroseconds(sample->GetEndTime()) + mTimestampOffset);
+        ? TimeInterval(mSourceBufferAttributes->GetTimestampOffset(),
+                       mSourceBufferAttributes->GetTimestampOffset() + TimeUnit::FromMicroseconds(sample->mDuration))
+        : TimeInterval(TimeUnit::FromMicroseconds(sample->mTime) + mSourceBufferAttributes->GetTimestampOffset(),
+                       TimeUnit::FromMicroseconds(sample->GetEndTime()) + mSourceBufferAttributes->GetTimestampOffset());
     TimeUnit decodeTimestamp =
       mSourceBufferAttributes->mGenerateTimestamps
-        ? mTimestampOffset
-        : TimeUnit::FromMicroseconds(sample->mTimecode) + mTimestampOffset;
+        ? mSourceBufferAttributes->GetTimestampOffset()
+        : TimeUnit::FromMicroseconds(sample->mTimecode) + mSourceBufferAttributes->GetTimestampOffset();
 
     // 6. If last decode timestamp for track buffer is set and decode timestamp is less than last decode timestamp:
     // OR
     // If last decode timestamp for track buffer is set and the difference between decode timestamp and last decode timestamp is greater than 2 times last frame duration:
 
     if (needDiscontinuityCheck && trackBuffer.mLastDecodeTimestamp.isSome() &&
         (decodeTimestamp < trackBuffer.mLastDecodeTimestamp.ref() ||
          decodeTimestamp - trackBuffer.mLastDecodeTimestamp.ref() > 2*trackBuffer.mLongestFrameDuration.ref())) {
       MSE_DEBUG("Discontinuity detected.");
       SourceBufferAppendMode appendMode = mSourceBufferAttributes->GetAppendMode();
 
       // 1a. If mode equals "segments":
       if (appendMode == SourceBufferAppendMode::Segments) {
         // Set group end timestamp to presentation timestamp.
-        mGroupEndTimestamp = sampleInterval.mStart;
+        mSourceBufferAttributes->SetGroupEndTimestamp(sampleInterval.mStart);
       }
       // 1b. If mode equals "sequence":
       if (appendMode == SourceBufferAppendMode::Sequence) {
         // Set group start timestamp equal to the group end timestamp.
-        mGroupStartTimestamp = Some(mGroupEndTimestamp);
+        mSourceBufferAttributes->SetGroupStartTimestamp(
+          mSourceBufferAttributes->GetGroupEndTimestamp());
       }
       for (auto& track : GetTracksList()) {
         // 2. Unset the last decode timestamp on all track buffers.
         // 3. Unset the last frame duration on all track buffers.
         // 4. Unset the highest end timestamp on all track buffers.
         // 5. Set the need random access point flag on all track buffers to true.
         track->ResetAppendState();
       }
@@ -1434,28 +1501,28 @@ TrackBuffersManager::ProcessFrames(Track
       TimeUnit presentationTimestamp = mSourceBufferAttributes->mGenerateTimestamps
         ? TimeUnit() : TimeUnit::FromMicroseconds(sample->mTime);
       CheckSequenceDiscontinuity(presentationTimestamp);
 
       if (!sample->mKeyframe) {
         continue;
       }
       if (appendMode == SourceBufferAppendMode::Sequence) {
-        // mTimestampOffset was modified during CheckSequenceDiscontinuity.
+        // mSourceBufferAttributes->GetTimestampOffset() was modified during CheckSequenceDiscontinuity.
         // We need to update our variables.
         sampleInterval =
           mSourceBufferAttributes->mGenerateTimestamps
-            ? TimeInterval(mTimestampOffset,
-                           mTimestampOffset + TimeUnit::FromMicroseconds(sample->mDuration))
-            : TimeInterval(TimeUnit::FromMicroseconds(sample->mTime) + mTimestampOffset,
-                           TimeUnit::FromMicroseconds(sample->GetEndTime()) + mTimestampOffset);
+            ? TimeInterval(mSourceBufferAttributes->GetTimestampOffset(),
+                           mSourceBufferAttributes->GetTimestampOffset() + TimeUnit::FromMicroseconds(sample->mDuration))
+            : TimeInterval(TimeUnit::FromMicroseconds(sample->mTime) + mSourceBufferAttributes->GetTimestampOffset(),
+                           TimeUnit::FromMicroseconds(sample->GetEndTime()) + mSourceBufferAttributes->GetTimestampOffset());
         decodeTimestamp =
           mSourceBufferAttributes->mGenerateTimestamps
-            ? mTimestampOffset
-            : TimeUnit::FromMicroseconds(sample->mTimecode) + mTimestampOffset;
+            ? mSourceBufferAttributes->GetTimestampOffset()
+            : TimeUnit::FromMicroseconds(sample->mTimecode) + mSourceBufferAttributes->GetTimestampOffset();
       }
       trackBuffer.mNeedRandomAccessPoint = false;
       needDiscontinuityCheck = false;
     }
 
     // 7. Let frame end timestamp equal the sum of presentation timestamp and frame duration.
     // This is sampleInterval.mEnd
 
@@ -1499,22 +1566,22 @@ TrackBuffersManager::ProcessFrames(Track
                       trackBuffer.mLongestFrameDuration.ref()));
 
     // 19. If highest end timestamp for track buffer is unset or frame end timestamp is greater than highest end timestamp, then set highest end timestamp for track buffer to frame end timestamp.
     if (trackBuffer.mHighestEndTimestamp.isNothing() ||
         sampleInterval.mEnd > trackBuffer.mHighestEndTimestamp.ref()) {
       trackBuffer.mHighestEndTimestamp = Some(sampleInterval.mEnd);
     }
     // 20. If frame end timestamp is greater than group end timestamp, then set group end timestamp equal to frame end timestamp.
-    if (sampleInterval.mEnd > mGroupEndTimestamp) {
-      mGroupEndTimestamp = sampleInterval.mEnd;
+    if (sampleInterval.mEnd > mSourceBufferAttributes->GetGroupEndTimestamp()) {
+      mSourceBufferAttributes->SetGroupEndTimestamp(sampleInterval.mEnd);
     }
     // 21. If generate timestamps flag equals true, then set timestampOffset equal to frame end timestamp.
     if (mSourceBufferAttributes->mGenerateTimestamps) {
-      mTimestampOffset = sampleInterval.mEnd;
+      mSourceBufferAttributes->SetTimestampOffset(sampleInterval.mEnd);
     }
   }
 
   if (samples.Length()) {
     InsertFrames(samples, samplesRange, trackBuffer);
     trackBuffer.mSizeBuffer += sizeNewSamples;
   }
 }
@@ -1761,66 +1828,21 @@ TrackBuffersManager::GetTracksList()
   }
   if (HasAudio()) {
     tracks.AppendElement(&mAudioTracks);
   }
   return tracks;
 }
 
 void
-TrackBuffersManager::RestoreCachedVariables()
-{
-  MOZ_ASSERT(OnTaskQueue());
-  if (mTimestampOffset != mLastTimestampOffset) {
-    mSourceBufferAttributes->SetTimestampOffset(mTimestampOffset);
-  }
-}
-
-void
-TrackBuffersManager::SetAppendState(TrackBuffersManager::AppendState aAppendState)
+TrackBuffersManager::SetAppendState(SourceBufferAttributes::AppendState aAppendState)
 {
   MSE_DEBUG("AppendState changed from %s to %s",
-            AppendStateToStr(mAppendState), AppendStateToStr(aAppendState));
-  mAppendState = aAppendState;
-}
-
-void
-TrackBuffersManager::SetGroupStartTimestamp(const TimeUnit& aGroupStartTimestamp)
-{
-  if (NS_IsMainThread()) {
-    nsCOMPtr<nsIRunnable> task =
-      NS_NewRunnableMethodWithArg<TimeUnit>(
-        this,
-        &TrackBuffersManager::SetGroupStartTimestamp,
-        aGroupStartTimestamp);
-    GetTaskQueue()->Dispatch(task.forget());
-    return;
-  }
-  MOZ_ASSERT(OnTaskQueue());
-  mGroupStartTimestamp = Some(aGroupStartTimestamp);
-}
-
-void
-TrackBuffersManager::RestartGroupStartTimestamp()
-{
-  if (NS_IsMainThread()) {
-    nsCOMPtr<nsIRunnable> task =
-      NS_NewRunnableMethod(this, &TrackBuffersManager::RestartGroupStartTimestamp);
-    GetTaskQueue()->Dispatch(task.forget());
-    return;
-  }
-  MOZ_ASSERT(OnTaskQueue());
-  mGroupStartTimestamp = Some(mGroupEndTimestamp);
-}
-
-TimeUnit
-TrackBuffersManager::GroupEndTimestamp()
-{
-  MonitorAutoLock mon(mMonitor);
-  return mOfficialGroupEndTimestamp;
+            AppendStateToStr(mSourceBufferAttributes->GetAppendState()), AppendStateToStr(aAppendState));
+  mSourceBufferAttributes->SetAppendState(aAppendState);
 }
 
 MediaInfo
 TrackBuffersManager::GetMetadata()
 {
   MonitorAutoLock mon(mMonitor);
   return mInfo;
 }
--- a/dom/media/mediasource/TrackBuffersManager.h
+++ b/dom/media/mediasource/TrackBuffersManager.h
@@ -5,90 +5,106 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_TRACKBUFFERSMANAGER_H_
 #define MOZILLA_TRACKBUFFERSMANAGER_H_
 
 #include "mozilla/Atomics.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/Monitor.h"
-#include "mozilla/MozPromise.h"
-#include "mozilla/Pair.h"
 #include "mozilla/dom/SourceBufferBinding.h"
 
 #include "MediaData.h"
 #include "MediaDataDemuxer.h"
 #include "MediaSourceDecoder.h"
+#include "SourceBufferTask.h"
 #include "TimeUnits.h"
 #include "nsProxyRelease.h"
 #include "nsString.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 
 class ContainerParser;
 class MediaByteBuffer;
 class MediaRawData;
 class MediaSourceDemuxer;
 class SourceBufferResource;
 
-namespace dom {
-  class SourceBufferAttributes;
-}
+class SourceBufferTaskQueue {
+public:
+  SourceBufferTaskQueue()
+  : mMonitor("SourceBufferTaskQueue")
+  {}
+
+  void Push(SourceBufferTask* aTask)
+  {
+    MonitorAutoLock mon(mMonitor);
+    mQueue.AppendElement(aTask);
+  }
+
+  already_AddRefed<SourceBufferTask> Pop()
+  {
+    MonitorAutoLock mon(mMonitor);
+    if (!mQueue.Length()) {
+      return nullptr;
+    }
+    RefPtr<SourceBufferTask> task = Move(mQueue[0]);
+    mQueue.RemoveElementAt(0);
+    return task.forget();
+  }
+
+  nsTArray<SourceBufferTask>::size_type Length() const
+  {
+    MonitorAutoLock mon(mMonitor);
+    return mQueue.Length();
+  }
+
+private:
+  mutable Monitor mMonitor;
+  nsTArray<RefPtr<SourceBufferTask>> mQueue;
+};
 
 class TrackBuffersManager {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TrackBuffersManager);
 
-  typedef MozPromise<bool, nsresult, /* IsExclusive = */ true> AppendPromise;
-  typedef AppendPromise RangeRemovalPromise;
-
   enum class EvictDataResult : int8_t
   {
     NO_DATA_EVICTED,
     CANT_EVICT,
     BUFFER_FULL,
   };
 
-  // Current state as per Segment Parser Loop Algorithm
-  // http://w3c.github.io/media-source/index.html#sourcebuffer-segment-parser-loop
-  enum class AppendState : int32_t
-  {
-    WAITING_FOR_SEGMENT,
-    PARSING_INIT_SEGMENT,
-    PARSING_MEDIA_SEGMENT,
-  };
-
   typedef TrackInfo::TrackType TrackType;
   typedef MediaData::Type MediaType;
   typedef nsTArray<RefPtr<MediaRawData>> TrackBuffer;
+  typedef SourceBufferTask::AppendPromise AppendPromise;
+  typedef SourceBufferTask::RangeRemovalPromise RangeRemovalPromise;
 
   // Interface for SourceBuffer
-  TrackBuffersManager(dom::SourceBufferAttributes* aAttributes,
-                      MediaSourceDecoder* aParentDecoder,
+  TrackBuffersManager(MediaSourceDecoder* aParentDecoder,
                       const nsACString& aType);
 
-  // Add data to the end of the input buffer.
-  // Returns false if the append failed.
-  bool AppendData(MediaByteBuffer* aData,
-                  media::TimeUnit aTimestampOffset);
-
-  // Run MSE Buffer Append Algorithm
+  // Queue a task to add data to the end of the input buffer and run the MSE
+  // Buffer Append Algorithm
   // 3.5.5 Buffer Append Algorithm.
   // http://w3c.github.io/media-source/index.html#sourcebuffer-buffer-append
-  RefPtr<AppendPromise> BufferAppend();
+  RefPtr<AppendPromise> AppendData(MediaByteBuffer* aData,
+                                   const SourceBufferAttributes& aAttributes);
 
-  // Abort any pending AppendData.
+  // Queue a task to abort any pending AppendData.
+  // Does nothing at this stage.
   void AbortAppendData();
 
-  // Run MSE Reset Parser State Algorithm.
+  // Queue a task to run MSE Reset Parser State Algorithm.
   // 3.5.2 Reset Parser State
-  void ResetParserState();
+  void ResetParserState(SourceBufferAttributes& aAttributes);
 
-  // Runs MSE range removal algorithm.
+  // Queue a task to run the MSE range removal algorithm.
   // http://w3c.github.io/media-source/#sourcebuffer-coded-frame-removal
   RefPtr<RangeRemovalPromise> RangeRemoval(media::TimeUnit aStart,
                                              media::TimeUnit aEnd);
 
   // Evicts data up to aPlaybackTime. aThreshold is used to
   // bound the data being evicted. It will not evict more than aThreshold
   // bytes. aBufferStartTime contains the new start time of the data after the
   // eviction.
@@ -106,24 +122,16 @@ public:
   int64_t GetSize();
 
   // Indicate that the MediaSource parent object got into "ended" state.
   void Ended();
 
   // The parent SourceBuffer is about to be destroyed.
   void Detach();
 
-  AppendState GetAppendState()
-  {
-    return mAppendState;
-  }
-
-  void SetGroupStartTimestamp(const media::TimeUnit& aGroupStartTimestamp);
-  void RestartGroupStartTimestamp();
-  media::TimeUnit GroupEndTimestamp();
   int64_t EvictionThreshold() const;
 
   // Interface for MediaSourceDemuxer
   MediaInfo GetMetadata();
   const TrackBuffer& GetTrackBuffer(TrackInfo::TrackType aTrack);
   const media::TimeIntervals& Buffered(TrackInfo::TrackType);
   media::TimeIntervals SafeBuffered(TrackInfo::TrackType) const;
   bool IsEnded() const
@@ -145,65 +153,55 @@ public:
 
 private:
   typedef MozPromise<bool, nsresult, /* IsExclusive = */ true> CodedFrameProcessingPromise;
 
   // for MediaSourceDemuxer::GetMozDebugReaderData
   friend class MediaSourceDemuxer;
   virtual ~TrackBuffersManager();
   // All following functions run on the taskqueue.
-  RefPtr<AppendPromise> InitSegmentParserLoop();
+  RefPtr<AppendPromise> DoAppendData(RefPtr<MediaByteBuffer> aData,
+                                     SourceBufferAttributes aAttributes);
   void ScheduleSegmentParserLoop();
   void SegmentParserLoop();
-  void AppendIncomingBuffers();
   void InitializationSegmentReceived();
   void ShutdownDemuxers();
   void CreateDemuxerforMIMEType();
   void ResetDemuxingState();
   void NeedMoreData();
   void RejectAppend(nsresult aRejectValue, const char* aName);
   // Will return a promise that will be resolved once all frames of the current
   // media segment have been processed.
   RefPtr<CodedFrameProcessingPromise> CodedFrameProcessing();
   void CompleteCodedFrameProcessing();
   // Called by ResetParserState.
   void CompleteResetParserState();
   RefPtr<RangeRemovalPromise>
     CodedFrameRemovalWithPromise(media::TimeInterval aInterval);
   bool CodedFrameRemoval(media::TimeInterval aInterval);
-  void SetAppendState(AppendState aAppendState);
+  void SetAppendState(SourceBufferAttributes::AppendState aAppendState);
 
   bool HasVideo() const
   {
     return mVideoTracks.mNumTracks > 0;
   }
   bool HasAudio() const
   {
     return mAudioTracks.mNumTracks > 0;
   }
 
-  typedef Pair<RefPtr<MediaByteBuffer>, media::TimeUnit> IncomingBuffer;
-  void AppendIncomingBuffer(IncomingBuffer aData);
-  nsTArray<IncomingBuffer> mIncomingBuffers;
-
   // The input buffer as per http://w3c.github.io/media-source/index.html#sourcebuffer-input-buffer
   RefPtr<MediaByteBuffer> mInputBuffer;
-  // The current append state as per https://w3c.github.io/media-source/#sourcebuffer-append-state
-  // Accessed on both the main thread and the task queue.
-  Atomic<AppendState> mAppendState;
   // Buffer full flag as per https://w3c.github.io/media-source/#sourcebuffer-buffer-full-flag.
   // Accessed on both the main thread and the task queue.
-  // TODO: Unused for now.
   Atomic<bool> mBufferFull;
   bool mFirstInitializationSegmentReceived;
   // Set to true once a new segment is started.
   bool mNewMediaSegmentStarted;
   bool mActiveTrack;
-  Maybe<media::TimeUnit> mGroupStartTimestamp;
-  media::TimeUnit mGroupEndTimestamp;
   nsCString mType;
 
   // ContainerParser objects and methods.
   // Those are used to parse the incoming input buffer.
 
   // Recreate the ContainerParser and if aReuseInitData is true then
   // feed it with the previous init segment found.
   void RecreateParser(bool aReuseInitData);
@@ -240,17 +238,17 @@ private:
   void DoDemuxAudio();
   void OnAudioDemuxCompleted(RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples);
   void OnAudioDemuxFailed(DemuxerFailureReason aFailure)
   {
     mAudioTracks.mDemuxRequest.Complete();
     OnDemuxFailed(TrackType::kAudioTrack, aFailure);
   }
 
-  void DoEvictData(const media::TimeUnit& aPlaybackTime, int64_t aThreshold);
+  void DoEvictData(const media::TimeUnit& aPlaybackTime, int64_t aSizeToEvict);
 
   struct TrackData {
     TrackData()
       : mNumTracks(0)
       , mNeedRandomAccessPoint(true)
       , mSizeBuffer(0)
     {}
     uint32_t mNumTracks;
@@ -343,18 +341,16 @@ private:
   uint32_t FindSampleIndex(const TrackBuffer& aTrackBuffer,
                            const media::TimeInterval& aInterval);
   void UpdateBufferedRanges();
   void RejectProcessing(nsresult aRejectValue, const char* aName);
   void ResolveProcessing(bool aResolveValue, const char* aName);
   MozPromiseRequestHolder<CodedFrameProcessingPromise> mProcessingRequest;
   MozPromiseHolder<CodedFrameProcessingPromise> mProcessingPromise;
 
-  MozPromiseHolder<AppendPromise> mAppendPromise;
-
   // Trackbuffers definition.
   nsTArray<TrackData*> GetTracksList();
   TrackData& GetTracksData(TrackType aTrack)
   {
     switch(aTrack) {
       case TrackType::kVideoTrack:
         return mVideoTracks;
       case TrackType::kAudioTrack:
@@ -370,42 +366,50 @@ private:
     return mTaskQueue;
   }
   bool OnTaskQueue()
   {
     return !GetTaskQueue() || GetTaskQueue()->IsCurrentThreadIn();
   }
   RefPtr<TaskQueue> mTaskQueue;
 
+  // SourceBuffer Queues and running context.
+  SourceBufferTaskQueue mQueue;
+  void ProcessTasks();
+  void CancelAllTasks();
+  // Set if the TrackBuffersManager is currently processing a task.
+  // At this stage, this task is always a AppendBufferTask.
+  RefPtr<SourceBufferTask> mCurrentTask;
+  // Current SourceBuffer state for ongoing task.
+  // Its content is returned to the SourceBuffer once the AppendBufferTask has
+  // completed.
+  UniquePtr<SourceBufferAttributes> mSourceBufferAttributes;
+  // The current sourcebuffer append window. It's content is equivalent to
+  // mSourceBufferAttributes.mAppendWindowStart/End
   media::TimeInterval mAppendWindow;
-  media::TimeUnit mTimestampOffset;
-  media::TimeUnit mLastTimestampOffset;
-  void RestoreCachedVariables();
 
   // Strong references to external objects.
-  RefPtr<dom::SourceBufferAttributes> mSourceBufferAttributes;
   nsMainThreadPtrHandle<MediaSourceDecoder> mParentDecoder;
 
   // Set to true if mediasource state changed to ended.
   Atomic<bool> mEnded;
+  // Set to true if the parent SourceBuffer has shutdown.
+  // We will not reschedule or process new task once mDetached is set.
+  Atomic<bool> mDetached;
 
   // Global size of this source buffer content.
   Atomic<int64_t> mSizeSourceBuffer;
   const int64_t mVideoEvictionThreshold;
   const int64_t mAudioEvictionThreshold;
   Atomic<bool> mEvictionOccurred;
 
   // Monitor to protect following objects accessed across multipple threads.
-  // mMonitor is also notified if the value of mAppendRunning becomes false.
   mutable Monitor mMonitor;
-  // Set to true while a BufferAppend is running or is pending.
-  Atomic<bool> mAppendRunning;
   // Stable audio and video track time ranges.
   media::TimeIntervals mVideoBufferedRanges;
   media::TimeIntervals mAudioBufferedRanges;
-  media::TimeUnit mOfficialGroupEndTimestamp;
   // MediaInfo of the first init segment read.
   MediaInfo mInfo;
 };
 
 } // namespace mozilla
 
 #endif /* MOZILLA_TRACKBUFFERSMANAGER_H_ */
--- a/dom/media/mediasource/moz.build
+++ b/dom/media/mediasource/moz.build
@@ -4,16 +4,18 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MOCHITEST_MANIFESTS += ['test/mochitest.ini']
 
 EXPORTS += [
     'AsyncEventRunner.h',
     'MediaSourceDecoder.h',
     'MediaSourceDemuxer.h',
+    'SourceBufferAttributes.h',
+    'SourceBufferTask.h',
     'TrackBuffersManager.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'MediaSource.h',
     'SourceBuffer.h',
     'SourceBufferList.h',
 ]