Bug 592833 - Backout due to suspected android crashtest permaorange. r=philor

This commit is contained in:
Chris Pearce 2011-07-06 19:21:49 +12:00
parent 560834288e
commit a24dd67388
17 changed files with 952 additions and 1238 deletions

View File

@ -1987,11 +1987,11 @@ void nsHTMLMediaElement::NetworkError()
void nsHTMLMediaElement::DecodeError()
{
if (mDecoder) {
mDecoder->Shutdown();
mDecoder = nsnull;
}
if (mIsLoadingFromSourceChildren) {
if (mDecoder) {
mDecoder->Shutdown();
mDecoder = nsnull;
}
mError = nsnull;
if (mSourceLoadCandidate) {
DispatchAsyncSourceError(mSourceLoadCandidate);

View File

@ -46,7 +46,6 @@
#include "nsTArray.h"
#include "VideoUtils.h"
#include "nsBuiltinDecoder.h"
#include "nsBuiltinDecoderStateMachine.h"
using namespace mozilla;
@ -123,6 +122,20 @@ PRBool nsBuiltinDecoder::Init(nsHTMLMediaElement* aElement)
return PR_TRUE;
}
void nsBuiltinDecoder::Stop()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread");
// The decode thread must die before the state machine can die.
// The state machine must die before the reader.
// The state machine must die before the decoder.
if (mStateMachineThread)
mStateMachineThread->Shutdown();
mStateMachineThread = nsnull;
mDecoderStateMachine = nsnull;
}
void nsBuiltinDecoder::Shutdown()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
@ -148,6 +161,17 @@ void nsBuiltinDecoder::Shutdown()
ChangeState(PLAY_STATE_SHUTDOWN);
nsMediaDecoder::Shutdown();
// We can't destroy mDecoderStateMachine until mStateMachineThread is shut down.
// It's unsafe to Shutdown() the decode thread here, as
// nsIThread::Shutdown() may run events, such as JS event handlers,
// and we could be running at an unsafe time such as during element
// destruction.
// So we destroy the decoder on the main thread in an asynchronous event.
// See bug 468721.
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(this, &nsBuiltinDecoder::Stop);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
nsContentUtils::UnregisterShutdownObserver(this);
}
@ -159,8 +183,8 @@ nsBuiltinDecoder::~nsBuiltinDecoder()
}
nsresult nsBuiltinDecoder::Load(nsMediaStream* aStream,
nsIStreamListener** aStreamListener,
nsMediaDecoder* aCloneDonor)
nsIStreamListener** aStreamListener,
nsMediaDecoder* aCloneDonor)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
if (aStreamListener) {
@ -205,7 +229,7 @@ nsresult nsBuiltinDecoder::Load(nsMediaStream* aStream,
ChangeState(PLAY_STATE_LOADING);
return ScheduleStateMachineThread();
return StartStateMachineThread();
}
nsresult nsBuiltinDecoder::RequestFrameBufferLength(PRUint32 aLength)
@ -220,25 +244,23 @@ nsresult nsBuiltinDecoder::RequestFrameBufferLength(PRUint32 aLength)
return res;
}
nsresult nsBuiltinDecoder::ScheduleStateMachineThread()
nsresult nsBuiltinDecoder::StartStateMachineThread()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
NS_ASSERTION(mDecoderStateMachine,
"Must have state machine to start state machine thread");
if (mShuttingDown)
if (mStateMachineThread) {
return NS_OK;
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
nsBuiltinDecoderStateMachine* m =
static_cast<nsBuiltinDecoderStateMachine*>(mDecoderStateMachine.get());
return m->ScheduleStateMachine();
}
nsresult rv = NS_NewThread(getter_AddRefs(mStateMachineThread));
NS_ENSURE_SUCCESS(rv, rv);
return mStateMachineThread->Dispatch(mDecoderStateMachine, NS_DISPATCH_NORMAL);
}
nsresult nsBuiltinDecoder::Play()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
nsresult res = ScheduleStateMachineThread();
nsresult res = StartStateMachineThread();
NS_ENSURE_SUCCESS(res,res);
if (mPlayState == PLAY_STATE_SEEKING) {
mNextState = PLAY_STATE_PLAYING;
@ -276,7 +298,7 @@ nsresult nsBuiltinDecoder::Seek(double aTime)
ChangeState(PLAY_STATE_SEEKING);
}
return ScheduleStateMachineThread();
return StartStateMachineThread();
}
nsresult nsBuiltinDecoder::PlaybackRateChanged()
@ -510,7 +532,7 @@ nsBuiltinDecoder::GetStatistics()
double nsBuiltinDecoder::ComputePlaybackRate(PRPackedBool* aReliable)
{
GetReentrantMonitor().AssertCurrentThreadIn();
NS_ASSERTION(NS_IsMainThread() || OnStateMachineThread(),
NS_ASSERTION(NS_IsMainThread() || IsCurrentThread(mStateMachineThread),
"Should be on main or state machine thread.");
PRInt64 length = mStream ? mStream->GetLength() : -1;
@ -523,7 +545,7 @@ double nsBuiltinDecoder::ComputePlaybackRate(PRPackedBool* aReliable)
void nsBuiltinDecoder::UpdatePlaybackRate()
{
NS_ASSERTION(NS_IsMainThread() || OnStateMachineThread(),
NS_ASSERTION(NS_IsMainThread() || IsCurrentThread(mStateMachineThread),
"Should be on main or state machine thread.");
GetReentrantMonitor().AssertCurrentThreadIn();
if (!mStream)
@ -849,9 +871,7 @@ void nsBuiltinDecoder::Resume(PRBool aForceBuffering)
void nsBuiltinDecoder::StopProgressUpdates()
{
NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
"Should be on state machine or decode thread.");
GetReentrantMonitor().AssertCurrentThreadIn();
NS_ASSERTION(IsCurrentThread(mStateMachineThread), "Should be on state machine thread.");
mIgnoreProgressData = PR_TRUE;
if (mStream) {
mStream->SetReadMode(nsMediaCacheStream::MODE_METADATA);
@ -860,9 +880,7 @@ void nsBuiltinDecoder::StopProgressUpdates()
void nsBuiltinDecoder::StartProgressUpdates()
{
NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
"Should be on state machine or decode thread.");
GetReentrantMonitor().AssertCurrentThreadIn();
NS_ASSERTION(IsCurrentThread(mStateMachineThread), "Should be on state machine thread.");
mIgnoreProgressData = PR_FALSE;
if (mStream) {
mStream->SetReadMode(nsMediaCacheStream::MODE_PLAYBACK);
@ -883,7 +901,3 @@ void nsBuiltinDecoder::UpdatePlaybackOffset(PRInt64 aOffset)
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mPlaybackPosition = NS_MAX(aOffset, mPlaybackPosition);
}
PRBool nsBuiltinDecoder::OnStateMachineThread() const {
return IsCurrentThread(nsBuiltinDecoderStateMachine::GetStateMachineThread());
}

View File

@ -37,28 +37,24 @@
*
* ***** END LICENSE BLOCK ***** */
/*
Each video element based on nsBuiltinDecoder has a state machine to manage
its play state and keep the current frame up to date. All state machines
share time in a single shared thread. Each decoder also has one thread
dedicated to decoding audio and video data. This thread is shutdown when
playback is paused. Each decoder also has a thread to push decoded audio
to the hardware. This thread is not created until playback starts, but
currently is not destroyed when paused, only when playback ends.
Each video element based on nsBuiltinDecoder has at least one thread
dedicated to decoding video.
The decoder owns the resources for downloading the media file, and the
high level state. It holds an owning reference to the state machine
(a subclass of nsDecoderStateMachine; nsBuiltinDecoderStateMachine) that
owns all the resources related to decoding data, and manages the low level
decoding operations and A/V sync.
This thread (called the state machine thread owns the resources for
downloading and reading the media file. nsDecoderStateMachine is the
class that needs to be implemented and it gets run on the state
machine thread.
Each state machine runs on the shared state machine thread. Every time some
action is required for a state machine, it is scheduled to run on the shared
the state machine thread. The state machine runs one "cycle" on the state
machine thread, and then returns. If necessary, it will schedule itself to
run again in future. While running this cycle, it must not block the
thread, as other state machines' events may need to run. State shared
between a state machine's threads is synchronised via the monitor owned
by its nsBuiltinDecoder object.
The state machine thread has one event that is dispatched to it (the
implementation of nsDecoderStateMachine) and that event runs for the
lifetime of the playback of the resource. State shared between threads
is synchronised with the main thread via a monitor held by the
nsBuiltinDecoder object.
The state machine thread event consist of a Run method which is an
infinite loop that performs the decoding operation and checks the
state that the state machine is in and processes operations on that
state.
The Main thread controls the decode state machine by setting the value
of a mPlayState variable and notifying on the monitor based on the
@ -89,37 +85,25 @@ SHUTDOWN
State transition occurs when the Media Element calls the Play, Seek,
etc methods on the nsBuiltinDecoder object. When the transition
occurs nsBuiltinDecoder then calls the methods on the decoder state
machine object to cause it to behave as required by the play state.
State transitions will likely schedule the state machine to run to
affect the change.
machine object to cause it to behave appropriate to the play state.
An implementation of the nsDecoderStateMachine class is the event
that gets dispatched to the state machine thread. Each time the event is run,
the state machine must cycle the state machine once, and then return.
The state machine has the following states:
that gets dispatched to the state machine thread. It has the following states:
DECODING_METADATA
The media headers are being loaded, and things like framerate, etc are
being determined, and the first frame of audio/video data is being decoded.
DECODING
The decode has started. If the PlayState is PLAYING, the decode thread
should be alive and decoding video and audio frame, the audio thread
should be playing audio, and the state machine should run periodically
to update the video frames being displayed.
The decode and audio threads are started and video frames displayed at
the required time.
SEEKING
A seek operation is in progress. The decode thread should be seeking.
A seek operation is in progress.
BUFFERING
Decoding is paused while data is buffered for smooth playback. If playback
is paused (PlayState transitions to PAUSED) we'll destory the decode thread.
Decoding is paused while data is buffered for smooth playback.
COMPLETED
The resource has completed decoding, but possibly not finished playback.
The decode thread will be destroyed. Once playback finished, the audio
thread will also be destroyed.
The resource has completed decoding, but not finished playback.
SHUTDOWN
The decoder object and its state machine are about to be destroyed.
Once the last state machine has been destroyed, the shared state machine
thread will also be destroyed. It will be recreated later if needed.
The decoder object is about to be destroyed.
The following result in state transitions.
@ -173,41 +157,41 @@ player SHUTDOWN decoder SHUTDOWN
The general sequence of events is:
1) The video element calls Load on nsMediaDecoder. This creates the
state machine and starts the channel for downloading the
file. It instantiates and schedules the nsDecoderStateMachine. The
state machine thread and starts the channel for downloading the
file. It instantiates and starts the nsDecoderStateMachine. The
high level LOADING state is entered, which results in the decode
thread being created and starting to decode metadata. These are
the headers that give the video size, framerate, etc. Load() returns
immediately to the calling video element.
state machine to start decoding metadata. These are the headers
that give the video size, framerate, etc. It returns immediately
to the calling video element.
2) When the metadata has been loaded by the decode thread, the state machine
will call a method on the video element object to inform it that this
step is done, so it can do the things required by the video specification
at this stage. The decode thread then continues to decode the first frame
2) When the metadata has been loaded by the decode thread it will call
a method on the video element object to inform it that this step is
done, so it can do the things required by the video specification
at this stage. The decoder then continues to decode the first frame
of data.
3) When the first frame of data has been successfully decoded the state
machine calls a method on the video element object to inform it that
this step has been done, once again so it can do the required things
by the video specification at this stage.
3) When the first frame of data has been successfully decoded it calls
a method on the video element object to inform it that this step
has been done, once again so it can do the required things by the
video specification at this stage.
This results in the high level state changing to PLAYING or PAUSED
depending on any user action that may have occurred.
While the play state is PLAYING, the decode thread will decode
data, and the audio thread will push audio data to the hardware to
be played. The state machine will run periodically on the shared
state machine thread to ensure video frames are played at the
correct time; i.e. the state machine manages A/V sync.
The decode thread plays audio and video, if the correct frame time
comes around and the decoder play state is PLAYING.
a/v synchronisation is handled by the nsDecoderStateMachine implementation.
The Shutdown method on nsBuiltinDecoder closes the download channel, and
signals to the state machine that it should shutdown. The state machine
shuts down asynchronously, and will release the owning reference to the
state machine once its threads are shutdown.
The owning object of a nsBuiltinDecoder object *MUST* call Shutdown when
destroying the nsBuiltinDecoder object.
The Shutdown method on nsBuiltinDecoder can spin the event loop as it
waits for threads to complete. Spinning the event loop is a bad thing
to happen during certain times like destruction of the media
element. To work around this the Shutdown method does nothing but
queue an event to the main thread to perform the actual Shutdown. This
way the shutdown can occur at a safe time.
This means the owning object of a nsBuiltinDecoder object *MUST* call
Shutdown when destroying the nsBuiltinDecoder object.
*/
#if !defined(nsBuiltinDecoder_h_)
#define nsBuiltinDecoder_h_
@ -285,9 +269,6 @@ public:
// on the appropriate threads.
virtual PRBool OnDecodeThread() const = 0;
// Returns PR_TRUE if the current thread is the state machine thread.
virtual PRBool OnStateMachineThread() const = 0;
virtual nsHTMLMediaElement::NextFrameStatus GetNextFrameStatus() = 0;
// Cause state transitions. These methods obtain the decoder monitor
@ -444,13 +425,19 @@ class nsBuiltinDecoder : public nsMediaDecoder
// Tells our nsMediaStream to put all loads in the background.
virtual void MoveLoadsToBackground();
// Stop the state machine thread and drop references to the thread and
// state machine.
void Stop();
void AudioAvailable(float* aFrameBuffer, PRUint32 aFrameBufferLength, float aTime);
// Called by the state machine to notify the decoder that the duration
// has changed.
void DurationChanged();
PRBool OnStateMachineThread() const;
PRBool OnStateMachineThread() {
return IsCurrentThread(mStateMachineThread);
}
PRBool OnDecodeThread() const {
return mDecoderStateMachine->OnDecodeThread();
@ -580,9 +567,9 @@ public:
// Notifies the element that decoding has failed.
void DecodeError();
// Schedules the state machine to run one cycle on the shared state
// machine thread. Main thread only.
nsresult ScheduleStateMachineThread();
// Ensures the state machine thread is running, starting a new one
// if necessary.
nsresult StartStateMachineThread();
/******
* The following members should be accessed with the decoder lock held.
@ -603,6 +590,9 @@ public:
// time of the last decoded video frame).
nsChannelStatistics mPlaybackStatistics;
// Thread to manage playback state machine.
nsCOMPtr<nsIThread> mStateMachineThread;
// The current playback position of the media resource in units of
// seconds. This is updated approximately at the framerate of the
// video (if it is a video) or the callback period of the audio.

View File

@ -190,7 +190,8 @@ VideoData* VideoData::Create(nsVideoInfo& aInfo,
}
nsBuiltinDecoderReader::nsBuiltinDecoderReader(nsBuiltinDecoder* aDecoder)
: mDecoder(aDecoder)
: mReentrantMonitor("media.decoderreader"),
mDecoder(aDecoder)
{
MOZ_COUNT_CTOR(nsBuiltinDecoderReader);
}
@ -213,8 +214,7 @@ nsresult nsBuiltinDecoderReader::ResetDecode()
VideoData* nsBuiltinDecoderReader::FindStartTime(PRInt64& aOutStartTime)
{
NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
"Should be on state machine or decode thread.");
NS_ASSERTION(mDecoder->OnStateMachineThread(), "Should be on state machine thread.");
// Extract the start times of the bitstreams in order to calculate
// the duration.
@ -274,6 +274,7 @@ nsresult nsBuiltinDecoderReader::DecodeToTarget(PRInt64 aTarget)
PRBool skip = PR_FALSE;
eof = !DecodeVideoFrame(skip, 0);
{
ReentrantMonitorAutoExit exitReaderMon(mReentrantMonitor);
ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
if (mDecoder->GetDecodeState() == nsBuiltinDecoderStateMachine::DECODER_STATE_SHUTDOWN) {
return NS_ERROR_FAILURE;
@ -298,6 +299,7 @@ nsresult nsBuiltinDecoderReader::DecodeToTarget(PRInt64 aTarget)
}
}
{
ReentrantMonitorAutoExit exitReaderMon(mReentrantMonitor);
ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
if (mDecoder->GetDecodeState() == nsBuiltinDecoderStateMachine::DECODER_STATE_SHUTDOWN) {
return NS_ERROR_FAILURE;
@ -313,6 +315,7 @@ nsresult nsBuiltinDecoderReader::DecodeToTarget(PRInt64 aTarget)
while (!eof && mAudioQueue.GetSize() == 0) {
eof = !DecodeAudioData();
{
ReentrantMonitorAutoExit exitReaderMon(mReentrantMonitor);
ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
if (mDecoder->GetDecodeState() == nsBuiltinDecoderStateMachine::DECODER_STATE_SHUTDOWN) {
return NS_ERROR_FAILURE;

View File

@ -398,10 +398,12 @@ private:
PRBool mEndOfStream;
};
// Encapsulates the decoding and reading of media data. Reading can only be
// done on the decode thread thread. Never hold the decoder monitor when
// calling into this class. Unless otherwise specified, methods and fields of
// this class can only be accessed on the decode thread.
// Encapsulates the decoding and reading of media data. Reading can be done
// on either the state machine thread (when loading and seeking) or on
// the reader thread (when it's reading and decoding). The reader encapsulates
// the reading state and maintains it's own monitor to ensure thread safety
// and correctness. Never hold the nsBuiltinDecoder's monitor when calling into
// this class.
class nsBuiltinDecoderReader : public nsRunnable {
public:
typedef mozilla::ReentrantMonitor ReentrantMonitor;
@ -450,12 +452,10 @@ public:
PRInt64 aEndTime,
PRInt64 aCurrentTime) = 0;
// Queue of audio samples. This queue is threadsafe, and is accessed from
// the audio, decoder, state machine, and main threads.
// Queue of audio samples. This queue is threadsafe.
MediaQueue<SoundData> mAudioQueue;
// Queue of video samples. This queue is threadsafe, and is accessed from
// the decoder, state machine, and main threads.
// Queue of video samples. This queue is threadsafe.
MediaQueue<VideoData> mVideoQueue;
// Populates aBuffered with the time ranges which are buffered. aStartTime
@ -492,10 +492,16 @@ protected:
return DecodeVideoFrame(f, 0);
}
// Reference to the owning decoder object.
// The lock which we hold whenever we read or decode. This ensures the thread
// safety of the reader and its data fields.
ReentrantMonitor mReentrantMonitor;
// Reference to the owning decoder object. Do not hold the
// reader's monitor when accessing this.
nsBuiltinDecoder* mDecoder;
// Stores presentation info required for playback.
// Stores presentation info required for playback. The reader's monitor
// must be held when accessing this.
nsVideoInfo mInfo;
};

File diff suppressed because it is too large Load Diff

View File

@ -37,10 +37,11 @@
*
* ***** END LICENSE BLOCK ***** */
/*
Each video element for a media file has two threads:
Each video element for a media file has two additional threads beyond
those needed by nsBuiltinDecoder.
1) The Audio thread writes the decoded audio data to the audio
hardware. This is done in a separate thread to ensure that the
hardware. This is done in a seperate thread to ensure that the
audio hardware gets a constant stream of data without
interruption due to decoding or display. At some point
libsydneyaudio will be refactored to have a callback interface
@ -48,27 +49,31 @@ Each video element for a media file has two threads:
needed.
2) The decode thread. This thread reads from the media stream and
decodes the Theora and Vorbis data. It places the decoded data into
queues for the other threads to pull from.
decodes the Theora and Vorbis data. It places the decoded data in
a queue for the other threads to pull from.
All file reads, seeks, and all decoding must occur on the decode thread.
Synchronisation of state between the thread is done via a monitor owned
by nsBuiltinDecoder.
All file reads and seeks must occur on either the state machine thread
or the decode thread. Synchronisation is done via a monitor owned by
nsBuiltinDecoder.
The lifetime of the decode and audio threads is controlled by the state
machine when it runs on the shared state machine thread. When playback
needs to occur they are created and events dispatched to them to run
them. These events exit when decoding/audio playback is completed or
no longer required.
The decode thread and the audio thread are created and destroyed in
the state machine thread. When playback needs to occur they are
created and events dispatched to them to start them. These events exit
when decoding is completed or no longer required (during seeking or
shutdown).
The decode thread has its own monitor to ensure that its internal
state is independent of the other threads, and to ensure that it's not
hogging the nsBuiltinDecoder monitor while decoding.
A/V synchronisation is handled by the state machine. It examines the audio
playback time and compares this to the next frame in the queue of video
frames. If it is time to play the video frame it is then displayed, otherwise
it schedules the state machine to run again at the time of the next frame.
a/v synchronisation is handled by the state machine thread. It
examines the audio playback time and compares this to the next frame
in the queue of frames. If it is time to play the video frame it is
then displayed.
Frame skipping is done in the following ways:
1) The state machine will skip all frames in the video queue whose
1) The state machine thread will skip all frames in the video queue whose
display time is less than the current audio time. This ensures
the correct frame for the current time is always displayed.
@ -81,30 +86,28 @@ Frame skipping is done in the following ways:
will be decoding video data that won't be displayed due
to the decode thread dropping the frame immediately.
When hardware accelerated graphics is not available, YCbCr conversion
is done on the decode thread when video frames are decoded.
YCbCr conversion is done on the decode thread when it is time to display
the video frame. This means frames that are skipped will not have the
YCbCr conversion done, improving playback.
The decode thread pushes decoded audio and videos frames into two
separate queues - one for audio and one for video. These are kept
separate to make it easy to constantly feed audio data to the sound
hardware while allowing frame skipping of video data. These queues are
threadsafe, and neither the decode, audio, or state machine should
threadsafe, and neither the decode, audio, or state machine thread should
be able to monopolize them, and cause starvation of the other threads.
Both queues are bounded by a maximum size. When this size is reached
the decode thread will no longer decode video or audio depending on the
queue that has reached the threshold. If both queues are full, the decode
thread will wait on the decoder monitor.
When the decode queues are full (they've reaced their maximum size) and
the decoder is not in PLAYING play state, the state machine may opt
to shut down the decode thread in order to conserve resources.
queue that has reached the threshold.
During playback the audio thread will be idle (via a Wait() on the
monitor) if the audio queue is empty. Otherwise it constantly pops
sound data off the queue and plays it with a blocking write to the audio
monitor) if the audio queue is empty. Otherwise it constantly pops an
item off the queue and plays it with a blocking write to the audio
hardware (via nsAudioStream and libsydneyaudio).
The decode thread idles if the video queue is empty or if it is
not yet time to display the next frame.
*/
#if !defined(nsBuiltinDecoderStateMachine_h__)
#define nsBuiltinDecoderStateMachine_h__
@ -116,17 +119,21 @@ hardware (via nsAudioStream and libsydneyaudio).
#include "nsAudioAvailableEventManager.h"
#include "nsHTMLMediaElement.h"
#include "mozilla/ReentrantMonitor.h"
#include "nsITimer.h"
/*
The state machine class. This manages the decoding and seeking in the
nsBuiltinDecoderReader on the decode thread, and A/V sync on the shared
The playback state machine class. This manages the decoding in the
nsBuiltinDecoderReader on the decode thread, seeking and in-sync-playback on the
state machine thread, and controls the audio "push" thread.
All internal state is synchronised via the decoder monitor. State changes
are either propagated by NotifyAll on the monitor (typically when state
changes need to be propagated to non-state machine threads) or by scheduling
the state machine to run another cycle on the shared state machine thread.
All internal state is synchronised via the decoder monitor. NotifyAll
on the monitor is called when the state of the state machine is changed
by the main thread. The following changes to state cause a notify:
mState and data related to that state changed (mSeekTime, etc)
Metadata Loaded
First Frame Loaded
Frame decoded
data pushed or popped from the video and audio queues
See nsBuiltinDecoder.h for more details.
*/
@ -165,7 +172,13 @@ public:
virtual void UpdatePlaybackPosition(PRInt64 aTime);
virtual void StartBuffering();
// State machine thread run function. Defers to RunStateMachine().
// 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();
// This is called on the state machine thread and audio thread.
@ -201,29 +214,26 @@ public:
// Functions used by assertions to ensure we're calling things
// on the appropriate threads.
PRBool OnAudioThread() const {
PRBool OnAudioThread() {
return IsCurrentThread(mAudioThread);
}
PRBool OnStateMachineThread() const {
return IsCurrentThread(GetStateMachineThread());
PRBool OnStateMachineThread() {
return mDecoder->OnStateMachineThread();
}
// The decoder object that created this state machine. The state machine
// holds a strong reference to the decoder to ensure that the decoder stays
// alive once media element has started the decoder shutdown process, and has
// dropped its reference to the decoder. This enables the state machine to
// keep using the decoder's monitor until the state machine has finished
// shutting down, without fear of the monitor being destroyed. After
// shutting down, the state machine will then release this reference,
// causing the decoder to be destroyed. This is accessed on the decode,
// state machine, audio and main threads.
nsRefPtr<nsBuiltinDecoder> mDecoder;
// Decode loop, called on the decode thread.
void DecodeLoop();
// The decoder object that created this state machine. The decoder
// always outlives us since it controls our lifetime. This is accessed
// read only on the AV, state machine, audio and main thread.
nsBuiltinDecoder* mDecoder;
// The decoder monitor must be obtained before modifying this state.
// NotifyAll on the monitor must be called when the state is changed so
// that interested threads can wake up and alter behaviour if appropriate
// Accessed on state machine, audio, main, and AV thread.
// NotifyAll on the monitor must be called when the state is changed by
// the main thread so the decoder thread can wake up.
// Accessed on state machine, audio, main, and AV thread.
State mState;
nsresult GetBuffered(nsTimeRanges* aBuffered);
@ -247,23 +257,6 @@ public:
// Accessed on the main and state machine threads.
virtual void SetFrameBufferLength(PRUint32 aLength);
// Returns the shared state machine thread.
static nsIThread* GetStateMachineThread();
// Schedules the shared state machine thread to run the state machine.
// If the state machine thread is the currently running the state machine,
// we wait until that has completely finished before running the state
// machine again.
nsresult ScheduleStateMachine();
// Schedules the shared state machine thread to run the state machine
// in aUsecs microseconds from now, if it's not already scheduled to run
// earlier, in which case the request is discarded.
nsresult ScheduleStateMachine(PRInt64 aUsecs);
// Timer function to implement ScheduleStateMachine(aUsecs).
void TimeoutExpired();
protected:
// Returns PR_TRUE if we've got less than aAudioUsecs microseconds of decoded
@ -297,14 +290,14 @@ protected:
// wait for a specified time, and that the myriad of Notify()s we do on
// the decoder monitor don't cause the audio thread to be starved. aUsecs
// values of less than 1 millisecond are rounded up to 1 millisecond
// (see bug 651023). The decoder monitor must be held. Called only on the
// audio thread.
// (see bug 651023). The decoder monitor must be held.
void Wait(PRInt64 aUsecs);
// Dispatches an asynchronous event to update the media element's ready state.
void UpdateReadyState();
// Resets playback timing data. Called when we seek, on the decode thread.
// Resets playback timing data. Called when we seek, on the state machine
// thread.
void ResetPlayback();
// Returns the audio clock, if we have audio, or -1 if we don't.
@ -323,8 +316,9 @@ protected:
// machine thread, caller must hold the decoder lock.
void UpdatePlaybackPositionInternal(PRInt64 aTime);
// Pushes the image down the rendering pipeline. Called on the shared state
// machine thread. The decoder monitor must *not* be held when calling this.
// Performs YCbCr to RGB conversion, and pushes the image down the
// rendering pipeline. Called on the state machine thread. The decoder
// monitor must not be held when calling this.
void RenderVideoFrame(VideoData* aData, TimeStamp aTarget);
// If we have video, display a video frame if it's time for display has
@ -350,35 +344,36 @@ protected:
// queued here. Called on the audio thread.
PRUint32 PlayFromAudioQueue(PRUint64 aSampleOffset, PRUint32 aChannels);
// Stops the decode thread. The decoder monitor must be held with exactly
// Stops the decode threads. The decoder monitor must be held with exactly
// one lock count. Called on the state machine thread.
void StopDecodeThread();
void StopDecodeThreads();
// Stops the audio thread. The decoder monitor must be held with exactly
// Starts the decode threads. The decoder monitor must be held with exactly
// one lock count. Called on the state machine thread.
void StopAudioThread();
// Starts the decode thread. The decoder monitor must be held with exactly
// one lock count. Called on the state machine thread.
nsresult StartDecodeThread();
// Starts the audio thread. The decoder monitor must be held with exactly
// one lock count. Called on the state machine thread.
nsresult StartAudioThread();
nsresult StartDecodeThreads();
// The main loop for the audio thread. Sent to the thread as
// an nsRunnableMethod. This continually does blocking writes to
// to audio stream to play audio data.
void AudioLoop();
// Sets internal state which causes playback of media to pause.
// The decoder monitor must be held. Called on the main, state machine,
// and decode threads.
void StopPlayback();
// Stop or pause playback of media. This has two modes, denoted by
// aMode being either AUDIO_PAUSE or AUDIO_SHUTDOWN.
//
// AUDIO_PAUSE: Suspends the audio stream to be resumed later.
// This does not close the OS based audio stream
//
// AUDIO_SHUTDOWN: Closes and destroys the audio stream and
// releases any OS resources.
//
// The decoder monitor must be held with exactly one lock count. Called
// on the state machine thread.
enum eStopMode {AUDIO_PAUSE, AUDIO_SHUTDOWN};
void StopPlayback(eStopMode aMode);
// Sets internal state which causes playback of media to begin or resume.
// Must be called with the decode monitor held. Called on the state machine
// and decode threads.
// Resume playback of media. Must be called with the decode monitor held.
// This resumes a paused audio stream. The decoder monitor must be held with
// exactly one lock count. Called on the state machine thread.
void StartPlayback();
// Moves the decoder into decoding state. Called on the state machine
@ -405,42 +400,14 @@ protected:
// which has been pushed to the audio hardware for playback. Note that after
// calling this, the audio hardware may play some of the audio pushed to
// hardware, so this can only be used as a upper bound. The decoder monitor
// must be held when calling this. Called on the decode thread.
// 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();
// Seeks to mSeekTarget. Called on the decode thread. The decoder monitor
// must be held with exactly one lock count.
void DecodeSeek();
// Decode loop, decodes data until EOF or shutdown.
// Called on the decode thread.
void DecodeLoop();
// Decode thread run function. Determines which of the Decode*() functions
// to call.
void DecodeThreadRun();
// State machine thread run function. Defers to RunStateMachine().
nsresult CallRunStateMachine();
// Performs one "cycle" of the state machine. Polls the state, and may send
// a video frame to be displayed, and generally manages the decode. Called
// periodically via timer to ensure the video stays in sync.
nsresult RunStateMachine();
PRBool IsStateMachineScheduled() const {
mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
return !mTimeout.IsNull() || mRunAgain;
}
// Returns PR_TRUE if we're not playing and the decode thread has filled its
// decode buffers and is waiting. We can shut the decode thread down in this
// case as it may not be needed again.
PRBool IsPausedAndDecoderWaiting();
// 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
// (typically when it's being written to on the audio thread).
ReentrantMonitor mAudioReentrantMonitor;
// The size of the decoded YCbCr frame.
// Accessed on state machine thread.
@ -456,15 +423,6 @@ protected:
// Thread for decoding video in background. The "decode thread".
nsCOMPtr<nsIThread> mDecodeThread;
// Timer to call the state machine Run() method. Used by
// ScheduleStateMachine(). Access protected by decoder monitor.
nsCOMPtr<nsITimer> mTimer;
// Timestamp at which the next state machine Run() method will be called.
// If this is non-null, a call to Run() is scheduled, either by a timer,
// or via an event. Access protected by decoder monitor.
TimeStamp mTimeout;
// The time that playback started from the system clock. This is used for
// timing the presentation of video frames when there's no audio.
// Accessed only via the state machine thread.
@ -483,24 +441,23 @@ protected:
// Start time of the media, in microseconds. This is the presentation
// time of the first sample decoded from the media, and is used to calculate
// duration and as a bounds for seeking. Accessed on state machine, decode,
// and main threads. Access controlled by decoder monitor.
// duration and as a bounds for seeking. Accessed on state machine and
// main thread. Access controlled by decoder monitor.
PRInt64 mStartTime;
// Time of the last page in the media, in microseconds. This is the
// end time of the last sample in the media. Accessed on state
// machine, decode, and main threads. Access controlled by decoder monitor.
// machine and main thread. Access controlled by decoder monitor.
PRInt64 mEndTime;
// Position to seek to in microseconds when the seek state transition occurs.
// The decoder monitor lock must be obtained before reading or writing
// this value. Accessed on main and decode thread.
// this value. Accessed on main and state machine thread.
PRInt64 mSeekTime;
// The audio stream resource. Used on the state machine, and audio threads.
// This is created and destroyed on the audio thread, while holding the
// decoder monitor, so if this is used off the audio thread, you must
// first acquire the decoder monitor and check that it is non-null.
// The audio stream resource. Used on the state machine, audio, and
// main threads. You must hold the mAudioReentrantMonitor, and must
// NOT hold the decoder monitor when using the audio stream!
nsRefPtr<nsAudioStream> mAudioStream;
// The reader, don't call its methods with the decoder monitor held.
@ -559,20 +516,9 @@ protected:
// the media index/metadata. Accessed on the state machine thread.
PRPackedBool mGotDurationFromMetaData;
// PR_FALSE while decode thread should be running. Accessed state machine
// and decode threads. Syncrhonised by decoder monitor.
PRPackedBool mStopDecodeThread;
// PR_TRUE when the decode thread run function has finished, but the thread
// has not necessarily been shut down yet. This can happen if we switch
// from COMPLETED state to SEEKING before the state machine has a chance
// to run in the COMPLETED state and shutdown the decode thread.
// Synchronised by the decoder monitor.
PRPackedBool mDecodeThreadIdle;
// PR_FALSE while audio thread should be running. Accessed state machine
// and audio threads. Syncrhonised by decoder monitor.
PRPackedBool mStopAudioThread;
// PR_FALSE while decode threads should be running. Accessed on audio,
// state machine and decode threads. Syncrhonised by decoder monitor.
PRPackedBool mStopDecodeThreads;
// If this is PR_TRUE while we're in buffering mode, we can exit early,
// as it's likely we may be able to playback. This happens when we enter
@ -581,26 +527,6 @@ protected:
// Synchronised via decoder monitor.
PRPackedBool mQuickBuffering;
// PR_TRUE if the shared state machine thread is currently running this
// state machine.
PRPackedBool mIsRunning;
// PR_TRUE if we should run the state machine again once the current
// state machine run has finished.
PRPackedBool mRunAgain;
// PR_TRUE if we've dispatched an event to run the state machine. It's
// imperative that we don't dispatch multiple events to run the state
// machine at the same time, as our code assume all events are synchronous.
// If we dispatch multiple events, the second event can run while the
// first is shutting down a thread, causing inconsistent state.
PRPackedBool mDispatchedRunEvent;
// PR_TRUE if the decode thread has gone filled its buffers and is now
// waiting to be awakened before it continues decoding. Synchronized
// by the decoder monitor.
PRPackedBool mDecodeThreadWaiting;
private:
// Manager for queuing and dispatching MozAudioAvailable events. The
// event manager is accessed from the state machine and audio threads,

View File

@ -65,6 +65,20 @@ class nsTimeRanges;
#define FRAMEBUFFER_LENGTH_MIN 512
#define FRAMEBUFFER_LENGTH_MAX 16384
// Shuts down a thread asynchronously.
class ShutdownThreadEvent : public nsRunnable
{
public:
ShutdownThreadEvent(nsIThread* aThread) : mThread(aThread) {}
~ShutdownThreadEvent() {}
NS_IMETHOD Run() {
mThread->Shutdown();
return NS_OK;
}
private:
nsCOMPtr<nsIThread> mThread;
};
// All methods of nsMediaDecoder must be called from the main thread only
// with the exception of GetImageContainer, SetVideoData and GetStatistics,
// which can be called from any thread.

View File

@ -140,20 +140,23 @@ nsresult nsOggReader::Init(nsBuiltinDecoderReader* aCloneDonor) {
nsresult nsOggReader::ResetDecode()
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
nsresult res = NS_OK;
if (NS_FAILED(nsBuiltinDecoderReader::ResetDecode())) {
res = NS_ERROR_FAILURE;
}
// Discard any previously buffered packets/pages.
ogg_sync_reset(&mOggState);
if (mVorbisState && NS_FAILED(mVorbisState->Reset())) {
res = NS_ERROR_FAILURE;
}
if (mTheoraState && NS_FAILED(mTheoraState->Reset())) {
res = NS_ERROR_FAILURE;
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
// Discard any previously buffered packets/pages.
ogg_sync_reset(&mOggState);
if (mVorbisState && NS_FAILED(mVorbisState->Reset())) {
res = NS_ERROR_FAILURE;
}
if (mTheoraState && NS_FAILED(mTheoraState->Reset())) {
res = NS_ERROR_FAILURE;
}
}
return res;
@ -175,7 +178,8 @@ PRBool nsOggReader::ReadHeaders(nsOggCodecState* aState)
nsresult nsOggReader::ReadMetadata(nsVideoInfo* aInfo)
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
NS_ASSERTION(mDecoder->OnStateMachineThread(), "Should be on play state machine thread.");
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
// We read packets until all bitstreams have read all their header packets.
// We record the offset of the first non-header page so that we know
@ -314,7 +318,8 @@ nsresult nsOggReader::ReadMetadata(nsVideoInfo* aInfo)
}
PRInt64 duration = 0;
if (NS_SUCCEEDED(mSkeletonState->GetDuration(tracks, duration))) {
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
ReentrantMonitorAutoExit exitReaderMon(mReentrantMonitor);
ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
mDecoder->GetStateMachine()->SetDuration(duration);
LOG(PR_LOG_DEBUG, ("Got duration from Skeleton index %lld", duration));
}
@ -322,7 +327,8 @@ nsresult nsOggReader::ReadMetadata(nsVideoInfo* aInfo)
}
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
ReentrantMonitorAutoExit exitReaderMon(mReentrantMonitor);
ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
nsMediaStream* stream = mDecoder->GetCurrentStream();
if (mDecoder->GetStateMachine()->GetDuration() == -1 &&
@ -396,7 +402,9 @@ nsresult nsOggReader::DecodeVorbis(ogg_packet* aPacket) {
PRBool nsOggReader::DecodeAudioData()
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
"Should be on playback or decode thread.");
NS_ASSERTION(mVorbisState!=0, "Need Vorbis state to decode audio");
// Read the next data packet. Skip any non-data packets we encounter.
@ -472,6 +480,9 @@ nsresult nsOggReader::DecodeTheora(ogg_packet* aPacket, PRInt64 aTimeThreshold)
b.mPlanes[i].mStride = buffer[i].stride;
}
// Need the monitor to be held to be able to use mInfo. This
// is held by our caller.
mReentrantMonitor.AssertCurrentThreadIn();
VideoData *v = VideoData::Create(mInfo,
mDecoder->GetImageContainer(),
mPageOffset,
@ -495,7 +506,9 @@ nsresult nsOggReader::DecodeTheora(ogg_packet* aPacket, PRInt64 aTimeThreshold)
PRBool nsOggReader::DecodeVideoFrame(PRBool &aKeyframeSkip,
PRInt64 aTimeThreshold)
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
"Should be on state machine or AV thread.");
// Record number of frames decoded and parsed. Automatically update the
// stats counters using the AutoNotifyDecoded stack-based class.
@ -544,7 +557,9 @@ PRBool nsOggReader::DecodeVideoFrame(PRBool &aKeyframeSkip,
PRInt64 nsOggReader::ReadOggPage(ogg_page* aPage)
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
"Should be on play state machine or decode thread.");
mReentrantMonitor.AssertCurrentThreadIn();
int ret = 0;
while((ret = ogg_sync_pageseek(&mOggState, aPage)) <= 0) {
@ -582,7 +597,9 @@ PRInt64 nsOggReader::ReadOggPage(ogg_page* aPage)
ogg_packet* nsOggReader::NextOggPacket(nsOggCodecState* aCodecState)
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
"Should be on play state machine or decode thread.");
mReentrantMonitor.AssertCurrentThreadIn();
if (!aCodecState || !aCodecState->mActive) {
return nsnull;
@ -625,7 +642,8 @@ GetChecksum(ogg_page* page)
PRInt64 nsOggReader::RangeStartTime(PRInt64 aOffset)
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
NS_ASSERTION(mDecoder->OnStateMachineThread(),
"Should be on state machine thread.");
nsMediaStream* stream = mDecoder->GetCurrentStream();
NS_ENSURE_TRUE(stream != nsnull, nsnull);
nsresult res = stream->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
@ -647,8 +665,9 @@ struct nsAutoOggSyncState {
PRInt64 nsOggReader::RangeEndTime(PRInt64 aEndOffset)
{
NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
"Should be on state machine or decode thread.");
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
NS_ASSERTION(mDecoder->OnStateMachineThread(),
"Should be on state machine thread.");
nsMediaStream* stream = mDecoder->GetCurrentStream();
NS_ENSURE_TRUE(stream != nsnull, -1);
@ -778,7 +797,9 @@ PRInt64 nsOggReader::RangeEndTime(PRInt64 aStartOffset,
nsresult nsOggReader::GetSeekRanges(nsTArray<SeekRange>& aRanges)
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
NS_ASSERTION(mDecoder->OnStateMachineThread(),
"Should be on state machine thread.");
mReentrantMonitor.AssertCurrentThreadIn();
nsTArray<nsByteRange> cached;
nsresult res = mDecoder->GetCurrentStream()->GetCachedRanges(cached);
NS_ENSURE_SUCCESS(res, res);
@ -817,7 +838,8 @@ nsOggReader::SelectSeekRange(const nsTArray<SeekRange>& ranges,
PRInt64 aEndTime,
PRBool aExact)
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
NS_ASSERTION(mDecoder->OnStateMachineThread(),
"Should be on state machine thread.");
PRInt64 so = 0;
PRInt64 eo = mDecoder->GetCurrentStream()->GetLength();
PRInt64 st = aStartTime;
@ -955,7 +977,8 @@ nsresult nsOggReader::SeekInBufferedRange(PRInt64 aTarget,
PRBool skip = PR_FALSE;
eof = !DecodeVideoFrame(skip, 0);
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
ReentrantMonitorAutoExit exitReaderMon(mReentrantMonitor);
ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
if (mDecoder->GetDecodeState() == nsBuiltinDecoderStateMachine::DECODER_STATE_SHUTDOWN) {
return NS_ERROR_FAILURE;
}
@ -1029,7 +1052,9 @@ nsresult nsOggReader::Seek(PRInt64 aTarget,
PRInt64 aEndTime,
PRInt64 aCurrentTime)
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
NS_ASSERTION(mDecoder->OnStateMachineThread(),
"Should be on state machine thread.");
LOG(PR_LOG_DEBUG, ("%p About to seek to %lldms", mDecoder, aTarget));
nsresult res;
nsMediaStream* stream = mDecoder->GetCurrentStream();
@ -1047,7 +1072,8 @@ nsresult nsOggReader::Seek(PRInt64 aTarget,
NS_ASSERTION(aStartTime != -1, "mStartTime should be known");
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
ReentrantMonitorAutoExit exitReaderMon(mReentrantMonitor);
ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
mDecoder->UpdatePlaybackPosition(aStartTime);
}
} else if (CanDecodeToTarget(aTarget, aCurrentTime)) {
@ -1157,7 +1183,8 @@ nsresult nsOggReader::SeekBisection(PRInt64 aTarget,
const SeekRange& aRange,
PRUint32 aFuzz)
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
NS_ASSERTION(mDecoder->OnStateMachineThread(),
"Should be on state machine thread.");
nsresult res;
nsMediaStream* stream = mDecoder->GetCurrentStream();

View File

@ -71,11 +71,15 @@ public:
virtual PRBool DecodeVideoFrame(PRBool &aKeyframeSkip,
PRInt64 aTimeThreshold);
virtual PRBool HasAudio() {
virtual PRBool HasAudio()
{
mozilla::ReentrantMonitorAutoEnter mon(mReentrantMonitor);
return mVorbisState != 0 && mVorbisState->mActive;
}
virtual PRBool HasVideo() {
virtual PRBool HasVideo()
{
mozilla::ReentrantMonitorAutoEnter mon(mReentrantMonitor);
return mTheoraState != 0 && mTheoraState->mActive;
}
@ -85,7 +89,9 @@ public:
private:
PRBool HasSkeleton() {
PRBool HasSkeleton()
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
return mSkeletonState != 0 && mSkeletonState->mActive;
}

View File

@ -123,6 +123,7 @@ var gPlayTests = [
{ name:"spacestorm-1000Hz-100ms.ogg", type:"audio/ogg", duration:0.099 },
{ name:"bogus.duh", type:"bogus/duh", duration:Number.NaN }
];
// Converts a path/filename to a file:// URI which we can load from disk.

View File

@ -29,8 +29,7 @@ function startTest() {
function seekStarted() {
if (completed)
return false;
ok(v.currentTime >= seekTime - 0.1,
"Video currentTime should be around " + seekTime + ": " + v.currentTime + " (seeking)");
ok(v.currentTime >= seekTime - 0.1, "Video currentTime should be around " + seekTime + ": " + v.currentTime);
v.pause();
startPassed = true;
return false;
@ -44,7 +43,7 @@ function seekEnded() {
// Since we were playing, and we only paused asynchronously, we can't be
// sure that we paused before the seek finished, so we may have played
// ahead arbitrarily far.
ok(t >= seekTime - 0.1, "Video currentTime should be around " + seekTime + ": " + t + " (seeked)");
ok(t >= seekTime - 0.1, "Video currentTime should be around " + seekTime + ": " + t);
v.play();
endPassed = true;
seekFlagEnd = v.seeking;

View File

@ -41,7 +41,7 @@ function ended(e) {
} catch (e) {
caught = e.code == DOMException.INDEX_SIZE_ERR;
}
is(caught, true, v._name + ": Should throw INDEX_SIZE_ERR on under start bounds range");
is(caught, true, "Should throw INDEX_SIZE_ERR on under start bounds range");
caught = false;
try {
@ -49,7 +49,7 @@ function ended(e) {
} catch (e) {
caught = e.code == DOMException.INDEX_SIZE_ERR;
}
is(caught, true, v._name + ": Should throw INDEX_SIZE_ERR on under end bounds range");
is(caught, true, "Should throw INDEX_SIZE_ERR on under end bounds range");
caught = false;
try {
@ -57,7 +57,7 @@ function ended(e) {
} catch (e) {
caught = e.code == DOMException.INDEX_SIZE_ERR;
}
is(caught, true, v._name + ": Should throw INDEX_SIZE_ERR on over start bounds range");
is(caught, true, "Should throw INDEX_SIZE_ERR on over start bounds range");
caught = false;
try {
@ -65,9 +65,8 @@ function ended(e) {
} catch (e) {
caught = e.code == DOMException.INDEX_SIZE_ERR;
}
is(caught, true, v._name + ": Should throw INDEX_SIZE_ERR on over end bounds range");
v.src = "";
is(caught, true, "Should throw INDEX_SIZE_ERR on over end bounds range");
v.parentNode.removeChild(v);
manager.finished(v.token);

View File

@ -33,7 +33,6 @@ function canPlayThrough(e) {
ok(true, "Got canplaythrough after seek for " + v._name);
v._finished = true;
v.parentNode.removeChild(v);
v.src = "";
manager.finished(v.token);
}
}

View File

@ -152,7 +152,8 @@ nsresult nsWaveReader::Init(nsBuiltinDecoderReader* aCloneDonor)
nsresult nsWaveReader::ReadMetadata(nsVideoInfo* aInfo)
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
NS_ASSERTION(mDecoder->OnStateMachineThread(), "Should be on state machine thread.");
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
PRBool loaded = LoadRIFFChunk() && LoadFormatChunk() && FindDataOffset();
if (!loaded) {
@ -166,7 +167,8 @@ nsresult nsWaveReader::ReadMetadata(nsVideoInfo* aInfo)
*aInfo = mInfo;
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
ReentrantMonitorAutoExit exitReaderMon(mReentrantMonitor);
ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
mDecoder->GetStateMachine()->SetDuration(
static_cast<PRInt64>(BytesToTime(GetDataLength()) * USECS_PER_S));
@ -176,7 +178,9 @@ nsresult nsWaveReader::ReadMetadata(nsVideoInfo* aInfo)
PRBool nsWaveReader::DecodeAudioData()
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
"Should be on state machine thread or decode thread.");
PRInt64 pos = GetPosition() - mWavePCMOffset;
PRInt64 len = GetDataLength();
@ -242,14 +246,18 @@ PRBool nsWaveReader::DecodeAudioData()
PRBool nsWaveReader::DecodeVideoFrame(PRBool &aKeyframeSkip,
PRInt64 aTimeThreshold)
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
"Should be on state machine or decode thread.");
return PR_FALSE;
}
nsresult nsWaveReader::Seek(PRInt64 aTarget, PRInt64 aStartTime, PRInt64 aEndTime, PRInt64 aCurrentTime)
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
NS_ASSERTION(mDecoder->OnStateMachineThread(),
"Should be on state machine thread.");
LOG(PR_LOG_DEBUG, ("%p About to seek to %lld", mDecoder, aTarget));
if (NS_FAILED(ResetDecode())) {
return NS_ERROR_FAILURE;

View File

@ -210,7 +210,8 @@ void nsWebMReader::Cleanup()
nsresult nsWebMReader::ReadMetadata(nsVideoInfo* aInfo)
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
NS_ASSERTION(mDecoder->OnStateMachineThread(), "Should be on state machine thread.");
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
nestegg_io io;
io.read = webm_read;
@ -225,7 +226,8 @@ nsresult nsWebMReader::ReadMetadata(nsVideoInfo* aInfo)
uint64_t duration = 0;
r = nestegg_duration(mContext, &duration);
if (r == 0) {
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
ReentrantMonitorAutoExit exitReaderMon(mReentrantMonitor);
ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
mDecoder->GetStateMachine()->SetDuration(duration / NS_PER_USEC);
}
@ -410,7 +412,7 @@ ogg_packet nsWebMReader::InitOggPacket(unsigned char* aData,
PRBool nsWebMReader::DecodeAudioPacket(nestegg_packet* aPacket, PRInt64 aOffset)
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
mReentrantMonitor.AssertCurrentThreadIn();
int r = 0;
unsigned int count = 0;
@ -588,8 +590,9 @@ nsReturnRef<NesteggPacketHolder> nsWebMReader::NextPacket(TrackType aTrackType)
PRBool nsWebMReader::DecodeAudioData()
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
"Should be on state machine thread or decode thread.");
nsAutoRef<NesteggPacketHolder> holder(NextPacket(AUDIO));
if (!holder) {
mAudioQueue.Finish();
@ -602,7 +605,9 @@ PRBool nsWebMReader::DecodeAudioData()
PRBool nsWebMReader::DecodeVideoFrame(PRBool &aKeyframeSkip,
PRInt64 aTimeThreshold)
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
"Should be on state machine or decode thread.");
// Record number of frames decoded and parsed. Automatically update the
// stats counters using the AutoNotifyDecoded stack-based class.
@ -648,6 +653,7 @@ PRBool nsWebMReader::DecodeVideoFrame(PRBool &aKeyframeSkip,
}
mVideoPackets.PushFront(next_holder.disown());
} else {
ReentrantMonitorAutoExit exitMon(mReentrantMonitor);
ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
nsBuiltinDecoderStateMachine* s =
static_cast<nsBuiltinDecoderStateMachine*>(mDecoder->GetStateMachine());
@ -761,8 +767,9 @@ PRBool nsWebMReader::CanDecodeToTarget(PRInt64 aTarget,
nsresult nsWebMReader::Seek(PRInt64 aTarget, PRInt64 aStartTime, PRInt64 aEndTime,
PRInt64 aCurrentTime)
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
NS_ASSERTION(mDecoder->OnStateMachineThread(),
"Should be on state machine thread.");
LOG(PR_LOG_DEBUG, ("%p About to seek to %lldms", mDecoder, aTarget));
if (CanDecodeToTarget(aTarget, aCurrentTime)) {
LOG(PR_LOG_DEBUG, ("%p Seek target (%lld) is close to current time (%lld), "

View File

@ -143,13 +143,13 @@ public:
virtual PRBool HasAudio()
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
mozilla::ReentrantMonitorAutoEnter mon(mReentrantMonitor);
return mHasAudio;
}
virtual PRBool HasVideo()
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
mozilla::ReentrantMonitorAutoEnter mon(mReentrantMonitor);
return mHasVideo;
}