зеркало из https://github.com/mozilla/pjs.git
Bug 462878. Ensure that nsMediaStream::Close is only called on the main thread, and prevent reentrant Stop calls. r+sr=roc,a=beltzner
This commit is contained in:
Родитель
92008e034e
Коммит
0a768b1b83
|
@ -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<nsChannelReader> 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.
|
||||
// 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
|
||||
|
|
|
@ -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<nsChannelReader*>(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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<nsIRunnable> 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) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче