Bug 560708 - Factor non-Ogg specific logic into decoder base class. r=cpearce sr=roc
authorChris Double <chris.double@double.co.nz>
Tue, 27 Apr 2010 20:53:44 +1200
changeset 41387 2bd54675c370d1022e1e5d788494f26c24bc6d24
parent 41386 5d89cbfa2053a8aacec289eca2350682b74168b9
child 41388 77edf4302cdd802fc23fe65f7f242cbcf9a7144f
push id12998
push usercpearce@mozilla.com
push dateTue, 27 Apr 2010 09:17:07 +0000
treeherdermozilla-central@590a7be0e60f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, roc
bugs560708
milestone1.9.3a5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
Bug 560708 - Factor non-Ogg specific logic into decoder base class. r=cpearce sr=roc
content/media/Makefile.in
content/media/VideoUtils.h
content/media/nsBuiltinDecoder.cpp
content/media/nsBuiltinDecoder.h
content/media/nsMediaDecoder.h
content/media/ogg/nsOggCodecState.cpp
content/media/ogg/nsOggDecoder.cpp
content/media/ogg/nsOggDecoder.h
content/media/ogg/nsOggPlayStateMachine.cpp
content/media/ogg/nsOggPlayStateMachine.h
content/media/ogg/nsOggReader.cpp
content/media/ogg/nsOggReader.h
--- a/content/media/Makefile.in
+++ b/content/media/Makefile.in
@@ -44,22 +44,25 @@ include $(DEPTH)/config/autoconf.mk
 MODULE = content
 LIBRARY_NAME = gkconmedia_s
 LIBXUL_LIBRARY = 1
 
 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
 EXPORTS += \
   nsAudioStream.h \
   $(NULL)
 CPPSRCS += \
   nsAudioStream.cpp \
--- a/content/media/VideoUtils.h
+++ b/content/media/VideoUtils.h
@@ -31,18 +31,20 @@
  * 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 ***** */
 
-#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
 // remove this file in the near future.
 
 
 // This belongs in prtypes.h
 /************************************************************************
--- a/content/media/nsBuiltinDecoder.cpp
+++ b/content/media/nsBuiltinDecoder.cpp
@@ -32,161 +32,163 @@
  * 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 <limits>
 #include "nsNetUtil.h"
 #include "nsAudioStream.h"
 #include "nsHTMLVideoElement.h"
 #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);
   if (mPlayState == PLAY_STATE_SEEKING || mPlayState == PLAY_STATE_ENDED) {
     mNextState = PLAY_STATE_PAUSED;
     return;
   }
 
   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) {
      return static_cast<float>(mDuration) / 1000.0;
   }
   return std::numeric_limits<float>::quiet_NaN();
 }
 
-nsOggDecoder::nsOggDecoder() :
+nsBuiltinDecoder::nsBuiltinDecoder() :
   mDecoderPosition(0),
   mPlaybackPosition(0),
   mCurrentTime(0.0),
   mInitialVolume(0.0),
   mRequestedSeekTime(-1.0),
   mDuration(-1),
   mSeekable(PR_TRUE),
   mMonitor("media.decoder"),
   mPlayState(PLAY_STATE_PAUSED),
   mNextState(PLAY_STATE_PAUSED),
   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))
     return PR_FALSE;
 
   nsContentUtils::RegisterShutdownObserver(this);
   mImageContainer = aElement->GetImageContainer();
   return PR_TRUE;
 }
 
-void nsOggDecoder::Stop()
+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;
-  mDecodeStateMachine = nsnull;
+  mDecoderStateMachine = nsnull;
 }
 
-void nsOggDecoder::Shutdown()
+void nsBuiltinDecoder::Shutdown()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   
   if (mShuttingDown)
     return;
 
   mShuttingDown = PR_TRUE;
 
   // 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
   // to prevent shutdown from deadlocking.
   if (mStream) {
     mStream->Close();
   }
 
   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
   // destruction.
   // 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.");
   if (aStreamListener) {
     *aStreamListener = nsnull;
   }
 
   {
@@ -202,121 +204,126 @@ nsresult nsOggDecoder::Load(nsMediaStrea
     }
 
     mStream = 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);
   if (mPlayState == PLAY_STATE_SEEKING) {
     mNextState = PLAY_STATE_PLAYING;
     return NS_OK;
   }
   if (mPlayState == PLAY_STATE_ENDED)
     return Seek(0);
 
   ChangeState(PLAY_STATE_PLAYING);
 
   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);
 
   if (aTime < 0.0)
     return NS_ERROR_FAILURE;
 
   mRequestedSeekTime = aTime;
 
   // If we are already in the seeking state, then setting mRequestedSeekTime
   // above will result in the new seek occurring when the current seek
   // completes.
   if (mPlayState != PLAY_STATE_SEEKING) {
     if (mPlayState == PLAY_STATE_ENDED) {
       mNextState = PLAY_STATE_PLAYING;
-    } else {
+    }
+    else {
       mNextState = mPlayState;
     }
     ChangeState(PLAY_STATE_SEEKING);
   }
 
   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)
     return;
 
   // Only inform the element of MetadataLoaded if not doing a load() in order
   // to fulfill a seek, otherwise we'll get multiple metadataloaded events.
   PRBool notifyElement = PR_TRUE;
   {
     MonitorAutoEnter mon(mMonitor);
-    mDuration = mDecodeStateMachine ? mDecodeStateMachine->GetDuration() : -1;
+    mDuration = mDecoderStateMachine ? mDecoderStateMachine->GetDuration() : -1;
     notifyElement = mNextState != PLAY_STATE_SEEKING;
   }
 
   if (mElement && notifyElement) {
     // Make sure the element and the frame (if any) are told about
     // our new size.
     Invalidate();
     mElement->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"));
   }
 
   // Only inform the element of FirstFrameLoaded if not doing a load() in order
   // to fulfill a seek, otherwise we'll get multiple loadedfirstframe events.
   PRBool resourceIsLoaded = !mResourceLoaded && mStream &&
@@ -328,27 +335,28 @@ void nsOggDecoder::MetadataLoaded()
   // The element can run javascript via events
   // before reaching here, so only change the
   // state if we're still set to the original
   // loading state.
   MonitorAutoEnter mon(mMonitor);
   if (mPlayState == PLAY_STATE_LOADING) {
     if (mRequestedSeekTime >= 0.0) {
       ChangeState(PLAY_STATE_SEEKING);
-    } else {
+    }
+    else {
       ChangeState(mNextState);
     }
   }
 
   if (resourceIsLoaded) {
     ResourceLoaded();
   }
 }
 
-void nsOggDecoder::ResourceLoaded()
+void nsBuiltinDecoder::ResourceLoaded()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
   // Don't handle ResourceLoaded if we are shutting down, or if
   // we need to ignore progress data due to seeking (in the case
   // that the seek results in reaching end of file, we get a bogus call
   // to ResourceLoaded).
   if (mShuttingDown)
@@ -369,228 +377,229 @@ void nsOggDecoder::ResourceLoaded()
 
   // Ensure the final progress event gets fired
   if (mElement) {
     mElement->DispatchAsyncProgressEvent(NS_LITERAL_STRING("progress"));
     mElement->ResourceLoaded();
   }
 }
 
-void nsOggDecoder::NetworkError()
+void nsBuiltinDecoder::NetworkError()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   if (mShuttingDown)
     return;
 
   if (mElement)
     mElement->NetworkError();
 
   Shutdown();
 }
 
-void nsOggDecoder::DecodeError()
+void nsBuiltinDecoder::DecodeError()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   if (mShuttingDown)
     return;
 
   if (mElement)
     mElement->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();
   ChangeState(PLAY_STATE_ENDED);
 
   if (mElement)  {
     UpdateReadyStateForData();
     mElement->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) {
     Shutdown();
   }
 
   return NS_OK;
 }
 
 nsMediaDecoder::Statistics
-nsOggDecoder::GetStatistics()
+nsBuiltinDecoder::GetStatistics()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   Statistics result;
 
   MonitorAutoEnter mon(mMonitor);
   if (mStream) {
     result.mDownloadRate = 
       mStream->GetDownloadRate(&result.mDownloadRateReliable);
     result.mDownloadPosition =
       mStream->GetCachedDataEnd(mDecoderPosition);
     result.mTotalBytes = mStream->GetLength();
     result.mPlaybackRate = ComputePlaybackRate(&result.mPlaybackRateReliable);
     result.mDecoderPosition = mDecoderPosition;
     result.mPlaybackPosition = mPlaybackPosition;
-  } else {
+  }
+  else {
     result.mDownloadRate = 0;
     result.mDownloadRateReliable = PR_TRUE;
     result.mPlaybackRate = 0;
     result.mPlaybackRateReliable = PR_TRUE;
     result.mDecoderPosition = 0;
     result.mPlaybackPosition = 0;
     result.mDownloadPosition = 0;
     result.mTotalBytes = 0;
   }
 
   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;
   if (mDuration >= 0 && length >= 0) {
     *aReliable = PR_TRUE;
     return double(length)*1000.0/mDuration;
   }
   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)
     return;
   PRPackedBool reliable;
   PRUint32 rate = PRUint32(ComputePlaybackRate(&reliable));
   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);
   }
   mStream->SetPlaybackRate(rate);
 }
 
-void nsOggDecoder::NotifySuspendedStatusChanged()
+void nsBuiltinDecoder::NotifySuspendedStatusChanged()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   if (!mStream)
     return;
   if (mStream->IsSuspendedByCache() && mElement) {
     // if this is an autoplay element, we need to kick off its autoplaying
     // now so we consume data and hopefully free up cache space
     mElement->NotifyAutoplayDataReady();
   }
 }
 
-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.");
 
   if (aStatus == NS_BINDING_ABORTED)
     return;
 
   {
     MonitorAutoEnter mon(mMonitor);
     UpdatePlaybackRate();
   }
 
   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.");
 
   if (mShuttingDown)
     return;
 
   {
     MonitorAutoEnter mon(mMonitor);
@@ -606,59 +615,60 @@ void nsOggDecoder::SeekingStopped()
   if (mElement) {
     UpdateReadyStateForData();
     mElement->SeekCompleted();
   }
 }
 
 // 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.");
 
   if (mShuttingDown)
     return;
 
   PRBool fireEnded = PR_FALSE;
   {
     MonitorAutoEnter mon(mMonitor);
 
     // An additional seek was requested while the current seek was
     // in operation.
     if (mRequestedSeekTime >= 0.0) {
       ChangeState(PLAY_STATE_SEEKING);
-    } else {
+    }
+    else {
       fireEnded = mNextState != PLAY_STATE_PLAYING;
       ChangeState(fireEnded ? PLAY_STATE_ENDED : mNextState);
     }
   }
 
   if (mElement) {
     UpdateReadyStateForData();
     mElement->SeekCompleted();
     if (fireEnded) {
       mElement->PlaybackEnded();
     }
   }
 }
 
-void nsOggDecoder::SeekingStarted()
+void nsBuiltinDecoder::SeekingStarted()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   if (mShuttingDown)
     return;
 
   if (mElement) {
     UpdateReadyStateForData();
     mElement->SeekStarted();
   }
 }
 
-void nsOggDecoder::ChangeState(PlayState aState)
+void nsBuiltinDecoder::ChangeState(PlayState aState)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");   
   MonitorAutoEnter mon(mMonitor);
 
   if (mNextState == aState) {
     mNextState = PLAY_STATE_PAUSED;
   }
 
@@ -668,20 +678,20 @@ void nsOggDecoder::ChangeState(PlayState
   }
 
   mPlayState = aState;
   switch (aState) {
   case PLAY_STATE_PAUSED:
     /* 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:
     /* No action needed */
     break;
   case PLAY_STATE_START:
     /* No action needed */
     break;
@@ -690,119 +700,118 @@ void nsOggDecoder::ChangeState(PlayState
     break;
   case PLAY_STATE_SHUTDOWN:
     /* No action needed */
     break;
   }
   mMonitor.NotifyAll();
 }
 
-void nsOggDecoder::PlaybackPositionChanged()
+void nsBuiltinDecoder::PlaybackPositionChanged()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   if (mShuttingDown)
     return;
 
   float lastTime = mCurrentTime;
 
   // Control the scope of the monitor so it is not
   // 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();
     }
   }
 
   // Invalidate the frame so any video data is displayed.
   // Do this before the timeupdate event so that if that
   // event runs JavaScript that queries the media size, the
   // frame has reflowed and the size updated beforehand.
   Invalidate();
 
   if (mElement && lastTime != mCurrentTime) {
     mElement->DispatchSimpleEvent(NS_LITERAL_STRING("timeupdate"));
   }
 }
 
-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) {
     mStream->Suspend(PR_TRUE);
   }
 }
 
-void nsOggDecoder::Resume()
+void nsBuiltinDecoder::Resume()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   if (mStream) {
     mStream->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);
     mDecoderPosition = mPlaybackPosition = mStream->Tell();
   }
 }
 
-void nsOggDecoder::MoveLoadsToBackground()
+void nsBuiltinDecoder::MoveLoadsToBackground()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   if (mStream) {
     mStream->MoveLoadsToBackground();
   }
 }
--- a/content/media/nsBuiltinDecoder.h
+++ b/content/media/nsBuiltinDecoder.h
@@ -32,114 +32,38 @@
  * 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 ***** */
 /*
-Each video element has three threads. 
-
-  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.
-
-  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.
-
-  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.
+Each video element based on nsBuiltinDecoder has at least one thread
+dedicated to decoding video.
 
-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:
+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.
 
-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.
+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.
 
-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
 decoder's state represents the actual state of the decoder.
 
 The high level state of the player is maintained via a PlayState value. 
 It can have the following states:
 
@@ -154,141 +78,141 @@ PLAYING
 SEEKING
   A request via the API has been received to start seeking.
 COMPLETED
   Playback has completed.
 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.
-
-The following represents the states that the player can be in, and the
-valid states the decode thread can be in at that time:
+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.
 
-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.
-
-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.
+a/v synchronisation is handled by the nsDecoderStateMachine implementation.
 
-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 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.
 
-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"
 
 #include "nsISupports.h"
 #include "nsCOMPtr.h"
 #include "nsIThread.h"
 #include "nsIChannel.h"
 #include "nsIObserver.h"
 #include "nsIFrame.h"
 #include "nsAutoPtr.h"
 #include "nsSize.h"
 #include "prlog.h"
 #include "gfxContext.h"
 #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;
+
+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
+{
+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;
 
-class nsOggDecoder : public nsMediaDecoder
+  // 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
 {
-  friend class nsOggReader;
-  friend class nsOggPlayStateMachine;
-
   // ISupports
   NS_DECL_ISUPPORTS
 
   // nsIObserver
   NS_DECL_NSIOBSERVER
 
  public:
   // Enumeration for the valid play states (see mPlayState)
@@ -297,32 +221,32 @@ class nsOggDecoder : public nsMediaDecod
     PLAY_STATE_LOADING,
     PLAY_STATE_PAUSED,
     PLAY_STATE_PLAYING,
     PLAY_STATE_SEEKING,
     PLAY_STATE_ENDED,
     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
   // object disposes of this decoder object.
   virtual void Shutdown();
   
   virtual float GetCurrentTime();
 
   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();
 
   // Seek to the time position in (seconds) from the start of the video.
   virtual nsresult Seek(float time);
 
   virtual nsresult PlaybackRateChanged();
@@ -386,24 +310,31 @@ class nsOggDecoder : public nsMediaDecod
   // Stop the state machine thread and drop references to the thread and
   // state machine.
   void Stop();
 
   // Called by the state machine to notify the decoder that the duration
   // 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() {
     return mPlayState;
   }
 
   // Stop updating the bytes downloaded for progress notifications. Called
   // when seeking to prevent wild changes to the progress notification.
@@ -469,17 +400,17 @@ protected:
   // Calls mElement->UpdateReadyStateForData, telling it whether we have
   // data for the next frame and if we're buffering. Main thread only.
   void UpdateReadyStateForData();
 
   // Find the end of the cached data starting at the current decoder
   // position.
   PRInt64 GetDownloadPosition();
 
-private:
+public:
   // Notifies the element that decoding has failed.
   void DecodeError();
 
   /******
    * The following members should be accessed with the decoder lock held.
    ******/
 
   // Current decoding position in the stream. This is where the decoder
@@ -530,25 +461,25 @@ private:
    * The following member variables can be accessed from any thread.
    ******/
 
   // The state machine object for handling the decoding. It is safe to
   // call methods of this object from other threads. Its internal data
   // 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;
 
   // 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
   // writing the state. Any change to the state on the main thread
   // must call NotifyAll on the monitor so the decode thread can wake up.
   PlayState mPlayState;
 
   // The state to change to after a seek or load operation. It must only
--- a/content/media/nsMediaDecoder.h
+++ b/content/media/nsMediaDecoder.h
@@ -223,30 +223,30 @@ public:
   // their nsMediaStream.
   virtual void MoveLoadsToBackground()=0;
 
   // Gets the image container for the media element. Will return null if
   // the element is not a video element. This can be called from any
   // 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.
   nsresult StartProgress();
 
   // 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;
 
   // This should only ever be accessed from the main thread.
   // It is set in Init and cleared in Shutdown when the element goes away.
   // The decoder does not add a reference the element.
   nsHTMLMediaElement* mElement;
--- a/content/media/ogg/nsOggCodecState.cpp
+++ b/content/media/ogg/nsOggCodecState.cpp
@@ -36,17 +36,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 #include "nsDebug.h"
 #include "nsOggCodecState.h"
 #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
    sanitizing the memory allocation of the RGB buffer.
    The maximum resolution we anticipate encountering in the
    wild is 2160p - 3840x2160 pixels.
 */
 #define MAX_VIDEO_WIDTH  4000
new file mode 100644
--- /dev/null
+++ b/content/media/ogg/nsOggDecoder.cpp
@@ -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);
+}
new file mode 100644
--- /dev/null
+++ b/content/media/ogg/nsOggDecoder.h
@@ -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
--- a/content/media/ogg/nsOggPlayStateMachine.cpp
+++ b/content/media/ogg/nsOggPlayStateMachine.cpp
@@ -35,33 +35,33 @@
  * 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 <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;
 
 // Adds two 32bit unsigned numbers, retuns PR_TRUE if addition succeeded,
 // or PR_FALSE the if addition would result in an overflow.
 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
 
 // Wait this number of seconds when buffering, then leave and play
 // as best as we can if the required amount of data hasn't been
 // retrieved.
 #define BUFFERING_WAIT 30
@@ -72,17 +72,17 @@ extern PRLogModuleInfo* gOggDecoderLog;
 // attempts at a time when the average download rate has not stabilised.
 #define BUFFERING_MIN_RATE 50000
 #define BUFFERING_RATE(x) ((x)< BUFFERING_MIN_RATE ? BUFFERING_MIN_RATE : (x))
 
 // The frame rate to use if there is no video data in the resource to
 // 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"),
   mCbCrSize(0),
   mPlayDuration(0),
   mBufferingEndOffset(0),
   mStartTime(-1),
   mEndTime(-1),
@@ -195,17 +195,17 @@ void nsOggPlayStateMachine::DecodeLoop()
       skipToNextKeyframe = PR_TRUE;
     }
 
     PRInt64 initialDownloadPosition = 0;
     PRInt64 currentTime = 0;
     {
       MonitorAutoEnter mon(mDecoder->GetMonitor());
       initialDownloadPosition =
-        mDecoder->mStream->GetCachedDataEnd(mDecoder->mDecoderPosition);
+        mDecoder->GetCurrentStream()->GetCachedDataEnd(mDecoder->mDecoderPosition);
       currentTime = mCurrentFrameTime + mStartTime;
     }
 
     // Determine how much audio data is decoded ahead of the current playback
     // position.
     int audioQueueSize = mReader->mAudioQueue.GetSize();
     PRInt64 audioDecoded = mReader->mAudioQueue.Duration();
 
@@ -229,17 +229,17 @@ void nsOggPlayStateMachine::DecodeLoop()
         if (mDecoder->mDecoderPosition >= initialDownloadPosition) {
           mBufferExhausted = PR_TRUE;
         }
       }
     }
     {
       MonitorAutoEnter mon(mDecoder->GetMonitor());
       initialDownloadPosition =
-        mDecoder->mStream->GetCachedDataEnd(mDecoder->mDecoderPosition);
+        mDecoder->GetCurrentStream()->GetCachedDataEnd(mDecoder->mDecoderPosition);
       mDecoder->GetMonitor().NotifyAll();
     }
 
     if (audioPlaying && !audioWait) {
       audioPlaying = mReader->DecodeAudioPage();
       {
         MonitorAutoEnter mon(mDecoder->GetMonitor());
         if (mDecoder->mDecoderPosition >= initialDownloadPosition) {
@@ -397,17 +397,17 @@ void nsOggPlayStateMachine::AudioLoop()
 nsresult nsOggPlayStateMachine::Init()
 {
   mReader = new nsOggReader(this);
   return mReader->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();
 
   // Reset mPlayStartTime before we pause/shutdown the nsAudioStream. This is
   // so that if the audio loop is about to write audio, it will have the chance
   // to check to see if we're paused and not write the audio. If not, the
   // audio thread can block in the write, and we deadlock trying to acquire
   // the audio monitor upon resume playback.
@@ -426,17 +426,17 @@ void nsOggPlayStateMachine::StopPlayback
         mAudioStream = nsnull;
       }
     }
   }
 }
 
 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();
   LOG(PR_LOG_DEBUG, ("%p StartPlayback", mDecoder));
   if (HasAudio()) {
     MonitorAutoExit exitMon(mDecoder->GetMonitor());
     MonitorAutoEnter audioMon(mAudioMonitor);
     if (mAudioStream) {
@@ -453,35 +453,35 @@ void nsOggPlayStateMachine::StartPlaybac
     }
   }
   mPlayStartTime = TimeStamp::Now();
   mDecoder->GetMonitor().NotifyAll();
 }
 
 void nsOggPlayStateMachine::UpdatePlaybackPosition(PRInt64 aTime)
 {
-  NS_ASSERTION(IsThread(mDecoder->mStateMachineThread),
+  NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
                "Should be on state machine thread.");
   mDecoder->GetMonitor().AssertCurrentThreadIn();
 
   NS_ASSERTION(mStartTime >= 0, "Should have positive mStartTime");
   mCurrentFrameTime = aTime - mStartTime;
   NS_ASSERTION(mCurrentFrameTime >= 0, "CurrentTime should be positive!");
   if (aTime > mEndTime) {
     NS_ASSERTION(mCurrentFrameTime > GetDuration(),
                  "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);
   }
 }
 
 void nsOggPlayStateMachine::ClearPositionChangeFlag()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   mDecoder->GetMonitor().AssertCurrentThreadIn();
@@ -577,30 +577,30 @@ void nsOggPlayStateMachine::Decode()
     LOG(PR_LOG_DEBUG, ("%p Changed state from BUFFERING to DECODING", mDecoder));
     mState = DECODER_STATE_DECODING;
     mDecoder->GetMonitor().NotifyAll();
   }
 }
 
 void nsOggPlayStateMachine::ResetPlayback()
 {
-  NS_ASSERTION(IsThread(mDecoder->mStateMachineThread),
+  NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
                "Should be on state machine thread.");
   mVideoFrameTime = -1;
   mAudioStartTime = -1;
   mAudioEndTime = -1;
   mAudioCompleted = PR_FALSE;
 }
 
 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,
                "We should have loaded metadata");
   double t = aTime * 1000.0;
   if (t > PR_INT64_MAX) {
     // Prevent integer overflow.
     return;
@@ -616,17 +616,17 @@ void nsOggPlayStateMachine::Seek(float a
   mSeekTime = NS_MIN(mSeekTime, mEndTime);
   mSeekTime = NS_MAX(mStartTime, mSeekTime);
   LOG(PR_LOG_DEBUG, ("%p Changed state to SEEKING (to %f)", mDecoder, aTime));
   mState = DECODER_STATE_SEEKING;
 }
 
 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;
   mDecoder->GetMonitor().NotifyAll();
   if (mDecodeThread) {
     {
       MonitorAutoExit exitMon(mDecoder->GetMonitor());
       mDecodeThread->Shutdown();
@@ -640,17 +640,17 @@ void nsOggPlayStateMachine::StopDecodeTh
     }
     mAudioThread = nsnull;
   }
 }
 
 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;
   if (!mDecodeThread && mState < DECODER_STATE_COMPLETED) {
     nsresult rv = NS_NewThread(getter_AddRefs(mDecodeThread));
     if (NS_FAILED(rv)) {
       mState = DECODER_STATE_SHUTDOWN;
       return rv;
@@ -669,19 +669,19 @@ nsOggPlayStateMachine::StartDecodeThread
       NS_NewRunnableMethod(this, &nsOggPlayStateMachine::AudioLoop);
     mAudioThread->Dispatch(event, NS_DISPATCH_NORMAL);
   }
   return NS_OK;
 }
 
 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) {
     MonitorAutoEnter mon(mDecoder->GetMonitor());
     switch (mState) {
     case DECODER_STATE_SHUTDOWN:
       if (IsPlaying()) {
         StopPlayback(AUDIO_SHUTDOWN);
@@ -719,26 +719,26 @@ nsresult nsOggPlayStateMachine::Run()
                            mDecoder, mStartTime, mEndTime, GetDuration(), mSeekable));
 
         if (mState == DECODER_STATE_SHUTDOWN)
           continue;
 
         // 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) {
           LOG(PR_LOG_DEBUG, ("%p Changed state from DECODING_METADATA to DECODING", mDecoder));
           mState = DECODER_STATE_DECODING;
         }
 
         // Start playback.
-        if (mDecoder->GetState() == nsOggDecoder::PLAY_STATE_PLAYING) {
+        if (mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING) {
           if (!IsPlaying()) {
             StartPlayback();
           }
         }
       }
       break;
 
     case DECODER_STATE_DECODING:
@@ -748,19 +748,19 @@ nsresult nsOggPlayStateMachine::Run()
         }
 
         AdvanceFrame();
 
         if (mState != DECODER_STATE_DECODING)
           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.
           if (IsPlaying()) {
             StopPlayback(AUDIO_PAUSE);
             mDecoder->GetMonitor().NotifyAll();
           }
 
@@ -812,17 +812,17 @@ nsresult nsOggPlayStateMachine::Run()
         ResetPlayback();
 
         // SeekingStarted will do a UpdateReadyStateForData which will
         // inform the element and its users that we have no frames
         // to display
         {
           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) {
           nsresult res;
           {
             MonitorAutoExit exitMon(mDecoder->GetMonitor());
             // Now perform the seek. We must not hold the state machine monitor
             // while we seek, since the seek decodes.
@@ -859,44 +859,44 @@ nsresult nsOggPlayStateMachine::Run()
         // Change state to DECODING or COMPLETED now. SeekingStopped will
         // call nsOggPlayStateMachine::Seek to reset our state to SEEKING
         // if we need to seek again.
         
         nsCOMPtr<nsIRunnable> stopEvent;
         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;
         mDecoder->GetMonitor().NotifyAll();
 
         {
           MonitorAutoExit exitMon(mDecoder->GetMonitor());
           NS_DispatchToMainThread(stopEvent, NS_DISPATCH_SYNC);
         }
       }
       break;
 
     case DECODER_STATE_BUFFERING:
       {
         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)
             continue;
         } else {
           LOG(PR_LOG_DEBUG, ("%p Changed state from BUFFERING to DECODING", mDecoder));
           LOG(PR_LOG_DEBUG, ("%p Buffered for %lf seconds",
                              mDecoder,
@@ -904,17 +904,17 @@ nsresult nsOggPlayStateMachine::Run()
           mState = DECODER_STATE_DECODING;
         }
 
         if (mState != DECODER_STATE_BUFFERING) {
           mBufferExhausted = PR_FALSE;
           // 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();
             }
           }
         }
 
         break;
       }
@@ -941,24 +941,24 @@ nsresult nsOggPlayStateMachine::Run()
         }
 
         if (mState != DECODER_STATE_COMPLETED)
           continue;
 
         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);
           }
         }
 
         while (mState == DECODER_STATE_COMPLETED) {
           mDecoder->GetMonitor().Wait();
         }
       }
@@ -966,17 +966,17 @@ nsresult nsOggPlayStateMachine::Run()
     }
   }
 
   return NS_OK;
 }
 
 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;
   }
 
   NS_ASSERTION(mInfo.mPicture.width != 0 && mInfo.mPicture.height != 0,
                "We can only render non-zero-sized video");
   NS_ASSERTION(aData->mBuffer[0].stride >= 0 && aData->mBuffer[0].height >= 0 &&
@@ -1049,30 +1049,30 @@ void nsOggPlayStateMachine::RenderVideoF
     videoImage->SetData(data);
     mDecoder->SetVideoData(data.mPicSize, mInfo.mAspectRatio, image);
   }
 }
 
 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();
   return (t == -1) ? -1 : t + mAudioStartTime;
 }
 
 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();
     }
 
     if (HasAudio() && mAudioStartTime == -1 && !mAudioCompleted) {
       // We've got audio (so we should sync off the audio clock), but we've
       // not played a sample on the audio thread, so we can't get a time
@@ -1179,17 +1179,17 @@ void nsOggPlayStateMachine::Wait(PRUint3
     NS_ASSERTION(ms <= aMs && ms > 0,
                  "nsOggPlayStateMachine::Wait interval very wrong!");
     mDecoder->GetMonitor().Wait(PR_MillisecondsToInterval(ms));
   }
 }
 
 void nsOggPlayStateMachine::LoadOggHeaders()
 {
-  NS_ASSERTION(IsThread(mDecoder->mStateMachineThread),
+  NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
                "Should be on state machine thread.");
   mDecoder->GetMonitor().AssertCurrentThreadIn();
 
   LOG(PR_LOG_DEBUG, ("Loading Ogg Headers"));
 
   nsMediaStream* stream = mDecoder->mStream;
 
   nsOggInfo info;
@@ -1198,17 +1198,17 @@ void nsOggPlayStateMachine::LoadOggHeade
     mReader->ReadOggHeaders(info);
   }
   mInfo = info;
   mDecoder->StartProgressUpdates();
 
   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;
   }
 
   if (!mInfo.mHasVideo) {
     mInfo.mCallbackPeriod = 1000 / AUDIO_FRAME_RATE;
   }
   LOG(PR_LOG_DEBUG, ("%p Callback Period: %u", mDecoder, mInfo.mCallbackPeriod));
@@ -1230,17 +1230,17 @@ void nsOggPlayStateMachine::LoadOggHeade
     FindEndTime();
     mDecoder->StartProgressUpdates();
     mDecoder->UpdatePlaybackRate();
   }
 }
 
 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;
   VideoData* v = nsnull;
   {
     MonitorAutoExit exitMon(mDecoder->GetMonitor());
     v = mReader->FindStartTime(mInfo.mDataOffset, startTime);
   }
@@ -1259,17 +1259,17 @@ VideoData* nsOggPlayStateMachine::FindSt
   return v;
 }
 
 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();
   NS_ASSERTION(length > 0, "Must have a content length to get end time");
 
   mEndTime = 0;
   PRInt64 endTime = 0;
   {
@@ -1290,23 +1290,23 @@ void nsOggPlayStateMachine::FindEndTime(
 }
 
 void nsOggPlayStateMachine::UpdateReadyState() {
   mDecoder->GetMonitor().AssertCurrentThreadIn();
 
   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");
   }
 
   NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
 }
 
--- a/content/media/ogg/nsOggPlayStateMachine.h
+++ b/content/media/ogg/nsOggPlayStateMachine.h
@@ -31,37 +31,169 @@
  * 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 ***** */
+/*
+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
   state machine thread, and controls the audio "push" 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:
@@ -69,44 +201,48 @@ static inline PRBool IsThread(nsIThread*
     mState and data related to that state changed (mSeekTime, etc)
     Ogg Metadata Loaded
     First Frame Loaded
     Frame decoded
     data pushed or popped from the video and audio queues
 
   See nsOggDecoder.h for more details.
 */
-class nsOggPlayStateMachine : public nsRunnable
+class nsOggPlayStateMachine : public nsDecoderStateMachine
 {
 public:
   // Enumeration for the valid states
   enum State {
     DECODER_STATE_DECODING_METADATA,
     DECODER_STATE_DECODING,
     DECODER_STATE_SEEKING,
     DECODER_STATE_BUFFERING,
     DECODER_STATE_COMPLETED,
     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.
   NS_IMETHOD Run();
 
   // This is called on the state machine thread and audio thread.
   // The decoder monitor must be obtained before calling this.
   PRBool HasAudio() const {
@@ -116,43 +252,16 @@ public:
 
   // This is called on the state machine thread and audio thread.
   // The decoder monitor must be obtained before calling this.
   PRBool HasVideo() const {
     mDecoder->GetMonitor().AssertCurrentThreadIn();
     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();
     return (mReader->mVideoQueue.GetSize() > 0 &&
             (!HasAudio() || audioQueueSize > 0)) ||
            audioQueueSize > 0;
   }
 
@@ -167,45 +276,39 @@ public:
   PRBool IsSeeking() const {
     mDecoder->GetMonitor().AssertCurrentThreadIn();
 
     return mState == nsOggPlayStateMachine::DECODER_STATE_SEEKING;
   }
 
   // Functions used by assertions to ensure we're calling things
   // on the appropriate threads.
-  PRBool OnStateMachineThread() {
-    return IsThread(mDecoder->mStateMachineThread);
+  PRBool OnAudioThread() {
+    return IsCurrentThread(mAudioThread);
   }
 
-  PRBool OnDecodeThread() {
-    return IsThread(mDecodeThread);
-  }
-
-  PRBool OnAudioThread() {
-    return IsThread(mAudioThread);
+  PRBool OnStateMachineThread() {
+    return mDecoder->OnStateMachineThread();
   }
 
   // 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.
-  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
   // there is no such event currently queued.
   // Only called on the decoder thread. Must be called with
   // 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.
   // Accessed on state machine, audio, main, and AV thread. 
   State mState;
 
 private:
 
--- a/content/media/ogg/nsOggReader.cpp
+++ b/content/media/ogg/nsOggReader.cpp
@@ -40,28 +40,28 @@
 #include "nsISeekableStream.h"
 #include "nsClassHashtable.h"
 #include "nsTArray.h"
 #include "nsOggDecoder.h"
 #include "nsOggReader.h"
 #include "nsOggCodecState.h"
 #include "nsOggPlayStateMachine.h"
 #include "mozilla/mozalloc.h"
-#include "nsOggHacks.h"
+#include "VideoUtils.h"
 
 using mozilla::MonitorAutoExit;
 
 // Un-comment to enable logging of seek bisections.
 //#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
 #else
 #define LOG(type, msg)
 #define SEEK_LOG(type, msg)
 #endif
 
@@ -561,17 +561,17 @@ PRBool nsOggReader::DecodeVideoPage(PRBo
 }
 
 nsresult nsOggReader::GetBufferedBytes(nsTArray<ByteRange>& aRanges)
 {
   NS_ASSERTION(mPlayer->OnStateMachineThread(),
                "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) {
       // Uncached at startOffset.
       endOffset = stream->GetNextCachedData(startOffset);
       if (endOffset == -1) {
         // Uncached at startOffset until endOffset of stream, or we're at
         // the end of stream.
@@ -607,17 +607,17 @@ nsOggReader::GetSeekRange(const nsTArray
                           PRInt64 aTarget,
                           PRInt64 aStartTime,
                           PRInt64 aEndTime,
                           PRBool aExact)
 {
   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++) {
     const ByteRange &r = ranges[i];
     if (r.mTimeStart < aTarget) {
       so = r.mOffsetStart;
       st = r.mTimeStart;
     }
@@ -635,17 +635,17 @@ nsOggReader::GetSeekRange(const nsTArray
 }
 
 nsresult nsOggReader::Seek(PRInt64 aTarget, PRInt64 aStartTime, PRInt64 aEndTime)
 {
   MonitorAutoEnter mon(mMonitor);
   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;
   }
   if (aTarget == aStartTime) {
     stream->Seek(nsISeekableStream::NS_SEEK_SET, mDataOffset);
     mPageOffset = mDataOffset;
     NS_ASSERTION(aStartTime != -1, "mStartTime should be known");
@@ -883,17 +883,17 @@ PageSync(ogg_sync_state* aState,
 }
 
 nsresult nsOggReader::SeekBisection(PRInt64 aTarget,
                                     const ByteRange& aRange,
                                     PRUint32 aFuzz)
 {
   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())) {
       return NS_ERROR_FAILURE;
     }
     stream->Seek(nsISeekableStream::NS_SEEK_SET, mDataOffset);
     mPageOffset = mDataOffset;
     return NS_OK;
@@ -1116,17 +1116,17 @@ PRInt64 nsOggReader::ReadOggPage(ogg_pag
     // with the given size. This buffer is stored
     // in the ogg synchronisation structure.
     char* buffer = ogg_sync_buffer(&mOggState, 4096);
     NS_ASSERTION(buffer, "ogg_sync_buffer failed");
 
     // 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;
     }
 
     mPlayer->mDecoder->NotifyBytesConsumed(bytesRead);
     // Update the synchronisation layer with the number
     // of bytes written to the buffer
@@ -1366,17 +1366,17 @@ Data* nsOggReader::DecodeToFirstData(Dec
   return (d = aQueue.PeekFront()) ? d : nsnull;
 }
 
 VideoData* nsOggReader::FindStartTime(PRInt64 aOffset,
                                       PRInt64& aOutStartTime)
 {
   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())) {
     return nsnull;
   }
 
   // Extract the start times of the bitstreams in order to calculate
   // the duration.
@@ -1422,17 +1422,17 @@ GetChecksum(ogg_page* page)
   return c;
 }
 
 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);
 
   // We need to find the last page which ends before aEndOffset that
   // has a granulepos that we can convert to a timestamp. We do this by
   // backing off from aEndOffset until we encounter a page on which we can
   // interpret the granulepos. If while backing off we encounter a page which
--- a/content/media/ogg/nsOggReader.h
+++ b/content/media/ogg/nsOggReader.h
@@ -39,17 +39,16 @@
 #if !defined(nsOggReader_h_)
 #define nsOggReader_h_
 
 #include <nsDeque.h>
 #include "nsOggCodecState.h"
 #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"
 #include "nsSize.h"
 #include "nsRect.h"
 #include "mozilla/Monitor.h"
 
 class nsOggPlayStateMachine;