diff --git a/dom/media/mediasource/TrackBuffer.cpp b/dom/media/mediasource/TrackBuffer.cpp index 1c37c1b1ea..3c23d97bcf 100644 --- a/dom/media/mediasource/TrackBuffer.cpp +++ b/dom/media/mediasource/TrackBuffer.cpp @@ -16,6 +16,7 @@ #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" @@ -108,6 +109,7 @@ TrackBuffer::Shutdown() mParentDecoder->GetReentrantMonitor().AssertCurrentThreadIn(); mShutdown = true; mInitializationPromise.RejectIfExists(NS_ERROR_ABORT, __func__); + mMetadataRequest.DisconnectIfExists(); MOZ_ASSERT(mShutdownPromise.IsEmpty()); nsRefPtr p = mShutdownPromise.Ensure(__func__); @@ -548,31 +550,56 @@ TrackBuffer::NewDecoder(int64_t aTimestampOffset) 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::value, + "SourceBufferDecoder must be inheriting from nsISupports"); RefPtr task = NS_NewRunnableMethodWithArg(this, &TrackBuffer::InitializeDecoder, aDecoder); - if (NS_FAILED(mTaskQueue->Dispatch(task))) { - MSE_DEBUG("failed to enqueue decoder initialization task"); - RemoveDecoder(aDecoder); - mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__); - return false; - } + // 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 mOwner; + nsRefPtr mDecoder; + bool mWasEnded; +}; + void TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder) { @@ -600,19 +627,16 @@ TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder) // 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 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 @@ -624,18 +648,13 @@ TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder) if (!wasEnded) { aDecoder->GetResource()->Ended(); } + nsRefPtr recipient = + new MetadataRecipient(this, aDecoder, wasEnded); + nsRefPtr 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 emptyBuffer = new MediaLargeByteBuffer; - aDecoder->GetResource()->AppendData(emptyBuffer); - } - // HACK END. - - reader->SetIdle(); if (mShutdown) { MSE_DEBUG("was shut down while reading metadata. Aborting initialization."); return; @@ -645,20 +664,43 @@ TrackBuffer::InitializeDecoder(SourceBufferDecoder* 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 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", @@ -681,9 +723,33 @@ TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder) } } +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; @@ -698,7 +764,6 @@ TrackBuffer::CompleteInitializeDecoder(SourceBufferDecoder* aDecoder) if (mShutdown) { MSE_DEBUG("was shut down. Aborting initialization."); - RemoveDecoder(aDecoder); return; } @@ -861,7 +926,16 @@ TrackBuffer::ResetParserState() void TrackBuffer::AbortAppendData() { + ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor()); + + nsRefPtr 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. diff --git a/dom/media/mediasource/TrackBuffer.h b/dom/media/mediasource/TrackBuffer.h index 647bb376fc..e8d16422cc 100644 --- a/dom/media/mediasource/TrackBuffer.h +++ b/dom/media/mediasource/TrackBuffer.h @@ -118,6 +118,7 @@ public: private: friend class DecodersToInitialize; + friend class MetadataRecipient; ~TrackBuffer(); // Create a new decoder, set mCurrentDecoder to the new decoder and @@ -163,6 +164,13 @@ private: // Remove all empty decoders from the provided list; void RemoveEmptyDecoders(nsTArray& aDecoders); + void OnMetadataRead(MetadataHolder* aMetadata, + SourceBufferDecoder* aDecoder, + bool aWasEnded); + + void OnMetadataNotRead(ReadMetadataFailureReason aReason, + SourceBufferDecoder* aDecoder); + nsAutoPtr mParser; // A task queue using the shared media thread pool. Used exclusively to @@ -213,7 +221,8 @@ private: bool mShutdown; MediaPromiseHolder mInitializationPromise; - + // Track our request for metadata from the reader. + MediaPromiseConsumerHolder mMetadataRequest; }; } // namespace mozilla