Bug 592833 - Move metadata decoding to decode thread. r=roc

This commit is contained in:
Chris Pearce 2011-07-06 10:03:11 +12:00
Родитель 0e9036119e
Коммит 609a7c3504
8 изменённых файлов: 115 добавлений и 103 удалений

Просмотреть файл

@ -871,7 +871,9 @@ void nsBuiltinDecoder::Resume(PRBool aForceBuffering)
void nsBuiltinDecoder::StopProgressUpdates() 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; mIgnoreProgressData = PR_TRUE;
if (mStream) { if (mStream) {
mStream->SetReadMode(nsMediaCacheStream::MODE_METADATA); mStream->SetReadMode(nsMediaCacheStream::MODE_METADATA);
@ -880,7 +882,9 @@ void nsBuiltinDecoder::StopProgressUpdates()
void nsBuiltinDecoder::StartProgressUpdates() 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; mIgnoreProgressData = PR_FALSE;
if (mStream) { if (mStream) {
mStream->SetReadMode(nsMediaCacheStream::MODE_PLAYBACK); mStream->SetReadMode(nsMediaCacheStream::MODE_PLAYBACK);

Просмотреть файл

@ -435,7 +435,7 @@ class nsBuiltinDecoder : public nsMediaDecoder
// has changed. // has changed.
void DurationChanged(); void DurationChanged();
PRBool OnStateMachineThread() { PRBool OnStateMachineThread() const {
return IsCurrentThread(mStateMachineThread); return IsCurrentThread(mStateMachineThread);
} }

Просмотреть файл

@ -214,7 +214,8 @@ nsresult nsBuiltinDecoderReader::ResetDecode()
VideoData* nsBuiltinDecoderReader::FindStartTime(PRInt64& aOutStartTime) 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 // Extract the start times of the bitstreams in order to calculate
// the duration. // the duration.

Просмотреть файл

@ -270,10 +270,16 @@ void nsBuiltinDecoderStateMachine::DecodeLoop()
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
PRBool videoPlaying = HasVideo(); if (mState == DECODER_STATE_DECODING_METADATA) {
PRBool audioPlaying = HasAudio(); if (NS_FAILED(DecodeMetadata())) {
LOG(PR_LOG_DEBUG, ("Decode metadata failed, shutting down decode thread"));
}
mDecoder->GetReentrantMonitor().NotifyAll();
}
// Main decode loop. // Main decode loop.
PRBool videoPlaying = HasVideo();
PRBool audioPlaying = HasAudio();
while (mState != DECODER_STATE_SHUTDOWN && while (mState != DECODER_STATE_SHUTDOWN &&
!mStopDecodeThread && !mStopDecodeThread &&
(videoPlaying || audioPlaying)) (videoPlaying || audioPlaying))
@ -794,7 +800,7 @@ void nsBuiltinDecoderStateMachine::SetVolume(double volume)
double nsBuiltinDecoderStateMachine::GetCurrentTime() const double nsBuiltinDecoderStateMachine::GetCurrentTime() const
{ {
NS_ASSERTION(NS_IsMainThread() || NS_ASSERTION(NS_IsMainThread() ||
mDecoder->OnStateMachineThread() || OnStateMachineThread() ||
OnDecodeThread(), OnDecodeThread(),
"Should be on main, decode, or state machine thread."); "Should be on main, decode, or state machine thread.");
@ -812,8 +818,8 @@ PRInt64 nsBuiltinDecoderStateMachine::GetDuration()
void nsBuiltinDecoderStateMachine::SetDuration(PRInt64 aDuration) void nsBuiltinDecoderStateMachine::SetDuration(PRInt64 aDuration)
{ {
NS_ASSERTION(NS_IsMainThread() || mDecoder->OnStateMachineThread(), NS_ASSERTION(NS_IsMainThread() || OnDecodeThread(),
"Should be on main or state machine thread."); "Should be on main or decode thread.");
mDecoder->GetReentrantMonitor().AssertCurrentThreadIn(); mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
if (aDuration == -1) { if (aDuration == -1) {
@ -830,7 +836,7 @@ void nsBuiltinDecoderStateMachine::SetDuration(PRInt64 aDuration)
void nsBuiltinDecoderStateMachine::SetEndTime(PRInt64 aEndTime) 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(); mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
mEndTime = aEndTime; mEndTime = aEndTime;
@ -860,8 +866,8 @@ void nsBuiltinDecoderStateMachine::Shutdown()
void nsBuiltinDecoderStateMachine::StartDecoding() void nsBuiltinDecoderStateMachine::StartDecoding()
{ {
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
"Should be on state machine thread."); "Should be on state machine or decode thread.");
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
if (mState != DECODER_STATE_DECODING) { if (mState != DECODER_STATE_DECODING) {
mDecodeStartTime = TimeStamp::Now(); mDecodeStartTime = TimeStamp::Now();
@ -1069,6 +1075,72 @@ void nsBuiltinDecoderStateMachine::SetFrameBufferLength(PRUint32 aLength)
mEventManager.SetSignalBufferLength(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<nsIRunnable> 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<nsIRunnable> 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() nsresult nsBuiltinDecoderStateMachine::Run()
{ {
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
@ -1091,62 +1163,23 @@ nsresult nsBuiltinDecoderStateMachine::Run()
case DECODER_STATE_DECODING_METADATA: case DECODER_STATE_DECODING_METADATA:
{ {
LoadMetadata(); // Start the decode threads, so that metadata decoding begins.
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.
if (NS_FAILED(StartDecodeThread())) { if (NS_FAILED(StartDecodeThread())) {
continue; continue;
} }
NS_ASSERTION(mStartTime != -1, "Must have start time"); while (mState == DECODER_STATE_DECODING_METADATA) {
NS_ASSERTION((!HasVideo() && !HasAudio()) || mon.Wait();
!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<nsIRunnable> 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();
} }
// Start playback. if ((mState == DECODER_STATE_DECODING || mState == DECODER_STATE_COMPLETED) &&
if (mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING) { mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING &&
if (!IsPlaying()) { !IsPlaying())
StartPlayback(); {
StartAudioThread(); StartPlayback();
} StartAudioThread();
} }
} }
break; break;
@ -1388,7 +1421,8 @@ nsresult nsBuiltinDecoderStateMachine::Run()
void nsBuiltinDecoderStateMachine::RenderVideoFrame(VideoData* aData, void nsBuiltinDecoderStateMachine::RenderVideoFrame(VideoData* aData,
TimeStamp aTarget) 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(); mDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
if (aData->mDuplicate) { if (aData->mDuplicate) {
@ -1577,7 +1611,7 @@ void nsBuiltinDecoderStateMachine::Wait(PRInt64 aUsecs) {
VideoData* nsBuiltinDecoderStateMachine::FindStartTime() 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(); mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
PRInt64 startTime = 0; PRInt64 startTime = 0;
mStartTime = 0; mStartTime = 0;
@ -1626,32 +1660,6 @@ void nsBuiltinDecoderStateMachine::UpdateReadyState() {
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); 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<nsIRunnable> event =
NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::DecodeError);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
return;
}
mDecoder->StartProgressUpdates();
mGotDurationFromMetaData = (GetDuration() != -1);
}
PRBool nsBuiltinDecoderStateMachine::JustExitedQuickBuffering() PRBool nsBuiltinDecoderStateMachine::JustExitedQuickBuffering()
{ {
return !mDecodeStartTime.IsNull() && return !mDecodeStartTime.IsNull() &&

Просмотреть файл

@ -172,11 +172,6 @@ public:
virtual void UpdatePlaybackPosition(PRInt64 aTime); virtual void UpdatePlaybackPosition(PRInt64 aTime);
virtual void StartBuffering(); 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 // State machine thread run function. Polls the state, sends frames to be
// displayed at appropriate times, and generally manages the decode. // displayed at appropriate times, and generally manages the decode.
NS_IMETHOD Run(); NS_IMETHOD Run();
@ -214,11 +209,11 @@ public:
// Functions used by assertions to ensure we're calling things // Functions used by assertions to ensure we're calling things
// on the appropriate threads. // on the appropriate threads.
PRBool OnAudioThread() { PRBool OnAudioThread() const {
return IsCurrentThread(mAudioThread); return IsCurrentThread(mAudioThread);
} }
PRBool OnStateMachineThread() { PRBool OnStateMachineThread() const {
return mDecoder->OnStateMachineThread(); return mDecoder->OnStateMachineThread();
} }
@ -411,6 +406,10 @@ protected:
// must be held when calling this. Called on the decoder thread. // must be held when calling this. Called on the decoder thread.
PRInt64 GetDecodedAudioDuration(); 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 // ReentrantMonitor on mAudioStream. This monitor must be held in
// order to delete or use the audio stream. This stops us destroying // order to delete or use the audio stream. This stops us destroying
// the audio stream while it's being used on another thread // the audio stream while it's being used on another thread

Просмотреть файл

@ -178,7 +178,7 @@ PRBool nsOggReader::ReadHeaders(nsOggCodecState* aState)
nsresult nsOggReader::ReadMetadata(nsVideoInfo* aInfo) 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); ReentrantMonitorAutoEnter mon(mReentrantMonitor);
// We read packets until all bitstreams have read all their header packets. // 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) PRInt64 nsOggReader::RangeStartTime(PRInt64 aOffset)
{ {
NS_ASSERTION(mDecoder->OnStateMachineThread(), NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
"Should be on state machine thread."); "Should be on state machine or decode thread.");
nsMediaStream* stream = mDecoder->GetCurrentStream(); nsMediaStream* stream = mDecoder->GetCurrentStream();
NS_ENSURE_TRUE(stream != nsnull, nsnull); NS_ENSURE_TRUE(stream != nsnull, nsnull);
nsresult res = stream->Seek(nsISeekableStream::NS_SEEK_SET, aOffset); nsresult res = stream->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
@ -666,8 +666,8 @@ struct nsAutoOggSyncState {
PRInt64 nsOggReader::RangeEndTime(PRInt64 aEndOffset) PRInt64 nsOggReader::RangeEndTime(PRInt64 aEndOffset)
{ {
ReentrantMonitorAutoEnter mon(mReentrantMonitor); ReentrantMonitorAutoEnter mon(mReentrantMonitor);
NS_ASSERTION(mDecoder->OnStateMachineThread(), NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
"Should be on state machine thread."); "Should be on state machine or decode thread.");
nsMediaStream* stream = mDecoder->GetCurrentStream(); nsMediaStream* stream = mDecoder->GetCurrentStream();
NS_ENSURE_TRUE(stream != nsnull, -1); NS_ENSURE_TRUE(stream != nsnull, -1);

Просмотреть файл

@ -152,7 +152,7 @@ nsresult nsWaveReader::Init(nsBuiltinDecoderReader* aCloneDonor)
nsresult nsWaveReader::ReadMetadata(nsVideoInfo* aInfo) 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); ReentrantMonitorAutoEnter mon(mReentrantMonitor);
PRBool loaded = LoadRIFFChunk() && LoadFormatChunk() && FindDataOffset(); PRBool loaded = LoadRIFFChunk() && LoadFormatChunk() && FindDataOffset();

Просмотреть файл

@ -210,7 +210,7 @@ void nsWebMReader::Cleanup()
nsresult nsWebMReader::ReadMetadata(nsVideoInfo* aInfo) 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); ReentrantMonitorAutoEnter mon(mReentrantMonitor);
nestegg_io io; nestegg_io io;