Bug 1157075: Part2. Use MediaPromise to read TrackBuffer metadata. r=bholley,r=karlt
authorJean-Yves Avenard <jyavenard@mozilla.com>
Fri, 01 May 2015 15:37:31 +1000
changeset 273349 9d6fc27d1c775b217b32f0ede8dadbac2877901e
parent 273348 5bb68945a86e326201d857d229c5874970b9559e
child 273350 8b3bb908f6fc2d0f97cf702f07515b29220c48db
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley, karlt
bugs1157075
milestone40.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 1157075: Part2. Use MediaPromise to read TrackBuffer metadata. r=bholley,r=karlt
dom/media/mediasource/TrackBuffer.cpp
dom/media/mediasource/TrackBuffer.h
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -11,16 +11,17 @@
 #include "MediaSourceDecoder.h"
 #include "SharedThreadPool.h"
 #include "MediaTaskQueue.h"
 #include "SourceBufferDecoder.h"
 #include "SourceBufferResource.h"
 #include "VideoUtils.h"
 #include "mozilla/dom/TimeRanges.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/TypeTraits.h"
 #include "nsError.h"
 #include "nsIRunnable.h"
 #include "nsThreadUtils.h"
 #include "prlog.h"
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* GetMediaSourceLog();
 
@@ -103,16 +104,17 @@ private:
 };
 
 nsRefPtr<ShutdownPromise>
 TrackBuffer::Shutdown()
 {
   mParentDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
   mShutdown = true;
   mInitializationPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
+  mMetadataRequest.DisconnectIfExists();
 
   MOZ_ASSERT(mShutdownPromise.IsEmpty());
   nsRefPtr<ShutdownPromise> p = mShutdownPromise.Ensure(__func__);
 
   RefPtr<MediaTaskQueue> queue = mTaskQueue;
   mTaskQueue = nullptr;
   queue->BeginShutdown()
        ->Then(mParentDecoder->GetReader()->GetTaskQueue(), __func__, this,
@@ -543,124 +545,168 @@ TrackBuffer::NewDecoder(int64_t aTimesta
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
   mCurrentDecoder = decoder;
   mDecoders.AppendElement(decoder);
 
   mLastStartTimestamp = 0;
   mLastEndTimestamp.reset();
   mLastTimestampOffset = aTimestampOffset;
 
-  decoder->SetTaskQueue(mTaskQueue);
+  decoder->SetTaskQueue(decoder->GetReader()->GetTaskQueue());
   return decoder.forget();
 }
 
 bool
 TrackBuffer::QueueInitializeDecoder(SourceBufferDecoder* aDecoder)
 {
-  if (NS_WARN_IF(!mTaskQueue)) {
-    mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
-    return false;
-  }
-
+  // Bug 1153295: We must ensure that the nsIRunnable hold a strong reference
+  // to aDecoder.
+  static_assert(mozilla::IsBaseOf<nsISupports, SourceBufferDecoder>::value,
+                "SourceBufferDecoder must be inheriting from nsISupports");
   RefPtr<nsIRunnable> task =
     NS_NewRunnableMethodWithArg<SourceBufferDecoder*>(this,
                                                       &TrackBuffer::InitializeDecoder,
                                                       aDecoder);
-  mTaskQueue->Dispatch(task);
+  // We need to initialize the reader on its own task queue
+  aDecoder->GetReader()->GetTaskQueue()->Dispatch(task);
   return true;
 }
 
+// MetadataRecipient is a is used to pass extra values required by the
+// MetadataPromise's target methods
+class MetadataRecipient {
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MetadataRecipient);
+
+  MetadataRecipient(TrackBuffer* aOwner,
+                    SourceBufferDecoder* aDecoder,
+                    bool aWasEnded)
+    : mOwner(aOwner)
+    , mDecoder(aDecoder)
+    , mWasEnded(aWasEnded) { }
+
+  void OnMetadataRead(MetadataHolder* aMetadata)
+  {
+    mOwner->OnMetadataRead(aMetadata, mDecoder, mWasEnded);
+  }
+
+  void OnMetadataNotRead(ReadMetadataFailureReason aReason)
+  {
+    mOwner->OnMetadataNotRead(aReason, mDecoder);
+  }
+
+private:
+  ~MetadataRecipient() {}
+  nsRefPtr<TrackBuffer> mOwner;
+  nsRefPtr<SourceBufferDecoder> mDecoder;
+  bool mWasEnded;
+};
+
 void
 TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
 {
   if (!mParentDecoder) {
     MSE_DEBUG("decoder was shutdown. Aborting initialization.");
     return;
   }
   // ReadMetadata may block the thread waiting on data, so we must be able
   // to leave the monitor while we call it. For the rest of this function
   // we want to hold the monitor though, since we run on a different task queue
   // from the reader and interact heavily with it.
   mParentDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
 
   if (mCurrentDecoder != aDecoder) {
     MSE_DEBUG("append was cancelled. Aborting initialization.");
-    RemoveDecoder(aDecoder);
     // If we reached this point, the SourceBuffer would have disconnected
     // the promise. So no need to reject it.
     return;
   }
 
   // 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("was shut down. Aborting initialization.");
-    RemoveDecoder(aDecoder);
     return;
   }
 
-  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+  MOZ_ASSERT(aDecoder->GetReader()->GetTaskQueue()->IsCurrentThreadIn());
+
   MediaDecoderReader* reader = aDecoder->GetReader();
+
   MSE_DEBUG("Initializing subdecoder %p reader %p",
             aDecoder, reader);
 
-  MediaInfo mi;
-  nsAutoPtr<MetadataTags> tags; // TODO: Handle metadata.
-  nsresult rv;
-
   // HACK WARNING:
   // We only reach this point once we know that we have a complete init segment.
   // We don't want the demuxer to do a blocking read as no more data can be
   // appended while this routine is running. Marking the SourceBufferResource
   // as ended will cause any incomplete reads to abort.
   // As this decoder hasn't been initialized yet, the resource isn't yet in use
   // and so it is safe to do so.
   bool wasEnded = aDecoder->GetResource()->IsEnded();
   if (!wasEnded) {
     aDecoder->GetResource()->Ended();
   }
+  nsRefPtr<MetadataRecipient> recipient =
+    new MetadataRecipient(this, aDecoder, wasEnded);
+  nsRefPtr<MediaDecoderReader::MetadataPromise> promise;
   {
     ReentrantMonitorAutoExit mon(mParentDecoder->GetReentrantMonitor());
-    rv = reader->ReadMetadata(&mi, getter_Transfers(tags));
+    promise = reader->AsyncReadMetadata();
   }
-  if (!wasEnded) {
-    // Adding an empty buffer will reopen the SourceBufferResource
-    nsRefPtr<MediaLargeByteBuffer> emptyBuffer = new MediaLargeByteBuffer;
-    aDecoder->GetResource()->AppendData(emptyBuffer);
-  }
-  // HACK END.
 
-  reader->SetIdle();
   if (mShutdown) {
     MSE_DEBUG("was shut down while reading metadata. Aborting initialization.");
     return;
   }
   if (mCurrentDecoder != aDecoder) {
     MSE_DEBUG("append was cancelled. Aborting initialization.");
-    RemoveDecoder(aDecoder);
     return;
   }
 
-  if (NS_SUCCEEDED(rv) && reader->IsWaitingOnCDMResource()) {
+  mMetadataRequest.Begin(promise
+                           ->RefableThen(reader->GetTaskQueue(), __func__,
+                                         recipient.get(),
+                                         &MetadataRecipient::OnMetadataRead,
+                                         &MetadataRecipient::OnMetadataNotRead));
+}
+
+void
+TrackBuffer::OnMetadataRead(MetadataHolder* aMetadata,
+                            SourceBufferDecoder* aDecoder,
+                            bool aWasEnded)
+{
+  MOZ_ASSERT(aDecoder->GetReader()->GetTaskQueue()->IsCurrentThreadIn());
+
+  mParentDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
+  ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
+
+  mMetadataRequest.Complete();
+
+  // Adding an empty buffer will reopen the SourceBufferResource
+  if (!aWasEnded) {
+    nsRefPtr<MediaLargeByteBuffer> emptyBuffer = new MediaLargeByteBuffer;
+    aDecoder->GetResource()->AppendData(emptyBuffer);
+  }
+  // HACK END.
+
+  MediaDecoderReader* reader = aDecoder->GetReader();
+  reader->SetIdle();
+
+  if (reader->IsWaitingOnCDMResource()) {
     mIsWaitingOnCDM = true;
   }
 
   aDecoder->SetTaskQueue(nullptr);
 
-  if (NS_FAILED(rv) || (!mi.HasVideo() && !mi.HasAudio())) {
-    // XXX: Need to signal error back to owning SourceBuffer.
-    MSE_DEBUG("Reader %p failed to initialize rv=%x audio=%d video=%d",
-              reader, rv, mi.HasAudio(), mi.HasVideo());
-    RemoveDecoder(aDecoder);
-    mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
-    return;
-  }
+  // A MediaDataPromise is only resolved if MediaInfo.HasValidMedia() is true.
+  MediaInfo mi = aMetadata->mInfo;
 
   if (mi.HasVideo()) {
     MSE_DEBUG("Reader %p video resolution=%dx%d",
               reader, mi.mVideo.mDisplay.width, mi.mVideo.mDisplay.height);
   }
   if (mi.HasAudio()) {
     MSE_DEBUG("Reader %p audio sampleRate=%d channels=%d",
               reader, mi.mAudio.mRate, mi.mAudio.mChannels);
@@ -674,34 +720,56 @@ TrackBuffer::InitializeDecoder(SourceBuf
     MSE_DEBUG("Failed to enqueue decoder initialization task");
     RemoveDecoder(aDecoder);
     mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
     return;
   }
 }
 
 void
+TrackBuffer::OnMetadataNotRead(ReadMetadataFailureReason aReason,
+                               SourceBufferDecoder* aDecoder)
+{
+  MOZ_ASSERT(aDecoder->GetReader()->GetTaskQueue()->IsCurrentThreadIn());
+
+  mParentDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
+  ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
+
+  mMetadataRequest.Complete();
+
+  MediaDecoderReader* reader = aDecoder->GetReader();
+  reader->SetIdle();
+
+  aDecoder->SetTaskQueue(nullptr);
+
+  MSE_DEBUG("Reader %p failed to initialize", reader);
+
+  RemoveDecoder(aDecoder);
+  mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
+}
+
+void
 TrackBuffer::CompleteInitializeDecoder(SourceBufferDecoder* aDecoder)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   if (!mParentDecoder) {
     MSE_DEBUG("was shutdown. Aborting initialization.");
     return;
   }
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
   if (mCurrentDecoder != aDecoder) {
     MSE_DEBUG("append was cancelled. Aborting initialization.");
     // If we reached this point, the SourceBuffer would have disconnected
     // the promise. So no need to reject it.
-    RemoveDecoder(aDecoder);
     return;
   }
 
   if (mShutdown) {
     MSE_DEBUG("was shut down. Aborting initialization.");
-    RemoveDecoder(aDecoder);
     return;
   }
 
   if (!RegisterDecoder(aDecoder)) {
     MSE_DEBUG("Reader %p not activated",
               aDecoder->GetReader());
     RemoveDecoder(aDecoder);
     mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
@@ -854,17 +922,26 @@ TrackBuffer::ResetParserState()
     mParser = ContainerParser::CreateForMIMEType(mType);
     DiscardCurrentDecoder();
   }
 }
 
 void
 TrackBuffer::AbortAppendData()
 {
+  ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
+
+  nsRefPtr<SourceBufferDecoder> current = mCurrentDecoder;
   DiscardCurrentDecoder();
+
+  if (mMetadataRequest.Exists() || !mInitializationPromise.IsEmpty()) {
+    MOZ_ASSERT(current);
+    RemoveDecoder(current);
+  }
+  mMetadataRequest.DisconnectIfExists();
   // The SourceBuffer would have disconnected its promise.
   // However we must ensure that the MediaPromiseHolder handle all pending
   // promises.
   mInitializationPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
 }
 
 const nsTArray<nsRefPtr<SourceBufferDecoder>>&
 TrackBuffer::Decoders()
--- a/dom/media/mediasource/TrackBuffer.h
+++ b/dom/media/mediasource/TrackBuffer.h
@@ -117,16 +117,17 @@ public:
 #endif
 
 #if defined(DEBUG)
   void Dump(const char* aPath);
 #endif
 
 private:
   friend class DecodersToInitialize;
+  friend class MetadataRecipient;
   ~TrackBuffer();
 
   // Create a new decoder, set mCurrentDecoder to the new decoder and
   // returns it. The new decoder must be queued using QueueInitializeDecoder
   // for initialization.
   // The decoder is not considered initialized until it is added to
   // mInitializedDecoders.
   already_AddRefed<SourceBufferDecoder> NewDecoder(int64_t aTimestampOffset /* microseconds */);
@@ -162,16 +163,23 @@ private:
   // to clean up the decoder.  If aDecoder was added to
   // mInitializedDecoders, it must have been removed before calling this
   // function.
   void RemoveDecoder(SourceBufferDecoder* aDecoder);
 
   // Remove all empty decoders from the provided list;
   void RemoveEmptyDecoders(nsTArray<SourceBufferDecoder*>& aDecoders);
 
+  void OnMetadataRead(MetadataHolder* aMetadata,
+                      SourceBufferDecoder* aDecoder,
+                      bool aWasEnded);
+
+  void OnMetadataNotRead(ReadMetadataFailureReason aReason,
+                         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
@@ -212,13 +220,14 @@ private:
   MediaInfo mInfo;
 
   void ContinueShutdown();
   MediaPromiseHolder<ShutdownPromise> mShutdownPromise;
   bool mDecoderPerSegment;
   bool mShutdown;
 
   MediaPromiseHolder<TrackBufferAppendPromise> mInitializationPromise;
-
+  // Track our request for metadata from the reader.
+  MediaPromiseConsumerHolder<MediaDecoderReader::MetadataPromise> mMetadataRequest;
 };
 
 } // namespace mozilla
 #endif /* MOZILLA_TRACKBUFFER_H_ */