From 2dcecb573a0004224b2b5b77cd79b7550aaa6d47 Mon Sep 17 00:00:00 2001 From: Chris Pearce Date: Tue, 12 Jul 2011 15:39:23 +1200 Subject: [PATCH] Bug 592833 - Move metadata decoding to decode thread. r=roc --- content/media/nsBuiltinDecoder.cpp | 8 +- content/media/nsBuiltinDecoder.h | 2 +- content/media/nsBuiltinDecoderReader.cpp | 3 +- .../media/nsBuiltinDecoderStateMachine.cpp | 178 +++++++++--------- content/media/nsBuiltinDecoderStateMachine.h | 13 +- content/media/ogg/nsOggReader.cpp | 10 +- content/media/wave/nsWaveReader.cpp | 2 +- content/media/webm/nsWebMReader.cpp | 2 +- 8 files changed, 115 insertions(+), 103 deletions(-) diff --git a/content/media/nsBuiltinDecoder.cpp b/content/media/nsBuiltinDecoder.cpp index 7c8d066795b..c94017b23f6 100644 --- a/content/media/nsBuiltinDecoder.cpp +++ b/content/media/nsBuiltinDecoder.cpp @@ -871,7 +871,9 @@ void nsBuiltinDecoder::Resume(PRBool aForceBuffering) void nsBuiltinDecoder::StopProgressUpdates() { - NS_ASSERTION(IsCurrentThread(mStateMachineThread), "Should be on state machine thread."); + NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(), + "Should be on state machine or decode thread."); + GetReentrantMonitor().AssertCurrentThreadIn(); mIgnoreProgressData = PR_TRUE; if (mStream) { mStream->SetReadMode(nsMediaCacheStream::MODE_METADATA); @@ -880,7 +882,9 @@ void nsBuiltinDecoder::StopProgressUpdates() void nsBuiltinDecoder::StartProgressUpdates() { - NS_ASSERTION(IsCurrentThread(mStateMachineThread), "Should be on state machine thread."); + NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(), + "Should be on state machine or decode thread."); + GetReentrantMonitor().AssertCurrentThreadIn(); mIgnoreProgressData = PR_FALSE; if (mStream) { mStream->SetReadMode(nsMediaCacheStream::MODE_PLAYBACK); diff --git a/content/media/nsBuiltinDecoder.h b/content/media/nsBuiltinDecoder.h index ea702c12c9a..449839c85be 100644 --- a/content/media/nsBuiltinDecoder.h +++ b/content/media/nsBuiltinDecoder.h @@ -435,7 +435,7 @@ class nsBuiltinDecoder : public nsMediaDecoder // has changed. void DurationChanged(); - PRBool OnStateMachineThread() { + PRBool OnStateMachineThread() const { return IsCurrentThread(mStateMachineThread); } diff --git a/content/media/nsBuiltinDecoderReader.cpp b/content/media/nsBuiltinDecoderReader.cpp index f749a77a492..c26e5edfe03 100644 --- a/content/media/nsBuiltinDecoderReader.cpp +++ b/content/media/nsBuiltinDecoderReader.cpp @@ -214,7 +214,8 @@ nsresult nsBuiltinDecoderReader::ResetDecode() VideoData* nsBuiltinDecoderReader::FindStartTime(PRInt64& aOutStartTime) { - NS_ASSERTION(mDecoder->OnStateMachineThread(), "Should be on state machine thread."); + NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(), + "Should be on state machine or decode thread."); // Extract the start times of the bitstreams in order to calculate // the duration. diff --git a/content/media/nsBuiltinDecoderStateMachine.cpp b/content/media/nsBuiltinDecoderStateMachine.cpp index 2a778509edb..50d5f550950 100644 --- a/content/media/nsBuiltinDecoderStateMachine.cpp +++ b/content/media/nsBuiltinDecoderStateMachine.cpp @@ -270,10 +270,16 @@ void nsBuiltinDecoderStateMachine::DecodeLoop() ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); - PRBool videoPlaying = HasVideo(); - PRBool audioPlaying = HasAudio(); + if (mState == DECODER_STATE_DECODING_METADATA) { + if (NS_FAILED(DecodeMetadata())) { + LOG(PR_LOG_DEBUG, ("Decode metadata failed, shutting down decode thread")); + } + mDecoder->GetReentrantMonitor().NotifyAll(); + } // Main decode loop. + PRBool videoPlaying = HasVideo(); + PRBool audioPlaying = HasAudio(); while (mState != DECODER_STATE_SHUTDOWN && !mStopDecodeThread && (videoPlaying || audioPlaying)) @@ -794,7 +800,7 @@ void nsBuiltinDecoderStateMachine::SetVolume(double volume) double nsBuiltinDecoderStateMachine::GetCurrentTime() const { NS_ASSERTION(NS_IsMainThread() || - mDecoder->OnStateMachineThread() || + OnStateMachineThread() || OnDecodeThread(), "Should be on main, decode, or state machine thread."); @@ -812,8 +818,8 @@ PRInt64 nsBuiltinDecoderStateMachine::GetDuration() void nsBuiltinDecoderStateMachine::SetDuration(PRInt64 aDuration) { - NS_ASSERTION(NS_IsMainThread() || mDecoder->OnStateMachineThread(), - "Should be on main or state machine thread."); + NS_ASSERTION(NS_IsMainThread() || OnDecodeThread(), + "Should be on main or decode thread."); mDecoder->GetReentrantMonitor().AssertCurrentThreadIn(); if (aDuration == -1) { @@ -830,7 +836,7 @@ void nsBuiltinDecoderStateMachine::SetDuration(PRInt64 aDuration) void nsBuiltinDecoderStateMachine::SetEndTime(PRInt64 aEndTime) { - NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread"); + NS_ASSERTION(OnDecodeThread(), "Should be on decode thread"); mDecoder->GetReentrantMonitor().AssertCurrentThreadIn(); mEndTime = aEndTime; @@ -860,8 +866,8 @@ void nsBuiltinDecoderStateMachine::Shutdown() void nsBuiltinDecoderStateMachine::StartDecoding() { - NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), - "Should be on state machine thread."); + NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(), + "Should be on state machine or decode thread."); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); if (mState != DECODER_STATE_DECODING) { mDecodeStartTime = TimeStamp::Now(); @@ -1069,6 +1075,72 @@ void nsBuiltinDecoderStateMachine::SetFrameBufferLength(PRUint32 aLength) mEventManager.SetSignalBufferLength(aLength); } +nsresult nsBuiltinDecoderStateMachine::DecodeMetadata() +{ + NS_ASSERTION(OnDecodeThread(), "Should be on decode thread."); + mDecoder->GetReentrantMonitor().AssertCurrentThreadIn(); + + LOG(PR_LOG_DEBUG, ("%p Decoding Media Headers", mDecoder)); + nsresult res; + nsVideoInfo info; + { + ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); + res = mReader->ReadMetadata(&info); + } + mInfo = info; + + if (NS_FAILED(res) || (!info.mHasVideo && !info.mHasAudio)) { + mState = DECODER_STATE_SHUTDOWN; + nsCOMPtr event = + NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::DecodeError); + NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); + return NS_ERROR_FAILURE; + } + mDecoder->StartProgressUpdates(); + mGotDurationFromMetaData = (GetDuration() != -1); + + VideoData* videoData = FindStartTime(); + if (videoData) { + ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); + RenderVideoFrame(videoData, TimeStamp::Now()); + } + + if (mState == DECODER_STATE_SHUTDOWN) { + return NS_ERROR_FAILURE; + } + + NS_ASSERTION(mStartTime != -1, "Must have start time"); + NS_ASSERTION((!HasVideo() && !HasAudio()) || + !mSeekable || mEndTime != -1, + "Active seekable media should have end time"); + NS_ASSERTION(!mSeekable || GetDuration() != -1, "Seekable media should have duration"); + LOG(PR_LOG_DEBUG, ("%p Media goes from %lld to %lld (duration %lld) seekable=%d", + mDecoder, mStartTime, mEndTime, GetDuration(), mSeekable)); + + // Inform the element that we've loaded the metadata and the first frame, + // setting the default framebuffer size for audioavailable events. Also, + // if there is audio, let the MozAudioAvailable event manager know about + // the metadata. + if (HasAudio()) { + mEventManager.Init(mInfo.mAudioChannels, mInfo.mAudioRate); + // Set the buffer length at the decoder level to be able, to be able + // to retrive the value via media element method. The RequestFrameBufferLength + // will call the nsBuiltinDecoderStateMachine::SetFrameBufferLength(). + PRUint32 frameBufferLength = mInfo.mAudioChannels * FRAMEBUFFER_LENGTH_PER_CHANNEL; + mDecoder->RequestFrameBufferLength(frameBufferLength); + } + nsCOMPtr metadataLoadedEvent = + new nsAudioMetadataEventRunner(mDecoder, mInfo.mAudioChannels, mInfo.mAudioRate); + NS_DispatchToMainThread(metadataLoadedEvent, NS_DISPATCH_NORMAL); + + if (mState == DECODER_STATE_DECODING_METADATA) { + LOG(PR_LOG_DEBUG, ("%p Changed state from DECODING_METADATA to DECODING", mDecoder)); + StartDecoding(); + } + + return NS_OK; +} + nsresult nsBuiltinDecoderStateMachine::Run() { NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), @@ -1091,62 +1163,23 @@ nsresult nsBuiltinDecoderStateMachine::Run() case DECODER_STATE_DECODING_METADATA: { - LoadMetadata(); - if (mState == DECODER_STATE_SHUTDOWN) { - continue; - } - - VideoData* videoData = FindStartTime(); - if (videoData) { - ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); - RenderVideoFrame(videoData, TimeStamp::Now()); - } - - // Start the decode threads, so that we can pre buffer the streams. - // and calculate the start time in order to determine the duration. + // Start the decode threads, so that metadata decoding begins. if (NS_FAILED(StartDecodeThread())) { continue; } - NS_ASSERTION(mStartTime != -1, "Must have start time"); - NS_ASSERTION((!HasVideo() && !HasAudio()) || - !mSeekable || mEndTime != -1, - "Active seekable media should have end time"); - NS_ASSERTION(!mSeekable || GetDuration() != -1, "Seekable media should have duration"); - LOG(PR_LOG_DEBUG, ("%p Media goes from %lld to %lld (duration %lld) seekable=%d", - mDecoder, mStartTime, mEndTime, GetDuration(), mSeekable)); - - if (mState == DECODER_STATE_SHUTDOWN) - continue; - - // Inform the element that we've loaded the metadata and the first frame, - // setting the default framebuffer size for audioavailable events. Also, - // if there is audio, let the MozAudioAvailable event manager know about - // the metadata. - if (HasAudio()) { - mEventManager.Init(mInfo.mAudioChannels, mInfo.mAudioRate); - // Set the buffer length at the decoder level to be able, to be able - // to retrive the value via media element method. The RequestFrameBufferLength - // will call the nsBuiltinDecoderStateMachine::SetFrameBufferLength(). - PRUint32 frameBufferLength = mInfo.mAudioChannels * FRAMEBUFFER_LENGTH_PER_CHANNEL; - mDecoder->RequestFrameBufferLength(frameBufferLength); - } - nsCOMPtr metadataLoadedEvent = - new nsAudioMetadataEventRunner(mDecoder, mInfo.mAudioChannels, mInfo.mAudioRate); - NS_DispatchToMainThread(metadataLoadedEvent, NS_DISPATCH_NORMAL); - - if (mState == DECODER_STATE_DECODING_METADATA) { - LOG(PR_LOG_DEBUG, ("%p Changed state from DECODING_METADATA to DECODING", mDecoder)); - StartDecoding(); + while (mState == DECODER_STATE_DECODING_METADATA) { + mon.Wait(); } - // Start playback. - if (mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING) { - if (!IsPlaying()) { - StartPlayback(); - StartAudioThread(); - } + if ((mState == DECODER_STATE_DECODING || mState == DECODER_STATE_COMPLETED) && + mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING && + !IsPlaying()) + { + StartPlayback(); + StartAudioThread(); } + } break; @@ -1388,7 +1421,8 @@ nsresult nsBuiltinDecoderStateMachine::Run() void nsBuiltinDecoderStateMachine::RenderVideoFrame(VideoData* aData, TimeStamp aTarget) { - NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread."); + NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(), + "Should be on state machine or decode thread."); mDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn(); if (aData->mDuplicate) { @@ -1577,7 +1611,7 @@ void nsBuiltinDecoderStateMachine::Wait(PRInt64 aUsecs) { VideoData* nsBuiltinDecoderStateMachine::FindStartTime() { - NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread."); + NS_ASSERTION(OnDecodeThread(), "Should be on decode thread."); mDecoder->GetReentrantMonitor().AssertCurrentThreadIn(); PRInt64 startTime = 0; mStartTime = 0; @@ -1626,32 +1660,6 @@ void nsBuiltinDecoderStateMachine::UpdateReadyState() { NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); } -void nsBuiltinDecoderStateMachine::LoadMetadata() -{ - NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), - "Should be on state machine thread."); - mDecoder->GetReentrantMonitor().AssertCurrentThreadIn(); - - LOG(PR_LOG_DEBUG, ("%p Loading Media Headers", mDecoder)); - nsresult res; - nsVideoInfo info; - { - ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); - res = mReader->ReadMetadata(&info); - } - mInfo = info; - - if (NS_FAILED(res) || (!info.mHasVideo && !info.mHasAudio)) { - mState = DECODER_STATE_SHUTDOWN; - nsCOMPtr event = - NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::DecodeError); - NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); - return; - } - mDecoder->StartProgressUpdates(); - mGotDurationFromMetaData = (GetDuration() != -1); -} - PRBool nsBuiltinDecoderStateMachine::JustExitedQuickBuffering() { return !mDecodeStartTime.IsNull() && diff --git a/content/media/nsBuiltinDecoderStateMachine.h b/content/media/nsBuiltinDecoderStateMachine.h index 738f9828b18..f095d067a40 100644 --- a/content/media/nsBuiltinDecoderStateMachine.h +++ b/content/media/nsBuiltinDecoderStateMachine.h @@ -172,11 +172,6 @@ public: virtual void UpdatePlaybackPosition(PRInt64 aTime); virtual void StartBuffering(); - - // Load metadata Called on the state machine thread. The decoder monitor must be held with - // exactly one lock count. - virtual void LoadMetadata(); - // State machine thread run function. Polls the state, sends frames to be // displayed at appropriate times, and generally manages the decode. NS_IMETHOD Run(); @@ -214,11 +209,11 @@ public: // Functions used by assertions to ensure we're calling things // on the appropriate threads. - PRBool OnAudioThread() { + PRBool OnAudioThread() const { return IsCurrentThread(mAudioThread); } - PRBool OnStateMachineThread() { + PRBool OnStateMachineThread() const { return mDecoder->OnStateMachineThread(); } @@ -411,6 +406,10 @@ protected: // must be held when calling this. Called on the decoder thread. PRInt64 GetDecodedAudioDuration(); + // Load metadata. Called on the decode thread. The decoder monitor + // must be held with exactly one lock count. + nsresult DecodeMetadata(); + // ReentrantMonitor on mAudioStream. This monitor must be held in // order to delete or use the audio stream. This stops us destroying // the audio stream while it's being used on another thread diff --git a/content/media/ogg/nsOggReader.cpp b/content/media/ogg/nsOggReader.cpp index 7a067374a78..56201dd3c97 100644 --- a/content/media/ogg/nsOggReader.cpp +++ b/content/media/ogg/nsOggReader.cpp @@ -178,7 +178,7 @@ PRBool nsOggReader::ReadHeaders(nsOggCodecState* aState) nsresult nsOggReader::ReadMetadata(nsVideoInfo* aInfo) { - NS_ASSERTION(mDecoder->OnStateMachineThread(), "Should be on play state machine thread."); + NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread."); ReentrantMonitorAutoEnter mon(mReentrantMonitor); // We read packets until all bitstreams have read all their header packets. @@ -642,8 +642,8 @@ GetChecksum(ogg_page* page) PRInt64 nsOggReader::RangeStartTime(PRInt64 aOffset) { - NS_ASSERTION(mDecoder->OnStateMachineThread(), - "Should be on state machine thread."); + NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(), + "Should be on state machine or decode thread."); nsMediaStream* stream = mDecoder->GetCurrentStream(); NS_ENSURE_TRUE(stream != nsnull, nsnull); nsresult res = stream->Seek(nsISeekableStream::NS_SEEK_SET, aOffset); @@ -666,8 +666,8 @@ struct nsAutoOggSyncState { PRInt64 nsOggReader::RangeEndTime(PRInt64 aEndOffset) { ReentrantMonitorAutoEnter mon(mReentrantMonitor); - NS_ASSERTION(mDecoder->OnStateMachineThread(), - "Should be on state machine thread."); + NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(), + "Should be on state machine or decode thread."); nsMediaStream* stream = mDecoder->GetCurrentStream(); NS_ENSURE_TRUE(stream != nsnull, -1); diff --git a/content/media/wave/nsWaveReader.cpp b/content/media/wave/nsWaveReader.cpp index d983abe73b4..a9f02b9fab7 100644 --- a/content/media/wave/nsWaveReader.cpp +++ b/content/media/wave/nsWaveReader.cpp @@ -152,7 +152,7 @@ nsresult nsWaveReader::Init(nsBuiltinDecoderReader* aCloneDonor) nsresult nsWaveReader::ReadMetadata(nsVideoInfo* aInfo) { - NS_ASSERTION(mDecoder->OnStateMachineThread(), "Should be on state machine thread."); + NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread."); ReentrantMonitorAutoEnter mon(mReentrantMonitor); PRBool loaded = LoadRIFFChunk() && LoadFormatChunk() && FindDataOffset(); diff --git a/content/media/webm/nsWebMReader.cpp b/content/media/webm/nsWebMReader.cpp index 631db80864e..601706666ec 100644 --- a/content/media/webm/nsWebMReader.cpp +++ b/content/media/webm/nsWebMReader.cpp @@ -209,7 +209,7 @@ void nsWebMReader::Cleanup() nsresult nsWebMReader::ReadMetadata(nsVideoInfo* aInfo) { - NS_ASSERTION(mDecoder->OnStateMachineThread(), "Should be on state machine thread."); + NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on state machine thread."); ReentrantMonitorAutoEnter mon(mReentrantMonitor); nestegg_io io;