diff --git a/content/media/video/public/nsOggDecoder.h b/content/media/video/public/nsOggDecoder.h index da789f136e0..074207911b4 100644 --- a/content/media/video/public/nsOggDecoder.h +++ b/content/media/video/public/nsOggDecoder.h @@ -333,7 +333,7 @@ class nsOggDecoder : public nsMediaDecoder // Called if the media file encounters a network error. // Call on the main thread only. - void NetworkError(); + virtual void NetworkError(); // Call from any thread safely. Return PR_TRUE if we are currently // seeking in the media resource. @@ -348,21 +348,20 @@ class nsOggDecoder : public nsMediaDecoder // Return PR_TRUE if seeking is supported. virtual PRBool GetSeekable(); + // Returns the channel reader. + nsChannelReader* GetReader() { return mReader; } + protected: - // Change to a new play state. This updates the mState variable and - // notifies any thread blocking on this objects monitor of the - // change. Can be called on any thread. - void ChangeState(PlayState aState); // Returns the monitor for other threads to synchronise access to - // state + // state. PRMonitor* GetMonitor() { return mMonitor; } - // Return the current state. The caller must have entered the - // monitor. + // Return the current state. Can be called on any thread. If called from + // a non-main thread, the decoder monitor must be held. PlayState GetState() { return mPlayState; @@ -373,6 +372,11 @@ protected: * thread. ******/ + // Change to a new play state. This updates the mState variable and + // notifies any thread blocking on this object's monitor of the + // change. Call on the main thread only. + void ChangeState(PlayState aState); + // Called when the metadata from the Ogg file has been read. // Call on the main thread only. void MetadataLoaded(); @@ -486,7 +490,7 @@ private: // the decoder thread, and the state machine for that thread keeps // a pointer to this reader. This is safe as the only methods called // are threadsafe (via the threadsafe nsMediaStream). - nsChannelReader* mReader; + nsAutoPtr mReader; // Monitor for detecting when the video play state changes. A call // to Wait on this monitor will block the thread until the next @@ -499,11 +503,16 @@ private: // must call NotifyAll on the monitor so the decode thread can wake up. PlayState mPlayState; - // The state to change to after a seek or load operation. It is - // protected by the monitor mMonitor. This monitor must be acquired - // when reading or writing the state. Any change to the state must - // call NotifyAll on the monitor. - PlayState mNextState; + // The state to change to after a seek or load operation. It must only + // be changed from the main thread. The decoder monitor must be acquired + // when writing to the state, or when reading from a non-main thread. + // Any change to the state must call NotifyAll on the monitor. + PlayState mNextState; + + // Flags if we've called Stop(). Prevents multiple events being + // sent to call Shutdown(). Accessed on the main thread + // only. + PRPackedBool mIsStopping; }; #endif diff --git a/content/media/video/src/nsChannelReader.cpp b/content/media/video/src/nsChannelReader.cpp index b910d422d4c..2724df8b63b 100644 --- a/content/media/video/src/nsChannelReader.cpp +++ b/content/media/video/src/nsChannelReader.cpp @@ -71,7 +71,6 @@ OggPlayErrorCode nsChannelReader::initialise(int aBlock) OggPlayErrorCode nsChannelReader::destroy() { mStream.Close(); - return E_OGGPLAY_OK; } @@ -113,9 +112,7 @@ static OggPlayErrorCode oggplay_channel_reader_initialise(OggPlayReader* aReader static OggPlayErrorCode oggplay_channel_reader_destroy(OggPlayReader* aReader) { nsChannelReader* me = static_cast(aReader); - OggPlayErrorCode result = me->destroy(); - delete me; - return result; + return me->destroy(); } static size_t oggplay_channel_reader_io_read(void* aReader, void* aBuffer, size_t aCount) diff --git a/content/media/video/src/nsMediaStream.cpp b/content/media/video/src/nsMediaStream.cpp index 593cc3ce961..af56d251b17 100644 --- a/content/media/video/src/nsMediaStream.cpp +++ b/content/media/video/src/nsMediaStream.cpp @@ -128,13 +128,12 @@ nsresult nsDefaultStreamStrategy::Close() if (mChannel) { mChannel->Cancel(NS_BINDING_ABORTED); mChannel = nsnull; - + } + if (mPipeInput) { mPipeInput->Close(); mPipeInput = nsnull; - - mListener = nsnull; } - + mListener = nsnull; return NS_OK; } @@ -466,13 +465,12 @@ nsresult nsHttpStreamStrategy::Close() if (mChannel) { mChannel->Cancel(NS_BINDING_ABORTED); mChannel = nsnull; - + } + if (mPipeInput) { mPipeInput->Close(); mPipeInput = nsnull; - - mListener = nsnull; } - + mListener = nsnull; return NS_OK; } diff --git a/content/media/video/src/nsOggDecoder.cpp b/content/media/video/src/nsOggDecoder.cpp index c3a5f0e79fd..a26c2ff7da9 100644 --- a/content/media/video/src/nsOggDecoder.cpp +++ b/content/media/video/src/nsOggDecoder.cpp @@ -215,7 +215,7 @@ public: DECODER_STATE_SHUTDOWN }; - nsOggDecodeStateMachine(nsOggDecoder* aDecoder, nsChannelReader* aReader); + nsOggDecodeStateMachine(nsOggDecoder* aDecoder); ~nsOggDecodeStateMachine(); // Cause state transitions. These methods obtain the decoder monitor @@ -389,13 +389,6 @@ private: PRInt32 mAudioChannels; PRInt32 mAudioTrack; - // Channel Reader. Originally created by the mDecoder object, it is - // destroyed when we close the mPlayer handle in the - // destructor. Used to obtain download and playback rate information - // for buffering. Synchronisation for those methods are handled by - // nsMediaStream. - nsChannelReader* mReader; - // Time that buffering started. Used for buffering timeout and only // accessed in the decoder thread. PRIntervalTime mBufferingStart; @@ -461,7 +454,7 @@ private: PRPackedBool mPositionChangeQueued; }; -nsOggDecodeStateMachine::nsOggDecodeStateMachine(nsOggDecoder* aDecoder, nsChannelReader* aReader) : +nsOggDecodeStateMachine::nsOggDecodeStateMachine(nsOggDecoder* aDecoder) : mDecoder(aDecoder), mPlayer(0), mPlayStartTime(0), @@ -474,7 +467,6 @@ nsOggDecodeStateMachine::nsOggDecodeStateMachine(nsOggDecoder* aDecoder, nsChann mAudioRate(0), mAudioChannels(0), mAudioTrack(-1), - mReader(aReader), mBufferingStart(0), mBufferingBytes(0), mLastFrameTime(0), @@ -494,11 +486,9 @@ nsOggDecodeStateMachine::~nsOggDecodeStateMachine() while (!mDecodedFrames.IsEmpty()) { delete mDecodedFrames.Pop(); } - oggplay_close(mPlayer); } - OggPlayErrorCode nsOggDecodeStateMachine::DecodeFrame() { NS_ASSERTION(mState > DECODER_STATE_DECODING_METADATA, "DecodeFrame() called during invalid state"); @@ -853,6 +843,8 @@ void nsOggDecodeStateMachine::Seek(float aTime) nsresult nsOggDecodeStateMachine::Run() { + nsChannelReader* reader = mDecoder->GetReader(); + NS_ENSURE_TRUE(reader, NS_ERROR_NULL_POINTER); while (PR_TRUE) { nsAutoMonitor mon(mDecoder->GetMonitor()); switch(mState) { @@ -902,8 +894,8 @@ nsresult nsOggDecodeStateMachine::Run() case DECODER_STATE_DECODING: { // Before decoding check if we should buffer more data - if (mReader->DownloadRate() >= 0 && - mReader->Available() < mReader->PlaybackRate() * BUFFERING_SECONDS_LOW_WATER_MARK) { + if (reader->DownloadRate() >= 0 && + reader->Available() < reader->PlaybackRate() * BUFFERING_SECONDS_LOW_WATER_MARK) { if (mDecoder->GetState() == nsOggDecoder::PLAY_STATE_PLAYING) { if (mPlaying) { StopPlayback(); @@ -911,7 +903,7 @@ nsresult nsOggDecodeStateMachine::Run() } mBufferingStart = PR_IntervalNow(); - mBufferingBytes = PRUint32(BUFFERING_RATE(mReader->PlaybackRate()) * BUFFERING_WAIT); + mBufferingBytes = PRUint32(BUFFERING_RATE(reader->PlaybackRate()) * BUFFERING_WAIT); mState = DECODER_STATE_BUFFERING; nsCOMPtr event = @@ -1009,11 +1001,11 @@ nsresult nsOggDecodeStateMachine::Run() case DECODER_STATE_BUFFERING: if ((PR_IntervalToMilliseconds(PR_IntervalNow() - mBufferingStart) < BUFFERING_WAIT*1000) && - mReader->DownloadRate() >= 0 && - mReader->Available() < mBufferingBytes) { + reader->DownloadRate() >= 0 && + reader->Available() < mBufferingBytes) { LOG(PR_LOG_DEBUG, ("Buffering data until %d bytes available or %d milliseconds", - mBufferingBytes - mReader->Available(), + mBufferingBytes - reader->Available(), BUFFERING_WAIT*1000 - (PR_IntervalToMilliseconds(PR_IntervalNow() - mBufferingStart)))); mon.Wait(PR_MillisecondsToInterval(1000)); if (mState == DECODER_STATE_SHUTDOWN) @@ -1069,7 +1061,7 @@ nsresult nsOggDecodeStateMachine::Run() void nsOggDecodeStateMachine::LoadOggHeaders() { LOG(PR_LOG_DEBUG, ("Loading Ogg Headers")); - mPlayer = oggplay_open_with_reader(mReader); + mPlayer = oggplay_open_with_reader(mDecoder->GetReader()); if (mPlayer) { LOG(PR_LOG_DEBUG, ("There are %d tracks", oggplay_get_num_tracks(mPlayer))); @@ -1184,7 +1176,8 @@ nsOggDecoder::nsOggDecoder() : mReader(0), mMonitor(0), mPlayState(PLAY_STATE_PAUSED), - mNextState(PLAY_STATE_PAUSED) + mNextState(PLAY_STATE_PAUSED), + mIsStopping(PR_FALSE) { MOZ_COUNT_CTOR(nsOggDecoder); } @@ -1239,6 +1232,10 @@ nsOggDecoder::~nsOggDecoder() nsresult nsOggDecoder::Load(nsIURI* aURI, nsIChannel* aChannel, nsIStreamListener** aStreamListener) { + // Reset Stop guard flag flag, else shutdown won't occur properly when + // reusing decoder. + mIsStopping = PR_FALSE; + if (aStreamListener) { *aStreamListener = nsnull; } @@ -1270,7 +1267,7 @@ nsresult nsOggDecoder::Load(nsIURI* aURI, nsIChannel* aChannel, rv = NS_NewThread(getter_AddRefs(mDecodeThread)); NS_ENSURE_SUCCESS(rv, rv); - mDecodeStateMachine = new nsOggDecodeStateMachine(this, mReader); + mDecodeStateMachine = new nsOggDecodeStateMachine(this); { nsAutoMonitor mon(mMonitor); mDecodeStateMachine->SetContentLength(mContentLength); @@ -1326,6 +1323,13 @@ nsresult nsOggDecoder::PlaybackRateChanged() void nsOggDecoder::Stop() { + NS_ASSERTION(NS_IsMainThread(), + "nsOggDecoder::Stop called on non-main thread"); + + if (mIsStopping) + return; + mIsStopping = PR_TRUE; + ChangeState(PLAY_STATE_ENDED); StopProgress(); @@ -1334,7 +1338,6 @@ void nsOggDecoder::Stop() // to prevent shutdown from deadlocking. if (mReader) { mReader->Cancel(); - mReader = nsnull; } // Shutdown must be on called the mDecodeStateMachine before deleting. @@ -1353,6 +1356,7 @@ void nsOggDecoder::Stop() } mDecodeStateMachine = nsnull; + mReader = nsnull; UnregisterShutdownObserver(); } @@ -1549,6 +1553,8 @@ void nsOggDecoder::UnregisterShutdownObserver() void nsOggDecoder::ChangeState(PlayState aState) { + NS_ASSERTION(NS_IsMainThread(), + "nsOggDecoder::ChangeState called on non-main thread"); nsAutoMonitor mon(mMonitor); if (mNextState == aState) {