Bug 560708 - Factor non-Ogg specific logic into decoder base class. r=cpearce sr=roc

This commit is contained in:
Chris Double 2010-04-27 20:53:44 +12:00
parent ea974f2afe
commit 954518497c
12 changed files with 569 additions and 425 deletions

View File

@ -49,12 +49,15 @@ EXPORTS = \
nsMediaDecoder.h \
nsMediaStream.h \
nsMediaCache.h \
nsBuiltinDecoder.h \
VideoUtils.h \
$(NULL)
CPPSRCS = \
nsMediaDecoder.cpp \
nsMediaCache.cpp \
nsMediaStream.cpp \
nsBuiltinDecoder.cpp \
$(NULL)
ifdef MOZ_SYDNEYAUDIO

View File

@ -36,8 +36,10 @@
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsOggHacks_h
#define nsOggHacks_h
#ifndef VideoUtils_h
#define VideoUtils_h
#include "mozilla/Monitor.h"
// This file contains stuff we'd rather put elsewhere, but which is
// dependent on other changes which we don't want to wait for. We plan to

View File

@ -37,7 +37,6 @@
*
* ***** END LICENSE BLOCK ***** */
#include "nsOggPlayStateMachine.h"
#include <limits>
#include "nsNetUtil.h"
#include "nsAudioStream.h"
@ -45,20 +44,23 @@
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsTArray.h"
#include "nsOggCodecState.h"
#include "nsOggDecoder.h"
#include "nsOggReader.h"
#include "VideoUtils.h"
#include "nsBuiltinDecoder.h"
using mozilla::Monitor;
using mozilla::MonitorAutoEnter;
using mozilla::MonitorAutoExit;
#ifdef PR_LOGGING
PRLogModuleInfo* gOggDecoderLog;
#define LOG(type, msg) PR_LOG(gOggDecoderLog, type, msg)
PRLogModuleInfo* gBuiltinDecoderLog;
#define LOG(type, msg) PR_LOG(gBuiltinDecoderLog, type, msg)
#else
#define LOG(type, msg)
#endif
NS_IMPL_THREADSAFE_ISUPPORTS1(nsOggDecoder, nsIObserver)
NS_IMPL_THREADSAFE_ISUPPORTS1(nsBuiltinDecoder, nsIObserver)
void nsOggDecoder::Pause()
void nsBuiltinDecoder::Pause()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
MonitorAutoEnter mon(mMonitor);
@ -70,16 +72,16 @@ void nsOggDecoder::Pause()
ChangeState(PLAY_STATE_PAUSED);
}
void nsOggDecoder::SetVolume(float volume)
void nsBuiltinDecoder::SetVolume(float volume)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
mInitialVolume = volume;
if (mDecodeStateMachine) {
mDecodeStateMachine->SetVolume(volume);
if (mDecoderStateMachine) {
mDecoderStateMachine->SetVolume(volume);
}
}
float nsOggDecoder::GetDuration()
float nsBuiltinDecoder::GetDuration()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
if (mDuration >= 0) {
@ -88,7 +90,7 @@ float nsOggDecoder::GetDuration()
return std::numeric_limits<float>::quiet_NaN();
}
nsOggDecoder::nsOggDecoder() :
nsBuiltinDecoder::nsBuiltinDecoder() :
mDecoderPosition(0),
mPlaybackPosition(0),
mCurrentTime(0.0),
@ -102,16 +104,16 @@ nsOggDecoder::nsOggDecoder() :
mResourceLoaded(PR_FALSE),
mIgnoreProgressData(PR_FALSE)
{
MOZ_COUNT_CTOR(nsOggDecoder);
MOZ_COUNT_CTOR(nsBuiltinDecoder);
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
#ifdef PR_LOGGING
if (!gOggDecoderLog) {
gOggDecoderLog = PR_NewLogModule("nsOggDecoder");
if (!gBuiltinDecoderLog) {
gBuiltinDecoderLog = PR_NewLogModule("nsBuiltinDecoder");
}
#endif
}
PRBool nsOggDecoder::Init(nsHTMLMediaElement* aElement)
PRBool nsBuiltinDecoder::Init(nsHTMLMediaElement* aElement)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
if (!nsMediaDecoder::Init(aElement))
@ -122,7 +124,7 @@ PRBool nsOggDecoder::Init(nsHTMLMediaElement* aElement)
return PR_TRUE;
}
void nsOggDecoder::Stop()
void nsBuiltinDecoder::Stop()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread");
@ -133,10 +135,10 @@ void nsOggDecoder::Stop()
mStateMachineThread->Shutdown();
mStateMachineThread = nsnull;
mDecodeStateMachine = nsnull;
mDecoderStateMachine = nsnull;
}
void nsOggDecoder::Shutdown()
void nsBuiltinDecoder::Shutdown()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
@ -148,8 +150,8 @@ void nsOggDecoder::Shutdown()
// This changes the decoder state to SHUTDOWN and does other things
// necessary to unblock the state machine thread if it's blocked, so
// the asynchronous shutdown in nsDestroyStateMachine won't deadlock.
if (mDecodeStateMachine) {
mDecodeStateMachine->Shutdown();
if (mDecoderStateMachine) {
mDecoderStateMachine->Shutdown();
}
// Force any outstanding seek and byterange requests to complete
@ -161,7 +163,7 @@ void nsOggDecoder::Shutdown()
ChangeState(PLAY_STATE_SHUTDOWN);
nsMediaDecoder::Shutdown();
// We can't destroy mDecodeStateMachine until mStateMachineThread is shut down.
// 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
@ -169,19 +171,19 @@ void nsOggDecoder::Shutdown()
// So we destroy the decoder on the main thread in an asynchronous event.
// See bug 468721.
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(this, &nsOggDecoder::Stop);
NS_NewRunnableMethod(this, &nsBuiltinDecoder::Stop);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
nsContentUtils::UnregisterShutdownObserver(this);
}
nsOggDecoder::~nsOggDecoder()
nsBuiltinDecoder::~nsBuiltinDecoder()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
MOZ_COUNT_DTOR(nsOggDecoder);
MOZ_COUNT_DTOR(nsBuiltinDecoder);
}
nsresult nsOggDecoder::Load(nsMediaStream* aStream,
nsresult nsBuiltinDecoder::Load(nsMediaStream* aStream,
nsIStreamListener** aStreamListener)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
@ -207,22 +209,26 @@ nsresult nsOggDecoder::Load(nsMediaStream* aStream,
nsresult rv = NS_NewThread(getter_AddRefs(mStateMachineThread));
NS_ENSURE_SUCCESS(rv, rv);
mDecodeStateMachine = new nsOggPlayStateMachine(this);
if (NS_FAILED(mDecodeStateMachine->Init())) {
mDecoderStateMachine = CreateStateMachine();
if (!mDecoderStateMachine) {
return NS_ERROR_FAILURE;
}
if (NS_FAILED(mDecoderStateMachine->Init())) {
return NS_ERROR_FAILURE;
}
{
MonitorAutoEnter mon(mMonitor);
mDecodeStateMachine->SetSeekable(mSeekable);
mDecodeStateMachine->SetDuration(mDuration);
mDecoderStateMachine->SetSeekable(mSeekable);
mDecoderStateMachine->SetDuration(mDuration);
}
ChangeState(PLAY_STATE_LOADING);
return mStateMachineThread->Dispatch(mDecodeStateMachine, NS_DISPATCH_NORMAL);
return mStateMachineThread->Dispatch(mDecoderStateMachine, NS_DISPATCH_NORMAL);
}
nsresult nsOggDecoder::Play()
nsresult nsBuiltinDecoder::Play()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
MonitorAutoEnter mon(mMonitor);
@ -238,7 +244,7 @@ nsresult nsOggDecoder::Play()
return NS_OK;
}
nsresult nsOggDecoder::Seek(float aTime)
nsresult nsBuiltinDecoder::Seek(float aTime)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
MonitorAutoEnter mon(mMonitor);
@ -254,7 +260,8 @@ nsresult nsOggDecoder::Seek(float aTime)
if (mPlayState != PLAY_STATE_SEEKING) {
if (mPlayState == PLAY_STATE_ENDED) {
mNextState = PLAY_STATE_PLAYING;
} else {
}
else {
mNextState = mPlayState;
}
ChangeState(PLAY_STATE_SEEKING);
@ -263,31 +270,30 @@ nsresult nsOggDecoder::Seek(float aTime)
return NS_OK;
}
nsresult nsOggDecoder::PlaybackRateChanged()
nsresult nsBuiltinDecoder::PlaybackRateChanged()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
return NS_ERROR_NOT_IMPLEMENTED;
}
float nsOggDecoder::GetCurrentTime()
float nsBuiltinDecoder::GetCurrentTime()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
return mCurrentTime;
}
nsMediaStream* nsOggDecoder::GetCurrentStream()
nsMediaStream* nsBuiltinDecoder::GetCurrentStream()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
return mStream;
}
already_AddRefed<nsIPrincipal> nsOggDecoder::GetCurrentPrincipal()
already_AddRefed<nsIPrincipal> nsBuiltinDecoder::GetCurrentPrincipal()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
return mStream ? mStream->GetCurrentPrincipal() : nsnull;
}
void nsOggDecoder::MetadataLoaded()
void nsBuiltinDecoder::MetadataLoaded()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
if (mShuttingDown)
@ -298,7 +304,7 @@ void nsOggDecoder::MetadataLoaded()
PRBool notifyElement = PR_TRUE;
{
MonitorAutoEnter mon(mMonitor);
mDuration = mDecodeStateMachine ? mDecodeStateMachine->GetDuration() : -1;
mDuration = mDecoderStateMachine ? mDecoderStateMachine->GetDuration() : -1;
notifyElement = mNextState != PLAY_STATE_SEEKING;
}
@ -311,7 +317,8 @@ void nsOggDecoder::MetadataLoaded()
if (!mResourceLoaded) {
StartProgress();
} else if (mElement) {
}
else if (mElement) {
// Resource was loaded during metadata loading, when progress
// events are being ignored. Fire the final progress event.
mElement->DispatchAsyncProgressEvent(NS_LITERAL_STRING("progress"));
@ -333,7 +340,8 @@ void nsOggDecoder::MetadataLoaded()
if (mPlayState == PLAY_STATE_LOADING) {
if (mRequestedSeekTime >= 0.0) {
ChangeState(PLAY_STATE_SEEKING);
} else {
}
else {
ChangeState(mNextState);
}
}
@ -343,7 +351,7 @@ void nsOggDecoder::MetadataLoaded()
}
}
void nsOggDecoder::ResourceLoaded()
void nsBuiltinDecoder::ResourceLoaded()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
@ -374,7 +382,7 @@ void nsOggDecoder::ResourceLoaded()
}
}
void nsOggDecoder::NetworkError()
void nsBuiltinDecoder::NetworkError()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
if (mShuttingDown)
@ -386,7 +394,7 @@ void nsOggDecoder::NetworkError()
Shutdown();
}
void nsOggDecoder::DecodeError()
void nsBuiltinDecoder::DecodeError()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
if (mShuttingDown)
@ -398,19 +406,19 @@ void nsOggDecoder::DecodeError()
Shutdown();
}
PRBool nsOggDecoder::IsSeeking() const
PRBool nsBuiltinDecoder::IsSeeking() const
{
return mPlayState == PLAY_STATE_SEEKING || mNextState == PLAY_STATE_SEEKING;
}
PRBool nsOggDecoder::IsEnded() const
PRBool nsBuiltinDecoder::IsEnded() const
{
return mPlayState == PLAY_STATE_ENDED || mPlayState == PLAY_STATE_SHUTDOWN;
}
void nsOggDecoder::PlaybackEnded()
void nsBuiltinDecoder::PlaybackEnded()
{
if (mShuttingDown || mPlayState == nsOggDecoder::PLAY_STATE_SEEKING)
if (mShuttingDown || mPlayState == nsBuiltinDecoder::PLAY_STATE_SEEKING)
return;
PlaybackPositionChanged();
@ -422,9 +430,9 @@ void nsOggDecoder::PlaybackEnded()
}
}
NS_IMETHODIMP nsOggDecoder::Observe(nsISupports *aSubjet,
const char *aTopic,
const PRUnichar *someData)
NS_IMETHODIMP nsBuiltinDecoder::Observe(nsISupports *aSubjet,
const char *aTopic,
const PRUnichar *someData)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
@ -435,7 +443,7 @@ NS_IMETHODIMP nsOggDecoder::Observe(nsISupports *aSubjet,
}
nsMediaDecoder::Statistics
nsOggDecoder::GetStatistics()
nsBuiltinDecoder::GetStatistics()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
Statistics result;
@ -450,7 +458,8 @@ nsOggDecoder::GetStatistics()
result.mPlaybackRate = ComputePlaybackRate(&result.mPlaybackRateReliable);
result.mDecoderPosition = mDecoderPosition;
result.mPlaybackPosition = mPlaybackPosition;
} else {
}
else {
result.mDownloadRate = 0;
result.mDownloadRateReliable = PR_TRUE;
result.mPlaybackRate = 0;
@ -464,10 +473,10 @@ nsOggDecoder::GetStatistics()
return result;
}
double nsOggDecoder::ComputePlaybackRate(PRPackedBool* aReliable)
double nsBuiltinDecoder::ComputePlaybackRate(PRPackedBool* aReliable)
{
GetMonitor().AssertCurrentThreadIn();
NS_ASSERTION(NS_IsMainThread() || IsThread(mStateMachineThread),
NS_ASSERTION(NS_IsMainThread() || IsCurrentThread(mStateMachineThread),
"Should be on main or state machine thread.");
PRInt64 length = mStream ? mStream->GetLength() : -1;
@ -478,9 +487,9 @@ double nsOggDecoder::ComputePlaybackRate(PRPackedBool* aReliable)
return mPlaybackStatistics.GetRateAtLastStop(aReliable);
}
void nsOggDecoder::UpdatePlaybackRate()
void nsBuiltinDecoder::UpdatePlaybackRate()
{
NS_ASSERTION(NS_IsMainThread() || IsThread(mStateMachineThread),
NS_ASSERTION(NS_IsMainThread() || IsCurrentThread(mStateMachineThread),
"Should be on main or state machine thread.");
GetMonitor().AssertCurrentThreadIn();
if (!mStream)
@ -490,7 +499,8 @@ void nsOggDecoder::UpdatePlaybackRate()
if (reliable) {
// Avoid passing a zero rate
rate = NS_MAX(rate, 1u);
} else {
}
else {
// Set a minimum rate of 10,000 bytes per second ... sometimes we just
// don't have good data
rate = NS_MAX(rate, 10000u);
@ -498,7 +508,7 @@ void nsOggDecoder::UpdatePlaybackRate()
mStream->SetPlaybackRate(rate);
}
void nsOggDecoder::NotifySuspendedStatusChanged()
void nsBuiltinDecoder::NotifySuspendedStatusChanged()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
if (!mStream)
@ -510,14 +520,14 @@ void nsOggDecoder::NotifySuspendedStatusChanged()
}
}
void nsOggDecoder::NotifyBytesDownloaded()
void nsBuiltinDecoder::NotifyBytesDownloaded()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
UpdateReadyStateForData();
Progress(PR_FALSE);
}
void nsOggDecoder::NotifyDownloadEnded(nsresult aStatus)
void nsBuiltinDecoder::NotifyDownloadEnded(nsresult aStatus)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
@ -531,61 +541,60 @@ void nsOggDecoder::NotifyDownloadEnded(nsresult aStatus)
if (NS_SUCCEEDED(aStatus)) {
ResourceLoaded();
} else if (aStatus != NS_BASE_STREAM_CLOSED) {
}
else if (aStatus != NS_BASE_STREAM_CLOSED) {
NetworkError();
}
UpdateReadyStateForData();
}
void nsOggDecoder::NotifyBytesConsumed(PRInt64 aBytes)
void nsBuiltinDecoder::NotifyBytesConsumed(PRInt64 aBytes)
{
MonitorAutoEnter mon(mMonitor);
NS_ASSERTION(mDecodeStateMachine->OnStateMachineThread() || mDecodeStateMachine->OnDecodeThread(),
NS_ASSERTION(OnStateMachineThread() || mDecoderStateMachine->OnDecodeThread(),
"Should be on play state machine or decode thread.");
if (!mIgnoreProgressData) {
mDecoderPosition += aBytes;
}
}
void nsOggDecoder::NextFrameUnavailableBuffering()
void nsBuiltinDecoder::NextFrameUnavailableBuffering()
{
NS_ASSERTION(NS_IsMainThread(), "Should be called on main thread");
if (!mElement || mShuttingDown || !mDecodeStateMachine)
if (!mElement || mShuttingDown || !mDecoderStateMachine)
return;
mElement->UpdateReadyStateForData(nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE_BUFFERING);
}
void nsOggDecoder::NextFrameAvailable()
void nsBuiltinDecoder::NextFrameAvailable()
{
NS_ASSERTION(NS_IsMainThread(), "Should be called on main thread");
if (!mElement || mShuttingDown || !mDecodeStateMachine)
if (!mElement || mShuttingDown || !mDecoderStateMachine)
return;
mElement->UpdateReadyStateForData(nsHTMLMediaElement::NEXT_FRAME_AVAILABLE);
}
void nsOggDecoder::NextFrameUnavailable()
void nsBuiltinDecoder::NextFrameUnavailable()
{
NS_ASSERTION(NS_IsMainThread(), "Should be called on main thread");
if (!mElement || mShuttingDown || !mDecodeStateMachine)
if (!mElement || mShuttingDown || !mDecoderStateMachine)
return;
mElement->UpdateReadyStateForData(nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE);
}
void nsOggDecoder::UpdateReadyStateForData()
void nsBuiltinDecoder::UpdateReadyStateForData()
{
NS_ASSERTION(NS_IsMainThread(), "Should be called on main thread");
if (!mElement || mShuttingDown || !mDecodeStateMachine)
if (!mElement || mShuttingDown || !mDecoderStateMachine)
return;
nsHTMLMediaElement::NextFrameStatus frameStatus =
mDecodeStateMachine->GetNextFrameStatus();
mDecoderStateMachine->GetNextFrameStatus();
mElement->UpdateReadyStateForData(frameStatus);
}
void nsOggDecoder::SeekingStopped()
void nsBuiltinDecoder::SeekingStopped()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
@ -611,7 +620,7 @@ void nsOggDecoder::SeekingStopped()
// This is called when seeking stopped *and* we're at the end of the
// media.
void nsOggDecoder::SeekingStoppedAtEnd()
void nsBuiltinDecoder::SeekingStoppedAtEnd()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
@ -626,7 +635,8 @@ void nsOggDecoder::SeekingStoppedAtEnd()
// in operation.
if (mRequestedSeekTime >= 0.0) {
ChangeState(PLAY_STATE_SEEKING);
} else {
}
else {
fireEnded = mNextState != PLAY_STATE_PLAYING;
ChangeState(fireEnded ? PLAY_STATE_ENDED : mNextState);
}
@ -641,7 +651,7 @@ void nsOggDecoder::SeekingStoppedAtEnd()
}
}
void nsOggDecoder::SeekingStarted()
void nsBuiltinDecoder::SeekingStarted()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
if (mShuttingDown)
@ -653,7 +663,7 @@ void nsOggDecoder::SeekingStarted()
}
}
void nsOggDecoder::ChangeState(PlayState aState)
void nsBuiltinDecoder::ChangeState(PlayState aState)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
MonitorAutoEnter mon(mMonitor);
@ -673,10 +683,10 @@ void nsOggDecoder::ChangeState(PlayState aState)
/* No action needed */
break;
case PLAY_STATE_PLAYING:
mDecodeStateMachine->Decode();
mDecoderStateMachine->Decode();
break;
case PLAY_STATE_SEEKING:
mDecodeStateMachine->Seek(mRequestedSeekTime);
mDecoderStateMachine->Seek(mRequestedSeekTime);
mRequestedSeekTime = -1.0;
break;
case PLAY_STATE_LOADING:
@ -695,7 +705,7 @@ void nsOggDecoder::ChangeState(PlayState aState)
mMonitor.NotifyAll();
}
void nsOggDecoder::PlaybackPositionChanged()
void nsBuiltinDecoder::PlaybackPositionChanged()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
if (mShuttingDown)
@ -707,10 +717,9 @@ void nsOggDecoder::PlaybackPositionChanged()
// held while the timeupdate and the invalidate is run.
{
MonitorAutoEnter mon(mMonitor);
if (mDecodeStateMachine) {
mCurrentTime = mDecodeStateMachine->GetCurrentTime();
mDecodeStateMachine->ClearPositionChangeFlag();
if (mDecoderStateMachine) {
mCurrentTime = mDecoderStateMachine->GetCurrentTime();
mDecoderStateMachine->ClearPositionChangeFlag();
}
}
@ -725,46 +734,46 @@ void nsOggDecoder::PlaybackPositionChanged()
}
}
void nsOggDecoder::DurationChanged()
void nsBuiltinDecoder::DurationChanged()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
MonitorAutoEnter mon(mMonitor);
PRInt64 oldDuration = mDuration;
mDuration = mDecodeStateMachine ? mDecodeStateMachine->GetDuration() : -1;
mDuration = mDecoderStateMachine ? mDecoderStateMachine->GetDuration() : -1;
if (mElement && oldDuration != mDuration) {
LOG(PR_LOG_DEBUG, ("%p duration changed to %lldms", this, mDuration));
mElement->DispatchSimpleEvent(NS_LITERAL_STRING("durationchange"));
}
}
void nsOggDecoder::SetDuration(PRInt64 aDuration)
void nsBuiltinDecoder::SetDuration(PRInt64 aDuration)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
mDuration = aDuration;
if (mDecodeStateMachine) {
if (mDecoderStateMachine) {
MonitorAutoEnter mon(mMonitor);
mDecodeStateMachine->SetDuration(mDuration);
mDecoderStateMachine->SetDuration(mDuration);
UpdatePlaybackRate();
}
}
void nsOggDecoder::SetSeekable(PRBool aSeekable)
void nsBuiltinDecoder::SetSeekable(PRBool aSeekable)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
mSeekable = aSeekable;
if (mDecodeStateMachine) {
if (mDecoderStateMachine) {
MonitorAutoEnter mon(mMonitor);
mDecodeStateMachine->SetSeekable(aSeekable);
mDecoderStateMachine->SetSeekable(aSeekable);
}
}
PRBool nsOggDecoder::GetSeekable()
PRBool nsBuiltinDecoder::GetSeekable()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
return mSeekable;
}
void nsOggDecoder::Suspend()
void nsBuiltinDecoder::Suspend()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
if (mStream) {
@ -772,7 +781,7 @@ void nsOggDecoder::Suspend()
}
}
void nsOggDecoder::Resume()
void nsBuiltinDecoder::Resume()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
if (mStream) {
@ -780,18 +789,18 @@ void nsOggDecoder::Resume()
}
}
void nsOggDecoder::StopProgressUpdates()
void nsBuiltinDecoder::StopProgressUpdates()
{
NS_ASSERTION(IsThread(mStateMachineThread), "Should be on state machine thread.");
NS_ASSERTION(IsCurrentThread(mStateMachineThread), "Should be on state machine thread.");
mIgnoreProgressData = PR_TRUE;
if (mStream) {
mStream->SetReadMode(nsMediaCacheStream::MODE_METADATA);
}
}
void nsOggDecoder::StartProgressUpdates()
void nsBuiltinDecoder::StartProgressUpdates()
{
NS_ASSERTION(IsThread(mStateMachineThread), "Should be on state machine thread.");
NS_ASSERTION(IsCurrentThread(mStateMachineThread), "Should be on state machine thread.");
mIgnoreProgressData = PR_FALSE;
if (mStream) {
mStream->SetReadMode(nsMediaCacheStream::MODE_PLAYBACK);
@ -799,7 +808,7 @@ void nsOggDecoder::StartProgressUpdates()
}
}
void nsOggDecoder::MoveLoadsToBackground()
void nsBuiltinDecoder::MoveLoadsToBackground()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
if (mStream) {

View File

@ -37,104 +37,28 @@
*
* ***** END LICENSE BLOCK ***** */
/*
Each video element has three threads.
Each video element based on nsBuiltinDecoder has at least one thread
dedicated to decoding video.
1) The state machine thread owns the resources for downloading and reading
the media file. It controls the lifetime of the other two threads
and renders the video data at the correct time during playback.
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.
2) The Audio thread writes the decoded audio data to the audio
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 diplay. At some point
libsydneyaudio will be refactored to have a callback interface
where it asks for data and an extra thread will no longer be
needed.
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.
3) The decode thread. This thread reads from the media stream and decodes
the Theora and Vorbis data. It places the decoded data in a queue
for the other threads to pull from.
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
nsOggDecoder.
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).
All threads have one event that is dispatched to it and that event
runs for the lifetime of the playback of the resource. State shared
between them is synchronised with the main thread via a monitor
held by the nsOggDecoder object. The decode thread also 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 monitor while decoding.
The events consist of a Run method which is an infinite loop that
perform the threads operation and checks the state that the state
machine is in and processes operations on that state.
The nsOggPlayStateMachine class is the event that gets dispatched to
the state machine thread. It has the following states:
DECODING_METADATA
The Ogg 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 and audio threads are started and video frames displayed at
the required time.
SEEKING
A seek operation is in progress.
BUFFERING
Decoding is paused while data is buffered for smooth playback.
COMPLETED
The resource has completed decoding, but not finished playback.
SHUTDOWN
The decoder object is about to be destroyed.
The following result in state transitions.
Shutdown()
Clean up any resources the nsOggPlayStateMachine owns.
Decode()
Start decoding video frames.
Buffer
This is not user initiated. It occurs when the
available data in the stream drops below a certain point.
Complete
This is not user initiated. It occurs when the
stream is completely decoded.
Seek(float)
Seek to the time position given in the resource.
A state transition diagram:
DECODING_METADATA
| |
v | Shutdown()
| |
v -->-------------------->--------------------------|
|---------------->----->------------------------| v
DECODING | | | | |
^ v Seek(t) | | | |
| Decode() | v | | |
^-----------<----SEEKING | v Complete v v
| | | | | |
| | | COMPLETED SHUTDOWN-<-|
^ ^ | |Shutdown() |
| | | >-------->-----^
| Decode() |Seek(t) |Buffer() |
-----------<--------<-------BUFFERING |
| ^
v Shutdown() |
| |
------------>-----|
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 high level player actions required (Seek, Pause, Play, etc).
of a mPlayState variable and notifying on the monitor based on the
high level player actions required (Seek, Pause, Play, etc).
The player states are the states requested by the client through the
DOM API. They represent the desired state of the player, while the
@ -159,102 +83,51 @@ SHUTDOWN
The decoder is about to be destroyed.
State transition occurs when the Media Element calls the Play, Seek,
etc methods on the nsOggDecoder object. When the transition occurs
nsOggDecoder then calls the methods on the decoder state machine
object to cause it to behave appropriate to the play state.
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 appropriate to the play state.
The following represents the states that the player can be in, and the
valid states the decode thread can be in at that time:
player LOADING decoder DECODING_METADATA
player PLAYING decoder DECODING, BUFFERING, SEEKING, COMPLETED
player PAUSED decoder DECODING, BUFFERING, SEEKING, COMPLETED
player SEEKING decoder SEEKING
player COMPLETED decoder SHUTDOWN
player SHUTDOWN decoder SHUTDOWN
The general sequence of events with these objects is:
The general sequence of events is:
1) The video element calls Load on nsMediaDecoder. This creates the
state machine thread and starts the channel for downloading the file. It
instantiates and starts the Decode state machine. The high level
LOADING state is entered, which results in the decode 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.
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
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 Ogg 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.
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 Ogg 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.
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.
The decode thread, while in the DECODING state, plays audio and
video, if the correct frame time comes around and the decoder
play state is PLAYING.
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 stete 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.
a/v synchronisation is handled by the nsDecoderStateMachine implementation.
Frame skipping is done in the following ways:
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.
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.
2) The decode thread will stop decoding interframes and read to the
next keyframe if it determines that decoding the remaining
interframes will cause playback issues. It detects this by:
a) If the amount of audio data in the audio queue drops
below a threshold whereby audio may start to skip.
b) If the video queue drops below a threshold where it
will be decoding video data that won't be displayed due
to the decode thread dropping the frame immediately.
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 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.
During playback the audio thread will be idle (via a Wait() on the
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.
The Shutdown method on nsOggDecoder 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 nsOggDecoder object *MUST* call Shutdown
when destroying the nsOggDecoder object.
This means the owning object of a nsBuiltinDecoder object *MUST* call
Shutdown when destroying the nsOggDecoder object.
*/
#if !defined(nsOggDecoder_h_)
#define nsOggDecoder_h_
#if !defined(nsBuiltinDecoder_h_)
#define nsBuiltinDecoder_h_
#include "nsMediaDecoder.h"
@ -271,19 +144,70 @@ when destroying the nsOggDecoder object.
#include "gfxRect.h"
#include "nsMediaStream.h"
#include "nsMediaDecoder.h"
#include "nsHTMLMediaElement.h"
#include "mozilla/Monitor.h"
using mozilla::Monitor;
class nsAudioStream;
class nsOggPlayStateMachine;
class nsOggReader;
class nsOggDecoder : public nsMediaDecoder
static inline PRBool IsCurrentThread(nsIThread* aThread) {
return NS_GetCurrentThread() == aThread;
}
// Decoder backends must implement this class to perform the codec
// specific parts of decoding the video/audio format.
class nsDecoderStateMachine : public nsRunnable
{
friend class nsOggReader;
friend class nsOggPlayStateMachine;
public:
// Initializes the state machine, returns NS_OK on success, or
// NS_ERROR_FAILURE on failure.
virtual nsresult Init() = 0;
// Set the audio volume. The decoder monitor must be obtained before
// calling this.
virtual void SetVolume(float aVolume) = 0;
virtual void Shutdown() = 0;
// Called from the main thread to get the duration. The decoder monitor
// must be obtained before calling this. It is in units of milliseconds.
virtual PRInt64 GetDuration() = 0;
// Called from the main thread to set the duration of the media resource
// if it is able to be obtained via HTTP headers. The decoder monitor
// must be obtained before calling this.
virtual void SetDuration(PRInt64 aDuration) = 0;
// Functions used by assertions to ensure we're calling things
// on the appropriate threads.
virtual PRBool OnDecodeThread() = 0;
virtual nsHTMLMediaElement::NextFrameStatus GetNextFrameStatus() = 0;
// Cause state transitions. These methods obtain the decoder monitor
// to synchronise the change of state, and to notify other threads
// that the state has changed.
virtual void Decode() = 0;
// Seeks to aTime in seconds
virtual void Seek(float aTime) = 0;
// Returns the current playback position in seconds.
// Called from the main thread to get the current frame time. The decoder
// monitor must be obtained before calling this.
virtual float GetCurrentTime() = 0;
// Clear the flag indicating that a playback position change event
// is currently queued. This is called from the main thread and must
// be called with the decode monitor held.
virtual void ClearPositionChangeFlag() = 0;
// Called from the main thread to set whether the media resource can
// be seeked. The decoder monitor must be obtained before calling this.
virtual void SetSeekable(PRBool aSeekable) = 0;
};
class nsBuiltinDecoder : public nsMediaDecoder
{
// ISupports
NS_DECL_ISUPPORTS
@ -302,11 +226,9 @@ class nsOggDecoder : public nsMediaDecoder
PLAY_STATE_SHUTDOWN
};
nsOggDecoder();
~nsOggDecoder();
nsBuiltinDecoder();
~nsBuiltinDecoder();
virtual nsMediaDecoder* Clone() { return new nsOggDecoder(); }
virtual PRBool Init(nsHTMLMediaElement* aElement);
// This method must be called by the owning object before that
@ -318,6 +240,8 @@ class nsOggDecoder : public nsMediaDecoder
virtual nsresult Load(nsMediaStream* aStream,
nsIStreamListener** aListener);
virtual nsDecoderStateMachine* CreateStateMachine() = 0;
// Start playback of a video. 'Load' must have previously been
// called.
virtual nsresult Play();
@ -391,14 +315,21 @@ class nsOggDecoder : public nsMediaDecoder
// has changed.
void DurationChanged();
protected:
PRBool OnStateMachineThread() {
return IsCurrentThread(mStateMachineThread);
}
PRBool OnDecodeThread() {
return mDecoderStateMachine->OnDecodeThread();
}
// Returns the monitor for other threads to synchronise access to
// state.
Monitor& GetMonitor() {
mozilla::Monitor& GetMonitor() {
return mMonitor;
}
public:
// 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() {
@ -474,7 +405,7 @@ protected:
// position.
PRInt64 GetDownloadPosition();
private:
public:
// Notifies the element that decoding has failed.
void DecodeError();
@ -535,7 +466,7 @@ private:
// is synchronised on a monitor. The lifetime of this object is
// after mPlayState is LOADING and before mPlayState is SHUTDOWN. It
// is safe to access it during this period.
nsCOMPtr<nsOggPlayStateMachine> mDecodeStateMachine;
nsCOMPtr<nsDecoderStateMachine> mDecoderStateMachine;
// Stream of media data.
nsAutoPtr<nsMediaStream> mStream;
@ -543,7 +474,7 @@ private:
// Monitor for detecting when the video play state changes. A call
// to Wait on this monitor will block the thread until the next
// state change.
Monitor mMonitor;
mozilla::Monitor mMonitor;
// Set to one of the valid play states. It is protected by the
// monitor mMonitor. This monitor must be acquired when reading or

View File

@ -228,6 +228,12 @@ public:
// thread; ImageContainers can be used from any thread.
ImageContainer* GetImageContainer() { return mImageContainer; }
// Set the video width, height, pixel aspect ratio, and current image.
// Ownership of the image is transferred to the decoder.
void SetVideoData(const gfxIntSize& aSize,
float aAspectRatio,
Image* aImage);
protected:
// Start timer to update download progress information.
@ -236,12 +242,6 @@ protected:
// Stop progress information timer.
nsresult StopProgress();
// Set the video width, height, pixel aspect ratio, and current image.
// Ownership of the image is transferred to the decoder.
void SetVideoData(const gfxIntSize& aSize,
float aAspectRatio,
Image* aImage);
protected:
// Timer used for updating progress events
nsCOMPtr<nsITimer> mProgressTimer;

View File

@ -41,7 +41,7 @@
#include "nsOggDecoder.h"
#include <string.h>
#include "nsTraceRefcnt.h"
#include "nsOggHacks.h"
#include "VideoUtils.h"
/*
The maximum height and width of the video. Used for

View File

@ -0,0 +1,46 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla code.
*
* The Initial Developer of the Original Code is the Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Chris Double <chris.double@double.co.nz>
* Chris Pearce <chris@pearce.org.nz>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsOggPlayStateMachine.h"
#include "nsOggDecoder.h"
nsDecoderStateMachine* nsOggDecoder::CreateStateMachine()
{
return new nsOggPlayStateMachine(this);
}

View File

@ -0,0 +1,51 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla code.
*
* The Initial Developer of the Original Code is the Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Chris Double <chris.double@double.co.nz>
* Chris Pearce <chris@pearce.org.nz>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#if !defined(nsOggDecoder_h_)
#define nsOggDecoder_h_
#include "nsBuiltinDecoder.h"
class nsOggDecoder : public nsBuiltinDecoder
{
public:
virtual nsMediaDecoder* Clone() { return new nsOggDecoder(); }
virtual nsDecoderStateMachine* CreateStateMachine();
};
#endif

View File

@ -40,12 +40,12 @@
#include <limits>
#include "nsAudioStream.h"
#include "nsTArray.h"
#include "nsOggDecoder.h"
#include "nsBuiltinDecoder.h"
#include "nsOggReader.h"
#include "nsOggPlayStateMachine.h"
#include "oggplay/oggplay.h"
#include "mozilla/mozalloc.h"
#include "nsOggHacks.h"
#include "VideoUtils.h"
using namespace mozilla::layers;
using mozilla::MonitorAutoExit;
@ -55,8 +55,8 @@ using mozilla::MonitorAutoExit;
static PRBool AddOverflow(PRUint32 a, PRUint32 b, PRUint32& aResult);
#ifdef PR_LOGGING
extern PRLogModuleInfo* gOggDecoderLog;
#define LOG(type, msg) PR_LOG(gOggDecoderLog, type, msg)
extern PRLogModuleInfo* gBuiltinDecoderLog;
#define LOG(type, msg) PR_LOG(gBuiltinDecoderLog, type, msg)
#else
#define LOG(type, msg)
#endif
@ -77,7 +77,7 @@ extern PRLogModuleInfo* gOggDecoderLog;
// be played.
#define AUDIO_FRAME_RATE 25.0
nsOggPlayStateMachine::nsOggPlayStateMachine(nsOggDecoder* aDecoder) :
nsOggPlayStateMachine::nsOggPlayStateMachine(nsBuiltinDecoder* aDecoder) :
mDecoder(aDecoder),
mState(DECODER_STATE_DECODING_METADATA),
mAudioMonitor("media.audiostream"),
@ -200,7 +200,7 @@ void nsOggPlayStateMachine::DecodeLoop()
{
MonitorAutoEnter mon(mDecoder->GetMonitor());
initialDownloadPosition =
mDecoder->mStream->GetCachedDataEnd(mDecoder->mDecoderPosition);
mDecoder->GetCurrentStream()->GetCachedDataEnd(mDecoder->mDecoderPosition);
currentTime = mCurrentFrameTime + mStartTime;
}
@ -234,7 +234,7 @@ void nsOggPlayStateMachine::DecodeLoop()
{
MonitorAutoEnter mon(mDecoder->GetMonitor());
initialDownloadPosition =
mDecoder->mStream->GetCachedDataEnd(mDecoder->mDecoderPosition);
mDecoder->GetCurrentStream()->GetCachedDataEnd(mDecoder->mDecoderPosition);
mDecoder->GetMonitor().NotifyAll();
}
@ -402,7 +402,7 @@ nsresult nsOggPlayStateMachine::Init()
void nsOggPlayStateMachine::StopPlayback(eStopMode aMode)
{
NS_ASSERTION(IsThread(mDecoder->mStateMachineThread),
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
"Should be on state machine thread.");
mDecoder->GetMonitor().AssertCurrentThreadIn();
@ -431,7 +431,7 @@ void nsOggPlayStateMachine::StopPlayback(eStopMode aMode)
void nsOggPlayStateMachine::StartPlayback()
{
NS_ASSERTION(IsThread(mDecoder->mStateMachineThread),
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
"Should be on state machine thread.");
NS_ASSERTION(!IsPlaying(), "Shouldn't be playing when StartPlayback() is called");
mDecoder->GetMonitor().AssertCurrentThreadIn();
@ -458,7 +458,7 @@ void nsOggPlayStateMachine::StartPlayback()
void nsOggPlayStateMachine::UpdatePlaybackPosition(PRInt64 aTime)
{
NS_ASSERTION(IsThread(mDecoder->mStateMachineThread),
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
"Should be on state machine thread.");
mDecoder->GetMonitor().AssertCurrentThreadIn();
@ -470,13 +470,13 @@ void nsOggPlayStateMachine::UpdatePlaybackPosition(PRInt64 aTime)
"CurrentTime must be after duration if aTime > endTime!");
mEndTime = aTime;
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(mDecoder, &nsOggDecoder::DurationChanged);
NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::DurationChanged);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
}
if (!mPositionChangeQueued) {
mPositionChangeQueued = PR_TRUE;
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(mDecoder, &nsOggDecoder::PlaybackPositionChanged);
NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::PlaybackPositionChanged);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
}
}
@ -582,7 +582,7 @@ void nsOggPlayStateMachine::Decode()
void nsOggPlayStateMachine::ResetPlayback()
{
NS_ASSERTION(IsThread(mDecoder->mStateMachineThread),
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
"Should be on state machine thread.");
mVideoFrameTime = -1;
mAudioStartTime = -1;
@ -594,8 +594,8 @@ void nsOggPlayStateMachine::Seek(float aTime)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
MonitorAutoEnter mon(mDecoder->GetMonitor());
// nsOggDecoder::mPlayState should be SEEKING while we seek, and
// in that case nsOggDecoder shouldn't be calling us.
// nsBuiltinDecoder::mPlayState should be SEEKING while we seek, and
// in that case nsBuiltinDecoder shouldn't be calling us.
NS_ASSERTION(mState != DECODER_STATE_SEEKING,
"We shouldn't already be seeking");
NS_ASSERTION(mState >= DECODER_STATE_DECODING,
@ -621,7 +621,7 @@ void nsOggPlayStateMachine::Seek(float aTime)
void nsOggPlayStateMachine::StopDecodeThreads()
{
NS_ASSERTION(IsThread(mDecoder->mStateMachineThread),
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
"Should be on state machine thread.");
mDecoder->GetMonitor().AssertCurrentThreadIn();
mStopDecodeThreads = PR_TRUE;
@ -645,7 +645,7 @@ void nsOggPlayStateMachine::StopDecodeThreads()
nsresult
nsOggPlayStateMachine::StartDecodeThreads()
{
NS_ASSERTION(IsThread(mDecoder->mStateMachineThread),
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
"Should be on state machine thread.");
mDecoder->GetMonitor().AssertCurrentThreadIn();
mStopDecodeThreads = PR_FALSE;
@ -674,9 +674,9 @@ nsOggPlayStateMachine::StartDecodeThreads()
nsresult nsOggPlayStateMachine::Run()
{
NS_ASSERTION(IsThread(mDecoder->mStateMachineThread),
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
"Should be on state machine thread.");
nsMediaStream* stream = mDecoder->mStream;
nsMediaStream* stream = mDecoder->GetCurrentStream();
NS_ENSURE_TRUE(stream, NS_ERROR_NULL_POINTER);
while (PR_TRUE) {
@ -724,7 +724,7 @@ nsresult nsOggPlayStateMachine::Run()
// Inform the element that we've loaded the Ogg metadata and the
// first frame.
nsCOMPtr<nsIRunnable> metadataLoadedEvent =
NS_NewRunnableMethod(mDecoder, &nsOggDecoder::MetadataLoaded);
NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::MetadataLoaded);
NS_DispatchToMainThread(metadataLoadedEvent, NS_DISPATCH_NORMAL);
if (mState == DECODER_STATE_DECODING_METADATA) {
@ -733,7 +733,7 @@ nsresult nsOggPlayStateMachine::Run()
}
// Start playback.
if (mDecoder->GetState() == nsOggDecoder::PLAY_STATE_PLAYING) {
if (mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING) {
if (!IsPlaying()) {
StartPlayback();
}
@ -753,9 +753,9 @@ nsresult nsOggPlayStateMachine::Run()
continue;
if (mBufferExhausted &&
mDecoder->GetState() == nsOggDecoder::PLAY_STATE_PLAYING &&
!mDecoder->mStream->IsDataCachedToEndOfStream(mDecoder->mDecoderPosition) &&
!mDecoder->mStream->IsSuspendedByCache()) {
mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING &&
!mDecoder->GetCurrentStream()->IsDataCachedToEndOfStream(mDecoder->mDecoderPosition) &&
!mDecoder->GetCurrentStream()->IsSuspendedByCache()) {
// There is at most one frame in the queue and there's
// more data to load. Let's buffer to make sure we can play a
// decent amount of video in the future.
@ -817,7 +817,7 @@ nsresult nsOggPlayStateMachine::Run()
{
MonitorAutoExit exitMon(mDecoder->GetMonitor());
nsCOMPtr<nsIRunnable> startEvent =
NS_NewRunnableMethod(mDecoder, &nsOggDecoder::SeekingStarted);
NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::SeekingStarted);
NS_DispatchToMainThread(startEvent, NS_DISPATCH_SYNC);
}
if (mCurrentFrameTime != mSeekTime - mStartTime) {
@ -864,12 +864,12 @@ nsresult nsOggPlayStateMachine::Run()
if (mCurrentFrameTime == mEndTime) {
LOG(PR_LOG_DEBUG, ("%p Changed state from SEEKING (to %lldms) to COMPLETED",
mDecoder, seekTime));
stopEvent = NS_NewRunnableMethod(mDecoder, &nsOggDecoder::SeekingStoppedAtEnd);
stopEvent = NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::SeekingStoppedAtEnd);
mState = DECODER_STATE_COMPLETED;
} else {
LOG(PR_LOG_DEBUG, ("%p Changed state from SEEKING (to %lldms) to DECODING",
mDecoder, seekTime));
stopEvent = NS_NewRunnableMethod(mDecoder, &nsOggDecoder::SeekingStopped);
stopEvent = NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::SeekingStopped);
mState = DECODER_STATE_DECODING;
}
mBufferExhausted = PR_FALSE;
@ -886,12 +886,12 @@ nsresult nsOggPlayStateMachine::Run()
{
TimeStamp now = TimeStamp::Now();
if (now - mBufferingStart < TimeDuration::FromSeconds(BUFFERING_WAIT) &&
mDecoder->mStream->GetCachedDataEnd(mDecoder->mDecoderPosition) < mBufferingEndOffset &&
!mDecoder->mStream->IsDataCachedToEndOfStream(mDecoder->mDecoderPosition) &&
!mDecoder->mStream->IsSuspendedByCache()) {
mDecoder->GetCurrentStream()->GetCachedDataEnd(mDecoder->mDecoderPosition) < mBufferingEndOffset &&
!mDecoder->GetCurrentStream()->IsDataCachedToEndOfStream(mDecoder->mDecoderPosition) &&
!mDecoder->GetCurrentStream()->IsSuspendedByCache()) {
LOG(PR_LOG_DEBUG,
("In buffering: buffering data until %d bytes available or %f seconds",
PRUint32(mBufferingEndOffset - mDecoder->mStream->GetCachedDataEnd(mDecoder->mDecoderPosition)),
PRUint32(mBufferingEndOffset - mDecoder->GetCurrentStream()->GetCachedDataEnd(mDecoder->mDecoderPosition)),
BUFFERING_WAIT - (now - mBufferingStart).ToSeconds()));
Wait(1000);
if (mState == DECODER_STATE_SHUTDOWN)
@ -909,7 +909,7 @@ nsresult nsOggPlayStateMachine::Run()
// Notify to allow blocked decoder thread to continue
mDecoder->GetMonitor().NotifyAll();
UpdateReadyState();
if (mDecoder->GetState() == nsOggDecoder::PLAY_STATE_PLAYING) {
if (mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING) {
if (!IsPlaying()) {
StartPlayback();
}
@ -946,14 +946,14 @@ nsresult nsOggPlayStateMachine::Run()
LOG(PR_LOG_DEBUG, ("Shutting down the state machine thread"));
StopDecodeThreads();
if (mDecoder->GetState() == nsOggDecoder::PLAY_STATE_PLAYING) {
if (mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING) {
PRInt64 videoTime = HasVideo() ? (mVideoFrameTime + mInfo.mCallbackPeriod) : 0;
PRInt64 clockTime = NS_MAX(mEndTime, NS_MAX(videoTime, GetAudioClock()));
UpdatePlaybackPosition(clockTime);
{
MonitorAutoExit exitMon(mDecoder->GetMonitor());
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(mDecoder, &nsOggDecoder::PlaybackEnded);
NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::PlaybackEnded);
NS_DispatchToMainThread(event, NS_DISPATCH_SYNC);
}
}
@ -971,7 +971,7 @@ nsresult nsOggPlayStateMachine::Run()
void nsOggPlayStateMachine::RenderVideoFrame(VideoData* aData)
{
NS_ASSERTION(IsThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
if (aData->mDuplicate) {
return;
@ -1054,7 +1054,7 @@ void nsOggPlayStateMachine::RenderVideoFrame(VideoData* aData)
PRInt64
nsOggPlayStateMachine::GetAudioClock()
{
NS_ASSERTION(IsThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
if (!mAudioStream || !HasAudio())
return -1;
PRInt64 t = mAudioStream->GetPosition();
@ -1063,11 +1063,11 @@ nsOggPlayStateMachine::GetAudioClock()
void nsOggPlayStateMachine::AdvanceFrame()
{
NS_ASSERTION(IsThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
mDecoder->GetMonitor().AssertCurrentThreadIn();
// When it's time to display a frame, decode the frame and display it.
if (mDecoder->GetState() == nsOggDecoder::PLAY_STATE_PLAYING) {
if (mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING) {
if (!IsPlaying()) {
StartPlayback();
mDecoder->GetMonitor().NotifyAll();
@ -1184,7 +1184,7 @@ void nsOggPlayStateMachine::Wait(PRUint32 aMs) {
void nsOggPlayStateMachine::LoadOggHeaders()
{
NS_ASSERTION(IsThread(mDecoder->mStateMachineThread),
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
"Should be on state machine thread.");
mDecoder->GetMonitor().AssertCurrentThreadIn();
@ -1203,7 +1203,7 @@ void nsOggPlayStateMachine::LoadOggHeaders()
if (!mInfo.mHasVideo && !mInfo.mHasAudio) {
mState = DECODER_STATE_SHUTDOWN;
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(mDecoder, &nsOggDecoder::DecodeError);
NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::DecodeError);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
return;
}
@ -1235,7 +1235,7 @@ void nsOggPlayStateMachine::LoadOggHeaders()
VideoData* nsOggPlayStateMachine::FindStartTime()
{
NS_ASSERTION(IsThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
mDecoder->GetMonitor().AssertCurrentThreadIn();
PRInt64 startTime = 0;
mStartTime = 0;
@ -1264,7 +1264,7 @@ void nsOggPlayStateMachine::FindEndTime()
NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
mDecoder->GetMonitor().AssertCurrentThreadIn();
nsMediaStream* stream = mDecoder->mStream;
nsMediaStream* stream = mDecoder->GetCurrentStream();
// Seek to the end of file to find the length and duration.
PRInt64 length = stream->GetLength();
@ -1295,13 +1295,13 @@ void nsOggPlayStateMachine::UpdateReadyState() {
nsCOMPtr<nsIRunnable> event;
switch (GetNextFrameStatus()) {
case nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE_BUFFERING:
event = NS_NewRunnableMethod(mDecoder, &nsOggDecoder::NextFrameUnavailableBuffering);
event = NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::NextFrameUnavailableBuffering);
break;
case nsHTMLMediaElement::NEXT_FRAME_AVAILABLE:
event = NS_NewRunnableMethod(mDecoder, &nsOggDecoder::NextFrameAvailable);
event = NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::NextFrameAvailable);
break;
case nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE:
event = NS_NewRunnableMethod(mDecoder, &nsOggDecoder::NextFrameUnavailable);
event = NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::NextFrameUnavailable);
break;
default:
PR_NOT_REACHED("unhandled frame state");

View File

@ -36,27 +36,159 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
Each video element for an Ogg 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 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
where it asks for data and an extra thread will no longer be
needed.
2) The decode thread. This thread reads from the media stream and
decodes the Theora and Vorbis data. It places the decoded data in
a queue for the other threads to pull from.
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 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.
The nsOggPlayStateMachine class is the event that gets dispatched to
the state machine thread. It has the following states:
DECODING_METADATA
The Ogg 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 and audio threads are started and video frames displayed at
the required time.
SEEKING
A seek operation is in progress.
BUFFERING
Decoding is paused while data is buffered for smooth playback.
COMPLETED
The resource has completed decoding, but not finished playback.
SHUTDOWN
The decoder object is about to be destroyed.
The following result in state transitions.
Shutdown()
Clean up any resources the nsOggPlayStateMachine owns.
Decode()
Start decoding video frames.
Buffer
This is not user initiated. It occurs when the
available data in the stream drops below a certain point.
Complete
This is not user initiated. It occurs when the
stream is completely decoded.
Seek(float)
Seek to the time position given in the resource.
A state transition diagram:
DECODING_METADATA
| |
v | Shutdown()
| |
v -->-------------------->--------------------------|
|---------------->----->------------------------| v
DECODING | | | | |
^ v Seek(t) | | | |
| Decode() | v | | |
^-----------<----SEEKING | v Complete v v
| | | | | |
| | | COMPLETED SHUTDOWN-<-|
^ ^ | |Shutdown() |
| | | >-------->-----^
| Decode() |Seek(t) |Buffer() |
-----------<--------<-------BUFFERING |
| ^
v Shutdown() |
| |
------------>-----|
The following represents the states that the nsBuiltinDecoder object
can be in, and the valid states the decode thread can be in at that
time:
player LOADING decoder DECODING_METADATA
player PLAYING decoder DECODING, BUFFERING, SEEKING, COMPLETED
player PAUSED decoder DECODING, BUFFERING, SEEKING, COMPLETED
player SEEKING decoder SEEKING
player COMPLETED decoder SHUTDOWN
player SHUTDOWN decoder SHUTDOWN
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 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.
2) The decode thread will stop decoding interframes and read to the
next keyframe if it determines that decoding the remaining
interframes will cause playback issues. It detects this by:
a) If the amount of audio data in the audio queue drops
below a threshold whereby audio may start to skip.
b) If the video queue drops below a threshold where it
will be decoding video data that won't be displayed due
to the decode thread dropping the frame immediately.
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 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.
During playback the audio thread will be idle (via a Wait() on the
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(nsOggPlayStateMachine_h__)
#define nsOggPlayStateMachine_h__
#include "prmem.h"
#include "nsThreadUtils.h"
#include "nsOggReader.h"
#include "nsOggDecoder.h"
#include "nsBuiltinDecoder.h"
#include "nsHTMLMediaElement.h"
#include "mozilla/Monitor.h"
using mozilla::TimeDuration;
using mozilla::TimeStamp;
class nsOggDecoder;
// Checks if we're on a specific thread or not. Used in assertions to
// verify thread safety.
static inline PRBool IsThread(nsIThread* aThread) {
return NS_GetCurrentThread() == aThread;
}
/*
The playback state machine class. This manages the decoding in the
nsOggReader on the decode thread, seeking and in-sync-playback on the
@ -74,7 +206,7 @@ static inline PRBool IsThread(nsIThread* aThread) {
See nsOggDecoder.h for more details.
*/
class nsOggPlayStateMachine : public nsRunnable
class nsOggPlayStateMachine : public nsDecoderStateMachine
{
public:
// Enumeration for the valid states
@ -87,21 +219,25 @@ public:
DECODER_STATE_SHUTDOWN
};
nsOggPlayStateMachine(nsOggDecoder* aDecoder);
nsOggPlayStateMachine(nsBuiltinDecoder* aDecoder);
~nsOggPlayStateMachine();
// Initializes the state machine, returns NS_OK on success, or
// NS_ERROR_FAILURE on failure.
nsresult Init();
// nsDecoderStateMachine interface
virtual nsresult Init();
virtual void SetVolume(float aVolume);
virtual void Shutdown();
virtual PRInt64 GetDuration();
virtual void SetDuration(PRInt64 aDuration);
virtual PRBool OnDecodeThread() {
return IsCurrentThread(mDecodeThread);
}
// Cause state transitions. These methods obtain the decoder monitor
// to synchronise the change of state, and to notify other threads
// that the state has changed.
void Shutdown();
void Decode();
// Seeks to aTime seconds.
void Seek(float aTime);
virtual nsHTMLMediaElement::NextFrameStatus GetNextFrameStatus();
virtual void Decode();
virtual void Seek(float aTime);
virtual float GetCurrentTime();
virtual void ClearPositionChangeFlag();
virtual void SetSeekable(PRBool aSeekable);
// State machine thread run function. Polls the state, sends frames to be
// displayed at appropriate times, and generally manages the decode.
@ -121,33 +257,6 @@ public:
return mInfo.mHasVideo;
}
// Returns the current playback position in seconds.
// Called from the main thread to get the current frame time. The decoder
// monitor must be obtained before calling this.
float GetCurrentTime();
// Called from the main thread to get the duration. The decoder monitor
// must be obtained before calling this. It is in units of milliseconds.
PRInt64 GetDuration();
// Called from the main thread to set the duration of the media resource
// if it is able to be obtained via HTTP headers. The decoder monitor
// must be obtained before calling this.
void SetDuration(PRInt64 aDuration);
// Called from the main thread to set whether the media resource can
// be seeked. The decoder monitor must be obtained before calling this.
void SetSeekable(PRBool aSeekable);
// Set the audio volume. The decoder monitor must be obtained before
// calling this.
void SetVolume(float aVolume);
// Clear the flag indicating that a playback position change event
// is currently queued. This is called from the main thread and must
// be called with the decode monitor held.
void ClearPositionChangeFlag();
// Should be called by main thread.
PRBool HaveNextFrameData() const {
PRUint32 audioQueueSize = mReader->mAudioQueue.GetSize();
@ -172,16 +281,12 @@ public:
// Functions used by assertions to ensure we're calling things
// on the appropriate threads.
PRBool OnStateMachineThread() {
return IsThread(mDecoder->mStateMachineThread);
}
PRBool OnDecodeThread() {
return IsThread(mDecodeThread);
}
PRBool OnAudioThread() {
return IsThread(mAudioThread);
return IsCurrentThread(mAudioThread);
}
PRBool OnStateMachineThread() {
return mDecoder->OnStateMachineThread();
}
// Decode loop, called on the decode thread.
@ -190,7 +295,7 @@ public:
// 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.
nsOggDecoder* mDecoder;
nsBuiltinDecoder* mDecoder;
// Update the playback position. This can result in a timeupdate event
// and an invalidate of the frame being dispatched asynchronously if
@ -199,8 +304,6 @@ public:
// the decode monitor held.
void UpdatePlaybackPosition(PRInt64 aTime);
nsHTMLMediaElement::NextFrameStatus GetNextFrameStatus();
// The decoder monitor must be obtained before modifying this state.
// NotifyAll on the monitor must be called when the state is changed by
// the main thread so the decoder thread can wake up.

View File

@ -45,7 +45,7 @@
#include "nsOggCodecState.h"
#include "nsOggPlayStateMachine.h"
#include "mozilla/mozalloc.h"
#include "nsOggHacks.h"
#include "VideoUtils.h"
using mozilla::MonitorAutoExit;
@ -53,10 +53,10 @@ using mozilla::MonitorAutoExit;
//#define SEEK_LOGGING
#ifdef PR_LOGGING
extern PRLogModuleInfo* gOggDecoderLog;
#define LOG(type, msg) PR_LOG(gOggDecoderLog, type, msg)
extern PRLogModuleInfo* gBuiltinDecoderLog;
#define LOG(type, msg) PR_LOG(gBuiltinDecoderLog, type, msg)
#ifdef SEEK_LOGGING
#define SEEK_LOG(type, msg) PR_LOG(gOggDecoderLog, type, msg)
#define SEEK_LOG(type, msg) PR_LOG(gBuiltinDecoderLog, type, msg)
#else
#define SEEK_LOG(type, msg)
#endif
@ -566,7 +566,7 @@ nsresult nsOggReader::GetBufferedBytes(nsTArray<ByteRange>& aRanges)
"Should be on state machine thread.");
mMonitor.AssertCurrentThreadIn();
PRInt64 startOffset = mDataOffset;
nsMediaStream* stream = mPlayer->mDecoder->mStream;
nsMediaStream* stream = mPlayer->mDecoder->GetCurrentStream();
while (PR_TRUE) {
PRInt64 endOffset = stream->GetCachedDataEnd(startOffset);
if (endOffset == startOffset) {
@ -612,7 +612,7 @@ nsOggReader::GetSeekRange(const nsTArray<ByteRange>& ranges,
NS_ASSERTION(mPlayer->OnStateMachineThread(),
"Should be on state machine thread.");
PRInt64 so = mDataOffset;
PRInt64 eo = mPlayer->mDecoder->mStream->GetLength();
PRInt64 eo = mPlayer->mDecoder->GetCurrentStream()->GetLength();
PRInt64 st = aStartTime;
PRInt64 et = aEndTime;
for (PRUint32 i = 0; i < ranges.Length(); i++) {
@ -640,7 +640,7 @@ nsresult nsOggReader::Seek(PRInt64 aTarget, PRInt64 aStartTime, PRInt64 aEndTime
NS_ASSERTION(mPlayer->OnStateMachineThread(),
"Should be on state machine thread.");
LOG(PR_LOG_DEBUG, ("%p About to seek to %lldms", mPlayer->mDecoder, aTarget));
nsMediaStream* stream = mPlayer->mDecoder->mStream;
nsMediaStream* stream = mPlayer->mDecoder->GetCurrentStream();
if (NS_FAILED(ResetDecode())) {
return NS_ERROR_FAILURE;
@ -888,7 +888,7 @@ nsresult nsOggReader::SeekBisection(PRInt64 aTarget,
{
NS_ASSERTION(mPlayer->OnStateMachineThread(),
"Should be on state machine thread.");
nsMediaStream* stream = mPlayer->mDecoder->mStream;
nsMediaStream* stream = mPlayer->mDecoder->GetCurrentStream();
if (aTarget == aRange.mTimeStart) {
if (NS_FAILED(ResetDecode())) {
@ -1121,7 +1121,7 @@ PRInt64 nsOggReader::ReadOggPage(ogg_page* aPage)
// Read from the stream into the buffer
PRUint32 bytesRead = 0;
nsresult rv = mPlayer->mDecoder->mStream->Read(buffer, 4096, &bytesRead);
nsresult rv = mPlayer->mDecoder->GetCurrentStream()->Read(buffer, 4096, &bytesRead);
if (NS_FAILED(rv) || (bytesRead == 0 && ret == 0)) {
// End of file.
return -1;
@ -1371,7 +1371,7 @@ VideoData* nsOggReader::FindStartTime(PRInt64 aOffset,
{
NS_ASSERTION(mPlayer->OnStateMachineThread(), "Should be on state machine thread.");
nsMediaStream* stream = mPlayer->mDecoder->mStream;
nsMediaStream* stream = mPlayer->mDecoder->GetCurrentStream();
stream->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
if (NS_FAILED(ResetDecode())) {
@ -1427,7 +1427,7 @@ PRInt64 nsOggReader::FindEndTime(PRInt64 aEndOffset)
MonitorAutoEnter mon(mMonitor);
NS_ASSERTION(mPlayer->OnStateMachineThread(), "Should be on state machine thread.");
nsMediaStream* stream = mPlayer->mDecoder->mStream;
nsMediaStream* stream = mPlayer->mDecoder->GetCurrentStream();
ogg_sync_reset(&mOggState);
stream->Seek(nsISeekableStream::NS_SEEK_SET, aEndOffset);

View File

@ -44,7 +44,6 @@
#include <ogg/ogg.h>
#include <theora/theoradec.h>
#include <vorbis/codec.h>
#include "prmon.h"
#include "nsAutoLock.h"
#include "nsClassHashtable.h"
#include "mozilla/TimeStamp.h"