Bug 1065250 - Factor some of SourceBuffer::AppendData into TrackBuffer::Append. Move ContainerParser to a new file. r=karlt
authorMatthew Gregan <kinetik@flim.org>
Wed, 17 Sep 2014 18:33:00 +1200
changeset 206222 17d48ba5979bc7e7b03d1475f74a73031f2a4ba1
parent 206221 a98f69d91d7bfa5ab2cf90749d6483581eac6b70
child 206223 92df0fb732367dd78b9270895b5a62279610e733
push id27516
push userryanvm@gmail.com
push dateFri, 19 Sep 2014 17:54:48 +0000
treeherdermozilla-central@b00bdb144e06 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskarlt
bugs1065250
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 1065250 - Factor some of SourceBuffer::AppendData into TrackBuffer::Append. Move ContainerParser to a new file. r=karlt
content/media/mediasource/ContainerParser.cpp
content/media/mediasource/ContainerParser.h
content/media/mediasource/SourceBuffer.cpp
content/media/mediasource/SourceBuffer.h
content/media/mediasource/TrackBuffer.cpp
content/media/mediasource/TrackBuffer.h
content/media/mediasource/moz.build
copy from content/media/mediasource/SourceBuffer.cpp
copy to content/media/mediasource/ContainerParser.cpp
--- a/content/media/mediasource/SourceBuffer.cpp
+++ b/content/media/mediasource/ContainerParser.cpp
@@ -1,113 +1,81 @@
 /* -*- 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/. */
 
-#include "SourceBuffer.h"
+#include "ContainerParser.h"
 
-#include "AsyncEventRunner.h"
-#include "MediaSourceUtils.h"
-#include "TrackBuffer.h"
 #include "WebMBufferedParser.h"
 #include "mozilla/Endian.h"
-#include "mozilla/ErrorResult.h"
-#include "mozilla/FloatingPoint.h"
-#include "mozilla/Preferences.h"
-#include "mozilla/dom/MediaSourceBinding.h"
-#include "mozilla/dom/TimeRanges.h"
 #include "mp4_demuxer/BufferStream.h"
 #include "mp4_demuxer/MoofParser.h"
-#include "nsError.h"
-#include "nsIEventTarget.h"
-#include "nsIRunnable.h"
-#include "nsThreadUtils.h"
 #include "prlog.h"
 
-struct JSContext;
-class JSObject;
-
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* GetMediaSourceLog();
 extern PRLogModuleInfo* GetMediaSourceAPILog();
 
 #define MSE_DEBUG(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
 #define MSE_DEBUGV(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG+1, (__VA_ARGS__))
 #define MSE_API(...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, (__VA_ARGS__))
 #else
 #define MSE_DEBUG(...)
 #define MSE_DEBUGV(...)
 #define MSE_API(...)
 #endif
 
 namespace mozilla {
 
-class ContainerParser {
-public:
-  virtual ~ContainerParser() {}
-
-  // Return true if aData starts with an initialization segment.
-  // The base implementation exists only for debug logging and is expected
-  // to be called first from the overriding implementation.
-  virtual bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
-  {
-    MSE_DEBUG("ContainerParser(%p)::IsInitSegmentPresent aLength=%u [%x%x%x%x]",
-              this, aLength,
-              aLength > 0 ? aData[0] : 0,
-              aLength > 1 ? aData[1] : 0,
-              aLength > 2 ? aData[2] : 0,
-              aLength > 3 ? aData[3] : 0);
-    return false;
-  }
+bool
+ContainerParser::IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
+{
+MSE_DEBUG("ContainerParser(%p)::IsInitSegmentPresent aLength=%u [%x%x%x%x]",
+            this, aLength,
+            aLength > 0 ? aData[0] : 0,
+            aLength > 1 ? aData[1] : 0,
+            aLength > 2 ? aData[2] : 0,
+            aLength > 3 ? aData[3] : 0);
+return false;
+}
 
-  // Return true if aData starts with a media segment.
-  // The base implementation exists only for debug logging and is expected
-  // to be called first from the overriding implementation.
-  virtual bool IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength)
-  {
-    MSE_DEBUG("ContainerParser(%p)::IsMediaSegmentPresent aLength=%u [%x%x%x%x]",
-              this, aLength,
-              aLength > 0 ? aData[0] : 0,
-              aLength > 1 ? aData[1] : 0,
-              aLength > 2 ? aData[2] : 0,
-              aLength > 3 ? aData[3] : 0);
-    return false;
-  }
+bool
+ContainerParser::IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength)
+{
+  MSE_DEBUG("ContainerParser(%p)::IsMediaSegmentPresent aLength=%u [%x%x%x%x]",
+            this, aLength,
+            aLength > 0 ? aData[0] : 0,
+            aLength > 1 ? aData[1] : 0,
+            aLength > 2 ? aData[2] : 0,
+            aLength > 3 ? aData[3] : 0);
+  return false;
+}
 
-  // Parse aData to extract the start and end frame times from the media
-  // segment.  aData may not start on a parser sync boundary.  Return true
-  // if aStart and aEnd have been updated.
-  virtual bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
-                                          int64_t& aStart, int64_t& aEnd)
-  {
-    return false;
-  }
+bool
+ContainerParser::ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
+                                            int64_t& aStart, int64_t& aEnd)
+{
+  return false;
+}
 
-  // Compare aLhs and rHs, considering any error that may exist in the
-  // timestamps from the format's base representation.  Return true if aLhs
-  // == aRhs within the error epsilon.
-  virtual bool TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs)
-  {
-    NS_WARNING("Using default ContainerParser::TimestampFuzzyEquals implementation");
-    return aLhs == aRhs;
-  }
+bool
+ContainerParser::TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs)
+{
+  NS_WARNING("Using default ContainerParser::TimestampFuzzyEquals implementation");
+  return aLhs == aRhs;
+}
 
-  virtual const nsTArray<uint8_t>& InitData()
-  {
-    MOZ_ASSERT(mInitData.Length() > 0);
-    return mInitData;
-  }
-
-  static ContainerParser* CreateForMIMEType(const nsACString& aType);
-
-protected:
-  nsTArray<uint8_t> mInitData;
-};
+const nsTArray<uint8_t>&
+ContainerParser::InitData()
+{
+  MOZ_ASSERT(mHasInitData);
+  return mInitData;
+}
 
 class WebMContainerParser : public ContainerParser {
 public:
   WebMContainerParser()
     : mParser(0), mOffset(0)
   {}
 
   static const unsigned NS_PER_USEC = 1000;
@@ -178,16 +146,17 @@ public:
       if (!mapping.IsEmpty()) {
         length = mapping[0].mSyncOffset;
         MOZ_ASSERT(length <= aLength);
       }
       MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
                 this, length);
 
       mInitData.ReplaceElementsAt(0, mInitData.Length(), aData, length);
+      mHasInitData = true;
     }
     mOffset += aLength;
 
     if (mapping.IsEmpty()) {
       return false;
     }
 
     // Exclude frames that we don't enough data to cover the end of.
@@ -268,16 +237,17 @@ public:
     if (initSegment) {
       const MediaByteRange& range = mParser->mInitRange;
       MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
                 this, range.mEnd - range.mStart);
 
       mInitData.ReplaceElementsAt(0, mInitData.Length(),
                                   aData + range.mStart,
                                   range.mEnd - range.mStart);
+      mHasInitData = true;
     }
 
     mp4_demuxer::Interval<mp4_demuxer::Microseconds> compositionRange =
       mParser->GetCompositionRange(byteRanges);
 
     mStream->DiscardBefore(mParser->mOffset);
 
     if (compositionRange.IsNull()) {
@@ -290,451 +260,22 @@ public:
     return true;
   }
 
 private:
   nsRefPtr<mp4_demuxer::BufferStream> mStream;
   nsAutoPtr<mp4_demuxer::MoofParser> mParser;
 };
 
-
 /*static*/ ContainerParser*
 ContainerParser::CreateForMIMEType(const nsACString& aType)
 {
   if (aType.LowerCaseEqualsLiteral("video/webm") || aType.LowerCaseEqualsLiteral("audio/webm")) {
     return new WebMContainerParser();
   }
 
   if (aType.LowerCaseEqualsLiteral("video/mp4") || aType.LowerCaseEqualsLiteral("audio/mp4")) {
     return new MP4ContainerParser();
   }
   return new ContainerParser();
 }
 
-namespace dom {
-
-void
-SourceBuffer::SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p)::SetMode(aMode=%d)", this, aMode);
-  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);
-  }
-  // TODO: Test append state.
-  // TODO: If aMode is "sequence", set sequence start time.
-  mAppendMode = aMode;
-}
-
-void
-SourceBuffer::SetTimestampOffset(double aTimestampOffset, ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p)::SetTimestampOffset(aTimestampOffset=%f)", this, 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);
-  }
-  // TODO: Test append state.
-  // TODO: If aMode is "sequence", set sequence start time.
-  mTimestampOffset = aTimestampOffset;
-}
-
-already_AddRefed<TimeRanges>
-SourceBuffer::GetBuffered(ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  if (!IsAttached()) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return nullptr;
-  }
-  nsRefPtr<TimeRanges> ranges = new TimeRanges();
-  double highestEndTime = mTrackBuffer->Buffered(ranges);
-  if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
-    // Set the end time on the last range to highestEndTime by adding a
-    // new range spanning the current end time to highestEndTime, which
-    // Normalize() will then merge with the old last range.
-    ranges->Add(ranges->GetEndTime(), highestEndTime);
-    ranges->Normalize();
-  }
-  MSE_DEBUGV("SourceBuffer(%p)::GetBuffered ranges=%s", this, DumpTimeRanges(ranges).get());
-  return ranges.forget();
-}
-
-void
-SourceBuffer::SetAppendWindowStart(double aAppendWindowStart, ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p)::SetAppendWindowStart(aAppendWindowStart=%f)", this, aAppendWindowStart);
-  if (!IsAttached() || mUpdating) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return;
-  }
-  if (aAppendWindowStart < 0 || aAppendWindowStart >= mAppendWindowEnd) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
-    return;
-  }
-  mAppendWindowStart = aAppendWindowStart;
-}
-
-void
-SourceBuffer::SetAppendWindowEnd(double aAppendWindowEnd, ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p)::SetAppendWindowEnd(aAppendWindowEnd=%f)", this, aAppendWindowEnd);
-  if (!IsAttached() || mUpdating) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return;
-  }
-  if (IsNaN(aAppendWindowEnd) ||
-      aAppendWindowEnd <= mAppendWindowStart) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
-    return;
-  }
-  mAppendWindowEnd = aAppendWindowEnd;
-}
-
-void
-SourceBuffer::AppendBuffer(const ArrayBuffer& aData, ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p)::AppendBuffer(ArrayBuffer)", this);
-  aData.ComputeLengthAndData();
-  AppendData(aData.Data(), aData.Length(), aRv);
-}
-
-void
-SourceBuffer::AppendBuffer(const ArrayBufferView& aData, ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p)::AppendBuffer(ArrayBufferView)", this);
-  aData.ComputeLengthAndData();
-  AppendData(aData.Data(), aData.Length(), aRv);
-}
-
-void
-SourceBuffer::Abort(ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%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("SourceBuffer(%p)::Abort() Discarding decoder", this);
-  mTrackBuffer->DiscardDecoder();
-}
-
-void
-SourceBuffer::Remove(double aStart, double aEnd, ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p)::Remove(aStart=%f, aEnd=%f)", this, aStart, aEnd);
-  if (!IsAttached()) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return;
-  }
-  if (IsNaN(mMediaSource->Duration()) ||
-      aStart < 0 || aStart > mMediaSource->Duration() ||
-      aEnd <= aStart || IsNaN(aEnd)) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
-    return;
-  }
-  if (mUpdating || mMediaSource->ReadyState() != MediaSourceReadyState::Open) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return;
-  }
-  StartUpdating();
-  /// TODO: Run coded frame removal algorithm.
-
-  // Run the final step of the coded frame removal algorithm asynchronously
-  // to ensure the SourceBuffer's updating flag transition behaves as
-  // required by the spec.
-  nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &SourceBuffer::StopUpdating);
-  NS_DispatchToMainThread(event);
-}
-
-void
-SourceBuffer::Detach()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MSE_DEBUG("SourceBuffer(%p)::Detach", this);
-  if (mTrackBuffer) {
-    mTrackBuffer->Detach();
-  }
-  mTrackBuffer = nullptr;
-  mMediaSource = nullptr;
-}
-
-void
-SourceBuffer::Ended()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(IsAttached());
-  MSE_DEBUG("SourceBuffer(%p)::Ended", this);
-  mTrackBuffer->DiscardDecoder();
-}
-
-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(NS_IsMainThread());
-  MOZ_ASSERT(aMediaSource);
-  mEvictionThreshold = Preferences::GetUint("media.mediasource.eviction_threshold",
-                                            75 * (1 << 20));
-  mParser = ContainerParser::CreateForMIMEType(aType);
-  mTrackBuffer = new TrackBuffer(aMediaSource->GetDecoder(), aType);
-  MSE_DEBUG("SourceBuffer(%p)::SourceBuffer: Create mParser=%p mTrackBuffer=%p",
-            this, mParser.get(), mTrackBuffer.get());
-}
-
-SourceBuffer::~SourceBuffer()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!mMediaSource);
-  MSE_DEBUG("SourceBuffer(%p)::~SourceBuffer", this);
-}
-
-MediaSource*
-SourceBuffer::GetParentObject() const
-{
-  return mMediaSource;
-}
-
-JSObject*
-SourceBuffer::WrapObject(JSContext* aCx)
-{
-  return SourceBufferBinding::Wrap(aCx, this);
-}
-
-void
-SourceBuffer::DispatchSimpleEvent(const char* aName)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p) Dispatch event '%s'", this, aName);
-  DispatchTrustedEvent(NS_ConvertUTF8toUTF16(aName));
-}
-
-void
-SourceBuffer::QueueAsyncSimpleEvent(const char* aName)
-{
-  MSE_DEBUG("SourceBuffer(%p) Queuing event '%s'", this, aName);
-  nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<SourceBuffer>(this, aName);
-  NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
-}
-
-void
-SourceBuffer::StartUpdating()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!mUpdating);
-  mUpdating = true;
-  QueueAsyncSimpleEvent("updatestart");
-}
-
-void
-SourceBuffer::StopUpdating()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mUpdating);
-  mUpdating = false;
-  QueueAsyncSimpleEvent("update");
-  QueueAsyncSimpleEvent("updateend");
-}
-
-void
-SourceBuffer::AbortUpdating()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mUpdating);
-  mUpdating = false;
-  QueueAsyncSimpleEvent("abort");
-  QueueAsyncSimpleEvent("updateend");
-}
-
-void
-SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
-{
-  MSE_DEBUG("SourceBuffer(%p)::AppendData(aLength=%u)", this, aLength);
-  if (!PrepareAppend(aRv)) {
-    return;
-  }
-  StartUpdating();
-  // TODO: Run more of the buffer append algorithm asynchronously.
-  if (mParser->IsInitSegmentPresent(aData, aLength)) {
-    MSE_DEBUG("SourceBuffer(%p)::AppendData: New initialization segment.", this);
-    mMediaSource->QueueInitializationEvent();
-    mTrackBuffer->DiscardDecoder();
-    if (!mTrackBuffer->NewDecoder()) {
-      aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling.
-      return;
-    }
-    MSE_DEBUG("SourceBuffer(%p)::AppendData: Decoder marked as initialized.", this);
-  } else if (!mTrackBuffer->HasInitSegment()) {
-    MSE_DEBUG("SourceBuffer(%p)::AppendData: Non-init segment appended during initialization.", this);
-    Optional<MediaSourceEndOfStreamError> decodeError(MediaSourceEndOfStreamError::Decode);
-    ErrorResult dummy;
-    mMediaSource->EndOfStream(decodeError, dummy);
-    aRv.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-  int64_t start, end;
-  if (mParser->ParseStartAndEndTimestamps(aData, aLength, start, end)) {
-    int64_t lastStart, lastEnd;
-    mTrackBuffer->LastTimestamp(lastStart, lastEnd);
-    if (mParser->IsMediaSegmentPresent(aData, aLength) &&
-        !mParser->TimestampsFuzzyEqual(start, lastEnd)) {
-      MSE_DEBUG("SourceBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
-                this, lastStart, lastEnd, start, end);
-
-      // This data is earlier in the timeline than data we have already
-      // processed, so we must create a new decoder to handle the decoding.
-      mTrackBuffer->DiscardDecoder();
-
-      // If we've got a decoder here, it's not initialized, so we can use it
-      // rather than creating a new one.
-      if (!mTrackBuffer->NewDecoder()) {
-        aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling.
-        return;
-      }
-      MSE_DEBUG("SourceBuffer(%p)::AppendData: Decoder marked as initialized.", this);
-      const nsTArray<uint8_t>& initData = mParser->InitData();
-      mTrackBuffer->AppendData(initData.Elements(), initData.Length());
-      mTrackBuffer->SetLastStartTimestamp(start);
-    }
-    mTrackBuffer->SetLastEndTimestamp(end);
-    MSE_DEBUG("SourceBuffer(%p)::AppendData: Segment last=[%lld, %lld] [%lld, %lld]",
-              this, lastStart, lastEnd, start, end);
-  }
-  if (!mTrackBuffer->AppendData(aData, aLength)) {
-    Optional<MediaSourceEndOfStreamError> decodeError(MediaSourceEndOfStreamError::Decode);
-    ErrorResult dummy;
-    mMediaSource->EndOfStream(decodeError, dummy);
-    aRv.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  // Schedule the state machine thread to ensure playback starts
-  // if required when data is appended.
-  mMediaSource->GetDecoder()->ScheduleStateMachineThread();
-
-  // Run the final step of the buffer append algorithm asynchronously to
-  // ensure the SourceBuffer's updating flag transition behaves as required
-  // by the spec.
-  nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &SourceBuffer::StopUpdating);
-  NS_DispatchToMainThread(event);
-}
-
-bool
-SourceBuffer::PrepareAppend(ErrorResult& aRv)
-{
-  if (!IsAttached() || mUpdating) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return false;
-  }
-  if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
-    mMediaSource->SetReadyState(MediaSourceReadyState::Open);
-  }
-
-  // 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 SourceBuffers it knows
-  // about.
-  // TODO: Make the eviction threshold smaller for audio-only streams.
-  // TODO: Drive evictions off memory pressure notifications.
-  // TODO: Consider a global eviction threshold  rather than per TrackBuffer.
-  bool evicted = mTrackBuffer->EvictData(mEvictionThreshold);
-  if (evicted) {
-    MSE_DEBUG("SourceBuffer(%p)::AppendData Evict; current buffered start=%f",
-              this, GetBufferedStart());
-
-    // We notify that we've evicted from the time range 0 through to
-    // the current start point.
-    mMediaSource->NotifyEvicted(0.0, GetBufferedStart());
-  }
-
-  // TODO: Test buffer full flag.
-  return true;
-}
-
-double
-SourceBuffer::GetBufferedStart()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  ErrorResult dummy;
-  nsRefPtr<TimeRanges> ranges = GetBuffered(dummy);
-  return ranges->Length() > 0 ? ranges->GetStartTime() : 0;
-}
-
-double
-SourceBuffer::GetBufferedEnd()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  ErrorResult dummy;
-  nsRefPtr<TimeRanges> ranges = GetBuffered(dummy);
-  return ranges->Length() > 0 ? ranges->GetEndTime() : 0;
-}
-
-void
-SourceBuffer::Evict(double aStart, double aEnd)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MSE_DEBUG("SourceBuffer(%p)::Evict(aStart=%f, aEnd=%f)", this, aStart, aEnd);
-  double currentTime = mMediaSource->GetDecoder()->GetCurrentTime();
-  double evictTime = aEnd;
-  const double safety_threshold = 5;
-  if (currentTime + safety_threshold >= evictTime) {
-    evictTime -= safety_threshold;
-  }
-  mTrackBuffer->EvictBefore(evictTime);
-}
-
-#if defined(DEBUG)
-void
-SourceBuffer::Dump(const char* aPath)
-{
-  if (mTrackBuffer) {
-    mTrackBuffer->Dump(aPath);
-  }
-}
-#endif
-
-NS_IMPL_CYCLE_COLLECTION_INHERITED(SourceBuffer, DOMEventTargetHelper,
-                                   mMediaSource)
-
-NS_IMPL_ADDREF_INHERITED(SourceBuffer, DOMEventTargetHelper)
-NS_IMPL_RELEASE_INHERITED(SourceBuffer, DOMEventTargetHelper)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(SourceBuffer)
-NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
-
-} // namespace dom
-
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/mediasource/ContainerParser.h
@@ -0,0 +1,55 @@
+/* -*- 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_CONTAINERPARSER_H_
+#define MOZILLA_CONTAINERPARSER_H_
+
+#include "nsTArray.h"
+
+namespace mozilla {
+
+class ContainerParser {
+public:
+  ContainerParser() : mHasInitData(false) {}
+  virtual ~ContainerParser() {}
+
+  // Return true if aData starts with an initialization segment.
+  // The base implementation exists only for debug logging and is expected
+  // to be called first from the overriding implementation.
+  virtual bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength);
+
+  // Return true if aData starts with a media segment.
+  // The base implementation exists only for debug logging and is expected
+  // to be called first from the overriding implementation.
+  virtual bool IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength);
+
+  // Parse aData to extract the start and end frame times from the media
+  // segment.  aData may not start on a parser sync boundary.  Return true
+  // if aStart and aEnd have been updated.
+  virtual bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
+                                          int64_t& aStart, int64_t& aEnd);
+
+  // Compare aLhs and rHs, considering any error that may exist in the
+  // timestamps from the format's base representation.  Return true if aLhs
+  // == aRhs within the error epsilon.
+  virtual bool TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs);
+
+  const nsTArray<uint8_t>& InitData();
+
+  bool HasInitData()
+  {
+    return mHasInitData;
+  }
+
+  static ContainerParser* CreateForMIMEType(const nsACString& aType);
+
+protected:
+  nsTArray<uint8_t> mInitData;
+  bool mHasInitData;
+};
+
+} // namespace mozilla
+#endif /* MOZILLA_CONTAINERPARSER_H_ */
--- a/content/media/mediasource/SourceBuffer.cpp
+++ b/content/media/mediasource/SourceBuffer.cpp
@@ -4,25 +4,21 @@
  * 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/. */
 
 #include "SourceBuffer.h"
 
 #include "AsyncEventRunner.h"
 #include "MediaSourceUtils.h"
 #include "TrackBuffer.h"
-#include "WebMBufferedParser.h"
-#include "mozilla/Endian.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/MediaSourceBinding.h"
 #include "mozilla/dom/TimeRanges.h"
-#include "mp4_demuxer/BufferStream.h"
-#include "mp4_demuxer/MoofParser.h"
 #include "nsError.h"
 #include "nsIEventTarget.h"
 #include "nsIRunnable.h"
 #include "nsThreadUtils.h"
 #include "prlog.h"
 
 struct JSContext;
 class JSObject;
@@ -37,283 +33,16 @@ extern PRLogModuleInfo* GetMediaSourceAP
 #else
 #define MSE_DEBUG(...)
 #define MSE_DEBUGV(...)
 #define MSE_API(...)
 #endif
 
 namespace mozilla {
 
-class ContainerParser {
-public:
-  virtual ~ContainerParser() {}
-
-  // Return true if aData starts with an initialization segment.
-  // The base implementation exists only for debug logging and is expected
-  // to be called first from the overriding implementation.
-  virtual bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
-  {
-    MSE_DEBUG("ContainerParser(%p)::IsInitSegmentPresent aLength=%u [%x%x%x%x]",
-              this, aLength,
-              aLength > 0 ? aData[0] : 0,
-              aLength > 1 ? aData[1] : 0,
-              aLength > 2 ? aData[2] : 0,
-              aLength > 3 ? aData[3] : 0);
-    return false;
-  }
-
-  // Return true if aData starts with a media segment.
-  // The base implementation exists only for debug logging and is expected
-  // to be called first from the overriding implementation.
-  virtual bool IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength)
-  {
-    MSE_DEBUG("ContainerParser(%p)::IsMediaSegmentPresent aLength=%u [%x%x%x%x]",
-              this, aLength,
-              aLength > 0 ? aData[0] : 0,
-              aLength > 1 ? aData[1] : 0,
-              aLength > 2 ? aData[2] : 0,
-              aLength > 3 ? aData[3] : 0);
-    return false;
-  }
-
-  // Parse aData to extract the start and end frame times from the media
-  // segment.  aData may not start on a parser sync boundary.  Return true
-  // if aStart and aEnd have been updated.
-  virtual bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
-                                          int64_t& aStart, int64_t& aEnd)
-  {
-    return false;
-  }
-
-  // Compare aLhs and rHs, considering any error that may exist in the
-  // timestamps from the format's base representation.  Return true if aLhs
-  // == aRhs within the error epsilon.
-  virtual bool TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs)
-  {
-    NS_WARNING("Using default ContainerParser::TimestampFuzzyEquals implementation");
-    return aLhs == aRhs;
-  }
-
-  virtual const nsTArray<uint8_t>& InitData()
-  {
-    MOZ_ASSERT(mInitData.Length() > 0);
-    return mInitData;
-  }
-
-  static ContainerParser* CreateForMIMEType(const nsACString& aType);
-
-protected:
-  nsTArray<uint8_t> mInitData;
-};
-
-class WebMContainerParser : public ContainerParser {
-public:
-  WebMContainerParser()
-    : mParser(0), mOffset(0)
-  {}
-
-  static const unsigned NS_PER_USEC = 1000;
-
-  bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
-  {
-    ContainerParser::IsInitSegmentPresent(aData, aLength);
-    // XXX: This is overly primitive, needs to collect data as it's appended
-    // to the SB and handle, rather than assuming everything is present in a
-    // single aData segment.
-    // 0x1a45dfa3 // EBML
-    // ...
-    // DocType == "webm"
-    // ...
-    // 0x18538067 // Segment (must be "unknown" size)
-    // 0x1549a966 // -> Segment Info
-    // 0x1654ae6b // -> One or more Tracks
-    if (aLength >= 4 &&
-        aData[0] == 0x1a && aData[1] == 0x45 && aData[2] == 0xdf && aData[3] == 0xa3) {
-      return true;
-    }
-    return false;
-  }
-
-  bool IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength)
-  {
-    ContainerParser::IsMediaSegmentPresent(aData, aLength);
-    // XXX: This is overly primitive, needs to collect data as it's appended
-    // to the SB and handle, rather than assuming everything is present in a
-    // single aData segment.
-    // 0x1a45dfa3 // EBML
-    // ...
-    // DocType == "webm"
-    // ...
-    // 0x18538067 // Segment (must be "unknown" size)
-    // 0x1549a966 // -> Segment Info
-    // 0x1654ae6b // -> One or more Tracks
-    if (aLength >= 4 &&
-        aData[0] == 0x1f && aData[1] == 0x43 && aData[2] == 0xb6 && aData[3] == 0x75) {
-      return true;
-    }
-    return false;
-  }
-
-  bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
-                                  int64_t& aStart, int64_t& aEnd)
-  {
-    bool initSegment = IsInitSegmentPresent(aData, aLength);
-    if (initSegment) {
-      mOffset = 0;
-      mParser = WebMBufferedParser(0);
-      mOverlappedMapping.Clear();
-    }
-
-    // XXX if it only adds new mappings, overlapped but not available
-    // (e.g. overlap < 0) frames are "lost" from the reported mappings here.
-    nsTArray<WebMTimeDataOffset> mapping;
-    mapping.AppendElements(mOverlappedMapping);
-    mOverlappedMapping.Clear();
-    ReentrantMonitor dummy("dummy");
-    mParser.Append(aData, aLength, mapping, dummy);
-
-    // XXX This is a bit of a hack.  Assume if there are no timecodes
-    // present and it's an init segment that it's _just_ an init segment.
-    // We should be more precise.
-    if (initSegment) {
-      uint32_t length = aLength;
-      if (!mapping.IsEmpty()) {
-        length = mapping[0].mSyncOffset;
-        MOZ_ASSERT(length <= aLength);
-      }
-      MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
-                this, length);
-
-      mInitData.ReplaceElementsAt(0, mInitData.Length(), aData, length);
-    }
-    mOffset += aLength;
-
-    if (mapping.IsEmpty()) {
-      return false;
-    }
-
-    // Exclude frames that we don't enough data to cover the end of.
-    uint32_t endIdx = mapping.Length() - 1;
-    while (mOffset < mapping[endIdx].mEndOffset && endIdx > 0) {
-      endIdx -= 1;
-    }
-
-    if (endIdx == 0) {
-      return false;
-    }
-
-    uint64_t frameDuration = mapping[endIdx].mTimecode - mapping[endIdx - 1].mTimecode;
-    aStart = mapping[0].mTimecode / NS_PER_USEC;
-    aEnd = (mapping[endIdx].mTimecode + frameDuration) / NS_PER_USEC;
-
-    MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: [%lld, %lld] [fso=%lld, leo=%lld, l=%u endIdx=%u]",
-              this, aStart, aEnd, mapping[0].mSyncOffset, mapping[endIdx].mEndOffset, mapping.Length(), endIdx);
-
-    mapping.RemoveElementsAt(0, endIdx + 1);
-    mOverlappedMapping.AppendElements(mapping);
-
-    return true;
-  }
-
-  bool TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs)
-  {
-    int64_t error = mParser.GetTimecodeScale() / NS_PER_USEC;
-    return llabs(aLhs - aRhs) <= error * 2;
-  }
-
-private:
-  WebMBufferedParser mParser;
-  nsTArray<WebMTimeDataOffset> mOverlappedMapping;
-  int64_t mOffset;
-};
-
-class MP4ContainerParser : public ContainerParser {
-public:
-  MP4ContainerParser() {}
-
-  bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
-  {
-    ContainerParser::IsInitSegmentPresent(aData, aLength);
-    // Each MP4 atom has a chunk size and chunk type. The root chunk in an MP4
-    // file is the 'ftyp' atom followed by a file type. We just check for a
-    // vaguely valid 'ftyp' atom.
-
-    if (aLength < 8) {
-      return false;
-    }
-
-    uint32_t chunk_size = BigEndian::readUint32(aData);
-    if (chunk_size < 8) {
-      return false;
-    }
-
-    return aData[4] == 'f' && aData[5] == 't' && aData[6] == 'y' &&
-           aData[7] == 'p';
-  }
-
-  bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
-                                  int64_t& aStart, int64_t& aEnd)
-  {
-    bool initSegment = IsInitSegmentPresent(aData, aLength);
-    if (initSegment) {
-      mStream = new mp4_demuxer::BufferStream();
-      mParser = new mp4_demuxer::MoofParser(mStream, 0);
-    } else if (!mStream || !mParser) {
-      return false;
-    }
-
-    mStream->AppendBytes(aData, aLength);
-    nsTArray<MediaByteRange> byteRanges;
-    byteRanges.AppendElement(mStream->GetByteRange());
-    mParser->RebuildFragmentedIndex(byteRanges);
-
-    if (initSegment) {
-      const MediaByteRange& range = mParser->mInitRange;
-      MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
-                this, range.mEnd - range.mStart);
-
-      mInitData.ReplaceElementsAt(0, mInitData.Length(),
-                                  aData + range.mStart,
-                                  range.mEnd - range.mStart);
-    }
-
-    mp4_demuxer::Interval<mp4_demuxer::Microseconds> compositionRange =
-      mParser->GetCompositionRange(byteRanges);
-
-    mStream->DiscardBefore(mParser->mOffset);
-
-    if (compositionRange.IsNull()) {
-      return false;
-    }
-    aStart = compositionRange.start;
-    aEnd = compositionRange.end;
-    MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: [%lld, %lld]",
-              this, aStart, aEnd);
-    return true;
-  }
-
-private:
-  nsRefPtr<mp4_demuxer::BufferStream> mStream;
-  nsAutoPtr<mp4_demuxer::MoofParser> mParser;
-};
-
-
-/*static*/ ContainerParser*
-ContainerParser::CreateForMIMEType(const nsACString& aType)
-{
-  if (aType.LowerCaseEqualsLiteral("video/webm") || aType.LowerCaseEqualsLiteral("audio/webm")) {
-    return new WebMContainerParser();
-  }
-
-  if (aType.LowerCaseEqualsLiteral("video/mp4") || aType.LowerCaseEqualsLiteral("audio/mp4")) {
-    return new MP4ContainerParser();
-  }
-  return new ContainerParser();
-}
-
 namespace dom {
 
 void
 SourceBuffer::SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_API("SourceBuffer(%p)::SetMode(aMode=%d)", this, aMode);
   if (!IsAttached() || mUpdating) {
@@ -492,31 +221,29 @@ SourceBuffer::Ended()
   MOZ_ASSERT(IsAttached());
   MSE_DEBUG("SourceBuffer(%p)::Ended", this);
   mTrackBuffer->DiscardDecoder();
 }
 
 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(NS_IsMainThread());
   MOZ_ASSERT(aMediaSource);
   mEvictionThreshold = Preferences::GetUint("media.mediasource.eviction_threshold",
                                             75 * (1 << 20));
-  mParser = ContainerParser::CreateForMIMEType(aType);
   mTrackBuffer = new TrackBuffer(aMediaSource->GetDecoder(), aType);
-  MSE_DEBUG("SourceBuffer(%p)::SourceBuffer: Create mParser=%p mTrackBuffer=%p",
-            this, mParser.get(), mTrackBuffer.get());
+  MSE_DEBUG("SourceBuffer(%p)::SourceBuffer: Create mTrackBuffer=%p",
+            this, mTrackBuffer.get());
 }
 
 SourceBuffer::~SourceBuffer()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mMediaSource);
   MSE_DEBUG("SourceBuffer(%p)::~SourceBuffer", this);
 }
@@ -581,73 +308,28 @@ SourceBuffer::AbortUpdating()
 void
 SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
 {
   MSE_DEBUG("SourceBuffer(%p)::AppendData(aLength=%u)", this, aLength);
   if (!PrepareAppend(aRv)) {
     return;
   }
   StartUpdating();
-  // TODO: Run more of the buffer append algorithm asynchronously.
-  if (mParser->IsInitSegmentPresent(aData, aLength)) {
-    MSE_DEBUG("SourceBuffer(%p)::AppendData: New initialization segment.", this);
-    mMediaSource->QueueInitializationEvent();
-    mTrackBuffer->DiscardDecoder();
-    if (!mTrackBuffer->NewDecoder()) {
-      aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling.
-      return;
-    }
-    MSE_DEBUG("SourceBuffer(%p)::AppendData: Decoder marked as initialized.", this);
-  } else if (!mTrackBuffer->HasInitSegment()) {
-    MSE_DEBUG("SourceBuffer(%p)::AppendData: Non-init segment appended during initialization.", this);
-    Optional<MediaSourceEndOfStreamError> decodeError(MediaSourceEndOfStreamError::Decode);
-    ErrorResult dummy;
-    mMediaSource->EndOfStream(decodeError, dummy);
-    aRv.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-  int64_t start, end;
-  if (mParser->ParseStartAndEndTimestamps(aData, aLength, start, end)) {
-    int64_t lastStart, lastEnd;
-    mTrackBuffer->LastTimestamp(lastStart, lastEnd);
-    if (mParser->IsMediaSegmentPresent(aData, aLength) &&
-        !mParser->TimestampsFuzzyEqual(start, lastEnd)) {
-      MSE_DEBUG("SourceBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
-                this, lastStart, lastEnd, start, end);
 
-      // This data is earlier in the timeline than data we have already
-      // processed, so we must create a new decoder to handle the decoding.
-      mTrackBuffer->DiscardDecoder();
-
-      // If we've got a decoder here, it's not initialized, so we can use it
-      // rather than creating a new one.
-      if (!mTrackBuffer->NewDecoder()) {
-        aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling.
-        return;
-      }
-      MSE_DEBUG("SourceBuffer(%p)::AppendData: Decoder marked as initialized.", this);
-      const nsTArray<uint8_t>& initData = mParser->InitData();
-      mTrackBuffer->AppendData(initData.Elements(), initData.Length());
-      mTrackBuffer->SetLastStartTimestamp(start);
-    }
-    mTrackBuffer->SetLastEndTimestamp(end);
-    MSE_DEBUG("SourceBuffer(%p)::AppendData: Segment last=[%lld, %lld] [%lld, %lld]",
-              this, lastStart, lastEnd, start, end);
-  }
   if (!mTrackBuffer->AppendData(aData, aLength)) {
     Optional<MediaSourceEndOfStreamError> decodeError(MediaSourceEndOfStreamError::Decode);
     ErrorResult dummy;
     mMediaSource->EndOfStream(decodeError, dummy);
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  // Schedule the state machine thread to ensure playback starts
-  // if required when data is appended.
-  mMediaSource->GetDecoder()->ScheduleStateMachineThread();
+  if (mTrackBuffer->HasInitSegment()) {
+    mMediaSource->QueueInitializationEvent();
+  }
 
   // Run the final step of the buffer append algorithm asynchronously to
   // ensure the SourceBuffer's updating flag transition behaves as required
   // by the spec.
   nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &SourceBuffer::StopUpdating);
   NS_DispatchToMainThread(event);
 }
 
--- a/content/media/mediasource/SourceBuffer.h
+++ b/content/media/mediasource/SourceBuffer.h
@@ -23,17 +23,16 @@
 #include "nsString.h"
 #include "nscore.h"
 
 class JSObject;
 struct JSContext;
 
 namespace mozilla {
 
-class ContainerParser;
 class ErrorResult;
 class TrackBuffer;
 template <typename T> class AsyncEventRunner;
 
 namespace dom {
 
 class TimeRanges;
 
@@ -115,43 +114,32 @@ public:
 
 private:
   ~SourceBuffer();
 
   friend class AsyncEventRunner<SourceBuffer>;
   void DispatchSimpleEvent(const char* aName);
   void QueueAsyncSimpleEvent(const char* aName);
 
-  // Create a new decoder for mType, and store the result in mDecoder.
-  // Returns true if mDecoder was set.
-  bool InitNewDecoder();
-
-  // Set mDecoder to null and reset mDecoderInitialized.
-  void DiscardDecoder();
-
   // 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);
 
   // Implements the "Prepare Append Algorithm".  Returns true if the append
   // may continue, or false (with aRv set) on error.
   bool PrepareAppend(ErrorResult& aRv);
 
   nsRefPtr<MediaSource> mMediaSource;
 
-  const nsCString mType;
-
   uint32_t mEvictionThreshold;
 
-  nsAutoPtr<ContainerParser> mParser;
-
   nsRefPtr<TrackBuffer> mTrackBuffer;
 
   double mAppendWindowStart;
   double mAppendWindowEnd;
 
   double mTimestampOffset;
 
   SourceBufferAppendMode mAppendMode;
--- a/content/media/mediasource/TrackBuffer.cpp
+++ b/content/media/mediasource/TrackBuffer.cpp
@@ -1,37 +1,29 @@
 /* -*- 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/. */
 
 #include "TrackBuffer.h"
 
+#include "ContainerParser.h"
 #include "MediaSourceDecoder.h"
 #include "SharedThreadPool.h"
 #include "MediaTaskQueue.h"
 #include "SourceBufferDecoder.h"
 #include "SourceBufferResource.h"
 #include "VideoUtils.h"
-#include "mozilla/FloatingPoint.h"
 #include "mozilla/dom/TimeRanges.h"
 #include "nsError.h"
 #include "nsIRunnable.h"
 #include "nsThreadUtils.h"
 #include "prlog.h"
 
-#if defined(DEBUG)
-#include <sys/stat.h>
-#include <sys/types.h>
-#endif
-
-struct JSContext;
-class JSObject;
-
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* GetMediaSourceLog();
 extern PRLogModuleInfo* GetMediaSourceAPILog();
 
 #define MSE_DEBUG(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
 #define MSE_DEBUGV(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG+1, (__VA_ARGS__))
 #define MSE_API(...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, (__VA_ARGS__))
 #else
@@ -42,19 +34,19 @@ extern PRLogModuleInfo* GetMediaSourceAP
 
 namespace mozilla {
 
 TrackBuffer::TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& aType)
   : mParentDecoder(aParentDecoder)
   , mType(aType)
   , mLastStartTimestamp(0)
   , mLastEndTimestamp(0)
-  , mHasInit(false)
 {
   MOZ_COUNT_CTOR(TrackBuffer);
+  mParser = ContainerParser::CreateForMIMEType(aType);
   mTaskQueue = new MediaTaskQueue(GetMediaDecodeThreadPool());
   aParentDecoder->AddTrackBuffer(this);
 }
 
 TrackBuffer::~TrackBuffer()
 {
   MOZ_COUNT_DTOR(TrackBuffer);
 }
@@ -99,16 +91,63 @@ TrackBuffer::Shutdown()
   MOZ_ASSERT(mDecoders.IsEmpty());
   mParentDecoder = nullptr;
 }
 
 bool
 TrackBuffer::AppendData(const uint8_t* aData, uint32_t aLength)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  // TODO: Run more of the buffer append algorithm asynchronously.
+  if (mParser->IsInitSegmentPresent(aData, aLength)) {
+    MSE_DEBUG("TrackBuffer(%p)::AppendData: New initialization segment.", this);
+    if (!NewDecoder()) {
+      return false;
+    }
+  } else if (!mParser->HasInitData()) {
+    MSE_DEBUG("TrackBuffer(%p)::AppendData: Non-init segment appended during initialization.", this);
+    return false;
+  }
+
+  int64_t start, end;
+  if (mParser->ParseStartAndEndTimestamps(aData, aLength, start, end)) {
+    if (mParser->IsMediaSegmentPresent(aData, aLength) &&
+        !mParser->TimestampsFuzzyEqual(start, mLastEndTimestamp)) {
+      MSE_DEBUG("TrackBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
+                this, mLastStartTimestamp, mLastEndTimestamp, start, end);
+
+      // This data is earlier in the timeline than data we have already
+      // processed, so we must create a new decoder to handle the decoding.
+      if (!NewDecoder()) {
+        return false;
+      }
+      MSE_DEBUG("TrackBuffer(%p)::AppendData: Decoder marked as initialized.", this);
+      const nsTArray<uint8_t>& initData = mParser->InitData();
+      AppendDataToCurrentResource(initData.Elements(), initData.Length());
+      mLastStartTimestamp = start;
+    }
+    mLastEndTimestamp = end;
+    MSE_DEBUG("TrackBuffer(%p)::AppendData: Segment last=[%lld, %lld] [%lld, %lld]",
+              this, mLastStartTimestamp, mLastEndTimestamp, start, end);
+  }
+
+  if (!AppendDataToCurrentResource(aData, aLength)) {
+    return false;
+  }
+
+  // Schedule the state machine thread to ensure playback starts if required
+  // when data is appended.
+  mParentDecoder->ScheduleStateMachineThread();
+  return true;
+}
+
+bool
+TrackBuffer::AppendDataToCurrentResource(const uint8_t* aData, uint32_t aLength)
+{
+  MOZ_ASSERT(NS_IsMainThread());
   if (!mCurrentDecoder) {
     return false;
   }
 
   SourceBufferResource* resource = mCurrentDecoder->GetResource();
   int64_t appendOffset = resource->GetLength();
   resource->AppendData(aData, aLength);
   // XXX: For future reference: NDA call must run on the main thread.
@@ -174,29 +213,30 @@ TrackBuffer::Buffered(dom::TimeRanges* a
 
   return highestEndTime;
 }
 
 bool
 TrackBuffer::NewDecoder()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!mCurrentDecoder && mParentDecoder);
+  MOZ_ASSERT(mParentDecoder);
+
+  DiscardDecoder();
 
   nsRefPtr<SourceBufferDecoder> decoder = mParentDecoder->CreateSubDecoder(mType);
   if (!decoder) {
     return false;
   }
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
   mCurrentDecoder = decoder;
   mDecoders.AppendElement(decoder);
 
   mLastStartTimestamp = 0;
   mLastEndTimestamp = 0;
-  mHasInit = true;
 
   return QueueInitializeDecoder(decoder);
 }
 
 bool
 TrackBuffer::QueueInitializeDecoder(nsRefPtr<SourceBufferDecoder> aDecoder)
 {
   RefPtr<nsIRunnable> task =
@@ -311,47 +351,25 @@ TrackBuffer::Detach()
     DiscardDecoder();
   }
 }
 
 bool
 TrackBuffer::HasInitSegment()
 {
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
-  return mHasInit;
+  return mParser->HasInitData();
 }
 
 bool
 TrackBuffer::IsReady()
 {
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
   MOZ_ASSERT((mInfo.HasAudio() || mInfo.HasVideo()) || mInitializedDecoders.IsEmpty());
-  return HasInitSegment() && (mInfo.HasAudio() || mInfo.HasVideo());
-}
-
-void
-TrackBuffer::LastTimestamp(int64_t& aStart, int64_t& aEnd)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  aStart = mLastStartTimestamp;
-  aEnd = mLastEndTimestamp;
-}
-
-void
-TrackBuffer::SetLastStartTimestamp(int64_t aStart)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  mLastStartTimestamp = aStart;
-}
-
-void
-TrackBuffer::SetLastEndTimestamp(int64_t aEnd)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  mLastEndTimestamp = aEnd;
+  return mParser->HasInitData() && (mInfo.HasAudio() || mInfo.HasVideo());
 }
 
 bool
 TrackBuffer::ContainsTime(int64_t aTime)
 {
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
   for (uint32_t i = 0; i < mInitializedDecoders.Length(); ++i) {
     nsRefPtr<dom::TimeRanges> r = new dom::TimeRanges();
--- a/content/media/mediasource/TrackBuffer.h
+++ b/content/media/mediasource/TrackBuffer.h
@@ -12,16 +12,17 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/mozalloc.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nscore.h"
 
 namespace mozilla {
 
+class ContainerParser;
 class MediaSourceDecoder;
 
 namespace dom {
 
 class TimeRanges;
 
 } // namespace dom
 
@@ -40,39 +41,29 @@ public:
   bool EvictData(uint32_t aThreshold);
   void EvictBefore(double aTime);
 
   // Returns the highest end time of all of the buffered ranges in the
   // decoders managed by this TrackBuffer, and returns the union of the
   // decoders buffered ranges in aRanges.
   double Buffered(dom::TimeRanges* aRanges);
 
-  // Create a new decoder, set mCurrentDecoder to the new decoder, and queue
-  // the decoder for initialization.  The decoder is not considered
-  // initialized until it is added to mDecoders.
-  bool NewDecoder();
-
   // Mark the current decoder's resource as ended, clear mCurrentDecoder and
   // reset mLast{Start,End}Timestamp.
   void DiscardDecoder();
 
   void Detach();
 
   // Returns true if an init segment has been appended.
   bool HasInitSegment();
 
-  // Returns true iff HasInitSegment() and the decoder using that init
+  // Returns true iff mParser->HasInitData() and the decoder using that init
   // segment has successfully initialized by setting mHas{Audio,Video}..
   bool IsReady();
 
-  // Query and update mLast{Start,End}Timestamp.
-  void LastTimestamp(int64_t& aStart, int64_t& aEnd);
-  void SetLastStartTimestamp(int64_t aStart);
-  void SetLastEndTimestamp(int64_t aEnd);
-
   // Returns true if any of the decoders managed by this track buffer
   // contain aTime in their buffered ranges.
   bool ContainsTime(int64_t aTime);
 
   void BreakCycles();
 
   // Call ResetDecode() on each decoder in mDecoders.
   void ResetDecode();
@@ -84,16 +75,25 @@ public:
 
 #if defined(DEBUG)
   void Dump(const char* aPath);
 #endif
 
 private:
   ~TrackBuffer();
 
+  // Create a new decoder, set mCurrentDecoder to the new decoder, and queue
+  // the decoder for initialization.  The decoder is not considered
+  // initialized until it is added to mDecoders.
+  bool NewDecoder();
+
+  // Helper for AppendData, ensures NotifyDataArrived is called whenever
+  // data is appended to the current decoder's SourceBufferResource.
+  bool AppendDataToCurrentResource(const uint8_t* aData, uint32_t aLength);
+
   // Queue execution of InitializeDecoder on mTaskQueue.
   bool QueueInitializeDecoder(nsRefPtr<SourceBufferDecoder> aDecoder);
 
   // Runs decoder initialization including calling ReadMetadata.  Runs as an
   // event on the decode thread pool.
   void InitializeDecoder(nsRefPtr<SourceBufferDecoder> aDecoder);
 
   // Adds a successfully initialized decoder to mDecoders and (if it's the
@@ -107,16 +107,18 @@ private:
   bool ValidateTrackFormats(const MediaInfo& aInfo);
 
   // Remove aDecoder from mDecoders and dispatch an event to the main thread
   // to clean up the decoder.  If aDecoder was added to
   // mInitializedDecoders, it must have been removed before calling this
   // function.
   void RemoveDecoder(nsRefPtr<SourceBufferDecoder> aDecoder);
 
+  nsAutoPtr<ContainerParser> mParser;
+
   // A task queue using the shared media thread pool.  Used exclusively to
   // initialize (i.e. call ReadMetadata on) decoders as they are created via
   // NewDecoder.
   RefPtr<MediaTaskQueue> mTaskQueue;
 
   // All of the decoders managed by this TrackBuffer.  Access protected by
   // mParentDecoder's monitor.
   nsTArray<nsRefPtr<SourceBufferDecoder>> mDecoders;
@@ -131,19 +133,15 @@ private:
   nsRefPtr<MediaSourceDecoder> mParentDecoder;
   const nsCString mType;
 
   // The last start and end timestamps added to the TrackBuffer via
   // AppendData.  Accessed on the main thread only.
   int64_t mLastStartTimestamp;
   int64_t mLastEndTimestamp;
 
-  // Set when the initialization segment is first seen and cached (implied
-  // by new decoder creation).  Protected by mParentDecoder's monitor.
-  bool mHasInit;
-
   // Set when the first decoder used by this TrackBuffer is initialized.
   // Protected by mParentDecoder's monitor.
   MediaInfo mInfo;
 };
 
 } // namespace mozilla
 #endif /* MOZILLA_TRACKBUFFER_H_ */
--- a/content/media/mediasource/moz.build
+++ b/content/media/mediasource/moz.build
@@ -12,16 +12,17 @@ EXPORTS += [
 
 EXPORTS.mozilla.dom += [
     'MediaSource.h',
     'SourceBuffer.h',
     'SourceBufferList.h',
 ]
 
 UNIFIED_SOURCES += [
+    'ContainerParser.cpp',
     'MediaSource.cpp',
     'MediaSourceDecoder.cpp',
     'MediaSourceReader.cpp',
     'MediaSourceUtils.cpp',
     'SourceBuffer.cpp',
     'SourceBufferDecoder.cpp',
     'SourceBufferList.cpp',
     'SourceBufferResource.cpp',