Bug 1204757: P1. Update MediaDataDemuxer::Init() behavior. r=cpearce
authorJean-Yves Avenard <jyavenard@mozilla.com>
Tue, 15 Sep 2015 13:03:48 +1000
changeset 295361 a3a0f4f5b416f442f3f3fe654e92aa43513484e1
parent 295360 3ba3e6ad85e050ceeae20f43042fa2e36bf00416
child 295362 cc917807f8ae5dba27ea0e3aa6b5e482529273a0
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs1204757
milestone43.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 1204757: P1. Update MediaDataDemuxer::Init() behavior. r=cpearce A MediaDataDemuxer is now not to resolve the init promise until it has all the metadata. Except MediaSource, all demuxers would be doing blocking read to scan for the metadata, and having partial metadata would be an error. For MediaSource, we pass the NotifyDataArrived message which will cause the MediaSourceDemuxer to re-attempt to search for the metadata. When used within MediaSource's TrackBuffersManager, a demuxer will never be created until we have received a complete init segment (this task is performed by the various ContainerParsers)
dom/media/MP3Demuxer.cpp
dom/media/MediaDataDemuxer.h
dom/media/MediaFormatReader.cpp
dom/media/fmp4/MP4Demuxer.cpp
dom/media/mediasource/MediaSourceDemuxer.cpp
dom/media/mediasource/MediaSourceDemuxer.h
--- a/dom/media/MP3Demuxer.cpp
+++ b/dom/media/MP3Demuxer.cpp
@@ -47,17 +47,17 @@ MP3Demuxer::InitInternal() {
 }
 
 nsRefPtr<MP3Demuxer::InitPromise>
 MP3Demuxer::Init() {
   if (!InitInternal()) {
     MP3DEMUXER_LOG("MP3Demuxer::Init() failure: waiting for data");
 
     return InitPromise::CreateAndReject(
-      DemuxerFailureReason::WAITING_FOR_DATA, __func__);
+      DemuxerFailureReason::DEMUXER_ERROR, __func__);
   }
 
   MP3DEMUXER_LOG("MP3Demuxer::Init() successful");
   return InitPromise::CreateAndResolve(NS_OK, __func__);
 }
 
 bool
 MP3Demuxer::HasTrackType(TrackInfo::TrackType aType) const {
--- a/dom/media/MediaDataDemuxer.h
+++ b/dom/media/MediaDataDemuxer.h
@@ -41,19 +41,20 @@ class MediaDataDemuxer
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDataDemuxer)
 
   typedef MozPromise<nsresult, DemuxerFailureReason, /* IsExclusive = */ true> InitPromise;
 
   // Initializes the demuxer. Other methods cannot be called unless
   // initialization has completed and succeeded.
   // Typically a demuxer will wait to parse the metadata before resolving the
-  // promise. The promise will be rejected with WAITING_FOR_DATA should
-  // insufficient data be available at the time. Init() would have to be called
-  // again to retry once more data has been received.
+  // promise. The promise must not be resolved until sufficient data is
+  // supplied. For example, an incomplete metadata would cause the promise to be
+  // rejected should no more data be coming, while the demuxer would wait
+  // otherwise.
   virtual nsRefPtr<InitPromise> Init() = 0;
 
   // Returns true if a aType track type is available.
   virtual bool HasTrackType(TrackInfo::TrackType aType) const = 0;
 
   // Returns the number of tracks of aType type available. A value of
   // 0 indicates that no such type is available.
   virtual uint32_t GetNumberTracks(TrackInfo::TrackType aType) const = 0;
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -370,21 +370,17 @@ MediaFormatReader::OnDemuxerInitDone(nsr
   MOZ_ASSERT(!mDecodersInitRequest.Exists());
   EnsureDecodersInitialized();
 }
 
 void
 MediaFormatReader::OnDemuxerInitFailed(DemuxerFailureReason aFailure)
 {
   mDemuxerInitRequest.Complete();
-  if (aFailure == DemuxerFailureReason::WAITING_FOR_DATA) {
-    mMetadataPromise.Reject(ReadMetadataFailureReason::WAITING_FOR_RESOURCES, __func__);
-  } else {
-    mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
-  }
+  mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
 }
 
 bool
 MediaFormatReader::EnsureDecodersCreated()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   if (!mPlatform) {
@@ -1596,57 +1592,52 @@ MediaFormatReader::VideoIsHardwareAccele
 }
 
 void
 MediaFormatReader::NotifyDemuxer(uint32_t aLength, int64_t aOffset)
 {
   MOZ_ASSERT(OnTaskQueue());
 
   LOGV("aLength=%u, aOffset=%lld", aLength, aOffset);
-  if (mShutdown) {
+  if (mShutdown || !mDemuxer) {
     return;
   }
 
   if (aLength || aOffset) {
     mDemuxer->NotifyDataArrived(aLength, aOffset);
   } else {
     mDemuxer->NotifyDataRemoved();
   }
+  if (!mInitDone) {
+    return;
+  }
   if (HasVideo()) {
     mVideo.mReceivedNewData = true;
     ScheduleUpdate(TrackType::kVideoTrack);
   }
   if (HasAudio()) {
     mAudio.mReceivedNewData = true;
     ScheduleUpdate(TrackType::kAudioTrack);
   }
 }
 
 void
 MediaFormatReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(aLength);
 
-  if (!mInitDone || mShutdown) {
-    return;
-  }
-
   NotifyDemuxer(aLength, aOffset);
 }
 
 void
 MediaFormatReader::NotifyDataRemoved()
 {
   MOZ_ASSERT(OnTaskQueue());
 
-  if (!mInitDone || mShutdown) {
-    return;
-  }
-
   NotifyDemuxer(0, 0);
 }
 
 bool
 MediaFormatReader::ForceZeroStartTime() const
 {
   return !mDemuxer->ShouldComputeStartTime();
 }
--- a/dom/media/fmp4/MP4Demuxer.cpp
+++ b/dom/media/fmp4/MP4Demuxer.cpp
@@ -75,17 +75,17 @@ MP4Demuxer::MP4Demuxer(MediaResource* aR
 
 nsRefPtr<MP4Demuxer::InitPromise>
 MP4Demuxer::Init()
 {
   AutoPinned<mp4_demuxer::ResourceStream> stream(mStream);
 
   // Check that we have enough data to read the metadata.
   if (!mp4_demuxer::MP4Metadata::HasCompleteMetadata(stream)) {
-    return InitPromise::CreateAndReject(DemuxerFailureReason::WAITING_FOR_DATA, __func__);
+    return InitPromise::CreateAndReject(DemuxerFailureReason::DEMUXER_ERROR, __func__);
   }
 
   mInitData = mp4_demuxer::MP4Metadata::Metadata(stream);
   if (!mInitData) {
     // OOM
     return InitPromise::CreateAndReject(DemuxerFailureReason::DEMUXER_ERROR, __func__);
   }
 
--- a/dom/media/mediasource/MediaSourceDemuxer.cpp
+++ b/dom/media/mediasource/MediaSourceDemuxer.cpp
@@ -22,16 +22,17 @@ using media::TimeIntervals;
 // Gap allowed between frames. Due to inaccuracies in determining buffer end
 // frames (Bug 1065207). This value is based on the end of frame
 // default value used in Blink, kDefaultBufferDurationInMs.
 #define EOS_FUZZ_US 125000
 
 MediaSourceDemuxer::MediaSourceDemuxer()
   : mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
                              /* aSupportsTailDispatch = */ true))
+  , mInitDone(false)
   , mMonitor("MediaSourceDemuxer")
 {
   MOZ_ASSERT(NS_IsMainThread());
 }
 
 nsRefPtr<MediaSourceDemuxer::InitPromise>
 MediaSourceDemuxer::Init()
 {
@@ -40,20 +41,40 @@ MediaSourceDemuxer::Init()
 }
 
 nsRefPtr<MediaSourceDemuxer::InitPromise>
 MediaSourceDemuxer::AttemptInit()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   if (ScanSourceBuffersForContent()) {
+    mInitDone = true;
     return InitPromise::CreateAndResolve(NS_OK, __func__);
   }
-  return InitPromise::CreateAndReject(DemuxerFailureReason::WAITING_FOR_DATA,
-                                      __func__);
+
+  nsRefPtr<InitPromise> p = mInitPromise.Ensure(__func__);
+
+  return p;
+}
+
+void MediaSourceDemuxer::NotifyDataArrived(uint32_t aLength, int64_t aOffset)
+{
+  nsRefPtr<MediaSourceDemuxer> self = this;
+  nsCOMPtr<nsIRunnable> task =
+    NS_NewRunnableFunction([self] () {
+      if (self->mInitDone) {
+        return;
+      }
+      MOZ_ASSERT(!self->mInitPromise.IsEmpty());
+      if (self->ScanSourceBuffersForContent()) {
+        self->mInitDone = true;
+        self->mInitPromise.ResolveIfExists(NS_OK, __func__);
+      }
+    });
+  GetTaskQueue()->Dispatch(task.forget());
 }
 
 bool
 MediaSourceDemuxer::ScanSourceBuffersForContent()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   if (mSourceBuffers.IsEmpty()) {
@@ -208,16 +229,17 @@ MediaSourceDemuxer::GetManager(TrackType
       return mVideoTrack;
     default:
       return nullptr;
   }
 }
 
 MediaSourceDemuxer::~MediaSourceDemuxer()
 {
+  mInitPromise.RejectIfExists(DemuxerFailureReason::SHUTDOWN, __func__);
   mTaskQueue->BeginShutdown();
   mTaskQueue = nullptr;
 }
 
 void
 MediaSourceDemuxer::GetMozDebugReaderData(nsAString& aString)
 {
   MonitorAutoLock mon(mMonitor);
--- a/dom/media/mediasource/MediaSourceDemuxer.h
+++ b/dom/media/mediasource/MediaSourceDemuxer.h
@@ -37,16 +37,18 @@ public:
                                                               uint32_t aTrackNumber) override;
 
   bool IsSeekable() const override;
 
   UniquePtr<EncryptionInfo> GetCrypto() override;
 
   bool ShouldComputeStartTime() const override { return false; }
 
+  void NotifyDataArrived(uint32_t aLength, int64_t aOffset) override;
+
   /* interface for TrackBuffersManager */
   void AttachSourceBuffer(TrackBuffersManager* aSourceBuffer);
   void DetachSourceBuffer(TrackBuffersManager* aSourceBuffer);
   TaskQueue* GetTaskQueue() { return mTaskQueue; }
 
   // Returns a string describing the state of the MediaSource internal
   // buffered data. Used for debugging purposes.
   void GetMozDebugReaderData(nsAString& aString);
@@ -66,16 +68,19 @@ private:
     return !GetTaskQueue() || GetTaskQueue()->IsCurrentThreadIn();
   }
 
   RefPtr<TaskQueue> mTaskQueue;
   nsTArray<nsRefPtr<MediaSourceTrackDemuxer>> mDemuxers;
 
   nsTArray<nsRefPtr<TrackBuffersManager>> mSourceBuffers;
 
+  MozPromiseHolder<InitPromise> mInitPromise;
+  bool mInitDone;
+
   // Monitor to protect members below across multiple threads.
   mutable Monitor mMonitor;
   nsRefPtr<TrackBuffersManager> mAudioTrack;
   nsRefPtr<TrackBuffersManager> mVideoTrack;
   MediaInfo mInfo;
 };
 
 class MediaSourceTrackDemuxer : public MediaTrackDemuxer