Bug 1125776: Part2. appendBuffer scanning the data before firing updateend. r=mattwoodrow a=lmandel
authorJean-Yves Avenard <jyavenard@mozilla.com>
Wed, 04 Feb 2015 20:20:15 +1100
changeset 249831 9fa4b3fd1a147ca555cbe598206a178732a61ec7
parent 249830 915d0431328bb22ae8680eb948a7b3f7d68e6f33
child 249832 c15a499a7eaa0c3145eb11e56b6ccf2e803b7101
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow, lmandel
bugs1125776
milestone37.0a2
Bug 1125776: Part2. appendBuffer scanning the data before firing updateend. r=mattwoodrow a=lmandel
dom/media/mediasource/MediaSourceDecoder.cpp
dom/media/mediasource/MediaSourceDecoder.h
dom/media/mediasource/MediaSourceReader.cpp
dom/media/mediasource/SourceBuffer.cpp
dom/media/mediasource/SourceBuffer.h
dom/media/mediasource/TrackBuffer.cpp
dom/media/mediasource/TrackBuffer.h
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -200,54 +200,48 @@ MediaSourceDecoder::DurationChanged(doub
   if (mMediaSource) {
     mMediaSource->DurationChange(aOldDuration, aNewDuration);
   }
   // Run the MediaElement duration changed algorithm
   MediaDecoder::DurationChanged();
 }
 
 void
-MediaSourceDecoder::SetDecodedDuration(int64_t aDuration)
+MediaSourceDecoder::SetInitialDuration(int64_t aDuration)
 {
   // Only use the decoded duration if one wasn't already
   // set.
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   if (!mMediaSource || !IsNaN(mMediaSourceDuration)) {
     return;
   }
   double duration = aDuration;
   // A duration of -1 is +Infinity.
   if (aDuration >= 0) {
     duration /= USECS_PER_S;
   }
-  DoSetMediaSourceDuration(duration);
+  SetMediaSourceDuration(duration, MSRangeRemovalAction::SKIP);
 }
 
 void
 MediaSourceDecoder::SetMediaSourceDuration(double aDuration, MSRangeRemovalAction aAction)
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   double oldDuration = mMediaSourceDuration;
-  DoSetMediaSourceDuration(aDuration);
-  ScheduleDurationChange(oldDuration, aDuration, aAction);
-}
-
-void
-MediaSourceDecoder::DoSetMediaSourceDuration(double aDuration)
-{
   if (aDuration >= 0) {
     mDecoderStateMachine->SetDuration(aDuration * USECS_PER_S);
     mMediaSourceDuration = aDuration;
   } else {
     mDecoderStateMachine->SetDuration(INT64_MAX);
     mMediaSourceDuration = PositiveInfinity<double>();
   }
   if (mReader) {
     mReader->SetMediaSourceDuration(mMediaSourceDuration);
   }
+  ScheduleDurationChange(oldDuration, aDuration, aAction);
 }
 
 void
 MediaSourceDecoder::ScheduleDurationChange(double aOldDuration,
                                            double aNewDuration,
                                            MSRangeRemovalAction aAction)
 {
   if (aAction == MSRangeRemovalAction::SKIP) {
--- a/dom/media/mediasource/MediaSourceDecoder.h
+++ b/dom/media/mediasource/MediaSourceDecoder.h
@@ -51,17 +51,17 @@ public:
                                                          int64_t aTimestampOffset /* microseconds */);
   void AddTrackBuffer(TrackBuffer* aTrackBuffer);
   void RemoveTrackBuffer(TrackBuffer* aTrackBuffer);
   void OnTrackBufferConfigured(TrackBuffer* aTrackBuffer, const MediaInfo& aInfo);
 
   void Ended();
   bool IsExpectingMoreData() MOZ_OVERRIDE;
 
-  void SetDecodedDuration(int64_t aDuration);
+  void SetInitialDuration(int64_t aDuration);
   void SetMediaSourceDuration(double aDuration, MSRangeRemovalAction aAction);
   double GetMediaSourceDuration();
   void DurationChanged(double aOldDuration, double aNewDuration);
 
   // Called whenever a TrackBuffer has new data appended or a new decoder
   // initializes.  Safe to call from any thread.
   void NotifyTimeRangesChanged();
 
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -896,48 +896,40 @@ MediaSourceReader::ReadMetadata(MediaInf
 
   mEssentialTrackBuffers.Clear();
   if (!mAudioTrack && !mVideoTrack) {
     MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata missing track: mAudioTrack=%p mVideoTrack=%p",
               this, mAudioTrack.get(), mVideoTrack.get());
     return NS_ERROR_FAILURE;
   }
 
-  int64_t maxDuration = -1;
-
   if (mAudioTrack) {
     MOZ_ASSERT(mAudioTrack->IsReady());
     mAudioReader = mAudioTrack->Decoders()[0]->GetReader();
 
     const MediaInfo& info = mAudioReader->GetMediaInfo();
     MOZ_ASSERT(info.HasAudio());
     mInfo.mAudio = info.mAudio;
-    maxDuration = std::max(maxDuration, mAudioReader->GetDecoder()->GetMediaDuration());
-    MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata audio reader=%p maxDuration=%lld",
-              this, mAudioReader.get(), maxDuration);
+    MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata audio reader=%p duration=%lld",
+              this, mAudioReader.get(),
+              mAudioReader->GetDecoder()->GetMediaDuration());
   }
 
   if (mVideoTrack) {
     MOZ_ASSERT(mVideoTrack->IsReady());
     mVideoReader = mVideoTrack->Decoders()[0]->GetReader();
 
     const MediaInfo& info = mVideoReader->GetMediaInfo();
     MOZ_ASSERT(info.HasVideo());
     mInfo.mVideo = info.mVideo;
-    maxDuration = std::max(maxDuration, mVideoReader->GetDecoder()->GetMediaDuration());
-    MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata video reader=%p maxDuration=%lld",
-              this, mVideoReader.get(), maxDuration);
+    MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata video reader=%p duration=%lld",
+              this, mVideoReader.get(),
+              mVideoReader->GetDecoder()->GetMediaDuration());
   }
 
-  if (!maxDuration) {
-    // Treat a duration of 0 as infinity
-    maxDuration = -1;
-  }
-  static_cast<MediaSourceDecoder*>(mDecoder)->SetDecodedDuration(maxDuration);
-
   *aInfo = mInfo;
   *aTags = nullptr; // TODO: Handle metadata.
 
   return NS_OK;
 }
 
 void
 MediaSourceReader::ReadUpdatedMetadata(MediaInfo* aInfo)
--- a/dom/media/mediasource/SourceBuffer.cpp
+++ b/dom/media/mediasource/SourceBuffer.cpp
@@ -264,16 +264,18 @@ SourceBuffer::RangeRemoval(double aStart
 
   nsRefPtr<nsIRunnable> task = new RangeRemovalRunnable(this, aStart, aEnd);
   NS_DispatchToMainThread(task);
 }
 
 void
 SourceBuffer::DoRangeRemoval(double aStart, double aEnd)
 {
+  MSE_DEBUG("SourceBuffer(%p)::DoRangeRemoval (updating:%d)",
+            this, mUpdating);
   if (!mUpdating) {
     // abort was called in between.
     return;
   }
   if (mTrackBuffer && !IsInfinite(aStart)) {
     int64_t start = aStart * USECS_PER_S;
     int64_t end = IsInfinite(aEnd) ? INT64_MAX : (int64_t)(aEnd * USECS_PER_S);
     mTrackBuffer->RangeRemoval(start, end);
@@ -392,19 +394,21 @@ SourceBuffer::AbortUpdating()
   mUpdating = false;
   QueueAsyncSimpleEvent("abort");
   QueueAsyncSimpleEvent("updateend");
 }
 
 void
 SourceBuffer::CheckEndTime()
 {
+  MOZ_ASSERT(NS_IsMainThread());
   // Check if we need to update mMediaSource duration
   double endTime = GetBufferedEnd();
-  if (endTime > mMediaSource->Duration()) {
+  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("SourceBuffer(%p)::AppendData(aLength=%u)", this, aLength);
@@ -432,31 +436,51 @@ SourceBuffer::AppendData(LargeDataBuffer
     // the first StopUpdating() runnable runs, then a second StopUpdating()
     // runnable will be scheduled, but still only one (the first) will queue
     // events.
     return;
   }
 
   MOZ_ASSERT(mMediaSource);
 
-  if (aData->Length()) {
-    if (!mTrackBuffer->AppendData(aData, aTimestampOffset * USECS_PER_S)) {
-      AppendError(true);
-      return;
-    }
+  if (!aData->Length()) {
+    StopUpdating();
+    return;
+  }
+
+  mTrackBuffer->AppendData(aData, aTimestampOffset * USECS_PER_S)
+                    ->Then(NS_GetCurrentThread(), __func__, this,
+                           &SourceBuffer::AppendDataCompletedWithSuccess,
+                           &SourceBuffer::AppendDataErrored);
+}
 
-    if (mTrackBuffer->HasInitSegment()) {
-      mMediaSource->QueueInitializationEvent();
-    }
+void
+SourceBuffer::AppendDataCompletedWithSuccess(bool aGotMedia)
+{
+  if (!mUpdating) {
+    // The buffer append algorithm has been interrupted by abort().
+    return;
+  }
 
+  if (mTrackBuffer->HasInitSegment()) {
+    mMediaSource->QueueInitializationEvent();
+  }
+
+  if (aGotMedia) {
     CheckEndTime();
   }
 
   StopUpdating();
- }
+}
+
+void
+SourceBuffer::AppendDataErrored(nsresult aError)
+{
+  AppendError(true);
+}
 
 void
 SourceBuffer::AppendError(bool aDecoderError)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!mUpdating) {
     // The buffer append algorithm has been interrupted by abort().
     return;
--- a/dom/media/mediasource/SourceBuffer.h
+++ b/dom/media/mediasource/SourceBuffer.h
@@ -148,16 +148,19 @@ private:
   void AppendError(bool aDecoderError);
 
   // Implements the "Prepare Append Algorithm". Returns LargeDataBuffer object
   // on success or nullptr (with aRv set) on error.
   already_AddRefed<LargeDataBuffer> PrepareAppend(const uint8_t* aData,
                                                 uint32_t aLength,
                                                 ErrorResult& aRv);
 
+  void AppendDataCompletedWithSuccess(bool aValue);
+  void AppendDataErrored(nsresult aError);
+
   nsRefPtr<MediaSource> mMediaSource;
 
   uint32_t mEvictionThreshold;
 
   nsRefPtr<TrackBuffer> mTrackBuffer;
 
   double mAppendWindowStart;
   double mAppendWindowEnd;
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -97,16 +97,21 @@ public:
     nsRefPtr<SourceBufferDecoder> decoder = mOwner->NewDecoder(aTimestampOffset);
     if (!decoder) {
       return false;
     }
     mDecoders.AppendElement(decoder);
     return true;
   }
 
+  size_t Length()
+  {
+    return mDecoders.Length();
+  }
+
 private:
   TrackBuffer* mOwner;
   nsAutoTArray<nsRefPtr<SourceBufferDecoder>,2> mDecoders;
 };
 
 nsRefPtr<ShutdownPromise>
 TrackBuffer::Shutdown()
 {
@@ -139,30 +144,34 @@ TrackBuffer::ContinueShutdown()
   }
 
   mInitializedDecoders.Clear();
   mParentDecoder = nullptr;
 
   mShutdownPromise.Resolve(true, __func__);
 }
 
-bool
+nsRefPtr<TrackBuffer::InitializationPromise>
 TrackBuffer::AppendData(LargeDataBuffer* aData, int64_t aTimestampOffset)
 {
   MOZ_ASSERT(NS_IsMainThread());
   DecodersToInitialize decoders(this);
+  nsRefPtr<InitializationPromise> p = mInitializationPromise.Ensure(__func__);
+
   // TODO: Run more of the buffer append algorithm asynchronously.
   if (mParser->IsInitSegmentPresent(aData)) {
     MSE_DEBUG("TrackBuffer(%p)::AppendData: New initialization segment.", this);
     if (!decoders.NewDecoder(aTimestampOffset)) {
-      return false;
+      mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
+      return p;
     }
   } else if (!mParser->HasInitData()) {
     MSE_DEBUG("TrackBuffer(%p)::AppendData: Non-init segment appended during initialization.", this);
-    return false;
+    mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
+    return p;
   }
 
   int64_t start = 0, end = 0;
   if (mParser->ParseStartAndEndTimestamps(aData, start, end)) {
     start += aTimestampOffset;
     end += aTimestampOffset;
     if (mParser->IsMediaSegmentPresent(aData) &&
         mLastEndTimestamp &&
@@ -170,38 +179,48 @@ TrackBuffer::AppendData(LargeDataBuffer*
          mLastTimestampOffset != aTimestampOffset ||
          mDecoderPerSegment || (mCurrentDecoder && mCurrentDecoder->WasTrimmed()))) {
       MSE_DEBUG("TrackBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
                 this, mLastStartTimestamp, mLastEndTimestamp.value(), 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 (!decoders.NewDecoder(aTimestampOffset)) {
-        return false;
+        mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
+        return p;
       }
       MSE_DEBUG("TrackBuffer(%p)::AppendData: Decoder marked as initialized.", this);
       nsRefPtr<LargeDataBuffer> initData = mParser->InitData();
       AppendDataToCurrentResource(initData, end - start);
       mLastStartTimestamp = start;
     } else {
       MSE_DEBUG("TrackBuffer(%p)::AppendData: Segment last=[%lld, %lld] [%lld, %lld]",
                 this, mLastStartTimestamp, mLastEndTimestamp ? mLastEndTimestamp.value() : 0, start, end);
     }
     mLastEndTimestamp.reset();
     mLastEndTimestamp.emplace(end);
   }
 
   if (!AppendDataToCurrentResource(aData, end - start)) {
-    return false;
+    mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
+    return p;
   }
 
+  if (decoders.Length()) {
+    // TODO: the theory is that we should only ever have one decoder to
+    // initialize in common use. We can't properly handle the condition where
+    // the source buffer needs to wait on two decoders to initialize.
+    return p;
+  }
   // Tell our reader that we have more data to ensure that playback starts if
   // required when data is appended.
   mParentDecoder->GetReader()->MaybeNotifyHaveData();
-  return true;
+
+  mInitializationPromise.Resolve(end - start > 0, __func__);
+  return p;
 }
 
 bool
 TrackBuffer::AppendDataToCurrentResource(LargeDataBuffer* aData, uint32_t aDuration)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!mCurrentDecoder) {
     return false;
@@ -405,26 +424,28 @@ TrackBuffer::NewDecoder(int64_t aTimesta
   decoder->SetTaskQueue(mTaskQueue);
   return decoder.forget();
 }
 
 bool
 TrackBuffer::QueueInitializeDecoder(SourceBufferDecoder* aDecoder)
 {
   if (NS_WARN_IF(!mTaskQueue)) {
+    mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
     return false;
   }
 
   RefPtr<nsIRunnable> task =
     NS_NewRunnableMethodWithArg<SourceBufferDecoder*>(this,
                                                       &TrackBuffer::InitializeDecoder,
                                                       aDecoder);
   if (NS_FAILED(mTaskQueue->Dispatch(task))) {
-    MSE_DEBUG("MediaSourceReader(%p): Failed to enqueue decoder initialization task", this);
+    MSE_DEBUG("TrackBuffer(%p): Failed to enqueue decoder initialization task", this);
     RemoveDecoder(aDecoder);
+    mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
     return false;
   }
   return true;
 }
 
 void
 TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
 {
@@ -436,16 +457,17 @@ TrackBuffer::InitializeDecoder(SourceBuf
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
 
   // We may be shut down at any time by the reader on another thread. So we need
   // to check for this each time we acquire the monitor. If that happens, we
   // need to abort immediately, because the reader has forgotten about us, and
   // important pieces of our state (like mTaskQueue) have also been torn down.
   if (mShutdown) {
     MSE_DEBUG("TrackBuffer(%p) was shut down. Aborting initialization.", this);
+    mInitializationPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
     return;
   }
 
   MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
   MediaDecoderReader* reader = aDecoder->GetReader();
   MSE_DEBUG("TrackBuffer(%p): Initializing subdecoder %p reader %p",
             this, aDecoder, reader);
 
@@ -455,55 +477,90 @@ TrackBuffer::InitializeDecoder(SourceBuf
   {
     ReentrantMonitorAutoExit mon(mParentDecoder->GetReentrantMonitor());
     rv = reader->ReadMetadata(&mi, getter_Transfers(tags));
   }
 
   reader->SetIdle();
   if (mShutdown) {
     MSE_DEBUG("TrackBuffer(%p) was shut down while reading metadata. Aborting initialization.", this);
+    mInitializationPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
     return;
   }
 
   if (NS_SUCCEEDED(rv) && reader->IsWaitingOnCDMResource()) {
     mWaitingDecoders.AppendElement(aDecoder);
     return;
   }
 
   aDecoder->SetTaskQueue(nullptr);
 
   if (NS_FAILED(rv) || (!mi.HasVideo() && !mi.HasAudio())) {
     // XXX: Need to signal error back to owning SourceBuffer.
     MSE_DEBUG("TrackBuffer(%p): Reader %p failed to initialize rv=%x audio=%d video=%d",
               this, reader, rv, mi.HasAudio(), mi.HasVideo());
     RemoveDecoder(aDecoder);
+    mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
     return;
   }
 
   if (mi.HasVideo()) {
     MSE_DEBUG("TrackBuffer(%p): Reader %p video resolution=%dx%d",
               this, reader, mi.mVideo.mDisplay.width, mi.mVideo.mDisplay.height);
   }
   if (mi.HasAudio()) {
     MSE_DEBUG("TrackBuffer(%p): Reader %p audio sampleRate=%d channels=%d",
               this, reader, mi.mAudio.mRate, mi.mAudio.mChannels);
   }
 
-  if (!RegisterDecoder(aDecoder)) {
-    // XXX: Need to signal error back to owning SourceBuffer.
-    MSE_DEBUG("TrackBuffer(%p): Reader %p not activated", this, reader);
+  RefPtr<nsIRunnable> task =
+    NS_NewRunnableMethodWithArg<SourceBufferDecoder*>(this,
+                                                      &TrackBuffer::CompleteInitializeDecoder,
+                                                      aDecoder);
+  if (NS_FAILED(NS_DispatchToMainThread(task))) {
+    MSE_DEBUG("TrackBuffer(%p): Failed to enqueue decoder initialization task", this);
     RemoveDecoder(aDecoder);
+    mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
     return;
   }
+}
+
+void
+TrackBuffer::CompleteInitializeDecoder(SourceBufferDecoder* aDecoder)
+{
+  ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
+  if (mShutdown) {
+    MSE_DEBUG("TrackBuffer(%p) was shut down while reading metadata. Aborting initialization.", this);
+    RemoveDecoder(aDecoder);
+    mInitializationPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
+    return;
+  }
+
+  if (!RegisterDecoder(aDecoder)) {
+    MSE_DEBUG("TrackBuffer(%p): Reader %p not activated",
+              this, aDecoder->GetReader());
+    RemoveDecoder(aDecoder);
+    mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
+    return;
+  }
+
+  int64_t duration = aDecoder->GetMediaDuration();
+  if (!duration) {
+    // Treat a duration of 0 as infinity
+    duration = -1;
+  }
+  mParentDecoder->SetInitialDuration(duration);
 
   // Tell our reader that we have more data to ensure that playback starts if
   // required when data is appended.
   mParentDecoder->GetReader()->MaybeNotifyHaveData();
 
-  MSE_DEBUG("TrackBuffer(%p): Reader %p activated", this, reader);
+  MSE_DEBUG("TrackBuffer(%p): Reader %p activated",
+            this, aDecoder->GetReader());
+  mInitializationPromise.ResolveIfExists(aDecoder->GetRealMediaDuration() > 0, __func__);
 }
 
 bool
 TrackBuffer::ValidateTrackFormats(const MediaInfo& aInfo)
 {
   if (mInfo.HasAudio() != aInfo.HasAudio() ||
       mInfo.HasVideo() != aInfo.HasVideo()) {
     MSE_DEBUG("TrackBuffer(%p)::ValidateTrackFormats audio/video track mismatch", this);
--- a/dom/media/mediasource/TrackBuffer.h
+++ b/dom/media/mediasource/TrackBuffer.h
@@ -28,24 +28,27 @@ namespace dom {
 class TimeRanges;
 
 } // namespace dom
 
 class TrackBuffer MOZ_FINAL {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TrackBuffer);
 
+  typedef MediaPromise<bool, nsresult, /* IsExclusive = */ false> InitializationPromise;
+
   TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& aType);
 
   nsRefPtr<ShutdownPromise> Shutdown();
 
   // Append data to the current decoder.  Also responsible for calling
   // NotifyDataArrived on the decoder to keep buffered range computation up
   // to date.  Returns false if the append failed.
-  bool AppendData(LargeDataBuffer* aData, int64_t aTimestampOffset /* microseconds */);
+  nsRefPtr<InitializationPromise> AppendData(LargeDataBuffer* aData,
+                                             int64_t aTimestampOffset /* microseconds */);
 
   // Evicts data held in the current decoders SourceBufferResource from the
   // start of the buffer through 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 current
   // decoders buffered data after the eviction. Returns true if data was
   // evicted.
   bool EvictData(double aPlaybackTime, uint32_t aThreshold, double* aBufferStartTime);
@@ -125,16 +128,21 @@ private:
                                    uint32_t aDuration /* microseconds */);
 
   // Queue execution of InitializeDecoder on mTaskQueue.
   bool QueueInitializeDecoder(SourceBufferDecoder* aDecoder);
 
   // Runs decoder initialization including calling ReadMetadata.  Runs as an
   // event on the decode thread pool.
   void InitializeDecoder(SourceBufferDecoder* aDecoder);
+  // Once decoder has been initialized, set mediasource duration if required
+  // and resolve any pending InitializationPromise.
+  // Setting the mediasource duration must be done on the main thread.
+  // TODO: Why is that so?
+  void CompleteInitializeDecoder(SourceBufferDecoder* aDecoder);
 
   // Adds a successfully initialized decoder to mDecoders and (if it's the
   // first decoder initialized), initializes mHasAudio/mHasVideo.  Called
   // from the decode thread pool.  Return true if the decoder was
   // successfully registered.
   bool RegisterDecoder(SourceBufferDecoder* aDecoder);
 
   // Returns true if aInfo is considered a supported or the same format as
@@ -188,12 +196,15 @@ private:
   // Set when the first decoder used by this TrackBuffer is initialized.
   // Protected by mParentDecoder's monitor.
   MediaInfo mInfo;
 
   void ContinueShutdown();
   MediaPromiseHolder<ShutdownPromise> mShutdownPromise;
   bool mDecoderPerSegment;
   bool mShutdown;
+
+  MediaPromiseHolder<InitializationPromise> mInitializationPromise;
+
 };
 
 } // namespace mozilla
 #endif /* MOZILLA_TRACKBUFFER_H_ */