diff --git a/content/media/video/public/nsOggDecoder.h b/content/media/video/public/nsOggDecoder.h index da789f136e01..7dc57469b559 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,11 @@ 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; }; #endif diff --git a/content/media/video/src/nsChannelReader.cpp b/content/media/video/src/nsChannelReader.cpp index b910d422d4c6..2724df8b63b8 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/nsOggDecoder.cpp b/content/media/video/src/nsOggDecoder.cpp index c3a5f0e79fdc..383cdcf0ff36 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))); @@ -1270,7 +1262,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 +1318,13 @@ nsresult nsOggDecoder::PlaybackRateChanged() void nsOggDecoder::Stop() { + NS_ASSERTION(NS_IsMainThread(), + "nsOggDecoder::Stop called on non-main thread"); + + if (mPlayState == PLAY_STATE_ENDED || + mPlayState == PLAY_STATE_SHUTDOWN) + return; + ChangeState(PLAY_STATE_ENDED); StopProgress(); @@ -1334,7 +1333,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 +1351,7 @@ void nsOggDecoder::Stop() } mDecodeStateMachine = nsnull; + mReader = nsnull; UnregisterShutdownObserver(); } @@ -1549,6 +1548,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) {