Bug 422538. Integrate Ogg decoders. r=ted,r+sr=roc
authorChris Double <chris.double@double.co.nz>
Tue, 29 Jul 2008 23:50:14 -0700
changeset 16300 f6b43d90489fc3766c4711a266d1536f206d8d86
parent 16299 b6e9bc7d95bb7eb7e6dcc8b77be58c096539dff8
child 16301 c2ea6c18e6b048f0171d6d1ad0a18037b044d8e7
push idunknown
push userunknown
push dateunknown
reviewersted, r
bugs422538
milestone1.9.1a2pre
Bug 422538. Integrate Ogg decoders. r=ted,r+sr=roc
config/autoconf.mk.in
config/system-headers
configure.in
content/html/content/src/nsHTMLMediaElement.cpp
content/html/content/src/nsHTMLVideoElement.cpp
content/media/video/public/Makefile.in
content/media/video/public/nsAudioStream.h
content/media/video/public/nsChannelReader.h
content/media/video/public/nsOggDecoder.h
content/media/video/src/Makefile.in
content/media/video/src/nsAudioStream.cpp
content/media/video/src/nsChannelReader.cpp
content/media/video/src/nsOggDecoder.cpp
content/media/video/src/nsVideoDecoder.cpp
layout/build/Makefile.in
layout/build/nsLayoutStatics.cpp
modules/liboggplay_audio/Makefile.in
modules/liboggplay_audio/README_MOZILLA
modules/liboggplay_audio/sydney_audio.h
modules/liboggplay_audio/sydney_audio_alsa.c
modules/liboggplay_audio/sydney_audio_mac.c
modules/liboggplay_audio/sydney_audio_oss.c
modules/liboggplay_audio/sydney_audio_waveapi.c
modules/liboggplay_audio/update.sh
toolkit/library/Makefile.in
toolkit/toolkit-makefiles.sh
toolkit/toolkit-tiers.mk
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -160,16 +160,17 @@ MOZ_PLACES_BOOKMARKS = @MOZ_PLACES_BOOKM
 MOZ_STORAGE = @MOZ_STORAGE@
 MOZ_SAFE_BROWSING = @MOZ_SAFE_BROWSING@
 MOZ_URL_CLASSIFIER = @MOZ_URL_CLASSIFIER@
 MOZ_ZIPWRITER = @MOZ_ZIPWRITER@
 MOZ_MORK = @MOZ_MORK@
 MOZ_MORKREADER = @MOZ_MORKREADER@
 MOZ_NO_XPCOM_OBSOLETE = @MOZ_NO_XPCOM_OBSOLETE@
 MOZ_NO_FAST_LOAD = @MOZ_NO_FAST_LOAD@
+MOZ_OGG = @MOZ_OGG@
 MOZ_MEDIA = @MOZ_MEDIA@
 NS_PRINTING = @NS_PRINTING@
 MOZ_CRASHREPORTER = @MOZ_CRASHREPORTER@
 MOZ_MOCHITEST = @MOZ_MOCHITEST@
 MOZ_HELP_VIEWER = @MOZ_HELP_VIEWER@
 
 MOZ_JAVAXPCOM = @MOZ_JAVAXPCOM@
 JAVA_INCLUDE_PATH="@JAVA_INCLUDE_PATH@"
--- a/config/system-headers
+++ b/config/system-headers
@@ -15,16 +15,19 @@ afxmt.h
 afxpriv.h
 afxtempl.h
 afxwin.h
 algorithm
 Aliases.h
 all.h
 alloca.h
 alloc.h
+alsa/asoundlib.h
+alsa/pcm.h
+alsa/mixer.h
 ansi_parms.h
 a.out.h
 app/Cursor.h
 Appearance.h
 AppFileInfo.h
 AppKit.h
 AppleEvents.h
 Application.h
--- a/configure.in
+++ b/configure.in
@@ -4353,16 +4353,17 @@ MOZ_MATHML=1
 MOZ_MOCHITEST=1
 MOZ_MORK=1
 MOZ_MORKREADER=
 MOZ_AUTH_EXTENSION=1
 MOZ_NO_ACTIVEX_SUPPORT=1
 MOZ_NO_INSPECTOR_APIS=
 MOZ_NO_XPCOM_OBSOLETE=
 MOZ_NO_FAST_LOAD=
+MOZ_OGG=
 MOZ_MEDIA=
 MOZ_OJI=1
 MOZ_PERMISSIONS=1
 MOZ_PLACES=
 MOZ_PLAINTEXT_EDITOR_ONLY=
 MOZ_PLUGINS=1
 MOZ_PREF_EXTENSIONS=1
 MOZ_PROFILELOCKING=1
@@ -4778,17 +4779,17 @@ cairo-mac|cairo-cocoa)
     else
         MOZ_WIDGET_TOOLKIT=mac
     fi
     MOZ_GFX_TOOLKIT=cairo
     MOZ_USER_DIR="Mozilla"
     AC_DEFINE(XP_MACOSX)
     AC_DEFINE(TARGET_CARBON)
     AC_DEFINE(TARGET_API_MAC_CARBON)
-    TK_LIBS='-framework Carbon'
+    TK_LIBS='-framework Carbon -framework CoreAudio -framework AudioToolbox -framework AudioUnit'
     TK_CFLAGS="-I${MACOS_SDK_DIR}/Developer/Headers/FlatCarbon"
     CFLAGS="$CFLAGS $TK_CFLAGS"
     CXXFLAGS="$CXXFLAGS $TK_CFLAGS"
     ;;
 esac
 
 if test "$MOZ_ENABLE_XREMOTE"; then
     AC_DEFINE(MOZ_ENABLE_XREMOTE)
@@ -5323,29 +5324,45 @@ MOZ_ARG_DISABLE_BOOL(xpcom-fastload,
 
 AC_SUBST(MOZ_NO_FAST_LOAD)
 
 if test -n "$MOZ_NO_FAST_LOAD"; then
     AC_DEFINE(MOZ_NO_FAST_LOAD)
 fi
 
 dnl ========================================================
-dnl = Enable HTML video and audio support
-dnl ========================================================
-MOZ_ARG_ENABLE_BOOL(media,
-[  --enable-media           Enable HTML5 media support],
-    MOZ_MEDIA=1,
-    MOZ_MEDIA=)
+dnl = Enable Ogg Codecs
+dnl ========================================================
+MOZ_ARG_DISABLE_BOOL(ogg,
+[  --disable-ogg           Disable Ogg Codec support],
+    MOZ_OGG=,
+    MOZ_OGG=1)
+
+AC_SUBST(MOZ_OGG)
+
+if test -n "$MOZ_OGG"; then
+    AC_DEFINE(MOZ_OGG)
+    MOZ_MEDIA=1
+fi
 
 AC_SUBST(MOZ_MEDIA)
 
 if test -n "$MOZ_MEDIA"; then
     AC_DEFINE(MOZ_MEDIA)
 fi
 
+dnl If using Ogg with Linux, ensure that the alsa library is available
+if test -n "$MOZ_OGG"; then
+   case "$target_os" in
+linux*)
+      AC_CHECK_LIB(asound, snd_pcm_open,,AC_MSG_ERROR([Ogg support on Linux requires the alsa library]))
+      ;;
+   esac
+fi
+
 dnl ========================================================
 dnl Permissions System
 dnl ========================================================
 MOZ_ARG_DISABLE_BOOL(permissions,
 [  --disable-permissions   Disable permissions (popup and cookie blocking)],
     MOZ_PERMISSIONS=,
     MOZ_PERMISSIONS=1
 )
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -63,16 +63,20 @@
 #include "nsIRenderingContext.h"
 #include "nsITimer.h"
 
 #include "nsEventDispatcher.h"
 #include "nsIDOMDocumentEvent.h"
 #include "nsIDOMProgressEvent.h"
 #include "nsHTMLMediaError.h"
 
+#ifdef MOZ_OGG
+#include "nsOggDecoder.h"
+#endif
+
 class nsAsyncEventRunner : public nsRunnable
 {
 private:
   nsString mName;
   nsCOMPtr<nsHTMLMediaElement> mElement;
   PRPackedBool mProgress;
   
 public:
@@ -173,17 +177,17 @@ NS_IMETHODIMP nsHTMLMediaElement::GetTot
 }
 
 /* void load (); */
 NS_IMETHODIMP nsHTMLMediaElement::Load()
 {
   if (mBegun) {
     mBegun = PR_FALSE;
     
-    mError = new (std::nothrow) nsHTMLMediaError(nsHTMLMediaError::MEDIA_ERR_ABORTED);
+    mError = new nsHTMLMediaError(nsHTMLMediaError::MEDIA_ERR_ABORTED);
     DispatchProgressEvent(NS_LITERAL_STRING("abort"));
     return NS_OK;
   }
 
   mError = nsnull;
   mLoadedFirstFrame = PR_FALSE;
   mAutoplaying = PR_TRUE;
 
@@ -445,17 +449,17 @@ nsHTMLMediaElement::ParseAttribute(PRInt
                                    nsAttrValue& aResult)
 {
   if (aNamespaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::src) {
       static const char* kWhitespace = " \n\r\t\b";
       aResult.SetTo(nsContentUtils::TrimCharsInSet(kWhitespace, aValue));
       return PR_TRUE;
     }
-    else if(aAttribute == nsGkAtoms::playbackrate
+    else if (aAttribute == nsGkAtoms::playbackrate
             || aAttribute == nsGkAtoms::defaultplaybackrate
             || aAttribute == nsGkAtoms::loopstart
             || aAttribute == nsGkAtoms::loopend
             || aAttribute == nsGkAtoms::start
             || aAttribute == nsGkAtoms::end) {
       return aResult.ParseFloatValue(aValue);
     }
     else if (ParseImageAttribute(aAttribute, aValue, aResult)) {
@@ -474,17 +478,17 @@ nsHTMLMediaElement::SetAttr(PRInt32 aNam
 {
   nsresult rv = 
     nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
                                     aNotify);
   if (aNotify && aNameSpaceID == kNameSpaceID_None) {
     if (aName == nsGkAtoms::src) {
       Load();
     }
-    else if(aName == nsGkAtoms::playbackrate || aName == nsGkAtoms::defaultplaybackrate) {
+    else if (aName == nsGkAtoms::playbackrate || aName == nsGkAtoms::defaultplaybackrate) {
       if (mDecoder) 
         mDecoder->PlaybackRateChanged();
       DispatchAsyncSimpleEvent(NS_LITERAL_STRING("ratechange"));
     }
   }
 
   return rv;
 }
@@ -514,42 +518,63 @@ void nsHTMLMediaElement::UnbindFromTree(
 
 
 nsresult nsHTMLMediaElement::PickMediaElement(nsAString& aChosenMediaResource)
 {
   // Implements:
   // http://www.whatwg.org/specs/web-apps/current-work/#pick-a
   nsAutoString src;
   if (HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
-    nsresult rv = GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
-    if (NS_SUCCEEDED(rv)) {
+    if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
       aChosenMediaResource = src;
 
-      // No decoder available
-      mDecoder = nsnull;
+#ifdef MOZ_OGG
+      // Currently assuming an Ogg file
+      // TODO: Instantiate decoder based on type
+      if (mDecoder) {
+        mDecoder->ElementUnavailable();
+        mDecoder->Stop();
+        mDecoder = nsnull;
+      }
+
+      mDecoder = new nsOggDecoder();
+      if (mDecoder && !mDecoder->Init()) {
+        mDecoder = nsnull;
+      }
+#endif
       return NS_OK;
     }
   }
 
   // Checking of 'source' elements as per:
   // http://www.whatwg.org/specs/web-apps/current-work/#pick-a
   PRUint32 count = GetChildCount();
   for (PRUint32 i = 0; i < count; ++i) {
     nsIContent* child = GetChildAt(i);
     NS_ASSERTION(child, "GetChildCount lied!");
     
     nsCOMPtr<nsIContent> source = do_QueryInterface(child);
     if (source) {
       if (source->HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
         nsAutoString type;
-        
-        nsresult rv = source->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type);
-        if (NS_SUCCEEDED(rv)) {
-          // Check type and instantiate for relevant decoders
-          // Currently no decoders supported
+
+        if (source->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type)) {
+#if MOZ_OGG
+          if (type.EqualsLiteral("video/ogg") || type.EqualsLiteral("application/ogg")) {
+            nsAutoString src;
+            if (source->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
+              mDecoder = new nsOggDecoder();
+              if (mDecoder && !mDecoder->Init()) {
+                mDecoder = nsnull;
+              }
+              aChosenMediaResource = src;
+              return NS_OK;
+            }
+          }
+#endif
         }
       }
     }    
   }        
 
   return NS_ERROR_DOM_INVALID_STATE_ERR;
 }
 
@@ -607,17 +632,17 @@ void nsHTMLMediaElement::ResourceLoaded(
   mNetworkState = nsIDOMHTMLMediaElement::LOADED;
   ChangeReadyState(nsIDOMHTMLMediaElement::CAN_PLAY_THROUGH);
 
   DispatchProgressEvent(NS_LITERAL_STRING("load"));
 }
 
 void nsHTMLMediaElement::NetworkError()
 {
-  mError = new (std::nothrow) nsHTMLMediaError(nsHTMLMediaError::MEDIA_ERR_NETWORK);
+  mError = new nsHTMLMediaError(nsHTMLMediaError::MEDIA_ERR_NETWORK);
   mBegun = PR_FALSE;
   DispatchProgressEvent(NS_LITERAL_STRING("error"));
   mNetworkState = nsIDOMHTMLMediaElement::EMPTY;
   DispatchSimpleEvent(NS_LITERAL_STRING("empty"));
 }
 
 void nsHTMLMediaElement::PlaybackCompleted()
 {
--- a/content/html/content/src/nsHTMLVideoElement.cpp
+++ b/content/html/content/src/nsHTMLVideoElement.cpp
@@ -144,17 +144,16 @@ void nsHTMLVideoElement::UnbindFromTree(
                                         PRBool aNullParent)
 {
   if (mDecoder) 
     mDecoder->ElementUnavailable();
 
   nsHTMLMediaElement::UnbindFromTree(aDeep, aNullParent);
 }
 
-
 nsresult nsHTMLVideoElement::InitializeDecoder(nsAString& aChosenMediaResource)
 {
   if (mDecoder) 
     mDecoder->ElementAvailable(this);
 
   return nsHTMLMediaElement::InitializeDecoder(aChosenMediaResource);
 }
 
--- a/content/media/video/public/Makefile.in
+++ b/content/media/video/public/Makefile.in
@@ -42,9 +42,17 @@ VPATH		= @srcdir@
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= content
 
 EXPORTS		= \
 		nsVideoDecoder.h \
 		$(NULL)
 
+ifdef MOZ_OGG
+EXPORTS		+= \
+		nsAudioStream.h \
+		nsChannelReader.h \
+		nsOggDecoder.h \
+		$(NULL)
+endif
+
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/content/media/video/public/nsAudioStream.h
@@ -0,0 +1,101 @@
+/* -*- 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>
+ *
+ * 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 "nscore.h"
+#include "prlog.h"
+
+extern PRLogModuleInfo* gAudioStreamLog;
+
+class nsAudioStream 
+{
+ public:
+  // Initialize Audio Library. Some Audio backends (eg. PortAudio) require initializing
+  // library before using it. 
+  static nsresult InitLibrary();
+
+  // Shutdown Audio Library. Some Audio backends (eg. PortAudio) require shutting down
+  // the library after using it. 
+  static void ShutdownLibrary();
+
+  nsAudioStream();
+
+  // Initialize the audio stream. aNumChannels is the number of audio channels 
+  // (1 for mono, 2 for stereo, etc) and aRate is the frequency of the sound 
+  // samples (22050, 44100, etc).
+  nsresult Init(PRInt32 aNumChannels, PRInt32 aRate);
+
+  // Closes the stream. All future use of the stream is an error.
+  nsresult Shutdown();
+
+  // Pause sound playback. 
+  nsresult Pause();
+
+  // Resume playback of sound.
+  nsresult Resume();
+
+  // Write sound data to the audio hardware. aBuf is an array of floats of
+  // length aCount. aCount should be evenly divisible by the number of 
+  // channels in this audio stream.
+  nsresult Write(float* aBuf, PRUint32 count);
+
+  // Store in aTime the position (in seconds) of the audio sample currently 
+  // being played by the audio hardware.
+  nsresult GetTime(double* aTime);
+
+  // Store in aVolume the value of the volume setting. This is a value from
+  // 0 (meaning muted) to 1 (meaning full volume).
+  nsresult GetVolume(float* aVolume);
+
+  // Set the current volume of the audio playback. This is a value from
+  // 0 (meaning muted) to 1 (meaning full volume).
+  nsresult SetVolume(float aVolume);
+
+ private:
+  double mVolume;
+#if defined(SYDNEY_AUDIO_NO_POSITION)
+  // The time, in seconds, that playback was last paused.
+  double mPauseTime;
+#else
+  // The byte position in the audio buffer where playback
+  // was last paused.
+  PRInt64 mPauseBytes;
+#endif
+  void* mAudioHandle;
+  int mRate;
+  int mChannels;
+  PRBool mPaused;
+};
new file mode 100644
--- /dev/null
+++ b/content/media/video/public/nsChannelReader.h
@@ -0,0 +1,115 @@
+/* -*- 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>
+ *
+ * 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(nsChannelReader_h___)
+#define nsChannelReader_h___
+
+#include "nsCOMPtr.h"
+#include "nsIChannel.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsIRequestObserver.h"
+#include "nsIStreamListener.h"
+
+#include "oggplay/oggplay.h"
+
+class nsOggDecoder;
+
+/* 
+   Reads all data on the input stream of a channel and
+   writes it to a pipe. This allows a seperate thread to
+   read data from a channel running on the main thread
+*/
+class nsChannelToPipeListener : public nsIStreamListener
+{
+  // ISupports
+  NS_DECL_ISUPPORTS
+
+  // IRequestObserver
+  NS_DECL_NSIREQUESTOBSERVER
+
+  // IStreamListener
+  NS_DECL_NSISTREAMLISTENER
+
+  public:
+  nsChannelToPipeListener(nsOggDecoder* aDecoder);
+  nsresult Init();
+  void GetInputStream(nsIInputStream** aStream);
+  void Stop();
+  double BytesPerSecond() const;
+
+private:
+  nsCOMPtr<nsIInputStream> mInput;
+  nsCOMPtr<nsIOutputStream> mOutput;
+  nsOggDecoder* mDecoder;
+
+  // Interval when download started. Used in
+  // computing bytes per second download rate.
+  PRIntervalTime mIntervalStart;
+
+  // Interval when last downloaded bytes occurred. Used in computer
+  // bytes per second download rate.
+  PRIntervalTime mIntervalEnd;
+
+  // Total bytes transferred so far
+  PRInt64 mTotalBytes;
+};
+
+class nsChannelReader : public OggPlayReader
+{
+public:
+  nsChannelReader();
+  nsresult Init(nsOggDecoder* aDecoder, nsIURI* aURI);
+  PRUint32 Available();
+  OggPlayErrorCode initialise(int aBlock);
+  OggPlayErrorCode destroy();
+  size_t io_read(char* aBuffer, size_t aCount);
+  int io_seek(long aOffset, int aWhence);
+  long io_tell();  
+
+  // Return average number of bytes per second that the 
+  // download of the media resource is achieving.
+  double BytesPerSecond() const;
+  
+public:
+  nsCOMPtr<nsIChannel>  mChannel;
+  nsCOMPtr<nsIInputStream>  mInput;
+  nsCOMPtr<nsChannelToPipeListener> mListener;
+  unsigned long mCurrentPosition;
+};
+
+#endif
new file mode 100644
--- /dev/null
+++ b/content/media/video/public/nsOggDecoder.h
@@ -0,0 +1,399 @@
+/* -*- 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: ML 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>
+ *
+ * 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 ***** */
+/*
+Each video element has two threads. They are:
+  Decode Thread
+    This thread owns the resources for downloading
+    and reading the video file. It goes through the file, decoding
+    the theora and vorbis data. It uses Oggplay to do the decoding.
+    
+  Presentation Thread This thread goes through the data decoded by the
+    decode thread, generates the RGB buffer. If there is audio data it
+    queues it for playing. This thread owns the audio device - all
+    audio operations occur on this thread. The video data is actually
+    displayed by a timer that goes off at the right framerate
+    interval. This timer sends an Invalidate event to the frame.
+
+Operations are performed in the threads by sending Events via the 
+Dispatch method on the thread.
+
+The general sequence of events with this objects is:
+
+1) The video element calls Load on nsVideoDecoder. This creates the 
+   threads and starts the channel for downloading the file. It sends
+   an event to the decode thread to load the initial Ogg 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 required things by the video specification
+   at this stage. 
+
+   It then queues an event to the decode thread to decode the first
+   frame of Ogg 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.
+
+   It then queues an event to the decode thread to enter the standard
+   decoding loop. This loop continuously reads data from the channel
+   and decodes it. It does this by reading the data, decoding it, and 
+   if there is more data to decode, queues itself back to the thread.
+
+   The final step of the 'first frame event' is to notify a condition
+   variable that we have decoded the first frame. The presentation thread
+   uses this notification to know when to start displaying video.
+
+4) At some point the video element calls Play() on the decoder object.
+   This queues an event to the presentation thread which goes through
+   the decoded data, displaying it if it is video, or playing it if it
+   is audio. 
+
+   Before starting this event will wait on the condition variable 
+   indicating if the first frame has decoded. 
+
+Pausing is handled by stopping the presentation thread. Resuming is 
+handled by restarting the presentation thread. The decode thread never
+gets too far ahead as it is throttled by the Oggplay library.
+*/
+#if !defined(nsOggDecoder_h___)
+#define nsOggDecoder_h___
+
+#include "nsISupports.h"
+#include "nsCOMPtr.h"
+#include "nsIThread.h"
+#include "nsIChannel.h"
+#include "nsChannelReader.h"
+#include "nsIObserver.h"
+#include "nsIFrame.h"
+#include "nsAutoPtr.h"
+#include "nsSize.h"
+#include "prlock.h"
+#include "prcvar.h"
+#include "prlog.h"
+#include "gfxContext.h"
+#include "gfxRect.h"
+#include "oggplay/oggplay.h"
+#include "nsVideoDecoder.h"
+
+class nsAudioStream;
+class nsVideoDecodeEvent;
+class nsVideoPresentationEvent;
+class nsChannelToPipeListener;
+
+class nsOggDecoder : public nsVideoDecoder
+{
+  friend class nsVideoDecodeEvent;
+  friend class nsVideoPresentationEvent;
+  friend class nsChannelToPipeListener;
+
+  // ISupports
+  NS_DECL_ISUPPORTS
+
+  // nsIObserver
+  NS_DECL_NSIOBSERVER
+
+ public:
+  nsOggDecoder();
+  PRBool Init();
+  void Shutdown();
+  ~nsOggDecoder();
+  
+  // Returns the current video frame width and height.
+  // If there is no video frame, returns the given default size.
+  nsIntSize GetVideoSize(nsIntSize defaultSize);
+  double GetVideoFramerate();
+
+  float GetCurrentTime();
+
+  // Start downloading the video at the given URI. Decode
+  // the downloaded data up to the point of the first frame
+  // of data. 
+  nsresult Load(nsIURI* aURI);
+
+  // Start playback of a video. 'Load' must have previously been
+  // called.
+  nsresult Play();
+
+  // Stop playback of a video, and stop download of video stream.
+  virtual void Stop();
+
+  // Seek to the time position in (seconds) from the start of the video.
+  nsresult Seek(float time);
+
+  nsresult PlaybackRateChanged();
+
+  void Pause();
+  float GetVolume();
+  void SetVolume(float volume);
+  float GetDuration();
+
+  void GetCurrentURI(nsIURI** aURI);
+
+  virtual void UpdateBytesDownloaded(PRUint32 aBytes);
+
+protected:
+  /******
+   * The following methods must only be called on the presentation
+   * thread.
+   ******/
+
+  // Find and render the first frame of video data. Call on 
+  // presentation thread only.
+  void DisplayFirstFrame();
+
+  // Process one frame of video/audio data.
+  // Call on presentation thread only.
+  PRBool StepDisplay();
+
+  // Process audio or video from one track of the Ogg stream.
+  // Call on presentation thread only.
+  void ProcessTrack(int aTrackNumber, OggPlayCallbackInfo* aTrackInfo);
+
+  // Return the time in seconds that the video display is
+  // synchronised to. This can be based on the current time of
+  // the audio buffer if available, or the system clock. Call
+  // on presentation thread only.
+  double GetSyncTime();
+
+  // Return true if the video is currently paused. Call on the
+  // presentation thread only.
+  PRBool IsPaused();
+
+  // Process the video/audio data. Call on presentation thread only. 
+  void HandleVideoData(int track_num, OggPlayVideoData* video_data);
+  void HandleAudioData(OggPlayAudioData* audio_data, int size);
+
+  // Pause the audio. Call on presentation thread only.
+  void DoPause();
+
+  // Initializes and opens the audio stream. Call from the
+  // presentation thread only.
+  void OpenAudioStream();
+
+  // Closes and releases resources used by the audio stream.
+  // Call from the presentation thread only.
+  void CloseAudioStream();
+
+  // Initializes the resources owned by the presentation thread,.
+  // Call from the presentation thread only.
+  void StartPresentationThread();
+
+  /******
+   * The following methods must only be called on the decode
+   * thread.
+   ******/
+
+  // Loads the header information from the ogg resource and
+  // stores the information about framerate, etc in member
+  // variables. Must be called from the decoder thread only.
+  void LoadOggHeaders();
+
+  // Loads the First frame of the video data, making it available
+  // in the RGB buffer. Must be called from the decoder thread only.
+  void LoadFirstFrame();
+
+  // Decode some data from the media file. This is placed in a 
+  // buffer that is used by the presentation thread. Must
+  // be called from the decoder thread only.
+  PRBool StepDecoding();
+
+  // Ensure that there is enough data buffered from the video 
+  // that we can have a reasonable playback experience. Must
+  // be called from the decoder thread only.
+  void BufferData();
+
+  /****** 
+   * The following methods must only be called on the main
+   * thread.
+   ******/
+
+  // Called when the metadata from the Ogg file has been read.
+  // Call on the main thread only.
+  void MetadataLoaded();
+
+  // Called when the first frame has been loaded.
+  // Call on the main thread only.
+  void FirstFrameLoaded();
+
+  // Called when the video file has completed downloading.
+  // Call on the main thread only.
+  void ResourceLoaded();
+
+  // Called when the video has completed playing.
+  // Call on the main thread only.
+  void PlaybackCompleted();
+
+  // Return the current number of bytes loaded from the video file.
+  // This is used for progress events.
+  virtual PRUint32 GetBytesLoaded();
+
+  // Return the size of the video file in bytes.
+  // This is used for progress events.
+  virtual PRUint32 GetTotalBytes();
+
+  // Buffering of data has stopped. Inform the element on the main
+  // thread.
+  void BufferingStopped();
+
+  // Buffering of data has started. Inform the element on the main
+  // thread.
+  void BufferingStarted();
+
+private:
+  // Starts the threads and timers that handle displaying the playing
+  // video and invalidating the frame. Called on the main thread only.
+  void StartPlaybackThreads();
+
+  /******
+   * The following member variables can be accessed from the
+   * decoding thread only.
+   ******/
+
+  // Total number of bytes downloaded so far. 
+  PRUint32 mBytesDownloaded;
+
+  /******
+   * The following members should be accessed on the main thread only
+   ******/
+  nsCOMPtr<nsIChannel> mChannel;
+  nsCOMPtr<nsChannelToPipeListener> mListener;
+
+  // The URI of the current resource
+  nsCOMPtr<nsIURI> mURI;
+
+  // The audio stream resource. It should only be accessed from
+  // the presentation thread.
+  nsAutoPtr<nsAudioStream> mAudioStream;
+
+  // The time that the next video frame should be displayed in
+  // seconds. This is referenced from 0.0 which is the initial start
+  // of the video stream.
+  double mVideoNextFrameTime;
+
+  // A load of the media resource is currently in progress. It is 
+  // complete when the media metadata is loaded.
+  PRPackedBool mLoadInProgress;
+
+  // A boolean that indicates that once the load has completed loading
+  // the metadata then it should start playing.
+  PRPackedBool mPlayAfterLoad;
+
+  /******
+   * The following member variables can be accessed from any thread.
+   ******/
+  
+  // Threads to handle decoding of Ogg data. Methods on these are
+  // called from the main thread only, but they can be read from other
+  // threads safely to see if they have been created/set.
+  nsCOMPtr<nsIThread> mDecodeThread;
+  nsCOMPtr<nsIThread> mPresentationThread;
+
+  // Events for doing the Ogg decoding, displaying and repainting on
+  // different threads. These are created on the main thread and are
+  // dispatched to other threads. Threads only access them to dispatch
+  // the event to other threads.
+  nsCOMPtr<nsVideoDecodeEvent> mDecodeEvent;
+  nsCOMPtr<nsVideoPresentationEvent> mPresentationEvent;
+
+  // The time of the current frame from the video stream in
+  // seconds. This is referenced from 0.0 which is the initial start
+  // of the video stream. Set by the presentation thread, and
+  // read-only from the main thread to get the current time value.
+  float mVideoCurrentFrameTime;
+
+  // Volume that playback should start at.  0.0 = muted. 1.0 = full
+  // volume.  Readable/Writeable from the main thread. Read from the
+  // audio thread when it is first started to get the initial volume
+  // level.
+  double mInitialVolume;
+
+  // Audio data. These are initially set on the Decoder thread when
+  // the metadata is loaded. They are read from the presentation
+  // thread after this.
+  PRInt32 mAudioRate;
+  PRInt32 mAudioChannels;
+  PRInt32 mAudioTrack;
+
+  // Video data. Initially set on the Decoder thread when the metadata
+  // is loaded. Read from the presentation thread after this.
+  PRInt32 mVideoTrack;
+
+  // liboggplay State. Passed to liboggplay functions on any
+  // thread. liboggplay handles a lock internally for this.
+  OggPlay* mPlayer;
+
+  // OggPlay object used to read data from a channel. Created on main
+  // thread. Passed to liboggplay and the locking for multithreaded
+  // access is handled by that library.
+  nsChannelReader* mReader;
+
+  // True if the video playback is paused. Read/Write from the main
+  // thread. Read from the decoder thread to not buffer data if
+  // paused.
+  PRPackedBool mPaused;
+
+  // True if the first frame of data has been loaded. This member,
+  // along with the condition variable and lock is used by threads 
+  // that need to wait for the first frame to be loaded before 
+  // performing some action. In particular it is used for 'autoplay' to
+  // start playback on loading of the first frame.
+  PRPackedBool mFirstFrameLoaded;
+  PRCondVar* mFirstFrameCondVar;
+  PRLock* mFirstFrameLock;
+
+  // System time in seconds since video start, or last pause/resume.
+  // Used for synching video framerate to the system clock if there is
+  // no audio hardware or no audio track. Written by main thread, read
+  // by presentation thread to handle frame rate synchronisation.
+  double mSystemSyncSeconds;
+
+  // The media resource has been completely loaded into the pipe. No
+  // need to attempt to buffer if it starves. Written on the main
+  // thread, read from the decoding thread.
+  PRPackedBool mResourceLoaded;
+
+  // PR_TRUE if the metadata for the video has been loaded. Written on
+  // the main thread, read from the decoding thread.
+  PRPackedBool mMetadataLoaded;
+};
+
+#endif
--- a/content/media/video/src/Makefile.in
+++ b/content/media/video/src/Makefile.in
@@ -36,16 +36,26 @@
 
 DEPTH		= ../../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
+ifdef MOZ_OGG
+ifeq ($(OS_ARCH),Linux)
+DEFINES += -DSYDNEY_AUDIO_NO_POSITION
+endif
+
+ifeq ($(OS_ARCH),WINNT)
+DEFINES += -DSYDNEY_AUDIO_NO_VOLUME -DSYDNEY_AUDIO_NO_POSITION
+endif
+endif
+
 MODULE		= content
 LIBRARY_NAME	= gkconvideo_s
 LIBXUL_LIBRARY 	= 1
 
 REQUIRES	= \
 		xpcom \
 		string \
 		gfx \
@@ -77,16 +87,24 @@ REQUIRES	= \
 		cairo \
 		libpixman \
 		$(NULL)
 
 CPPSRCS		= \
 		nsVideoDecoder.cpp \
 		$(NULL)
 
+ifdef MOZ_OGG
+CPPSRCS		+= \
+		nsAudioStream.cpp \
+		nsChannelReader.cpp \
+		nsOggDecoder.cpp \
+		$(NULL)
+endif
+
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
 
 INCLUDES	+= \
 		-I$(srcdir)/../../../base/src \
 		-I$(srcdir)/../../../html/content/src \
 		$(NULL)
new file mode 100644
--- /dev/null
+++ b/content/media/video/src/nsAudioStream.cpp
@@ -0,0 +1,259 @@
+/* -*- 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>
+ *
+ * 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 <stdio.h>
+#include <math.h>
+#include "prlog.h"
+#include "prmem.h"
+#include "nsAutoPtr.h"
+#include "nsAudioStream.h"
+extern "C" {
+#include "oggplay_audio/sydney_audio.h"
+}
+
+#ifdef PR_LOGGING
+PRLogModuleInfo* gAudioStreamLog = nsnull;
+#endif
+
+nsresult nsAudioStream::InitLibrary()
+{
+#ifdef PR_LOGGING
+  gAudioStreamLog = PR_NewLogModule("nsAudioStream");
+#endif
+  return NS_OK;
+}
+
+void nsAudioStream::ShutdownLibrary()
+{
+}
+
+nsAudioStream::nsAudioStream() :
+  mVolume(1.0),
+#if defined(SYDNEY_AUDIO_NO_POSITION)
+  mPauseTime(0.0),
+#else
+  mPauseBytes(0),
+#endif
+  mAudioHandle(0),
+  mRate(0),
+  mChannels(0),
+  mPaused(PR_FALSE)
+{
+}
+
+nsresult nsAudioStream::Init(PRInt32 aNumChannels, PRInt32 aRate)
+{
+  mRate = aRate;
+  mChannels = aNumChannels;
+  if (sa_stream_create_pcm(reinterpret_cast<sa_stream_t**>(&mAudioHandle),
+                           NULL, 
+                           SA_MODE_WRONLY, 
+                           SA_PCM_FORMAT_S16_LE,
+                           aRate,
+                           aNumChannels) != SA_SUCCESS) {
+    mAudioHandle = nsnull;
+    PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_create_pcm error"));
+    return NS_OK;
+  }
+  
+  if (sa_stream_open(reinterpret_cast<sa_stream_t*>(mAudioHandle)) != SA_SUCCESS) {
+    sa_stream_destroy((sa_stream_t*)mAudioHandle);
+    mAudioHandle = nsnull;
+    PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_open error"));
+    return NS_OK;
+  }
+
+#if defined(SYDNEY_AUDIO_NO_POSITION)
+    mPauseTime = double(PR_IntervalToMilliseconds(PR_IntervalNow()))/1000.0;
+#endif
+
+  return NS_OK;
+}
+
+nsresult nsAudioStream::Shutdown()
+{
+  if (!mAudioHandle) 
+    return NS_OK;
+
+  sa_stream_destroy(reinterpret_cast<sa_stream_t*>(mAudioHandle));
+  mAudioHandle = nsnull;
+
+  return NS_OK;
+}
+
+nsresult nsAudioStream::Pause()
+{
+#if defined(USE_SYDNEY_AUDIO_OLD)
+  return NS_ERROR_NOT_IMPLEMENTED;
+#else
+  if (mPaused) 
+    return NS_OK;
+
+  mPaused = PR_TRUE;
+
+  if (!mAudioHandle) 
+    return NS_OK;
+
+#if defined(SYDNEY_AUDIO_NO_POSITION)
+  mPauseTime -= double(PR_IntervalToMilliseconds(PR_IntervalNow()))/1000.0;
+#else
+  // The audio hardware resets the count of the number of bytes
+  // when paused so we need to save it.
+  int64_t bytes = 0;
+  sa_stream_get_position(reinterpret_cast<sa_stream_t*>(mAudioHandle), SA_POSITION_WRITE_SOFTWARE, &bytes);
+  mPauseBytes += bytes;
+  sa_stream_pause(reinterpret_cast<sa_stream_t*>(mAudioHandle));
+#endif
+
+  return NS_OK;
+#endif
+}
+
+nsresult nsAudioStream::Resume()
+{
+#if defined(USE_SYDNEY_AUDIO_OLD)
+  return NS_ERROR_NOT_IMPLEMENTED;
+#else
+  if (!mPaused)
+    return NS_OK;
+
+  mPaused = PR_FALSE;
+
+  if (!mAudioHandle)
+    return NS_OK;
+
+  sa_stream_resume(reinterpret_cast<sa_stream_t*>(mAudioHandle));
+
+#if defined(SYDNEY_AUDIO_NO_POSITION)
+  mPauseTime += double(PR_IntervalToMilliseconds(PR_IntervalNow()))/1000.0;
+#endif
+
+  return NS_OK;
+#endif
+}
+
+nsresult nsAudioStream::Write(float* aBuf, PRUint32 aCount)
+{
+  if (!mAudioHandle)
+    return NS_OK;
+
+  // Convert array of floats, to an array of signed shorts
+  nsAutoArrayPtr<short> s_data(new short[aCount]);
+
+  if (s_data) {
+    for (PRUint32 i=0; i <  aCount; ++i) {
+      float scaled_value = floorf(0.5 + 32768 * aBuf[i]);
+      if (aBuf[i] < 0.0) {
+        s_data[i] = (scaled_value < -32768.0) ? 
+          -32768 : 
+          short(scaled_value);
+      }
+      else {
+        s_data[i] = (scaled_value > 32767.0) ? 
+          32767 : 
+          short(scaled_value);
+      }
+    }
+    
+#if defined(SYDNEY_AUDIO_NO_VOLUME)
+    if (mVolume > 0.00001) {
+#endif
+      if (sa_stream_write(reinterpret_cast<sa_stream_t*>(mAudioHandle), s_data.get(), aCount * sizeof(short)) != SA_SUCCESS) {
+        PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_write error"));
+        Shutdown();
+      }     
+#if defined(SYDNEY_AUDIO_NO_VOLUME)
+    }
+#endif
+  }
+
+  return NS_OK;
+}
+
+nsresult nsAudioStream::GetTime(double *aTime)
+{
+  if (!aTime)
+    return NS_OK;
+
+#if defined(SYDNEY_AUDIO_NO_POSITION)
+  *aTime = double(PR_IntervalToMilliseconds(PR_IntervalNow()))/1000.0 - mPauseTime;
+#else
+
+  int64_t bytes = 0;
+  if (mAudioHandle) {
+    sa_stream_get_position(reinterpret_cast<sa_stream_t*>(mAudioHandle), SA_POSITION_WRITE_SOFTWARE, &bytes);
+    *aTime = double(((bytes + mPauseBytes) * 1000 / mRate / (sizeof(short) * mChannels))) / 1000.0;
+  }
+  else {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+  
+#endif
+  return NS_OK;
+}
+
+nsresult nsAudioStream::GetVolume(float *aVolume)
+{
+  if (!aVolume)
+    return NS_OK;
+
+#if defined(SYDNEY_AUDIO_NO_VOLUME)
+  *aVolume = mVolume;
+#else
+  float volume = 0.0;
+  if (mAudioHandle) {
+    sa_stream_get_volume_abs(reinterpret_cast<sa_stream_t*>(mAudioHandle), &volume);
+  }
+    
+  *aVolume = volume;
+#endif
+
+  return NS_OK;
+}
+
+nsresult nsAudioStream::SetVolume(float aVolume)
+{
+#if defined(SYDNEY_AUDIO_NO_VOLUME) 
+  mVolume = aVolume;
+#else
+  if (!mAudioHandle)
+    return NS_OK;
+
+  sa_stream_set_volume_abs(reinterpret_cast<sa_stream_t*>(mAudioHandle), aVolume);
+#endif
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/content/media/video/src/nsChannelReader.cpp
@@ -0,0 +1,255 @@
+/* -*- 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>
+ *
+ * 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 "nsAString.h"
+#include "nsThreadUtils.h"
+#include "nsNetUtil.h"
+#include "prlog.h"
+#include "nsOggDecoder.h"
+#include "nsChannelReader.h"
+
+nsChannelToPipeListener::nsChannelToPipeListener(nsOggDecoder* aDecoder) :
+  mDecoder(aDecoder),
+  mIntervalStart(0),
+  mIntervalEnd(0),
+  mTotalBytes(0)
+{
+}
+
+nsresult nsChannelToPipeListener::Init() 
+{
+  nsresult rv = NS_NewPipe(getter_AddRefs(mInput), 
+                           getter_AddRefs(mOutput),
+                           0, 
+                           PR_UINT32_MAX);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+void nsChannelToPipeListener::Stop()
+{
+  mDecoder = nsnull;
+  mInput = nsnull;
+  mOutput = nsnull;
+  mDecoder = nsnull;
+}
+
+double nsChannelToPipeListener::BytesPerSecond() const
+{
+  return mTotalBytes / ((PR_IntervalToMilliseconds(mIntervalEnd-mIntervalStart)) / 1000.0);
+}
+
+void nsChannelToPipeListener::GetInputStream(nsIInputStream** aStream)
+{
+  if (aStream) {
+    NS_IF_ADDREF(*aStream = mInput);
+  }
+}
+
+nsresult nsChannelToPipeListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
+{
+  mIntervalStart = PR_IntervalNow();
+  mIntervalEnd = mIntervalStart;
+  mTotalBytes = 0;
+  mDecoder->UpdateBytesDownloaded(mTotalBytes);
+  return NS_OK;
+}
+
+nsresult nsChannelToPipeListener::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus) 
+{
+  mOutput = nsnull;
+  if (mDecoder) {
+    mDecoder->ResourceLoaded();
+  }
+  return NS_OK;
+}
+
+nsresult nsChannelToPipeListener::OnDataAvailable(nsIRequest* aRequest, 
+                                                nsISupports* aContext, 
+                                                nsIInputStream* aStream,
+                                                PRUint32 aOffset,
+                                                PRUint32 aCount)
+{
+  if (mOutput) {
+    PRUint32 bytes = 0;
+  
+    do {
+      nsresult rv = mOutput->WriteFrom(aStream, aCount, &bytes);
+      NS_ENSURE_SUCCESS(rv, rv);
+      
+      aCount -= bytes;
+      mTotalBytes += bytes;
+      mDecoder->UpdateBytesDownloaded(mTotalBytes);
+    } while (aCount) ;
+    
+    nsresult rv = mOutput->Flush();
+    NS_ENSURE_SUCCESS(rv, rv);
+    mIntervalEnd = PR_IntervalNow();
+  }
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS2(nsChannelToPipeListener, nsIRequestObserver, nsIStreamListener)
+
+PRUint32 nsChannelReader::Available()
+{
+  PRUint32 available = 0;
+  mInput->Available(&available);
+  return available;
+}
+
+double nsChannelReader::BytesPerSecond() const
+{
+  return mListener->BytesPerSecond();
+}
+
+OggPlayErrorCode nsChannelReader::initialise(int aBlock)
+{
+  return E_OGGPLAY_OK;
+}
+
+OggPlayErrorCode nsChannelReader::destroy()
+{
+  mChannel->Cancel(NS_BINDING_ABORTED);
+  mChannel = nsnull;
+  mInput->Close();
+  mInput = nsnull;
+  mListener->Stop();
+  mListener = nsnull;
+
+  return E_OGGPLAY_OK;
+}
+
+size_t nsChannelReader::io_read(char* aBuffer, size_t aCount)
+{
+  PRUint32 bytes = 0;
+  nsresult rv = mInput->Read(aBuffer, aCount, &bytes);
+  if (!NS_SUCCEEDED(rv)) {
+    bytes = 0;
+  }
+
+  mCurrentPosition += bytes;
+  return bytes == 0 ? static_cast<size_t>(OGGZ_ERR_SYSTEM) : bytes;
+}
+
+int nsChannelReader::io_seek(long aOffset, int aWhence)
+{
+  // How to implement seek in channels?
+  // fseek(file, offset, whence);
+
+  return -1;
+}
+
+long nsChannelReader::io_tell()
+{
+  return mCurrentPosition;
+}
+
+static OggPlayErrorCode oggplay_channel_reader_initialise(OggPlayReader* aReader, int aBlock) 
+{
+  nsChannelReader * me = static_cast<nsChannelReader*>(aReader);
+
+  if (me == NULL) {
+    return E_OGGPLAY_BAD_READER;
+  }
+  return me->initialise(aBlock);
+}
+
+static OggPlayErrorCode oggplay_channel_reader_destroy(OggPlayReader* aReader) 
+{
+  nsChannelReader* me = static_cast<nsChannelReader*>(aReader);
+  OggPlayErrorCode result = me->destroy();
+  delete me;
+  return result;
+}
+
+static size_t oggplay_channel_reader_io_read(void* aReader, void* aBuffer, size_t aCount) 
+{
+  nsChannelReader* me = static_cast<nsChannelReader*>(aReader);
+  return me->io_read(static_cast<char*>(aBuffer), aCount);
+}
+
+static int oggplay_channel_reader_io_seek(void* aReader, long aOffset, int aWhence) 
+{
+  nsChannelReader* me = static_cast<nsChannelReader*>(aReader);
+  return me->io_seek(aOffset, aWhence);
+}
+
+static long oggplay_channel_reader_io_tell(void* aReader) 
+{
+  nsChannelReader* me = static_cast<nsChannelReader*>(aReader);
+  return me->io_tell();
+}
+
+nsresult nsChannelReader::Init(nsOggDecoder* aDecoder, nsIURI* aURI)
+{
+  nsresult rv;
+
+  mCurrentPosition = 0;
+  mListener = new nsChannelToPipeListener(aDecoder);
+  NS_ENSURE_TRUE(mListener, NS_ERROR_OUT_OF_MEMORY);
+
+  rv = mListener->Init();
+  NS_ENSURE_SUCCESS(rv, rv);
+  
+  rv = NS_NewChannel(getter_AddRefs(mChannel), 
+                     aURI, 
+                     nsnull,
+                     nsnull,
+                     nsnull,
+                     nsIRequest::LOAD_NORMAL);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mChannel->AsyncOpen(mListener, nsnull);
+  NS_ENSURE_SUCCESS(rv, rv);
+  
+  mListener->GetInputStream(getter_AddRefs(mInput));
+
+  return NS_OK;
+}
+
+nsChannelReader::nsChannelReader() 
+{
+  OggPlayReader* reader = this;
+  reader->initialise = &oggplay_channel_reader_initialise;
+  reader->destroy = &oggplay_channel_reader_destroy;
+  reader->seek = nsnull;
+  reader->io_read = &oggplay_channel_reader_io_read;
+  reader->io_seek = &oggplay_channel_reader_io_seek;
+  reader->io_tell = &oggplay_channel_reader_io_tell;
+}
new file mode 100644
--- /dev/null
+++ b/content/media/video/src/nsOggDecoder.cpp
@@ -0,0 +1,892 @@
+/* -*- 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: ML 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>
+ *
+ * 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 "prlog.h"
+#include "prmem.h"
+#include "nsIFrame.h"
+#include "nsIDocument.h"
+#include "nsThreadUtils.h"
+#include "nsIDOMHTMLMediaElement.h"
+#include "nsNetUtil.h"
+#include "nsAudioStream.h"
+#include "nsChannelReader.h"
+#include "nsHTMLVideoElement.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsAutoLock.h"
+#include "nsIRenderingContext.h"
+#include "gfxContext.h"
+#include "gfxImageSurface.h"
+#include "nsPresContext.h"
+#include "nsOggDecoder.h"
+
+/* 
+   The maximum height and width of the video. Used for
+   sanitizing the memory allocation of the RGB buffer
+*/
+#define MAX_VIDEO_WIDTH  2000
+#define MAX_VIDEO_HEIGHT 2000
+
+// The number of entries in oggplay buffer list. This value
+// is the one used by the oggplay examples.
+#define OGGPLAY_BUFFER_SIZE 20
+
+// The number of frames to read before audio callback is called.
+// This value is the one used by the oggplay examples.
+#define OGGPLAY_FRAMES_PER_CALLBACK 2048
+
+// Offset into Ogg buffer containing audio information. This value
+// is the one used by the oggplay examples.
+#define OGGPLAY_AUDIO_OFFSET 250L
+
+// Maximum mumber of milliseconds to pause while buffering video data
+// on a slow connection. The DATA_WAIT is used to compute the maximum
+// amount of data to wait for based on the current bytes/second
+// download rate. The TIME_WAIT is the total maximum time to wait in
+// case the bytes/second rate slows down.
+#define MAX_BUFFERING_DATA_WAIT_MS 15000
+#define MAX_BUFFERING_TIME_WAIT_MS 30000
+
+// An Event that uses an nsOggDecoder object. It provides a
+// way to inform the event that the decoder object is no longer
+// valid, so queued events can safely ignore it.
+class nsDecoderEvent : public nsRunnable {
+public:
+  nsDecoderEvent(nsOggDecoder* decoder) : 
+    mLock(nsnull), 
+    mDecoder(decoder)
+  {
+  }
+
+  PRBool Init()
+  {
+    mLock = PR_NewLock();
+    return mLock != nsnull;
+  }
+
+  virtual ~nsDecoderEvent()
+  {
+    if (mLock) {
+      PR_DestroyLock(mLock);
+      mLock = nsnull;
+    }
+  }
+
+  void Revoke()
+  {
+    nsAutoLock lock(mLock);
+    mDecoder = nsnull;
+  }
+
+  void Lock() 
+  {
+    PR_Lock(mLock);
+  }
+
+  void Unlock() 
+  {
+    PR_Unlock(mLock);
+  }
+
+  NS_IMETHOD Run() {
+    nsAutoLock lock(mLock);
+    return RunWithLock();
+  }
+
+protected:
+  virtual nsresult RunWithLock() = 0;
+
+  PRLock* mLock;
+  nsOggDecoder* mDecoder;
+};
+
+// This handles the Ogg decoding loop. It blocks waiting for
+// ogg data, decodes that data, and then goes back to blocking.
+// It is synchronised with the nsVideoPresentationEvent internally
+// by OggPlay so that it doesn't go to far ahead or behind the
+// display of the video frame data.
+class nsVideoDecodeEvent : public nsDecoderEvent
+{
+public:
+  nsVideoDecodeEvent(nsOggDecoder* decoder) :
+    nsDecoderEvent(decoder)
+  {
+  }
+
+protected:
+  nsresult RunWithLock() 
+  {
+    if (mDecoder && mDecoder->StepDecoding()) {
+      NS_GetCurrentThread()->Dispatch(this, NS_DISPATCH_NORMAL);
+    }
+    else {
+      LOG(PR_LOG_DEBUG, ("Decoding thread completed"));
+    }
+    return NS_OK;
+  }
+
+private:
+  // PR_TRUE if we are actively decoding
+  PRPackedBool mDecoding;
+};
+
+class nsVideoPresentationEvent : public nsDecoderEvent
+{
+public:
+  nsVideoPresentationEvent(nsOggDecoder* decoder) :
+    nsDecoderEvent(decoder)
+  {
+  }
+
+  // Stop the invalidation timer. When we aren't decoding
+  // video frames we stop the timer since it takes a fair
+  // amount of CPU on some platforms.
+  void StopInvalidating()
+  {
+    if (mDecoder) {
+      // Stop the invalidation timer
+      nsCOMPtr<nsIRunnable> event = 
+        NS_NEW_RUNNABLE_METHOD(nsOggDecoder, mDecoder, StopInvalidating); 
+      
+      if (event) {
+        NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+      }
+    }
+  }
+  
+  nsresult RunWithLock() {
+    if (mDecoder && !mDecoder->IsPaused() && mDecoder->StepDisplay()) {
+      NS_GetCurrentThread()->Dispatch(this, NS_DISPATCH_NORMAL);
+    }
+    else {
+      LOG(PR_LOG_DEBUG, ("Presentation thread completed"));
+      StopInvalidating();
+    }
+    return NS_OK;
+  }
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsOggDecoder, nsIObserver)
+
+void nsOggDecoder::Pause() 
+{
+  if (!mPresentationThread)
+    return;
+
+  mPaused = PR_TRUE;
+  nsCOMPtr<nsIRunnable> event = 
+    NS_NEW_RUNNABLE_METHOD(nsOggDecoder, this, DoPause); 
+  if (event)
+    mPresentationThread->Dispatch(event, NS_DISPATCH_NORMAL);
+}
+
+void nsOggDecoder::DoPause() 
+{
+  mPaused = PR_TRUE;
+  if (mAudioStream) {
+    mAudioStream->Pause();
+  }
+  mSystemSyncSeconds = double(PR_IntervalToMilliseconds(PR_IntervalNow()))/1000.0;
+}
+
+float nsOggDecoder::GetVolume()
+{
+  float volume = 0.0;
+  if (mAudioStream) {
+    mAudioStream->GetVolume(&volume);
+  }
+  else {
+    volume = mInitialVolume;
+  }
+
+  return volume;
+}
+
+void nsOggDecoder::SetVolume(float volume)
+{
+  if (mAudioStream) {
+    mAudioStream->SetVolume(volume);
+  }
+  else {
+    mInitialVolume = volume;
+  }
+}
+
+float nsOggDecoder::GetDuration()
+{
+  // Currently not implemented. Video Spec says to return
+  // NaN if unknown.
+  // TODO: return NaN
+  return 0.0;
+}
+
+nsOggDecoder::nsOggDecoder() :
+  nsVideoDecoder(),
+  mBytesDownloaded(0),
+  mVideoNextFrameTime(0.0),
+  mLoadInProgress(PR_FALSE),
+  mPlayAfterLoad(PR_FALSE),
+  mVideoCurrentFrameTime(0.0),
+  mInitialVolume(1.0),
+  mAudioRate(0),
+  mAudioChannels(0),
+  mAudioTrack(-1),
+  mVideoTrack(-1),
+  mPlayer(0),
+  mReader(0),
+  mPaused(PR_TRUE),
+  mFirstFrameLoaded(PR_FALSE),
+  mFirstFrameCondVar(nsnull),
+  mFirstFrameLock(nsnull),
+  mSystemSyncSeconds(0.0),
+  mResourceLoaded(PR_FALSE),
+  mMetadataLoaded(PR_FALSE)
+{
+}
+
+PRBool nsOggDecoder::Init() 
+{
+  mFirstFrameLock = PR_NewLock();  
+  mFirstFrameCondVar = mFirstFrameLock ? PR_NewCondVar(mFirstFrameLock) : nsnull ;
+
+  mDecodeEvent = new nsVideoDecodeEvent(this);
+  mPresentationEvent = new nsVideoPresentationEvent(this);
+
+  nsCOMPtr<nsIObserverService> observerService =
+    do_GetService("@mozilla.org/observer-service;1");
+  if (observerService) {
+    observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
+  }
+  else {
+    NS_WARNING("Could not get an observer service. Video decoding events may not shutdown cleanly.");
+  }
+
+  return mFirstFrameLock &&
+    mFirstFrameCondVar &&
+    mDecodeEvent && mDecodeEvent->Init() &&
+    mPresentationEvent && mPresentationEvent->Init() &&
+    nsVideoDecoder::Init();
+}
+
+void nsOggDecoder::Shutdown() 
+{
+  if (mDecodeEvent) {
+    // Must prepare oggplay for close to stop the decode event from
+    // waiting on oggplay's semaphore. Without this, revoke deadlocks.
+    if (mPlayer) {
+      oggplay_prepare_for_close(mPlayer);
+    }
+
+    mDecodeEvent->Revoke();
+    mDecodeEvent = nsnull;
+  }
+  if (mPresentationEvent) {
+    mPresentationEvent->Revoke();
+    mPresentationEvent = nsnull;
+  }
+
+  Stop();
+  nsVideoDecoder::Shutdown();
+}
+
+nsOggDecoder::~nsOggDecoder()
+{
+  Shutdown();
+  if (mFirstFrameCondVar) {
+    PR_DestroyCondVar(mFirstFrameCondVar);
+    mFirstFrameCondVar = nsnull;
+  }
+  if (mFirstFrameLock) {
+    PR_DestroyLock(mFirstFrameLock);
+    mFirstFrameLock = nsnull;
+  }
+}
+
+nsIntSize nsOggDecoder::GetVideoSize(nsIntSize defaultSize)
+{
+  return (mRGBWidth == -1 || mRGBHeight == -1) ? defaultSize : nsIntSize(mRGBWidth, mRGBHeight);
+}
+
+double nsOggDecoder::GetVideoFramerate() {
+  return mFramerate;
+}
+
+PRBool nsOggDecoder::IsPaused()
+{
+  return mPaused;
+}
+
+PRBool nsOggDecoder::StepDecoding()
+{
+  PRBool stop = PR_TRUE;
+  if (mPlayer && mDecodeThread) {
+    OggPlayErrorCode r = oggplay_step_decoding(mPlayer);
+    if (r != E_OGGPLAY_CONTINUE && 
+        r != E_OGGPLAY_USER_INTERRUPT &&
+        r != E_OGGPLAY_TIMEOUT) {
+      stop = PR_TRUE;
+      // Inform the element that we've ended the video
+      nsCOMPtr<nsIRunnable> event = 
+        NS_NEW_RUNNABLE_METHOD(nsOggDecoder, this, PlaybackCompleted); 
+      
+      if (event) {
+        NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+      }
+    }
+    else {
+      stop = PR_FALSE;
+
+      if (r == E_OGGPLAY_CONTINUE)
+        BufferData();
+    }
+  }
+  return !stop;
+}
+
+void nsOggDecoder::BufferData()
+{
+  if (!mPaused && mReader && mMetadataLoaded && !mResourceLoaded) {    
+    double bps = mReader->BytesPerSecond();
+    PRUint32 bytes = static_cast<PRUint32>((bps * MAX_BUFFERING_DATA_WAIT_MS)/1000.0);
+
+    // Buffer if it looks like we'll starve for data during
+    // MAX_BUFFERING_DATA_WAIT_MS time period.
+    if (mReader->Available() < bytes) {
+      PRIntervalTime start = PR_IntervalNow();
+      if (mElement) {
+        nsCOMPtr<nsIRunnable> event = 
+          NS_NEW_RUNNABLE_METHOD(nsOggDecoder, this, BufferingStarted);
+        if (event) 
+          // The pause event must be processed before the
+          // buffering loop occurs. Otherwise the loop exits
+          // immediately as it checks to see if the user chose
+          // to unpause. 
+          NS_DispatchToMainThread(event, NS_DISPATCH_SYNC);
+      }
+      
+      // Sleep to allow more data to be downloaded. Note that we are
+      // sleeping the decode thread, not the main thread. We stop
+      // sleeping when the resource is completely loaded, or the user
+      // explicitly chooses to continue playing, or an arbitary time
+      // period has elapsed or we've downloaded enough bytes to
+      // continue playing at the current download rate.
+      while (!mResourceLoaded && 
+             mPaused && 
+             (PR_IntervalToMilliseconds(PR_IntervalNow() - start) < MAX_BUFFERING_TIME_WAIT_MS) &&
+             mReader->Available() < bytes) {
+        LOG(PR_LOG_DEBUG, 
+            ("Buffering data until %d bytes available or %d milliseconds", 
+             (long)(bytes - mReader->Available()),
+             MAX_BUFFERING_TIME_WAIT_MS - (PR_IntervalToMilliseconds(PR_IntervalNow() - start))));
+        
+        PR_Sleep(PR_MillisecondsToInterval(1000));
+        
+        bps = mReader->BytesPerSecond();       
+        bytes = static_cast<PRUint32>((bps * (MAX_BUFFERING_DATA_WAIT_MS))/1000.0);
+      }
+
+      if (mElement) {
+        nsCOMPtr<nsIRunnable> event = 
+          NS_NEW_RUNNABLE_METHOD(nsOggDecoder, this, BufferingStopped);
+        if (event) 
+          NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+      }
+    }
+  }
+}
+
+nsresult nsOggDecoder::Load(nsIURI* aURI) 
+{
+  nsresult rv;
+  Stop();
+  mURI = aURI;
+
+  mReader = new nsChannelReader();
+  NS_ENSURE_TRUE(mReader, NS_ERROR_OUT_OF_MEMORY);
+
+  rv = mReader->Init(this, aURI);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = NS_NewThread(getter_AddRefs(mDecodeThread));
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = NS_NewThread(getter_AddRefs(mPresentationThread));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mLoadInProgress = PR_TRUE;
+  nsCOMPtr<nsIRunnable> event = 
+    NS_NEW_RUNNABLE_METHOD(nsOggDecoder, this, LoadOggHeaders); 
+  NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
+
+  rv = mDecodeThread->Dispatch(event, NS_DISPATCH_NORMAL);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  StartProgress();
+
+  return NS_OK;
+}
+
+nsresult nsOggDecoder::Play()
+{
+  mPaused = PR_FALSE;
+  if (mLoadInProgress) {
+    mPlayAfterLoad = PR_TRUE;
+    return NS_OK;
+  }
+  else if (!mPlayer) {
+    Load(mURI);
+  }
+  else {
+    StartPlaybackThreads();
+  }
+
+  return NS_OK;
+} 
+
+nsresult nsOggDecoder::Seek(float time)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;  
+}
+
+nsresult nsOggDecoder::PlaybackRateChanged()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+void nsOggDecoder::Stop()
+{
+  mLoadInProgress = PR_FALSE;
+  StopInvalidating();
+  StopProgress();
+  if (mDecodeThread) {
+    if (mPlayer) {
+      oggplay_prepare_for_close(mPlayer);
+    }
+    mDecodeThread->Shutdown();
+    mDecodeThread = nsnull;
+  }
+  if (mPresentationThread) {
+    if (!mFirstFrameLoaded) {
+      mFirstFrameLoaded = PR_TRUE;
+      PR_NotifyAllCondVar(mFirstFrameCondVar);
+    }
+
+    mPresentationThread->Shutdown();
+    mPresentationThread = nsnull;
+  }
+  CloseAudioStream();
+  if (mPlayer){
+    oggplay_close(mPlayer);
+    mPlayer = nsnull;
+  }
+  mPaused = PR_TRUE;
+  mVideoCurrentFrameTime = 0.0;
+}
+
+void nsOggDecoder::HandleVideoData(int track_num, OggPlayVideoData* video_data) {
+  int y_width;
+  int y_height;
+  oggplay_get_video_y_size(mPlayer, track_num, &y_width, &y_height);
+
+  int uv_width;
+  int uv_height;
+  oggplay_get_video_uv_size(mPlayer, track_num, &uv_width, &uv_height);
+
+  if (y_width >= MAX_VIDEO_WIDTH || y_height >= MAX_VIDEO_HEIGHT)
+    return;
+
+  {
+    nsAutoLock lock(mVideoUpdateLock);
+
+    SetRGBData(y_width, y_height, mFramerate, nsnull);
+
+    // If there is not enough memory to allocate the RGB buffer,
+    // don't display it, but continue without error. When enough
+    // memory is available the display will start working again.
+    if (mRGB) {
+      OggPlayYUVChannels yuv;
+      OggPlayRGBChannels rgb;
+      
+      yuv.ptry = video_data->y;
+      yuv.ptru = video_data->u;
+      yuv.ptrv = video_data->v;
+      yuv.uv_width = uv_width;
+      yuv.uv_height = uv_height;
+      yuv.y_width = y_width;
+      yuv.y_height = y_height;
+      
+      rgb.ptro = mRGB.get();
+      rgb.rgb_width = mRGBWidth;
+      rgb.rgb_height = mRGBHeight;
+
+      oggplay_yuv2bgr(&yuv, &rgb);
+    }
+  }
+}
+
+void nsOggDecoder::HandleAudioData(OggPlayAudioData* audio_data, int size) {
+  if (mAudioStream) {
+    // 'size' is number of samples. Multiply by number of channels
+    // to get the actual number of floats being sent.
+    nsresult rv = mAudioStream->Write(reinterpret_cast<float*>(audio_data), size * mAudioChannels);
+    if (!NS_SUCCEEDED(rv)) {
+      LOG(PR_LOG_ERROR, ("Could not write audio data to pipe"));
+    }
+  }
+}
+
+double nsOggDecoder::GetSyncTime() 
+{
+  double time = 0.0;
+  if (mAudioStream && mAudioTrack != -1) {
+    mAudioStream->GetTime(&time);
+  }
+  else {
+    // No audio stream, or audio track. Sync to system clock.
+    time = 
+      (mSystemSyncSeconds == 0.0) ?
+      0.0 :
+      double(PR_IntervalToMilliseconds(PR_IntervalNow()))/1000.0 - mSystemSyncSeconds;
+  }
+
+  return time;
+}
+
+void nsOggDecoder::OpenAudioStream()
+{
+  mAudioStream = new nsAudioStream();
+  if (!mAudioStream) {
+    LOG(PR_LOG_ERROR, ("Could not create audio stream"));
+  }
+  else {
+    mAudioStream->Init(mAudioChannels, mAudioRate);
+    mAudioStream->SetVolume(mInitialVolume);
+  }
+}
+
+void nsOggDecoder::CloseAudioStream()
+{
+  if (mAudioStream) {
+    mAudioStream->Shutdown();
+    mAudioStream = nsnull;
+  }
+}
+
+void nsOggDecoder::LoadOggHeaders() 
+{
+  LOG(PR_LOG_DEBUG, ("Loading Ogg Headers"));
+  mPlayer = oggplay_open_with_reader((OggPlayReader*)mReader);
+  if (mPlayer) {
+    LOG(PR_LOG_DEBUG, ("There are %d tracks", oggplay_get_num_tracks(mPlayer)));
+
+    for (int i = 0; i < oggplay_get_num_tracks(mPlayer); ++i) {
+      LOG(PR_LOG_DEBUG, ("Tracks %d: %s", i, oggplay_get_track_typename(mPlayer, i)));
+      if (mVideoTrack == -1 && oggplay_get_track_type(mPlayer, i) == OGGZ_CONTENT_THEORA) {
+        oggplay_set_callback_num_frames(mPlayer, i, 1);
+        mVideoTrack = i;
+        int fpsd, fpsn;
+        oggplay_get_video_fps(mPlayer, i, &fpsd, &fpsn);
+        mFramerate = fpsd == 0 ? 0.0 : double(fpsn)/double(fpsd);
+        LOG(PR_LOG_DEBUG, ("Frame rate: %f", mFramerate));
+      }
+      else if (mAudioTrack == -1 && oggplay_get_track_type(mPlayer, i) == OGGZ_CONTENT_VORBIS) {
+        mAudioTrack = i;
+        oggplay_set_offset(mPlayer, i, OGGPLAY_AUDIO_OFFSET);
+        oggplay_get_audio_samplerate(mPlayer, i, &mAudioRate);
+        oggplay_get_audio_channels(mPlayer, i, &mAudioChannels);
+        LOG(PR_LOG_DEBUG, ("samplerate: %d, channels: %d", mAudioRate, mAudioChannels));
+      }
+      
+      if (oggplay_set_track_active(mPlayer, i) < 0) 
+        LOG(PR_LOG_ERROR, ("Could not set track %d active", i));
+    }
+    
+    if (mVideoTrack == -1) {
+      oggplay_set_callback_num_frames(mPlayer, mAudioTrack, OGGPLAY_FRAMES_PER_CALLBACK);
+    }
+
+    oggplay_use_buffer(mPlayer, OGGPLAY_BUFFER_SIZE);
+
+    // Inform the element that we've loaded the Ogg metadata
+    nsCOMPtr<nsIRunnable> metadataLoadedEvent = 
+      NS_NEW_RUNNABLE_METHOD(nsOggDecoder, this, MetadataLoaded); 
+    
+    if (metadataLoadedEvent) {
+      NS_DispatchToMainThread(metadataLoadedEvent, NS_DISPATCH_NORMAL);
+    }
+
+    // Load the first frame of data 
+    nsCOMPtr<nsIRunnable> firstFrameEvent = 
+      NS_NEW_RUNNABLE_METHOD(nsOggDecoder, this, LoadFirstFrame); 
+    
+    if (firstFrameEvent) {
+      NS_GetCurrentThread()->Dispatch(firstFrameEvent, NS_DISPATCH_NORMAL);
+    }
+  }
+}
+
+// This is always run in the decoder thread
+void nsOggDecoder::LoadFirstFrame() 
+{
+  if (StepDecoding()) {
+    // Inform the element that we've loaded the first frame of data
+    nsCOMPtr<nsIRunnable> frameLoadedEvent = 
+      NS_NEW_RUNNABLE_METHOD(nsOggDecoder, this, FirstFrameLoaded); 
+
+    if (frameLoadedEvent) {
+      NS_DispatchToMainThread(frameLoadedEvent, NS_DISPATCH_NORMAL);
+    }
+    nsCOMPtr<nsIRunnable> displayEvent = 
+      NS_NEW_RUNNABLE_METHOD(nsOggDecoder, this, DisplayFirstFrame); 
+    if (displayEvent) {
+      mPresentationThread->Dispatch(displayEvent, NS_DISPATCH_NORMAL);
+    }
+
+    // Notify waiting presentation thread that it can start playing
+    {
+      nsAutoLock lock(mFirstFrameLock);
+
+      mFirstFrameLoaded = PR_TRUE;
+      PR_NotifyAllCondVar(mFirstFrameCondVar);
+    }
+    mDecodeThread->Dispatch(mDecodeEvent, NS_DISPATCH_NORMAL);
+  }
+}
+
+void nsOggDecoder::StartPresentationThread() 
+{
+  {
+    nsAutoLock lock(mFirstFrameLock);
+
+    while (!mFirstFrameLoaded)
+      PR_WaitCondVar(mFirstFrameCondVar, PR_INTERVAL_NO_TIMEOUT);
+  }
+
+  if (mAudioStream) {
+    mAudioStream->Resume();
+  }
+  else {
+    OpenAudioStream();
+  }
+  
+  mSystemSyncSeconds = double(PR_IntervalToMilliseconds(PR_IntervalNow()))/1000.0;
+  mPresentationThread->Dispatch(mPresentationEvent, NS_DISPATCH_NORMAL);
+}
+ 
+float nsOggDecoder::GetCurrentTime()
+{
+  return mVideoCurrentFrameTime;
+}
+
+void nsOggDecoder::GetCurrentURI(nsIURI** aURI)
+{
+  NS_IF_ADDREF(*aURI = mURI);
+}
+
+
+void nsOggDecoder::DisplayFirstFrame()
+{
+  // Step through the decoded frames, allowing display of the first frame
+  // of video.
+  StepDisplay();
+}
+
+void nsOggDecoder::ProcessTrack(int aTrackNumber, OggPlayCallbackInfo* aTrackInfo)
+{
+  OggPlayDataType type = oggplay_callback_info_get_type(aTrackInfo);
+  OggPlayDataHeader ** headers = oggplay_callback_info_get_headers(aTrackInfo);
+  switch(type) {
+  case OGGPLAY_INACTIVE:
+    {
+      break;
+    }
+    
+  case OGGPLAY_YUV_VIDEO:
+    {
+      double video_time = ((double)oggplay_callback_info_get_presentation_time(headers[0]))/1000.0;
+      mVideoCurrentFrameTime = video_time;
+          
+      OggPlayVideoData* video_data = oggplay_callback_info_get_video_data(headers[0]);
+      HandleVideoData(aTrackNumber, video_data);
+    }
+    break;
+  case OGGPLAY_FLOATS_AUDIO:
+    {
+      int required = oggplay_callback_info_get_required(aTrackInfo);
+      for (int j = 0; j < required; ++j) {
+        int size = oggplay_callback_info_get_record_size(headers[j]);
+        OggPlayAudioData* audio_data = oggplay_callback_info_get_audio_data(headers[j]);
+        HandleAudioData(audio_data, size);
+      }
+      break;
+    }
+  case OGGPLAY_CMML:
+    {
+      if (oggplay_callback_info_get_required(aTrackInfo) > 0)
+        LOG(PR_LOG_DEBUG, ("CMML: %s", oggplay_callback_info_get_text_data(headers[0])));
+      break;
+    }
+  default:
+    break;
+  }
+}
+
+PRBool nsOggDecoder::StepDisplay()
+{
+  if (!mPlayer/* || !mDecodeThread */) {
+    return PR_FALSE;
+  }
+
+  int num_tracks = oggplay_get_num_tracks(mPlayer);
+  OggPlayCallbackInfo  ** track_info = oggplay_buffer_retrieve_next(mPlayer);
+
+  if (track_info) {
+   double audio_time = GetSyncTime();
+    PRInt32 millis = PRInt32((mVideoNextFrameTime-audio_time) * 1000.0);      
+    // LOG(PR_LOG_DEBUG, ("Sleep: %d %f %f", millis, audio_time, mVideoNextFrameTime));
+    mVideoNextFrameTime += 1.0/mFramerate;
+
+    if (millis > 0) {
+      PR_Sleep(PR_MillisecondsToInterval(millis));
+    }
+
+   if (mVideoTrack != -1 && num_tracks > mVideoTrack)  {
+      ProcessTrack(mVideoTrack, track_info[mVideoTrack]);
+   }
+
+   if (mAudioTrack != -1 && num_tracks > mAudioTrack) {
+      ProcessTrack(mAudioTrack, track_info[mAudioTrack]);
+   }
+
+   oggplay_buffer_release(mPlayer, track_info);
+  }
+  else {
+    PR_Sleep(PR_MillisecondsToInterval(10));
+  }
+
+  return PR_TRUE;
+}
+
+void nsOggDecoder::StartPlaybackThreads()
+{
+  StartInvalidating(mFramerate);
+
+  nsCOMPtr<nsIRunnable> event = 
+    NS_NEW_RUNNABLE_METHOD(nsOggDecoder, this, StartPresentationThread); 
+  if (event)
+    mPresentationThread->Dispatch(event, NS_DISPATCH_NORMAL);
+}
+
+void nsOggDecoder::MetadataLoaded()
+{
+  mMetadataLoaded = PR_TRUE;
+  if (mElement) {
+    mElement->MetadataLoaded();
+  }
+
+  if (mPlayAfterLoad) {
+    mPlayAfterLoad = PR_FALSE;
+    StartPlaybackThreads();
+  }
+  mLoadInProgress = PR_FALSE;
+}
+
+void nsOggDecoder::FirstFrameLoaded()
+{
+  StartInvalidating(mFramerate);
+  if (mElement) {
+    mElement->FirstFrameLoaded();
+  }
+}
+
+void nsOggDecoder::ResourceLoaded()
+{
+  mResourceLoaded = PR_TRUE;
+  if (mElement) {
+    mElement->ResourceLoaded();
+  }
+  StopProgress();
+}
+
+void nsOggDecoder::PlaybackCompleted()
+{
+  if (mElement) {
+    mElement->PlaybackCompleted();
+  }
+}
+
+NS_IMETHODIMP nsOggDecoder::Observe(nsISupports *aSubjet,
+                                      const char *aTopic,
+                                      const PRUnichar *someData)
+{
+  if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
+    Shutdown();
+  }
+
+  return NS_OK;
+}
+
+PRUint32 nsOggDecoder::GetBytesLoaded()
+{
+  return mBytesDownloaded;
+}
+
+PRUint32 nsOggDecoder::GetTotalBytes()
+{
+  // TODO: Need to compute this for ogg files
+  return 0;
+}
+
+void nsOggDecoder::UpdateBytesDownloaded(PRUint32 aBytes)
+{
+  mBytesDownloaded = aBytes;
+}
+
+void nsOggDecoder::BufferingStopped()
+{
+  if (mElement) {
+    mElement->ChangeReadyState(nsIDOMHTMLMediaElement::CAN_SHOW_CURRENT_FRAME);
+    mElement->Play();
+  }
+}
+
+void nsOggDecoder::BufferingStarted()
+{
+  if (mElement) {
+    mElement->Pause();
+    mElement->ChangeReadyState(nsIDOMHTMLMediaElement::DATA_UNAVAILABLE);
+  }
+}
--- a/content/media/video/src/nsVideoDecoder.cpp
+++ b/content/media/video/src/nsVideoDecoder.cpp
@@ -30,17 +30,16 @@
  * 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 <new>
 #include "prlog.h"
 #include "prmem.h"
 #include "nsIFrame.h"
 #include "nsIDocument.h"
 #include "nsThreadUtils.h"
 #include "nsIDOMHTMLMediaElement.h"
 #include "nsNetUtil.h"
 #include "nsHTMLMediaElement.h"
@@ -180,28 +179,27 @@ nsresult nsVideoDecoder::StopProgress()
     rv = mProgressTimer->Cancel();
     mProgressTimer = nsnull;
   }
   return rv;
 }
 
 void nsVideoDecoder::SetRGBData(PRInt32 aWidth, PRInt32 aHeight, double aFramerate, unsigned char* aRGBBuffer)
 {
-  nsAutoLock lock(mVideoUpdateLock);
   if (mRGBWidth != aWidth || mRGBHeight != aHeight) {
     mRGBWidth = aWidth;
     mRGBHeight = aHeight;
     mSizeChanged = PR_TRUE;
     // Delete buffer so we'll reallocate it
     mRGB = nsnull;
   }
   mFramerate = aFramerate;
 
   if (!mRGB) 
-    mRGB = new (std::nothrow) unsigned char[aWidth * aHeight * 4];
+    mRGB = new unsigned char[aWidth * aHeight * 4];
   if (mRGB && aRGBBuffer) {
     memcpy(mRGB.get(), aRGBBuffer, aWidth*aHeight*4);
   }
 }
 
 void nsVideoDecoder::Paint(gfxContext* aContext, const gfxRect& aRect)
 {
   nsAutoLock lock(mVideoUpdateLock);
--- a/layout/build/Makefile.in
+++ b/layout/build/Makefile.in
@@ -144,16 +144,28 @@ SHARED_LIBRARY_LIBS = \
 	$(NULL)
 
 ifdef MOZ_MEDIA
 SHARED_LIBRARY_LIBS 	+= \
 	$(DEPTH)/content/media/video/src/$(LIB_PREFIX)gkconvideo_s.$(LIB_SUFFIX) \
 	$(NULL)
 endif
 
+ifdef MOZ_OGG
+SHARED_LIBRARY_LIBS 	+= \
+	$(DEPTH)/modules/libfishsound/src/libfishsound/$(LIB_PREFIX)fishsound.$(LIB_SUFFIX) \
+	$(DEPTH)/modules/libogg/src/$(LIB_PREFIX)ogg.$(LIB_SUFFIX) \
+	$(DEPTH)/modules/liboggplay/src/liboggplay/$(LIB_PREFIX)oggplay.$(LIB_SUFFIX) \
+	$(DEPTH)/modules/liboggz/src/liboggz/$(LIB_PREFIX)oggz.$(LIB_SUFFIX) \
+	$(DEPTH)/modules/liboggplay_audio/$(LIB_PREFIX)oggplay_audio.$(LIB_SUFFIX) \
+	$(DEPTH)/modules/libtheora/lib/$(LIB_PREFIX)theora.$(LIB_SUFFIX) \
+	$(DEPTH)/modules/libvorbis/lib/$(LIB_PREFIX)vorbis.$(LIB_SUFFIX) \
+	$(NULL)
+endif
+
 ifdef NS_PRINTING
 SHARED_LIBRARY_LIBS += \
 		../printing/$(LIB_PREFIX)gkprinting_s.$(LIB_SUFFIX) \
 		$(NULL)
 endif
 
 ifdef MOZ_XUL
 SHARED_LIBRARY_LIBS += \
@@ -235,16 +247,22 @@ EXTRA_DSO_LDOPTS += \
 	$(NULL)
 endif
 
 # Add explicit X11 dependency when building against X11 toolkits
 ifneq (,$(filter gtk2,$(MOZ_WIDGET_TOOLKIT)))
 EXTRA_DSO_LDOPTS += $(XLDFLAGS) $(XLIBS)
 endif
 
+ifdef MOZ_OGG
+ifeq ($(OS_ARCH),Darwin)
+OS_LIBS += -framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon
+endif
+endif
+
 include $(topsrcdir)/config/rules.mk
 
 LOCAL_INCLUDES	+= -I$(srcdir)/../base \
 		   -I$(srcdir)/../generic \
 		   -I$(srcdir)/../forms \
 		   -I$(srcdir)/../tables \
 		   -I$(srcdir)/../style \
 		   -I$(srcdir)/../xul/content/src \
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -109,16 +109,21 @@ PRBool NS_SVGEnabled();
 #include "nsHTMLEditor.h"
 #include "nsTextServicesDocument.h"
 #endif
 
 #ifdef MOZ_MEDIA
 #include "nsVideoDecoder.h"
 #endif
 
+#ifdef MOZ_OGG
+#include "nsAudioStream.h"
+#include "nsVideoDecoder.h"
+#endif
+
 #include "nsError.h"
 #include "nsTraceRefcnt.h"
 
 #include "nsCycleCollector.h"
 
 static nsrefcnt sLayoutStaticRefcnt;
 
 nsresult
@@ -243,16 +248,24 @@ nsLayoutStatics::Initialize()
   rv = nsVideoDecoder::InitLogger();
   if (NS_FAILED(rv)) {
     NS_ERROR("Could not initialize nsVideoDecoder");
     return rv;
   }
   
 #endif
 
+#ifdef MOZ_OGG
+  rv = nsAudioStream::InitLibrary();
+  if (NS_FAILED(rv)) {
+    NS_ERROR("Could not initialize nsAudioStream");
+    return rv;
+  }
+#endif
+
   return NS_OK;
 }
 
 void
 nsLayoutStatics::Shutdown()
 {
 #ifdef MOZ_XUL
   nsXULPopupManager::Shutdown();
@@ -319,16 +332,20 @@ nsLayoutStatics::Shutdown()
   nsAutoCopyListener::Shutdown();
 
 #ifndef MOZILLA_PLAINTEXT_EDITOR_ONLY
   nsHTMLEditor::Shutdown();
   nsTextServicesDocument::Shutdown();
 #endif
 
   NS_ShutdownFocusSuppressor();
+
+#ifdef MOZ_OGG
+  nsAudioStream::ShutdownLibrary();
+#endif
 }
 
 void
 nsLayoutStatics::AddRef()
 {
   NS_ASSERTION(sLayoutStaticRefcnt,
                "nsLayoutStatics already dropped to zero!");
 
new file mode 100644
--- /dev/null
+++ b/modules/liboggplay_audio/Makefile.in
@@ -0,0 +1,79 @@
+# ***** 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>
+#
+# 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 *****
+
+DEPTH		= ../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE		= oggplay_audio
+LIBRARY_NAME	= oggplay_audio
+FORCE_STATIC_LIB= 1
+
+EXPORTS		= \
+		sydney_audio.h \
+		$(NULL)
+
+ifeq ($(OS_ARCH),Linux)
+CSRCS		= \
+		sydney_audio_alsa.c \
+		$(NULL)
+endif
+
+ifeq ($(OS_ARCH),WINNT)
+CSRCS		= \
+		sydney_audio_waveapi.c \
+		$(NULL)
+endif
+
+ifeq ($(OS_ARCH),Darwin)
+CSRCS		= \
+		sydney_audio_mac.c \
+		$(NULL)
+
+OS_LIBS += -framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon
+endif
+
+ifeq ($(OS_ARCH),WINNT)
+OS_LIBS += winmm.lib
+endif
+
+include $(topsrcdir)/config/rules.mk
+
+LOCAL_INCLUDES = -I$(srcdir)
+
new file mode 100644
--- /dev/null
+++ b/modules/liboggplay_audio/README_MOZILLA
@@ -0,0 +1,11 @@
+The liboggplay_audio code is from the audio portion of the Annodex
+browser_plugin svn repository:
+
+http://svn.annodex.net/browser_plugin/trunk/src/audio/
+
+It was originally part of liboggplay but recently split into the
+separate browser_plugin directory. I only use the audio portion so
+only the audio part of the source is here. Run update.sh giving it the
+path to a checked out version of browser_plugin to update this code.
+
+This code is using svn revision r3605.
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/modules/liboggplay_audio/sydney_audio.h
@@ -0,0 +1,414 @@
+#ifndef foosydneyhfoo
+#define foosydneyhfoo
+
+/* Requirements:
+
+- In sync mode, the device will automatically write data so that an initial read causes writes
+of zeros to be issued to that one can do "while (1); {read(); write()}
+
+- All functions are thread-safe and can be called in any thread context. None of the functions is
+async-signal safe.
+  
+- It is assumed that duplex streams have a single clock (synchronised)
+*/
+
+#include <sys/types.h>
+#if !defined (WIN32)
+#include <sys/param.h>
+#include <inttypes.h>
+#else
+#include <stddef.h>
+#endif
+
+/* Detect byte order, based on sys/param.h */
+#undef SA_LITTLE_ENDIAN
+#undef SA_BIG_ENDIAN
+
+#if defined(__BYTE_ORDER)
+#   if __BYTE_ORDER == __LITTLE_ENDIAN
+#       define SA_LITTLE_ENDIAN 1
+#   elif __BYTE_ORDER == __BIG_ENDIAN
+#       define SA_BIG_ENDIAN 1
+#   endif
+#elif defined(WIN32)
+#   define SA_LITTLE_ENDIAN 1
+#elif defined(__APPLE__)
+#   if defined(__BIG_ENDIAN__)
+#       define SA_BIG_ENDIAN 1
+#   else
+#       define SA_LITTLE_ENDIAN 1
+#   endif
+#else
+#    error "Cannot determine byte order!"
+#endif
+
+#if defined(WIN32)
+#if !defined(int32_t)
+typedef __int32 int32_t;
+#endif
+#if !defined(int64_t)
+typedef __int64 int64_t;
+#endif
+#endif
+
+typedef struct sa_stream sa_stream_t;
+
+#if defined(WIN32)
+// (left << 16 | right) (16 bits per channel)
+#define SA_VOLUME_MUTED ((int32_t) (0x00000000))
+#else
+/** Volume that corresponds to muted in/out */
+#define SA_VOLUME_MUTED ((int32_t) (-0x80000000))
+#endif
+
+/** Ways to express seek offsets for pread/pwrite */
+typedef enum {
+    SA_SEEK_RELATIVE,
+    SA_SEEK_ABSOLUTE,
+    SA_SEEK_RELATIVE_END,
+    _SA_SEEK_MAX
+} sa_seek_t;
+
+/** Supported formats */
+typedef enum {
+    SA_PCM_FORMAT_U8,
+    SA_PCM_FORMAT_ULAW,
+    SA_PCM_FORMAT_ALAW,
+    SA_PCM_FORMAT_S16_LE,
+    SA_PCM_FORMAT_S16_BE,
+    SA_PCM_FORMAT_S24_LE,
+    SA_PCM_FORMAT_S24_BE,
+    SA_PCM_FORMAT_S32_LE,
+    SA_PCM_FORMAT_S32_BE,
+    SA_PCM_FORMAT_FLOAT32_LE,
+    SA_PCM_FORMAT_FLOAT32_BE,
+    _SA_PCM_FORMAT_MAX
+} sa_pcm_format_t;
+
+/* Native/reverse endianness definitions for PCM */
+#ifdef SA_LITTLE_ENDIAN
+#define SA_PCM_FORMAT_S16_NE SA_PCM_FORMAT_S16_LE
+#define SA_PCM_FORMAT_S24_NE SA_PCM_FORMAT_S24_LE
+#define SA_PCM_FORMAT_S32_NE SA_PCM_FORMAT_S32_LE
+#define SA_PCM_FORMAT_FLOAT32_NE SA_PCM_FORMAT_FLOAT32_LE
+#define SA_PCM_FORMAT_S16_RE SA_PCM_FORMAT_S16_BE
+#define SA_PCM_FORMAT_S24_RE SA_PCM_FORMAT_S24_BE
+#define SA_PCM_FORMAT_S32_RE SA_PCM_FORMAT_S32_BE
+#define SA_PCM_FORMAT_FLOAT32_RE SA_PCM_FORMAT_FLOAT32_BE
+#else
+#define SA_PCM_FORMAT_S16_NE SA_PCM_FORMAT_S16_BE
+#define SA_PCM_FORMAT_S24_NE SA_PCM_FORMAT_S24_BE
+#define SA_PCM_FORMAT_S32_NE SA_PCM_FORMAT_S32_BE
+#define SA_PCM_FORMAT_FLOAT32_NE SA_PCM_FORMAT_FLOAT32_BE
+#define SA_PCM_FORMAT_S16_RE SA_PCM_FORMAT_S16_LE
+#define SA_PCM_FORMAT_S24_RE SA_PCM_FORMAT_S24_LE
+#define SA_PCM_FORMAT_S32_RE SA_PCM_FORMAT_S32_LE
+#define SA_PCM_FORMAT_FLOAT32_RE SA_PCM_FORMAT_FLOAT32_LE
+#endif
+
+#define SA_CODEC_MPEG "mpeg"
+#define SA_CODEC_AC3 "ac3"
+#define SA_CODEC_GSM "gsm"
+#define SA_CODEC_VORBIS "vorbis"
+#define SA_CODEC_SPEEX "speex"
+
+/** Device opening modes */
+typedef enum {
+    SA_MODE_WRONLY = 1,
+    SA_MODE_RDONLY = 2,
+    SA_MODE_RDWR   = 3,
+    _SA_MODE_MAX   = 4
+} sa_mode_t;
+
+/** Error codes */
+typedef enum {
+    SA_SUCCESS = 0,
+    SA_ERROR_NOT_SUPPORTED = -1,
+    SA_ERROR_INVALID = -2,
+    SA_ERROR_STATE = -3,
+    SA_ERROR_OOM = -4,
+    SA_ERROR_NO_DEVICE = -5,
+    SA_ERROR_NO_DRIVER = -6,
+    SA_ERROR_NO_CODEC = -7,
+    SA_ERROR_NO_PCM_FORMAT = -7,
+    SA_ERROR_SYSTEM = -8,
+    SA_ERROR_NO_INIT = -9,
+    SA_ERROR_NO_META = -10,
+    SA_ERROR_NO_DATA = -11,
+    SA_ERROR_NO_SPACE = -12,
+    _SA_ERROR_MAX = -13
+} sa_error_t;
+
+/** Possible events for notifications */
+typedef enum {
+    SA_NOTIFY_REQUEST_START,
+    SA_NOTIFY_REQUEST_STOP,
+    SA_NOTIFY_CHANGED_READ_VOLUME,
+    SA_NOTIFY_CHANGED_WRITE_VOLUME,
+    SA_NOTIFY_CHANGED_DEVICE,
+    _SA_NOTIFY_MAX
+} sa_notify_t;
+
+/** Classes of events */
+typedef enum {
+    SA_EVENT_REQUEST_IO,
+    SA_EVENT_INIT_THREAD,
+    SA_EVENT_NOTIFY,
+    SA_EVENT_ERROR,
+    _SA_EVENT_MAX
+} sa_event_t;
+
+/** List of sample position queries */
+typedef enum {
+    SA_POSITION_WRITE_DELAY,
+    SA_POSITION_WRITE_HARDWARE,
+    SA_POSITION_WRITE_SOFTWARE,
+    SA_POSITION_READ_DELAY,
+    SA_POSITION_READ_HARDWARE,
+    SA_POSITION_READ_SOFTWARE,
+    SA_POSITION_DUPLEX_DELAY,
+    _SA_POSITION_MAX
+} sa_position_t;
+
+/* Channel positions */
+typedef enum {
+    SA_CHANNEL_MONO,
+    SA_CHANNEL_LEFT,
+    SA_CHANNEL_RIGHT,
+    SA_CHANNEL_CENTER,
+    SA_CHANNEL_FRONT_LEFT,
+    SA_CHANNEL_FRONT_RIGHT,
+    SA_CHANNEL_FRONT_CENTER,
+    SA_CHANNEL_REAR_LEFT,
+    SA_CHANNEL_REAR_RIGHT,
+    SA_CHANNEL_REAR_CENTER,
+    SA_CHANNEL_LFE,
+    SA_CHANNEL_FRONT_LEFT_OF_CENTER,
+    SA_CHANNEL_FRONT_RIGHT_OF_CENTER,
+    SA_CHANNEL_SIDE_LEFT,
+    SA_CHANNEL_SIDE_RIGHT,
+    SA_CHANNEL_TOP_CENTER,
+    SA_CHANNEL_TOP_FRONT_LEFT,
+    SA_CHANNEL_TOP_FRONT_RIGHT,
+    SA_CHANNEL_TOP_FRONT_CENTER,
+    SA_CHANNEL_TOP_REAR_LEFT,
+    SA_CHANNEL_TOP_REAR_RIGHT,
+    SA_CHANNEL_TOP_REAR_CENTER,
+    SA_CHANNEL_AUX0,
+    SA_CHANNEL_AUX1,
+    SA_CHANNEL_AUX2,
+    SA_CHANNEL_AUX3,
+    SA_CHANNEL_AUX4,
+    SA_CHANNEL_AUX5,
+    SA_CHANNEL_AUX6,
+    SA_CHANNEL_AUX7,
+    SA_CHANNEL_AUX8,
+    SA_CHANNEL_AUX9,
+    SA_CHANNEL_AUX10,
+    SA_CHANNEL_AUX11,
+    SA_CHANNEL_AUX12,
+    SA_CHANNEL_AUX13,
+    SA_CHANNEL_AUX14,
+    SA_CHANNEL_AUX15,
+    SA_CHANNEL_AUX16,
+    SA_CHANNEL_AUX17,
+    SA_CHANNEL_AUX18,
+    SA_CHANNEL_AUX19,
+    SA_CHANNEL_AUX20,
+    SA_CHANNEL_AUX21,
+    SA_CHANNEL_AUX22,
+    SA_CHANNEL_AUX23,
+    SA_CHANNEL_AUX24,
+    SA_CHANNEL_AUX25,
+    SA_CHANNEL_AUX26,
+    SA_CHANNEL_AUX27,
+    SA_CHANNEL_AUX28,
+    SA_CHANNEL_AUX29,
+    SA_CHANNEL_AUX30,
+    SA_CHANNEL_AUX31,
+    _SA_CHANNEL_MAX
+} sa_channel_t;
+
+typedef enum {
+    SA_STATE_INIT,
+    SA_STATE_RUNNING,
+    SA_STATE_STOPPED,
+    /* put more stuff */
+    _SA_STATE_MAX
+} sa_state_t;
+
+typedef enum {
+    SA_XRUN_MODE_STOP,
+    SA_XRUN_MODE_SPIN,
+    _SA_XRUN_MODE_MAX
+} sa_xrun_mode_t;
+
+typedef enum {
+    SA_ADJUST_UP = 1,
+    SA_ADJUST_DOWN = -1,
+    SA_ADJUST_NONE = 0
+} sa_adjust_t;
+
+/* Some kind of meta information.  */
+#define SA_META_CLIENT_NAME "sydney.client-name"     /* utf-8 */ 
+#define SA_META_PROCESS_ID "sydney.process-id"       /* getpid() */
+#define SA_META_LANGUAGE "sydney.language"           /* de_DE and similar */
+
+/* Some kind of meta information. Not filled in */
+#define SA_META_STREAM_NAME "sydney.stream-name"     /* utf-8 */ 
+#define SA_META_ICON_NAME "sydney.icon-name"         /* file name (no slashes) */
+#define SA_META_ICON_PNG "sydney.icon-png"           /* PNG blob */
+#define SA_META_ROLE "sydney.role"                   /* one of: "music", "phone", "game", "event" */
+#define SA_META_X11_DISPLAY "sydney.x11-display"     /* X11 display */
+#define SA_META_X11_WINDOW "sydney.x11-window"       /* X11 window id */
+
+/** Main callback function */
+typedef int (*sa_event_callback_t)(sa_stream_t *s, sa_event_t event);
+
+/** Create an opaque (e.g. AC3) codec stream */
+int sa_stream_create_opaque(sa_stream_t **s, const char *client_name, sa_mode_t mode, const char *codec);
+
+/** Normal way to open a PCM device */
+int sa_stream_create_pcm(sa_stream_t **s, const char *client_name, sa_mode_t mode, sa_pcm_format_t format, unsigned int rate, unsigned int nchannels);
+
+/** Initialise the device */
+int sa_stream_open(sa_stream_t *s);
+
+/** Close/destroy everything */
+int sa_stream_destroy(sa_stream_t *s);
+
+/* "Soft" params */
+int sa_stream_set_write_lower_watermark(sa_stream_t *s, size_t size);
+int sa_stream_set_read_lower_watermark(sa_stream_t *s, size_t size);
+int sa_stream_set_write_upper_watermark(sa_stream_t *s, size_t size);
+int sa_stream_set_read_upper_watermark(sa_stream_t *s, size_t size);
+
+/** Set the mapping between channels and the loudspeakers */
+int sa_stream_set_channel_map(sa_stream_t *s, const sa_channel_t map[], unsigned int n);
+
+/** Whether xruns cause the card to reset */
+int sa_stream_set_xrun_mode(sa_stream_t *s, sa_xrun_mode_t mode);
+
+/** Set the device to non-interleaved mode */
+int sa_stream_set_non_interleaved(sa_stream_t *s, int enable);
+
+/** Require dynamic sample rate */
+int sa_stream_set_dynamic_rate(sa_stream_t *s, int enable);
+
+/** Select driver */
+int sa_stream_set_driver(sa_stream_t *s, const char *driver);
+
+/** Start callback */
+int sa_stream_start_thread(sa_stream_t *s, sa_event_callback_t callback);
+
+/** Start callback */
+int sa_stream_stop_thread(sa_stream_t *s);
+
+/** Change the device connected to the stream */
+int sa_stream_change_device(sa_stream_t *s, const char *device_name);
+
+/** volume in hundreths of dB*/
+int sa_stream_change_read_volume(sa_stream_t *s, const int32_t vol[], unsigned int n);
+
+/** volume in hundreths of dB*/
+int sa_stream_change_write_volume(sa_stream_t *s, const int32_t vol[], unsigned int n);
+
+/** Change the sampling rate */
+int sa_stream_change_rate(sa_stream_t *s, unsigned int rate);
+
+/** Change some meta data that is attached to the stream */
+int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *data, size_t size);
+
+/** Associate opaque user data */
+int sa_stream_change_user_data(sa_stream_t *s, const void *value);
+
+/* Hardware-related. This is implementation-specific and hardware specific. */
+int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction);
+int sa_stream_set_adjust_nchannels(sa_stream_t *s, sa_adjust_t direction);
+int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction);
+int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction);
+
+/* Query functions */
+
+int sa_stream_get_mode(sa_stream_t *s, sa_mode_t *access_mode);
+int sa_stream_get_codec(sa_stream_t *s, char *codec, size_t *size);
+int sa_stream_get_pcm_format(sa_stream_t *s, sa_pcm_format_t *format);
+int sa_stream_get_rate(sa_stream_t *s, unsigned int *rate);
+int sa_stream_get_nchannels(sa_stream_t *s, int *nchannels);
+int sa_stream_get_user_data(sa_stream_t *s, void **value);
+int sa_stream_get_write_lower_watermark(sa_stream_t *s, size_t *size);
+int sa_stream_get_read_lower_watermark(sa_stream_t *s, size_t *size);
+int sa_stream_get_write_upper_watermark(sa_stream_t *s, size_t *size);
+int sa_stream_get_read_upper_watermark(sa_stream_t *s, size_t *size);
+int sa_stream_get_channel_map(sa_stream_t *s, sa_channel_t map[], unsigned int *n);
+int sa_stream_get_xrun_mode(sa_stream_t *s, sa_xrun_mode_t *mode);
+int sa_stream_get_non_interleaved(sa_stream_t *s, int *enabled);
+int sa_stream_get_dynamic_rate(sa_stream_t *s, int *enabled);
+int sa_stream_get_driver(sa_stream_t *s, char *driver_name, size_t *size);
+int sa_stream_get_device(sa_stream_t *s, char *device_name, size_t *size);
+int sa_stream_get_read_volume(sa_stream_t *s, int32_t vol[], unsigned int *n);
+int sa_stream_get_write_volume(sa_stream_t *s, int32_t vol[], unsigned int *n);
+int sa_stream_get_meta_data(sa_stream_t *s, const char *name, void*data, size_t *size);
+int sa_stream_get_adjust_rate(sa_stream_t *s, sa_adjust_t *direction);
+int sa_stream_get_adjust_nchannels(sa_stream_t *s, sa_adjust_t *direction);
+int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction);
+int sa_stream_get_adjust_watermarks(sa_stream_t *s, sa_adjust_t *direction);
+
+/** Get current state of the audio device */
+int sa_stream_get_state(sa_stream_t *s, sa_state_t *state);
+
+/** Obtain the error code */
+int sa_stream_get_event_error(sa_stream_t *s, sa_error_t *error);
+
+/** Obtain the notification code */
+int sa_stream_get_event_notify(sa_stream_t *s, sa_notify_t *notify);
+
+/** sync/timing */
+int sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos);
+
+
+/* Blocking IO calls */
+
+/** Interleaved capture function */
+int sa_stream_read(sa_stream_t *s, void *data, size_t nbytes);
+/** Non-interleaved capture function */
+int sa_stream_read_ni(sa_stream_t *s, unsigned int channel, void *data, size_t nbytes);
+
+/** Interleaved playback function */
+int sa_stream_write(sa_stream_t *s, const void *data, size_t nbytes);
+/** Non-interleaved playback function */
+int sa_stream_write_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes);
+/** Interleaved playback function with seek offset */
+int sa_stream_pwrite(sa_stream_t *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence);
+/** Non-interleaved playback function with seek offset */
+int sa_stream_pwrite_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence);
+
+
+/** Query how much can be read without blocking */
+int sa_stream_get_read_size(sa_stream_t *s, size_t *size);
+/** Query how much can be written without blocking */
+int sa_stream_get_write_size(sa_stream_t *s, size_t *size);
+
+
+/* Control/xrun */
+
+/** Resume playing after a pause */
+int sa_stream_resume(sa_stream_t *s);
+
+/** Pause audio playback (do not empty the buffer) */
+int sa_stream_pause(sa_stream_t *s);
+
+/** Block until all audio has been played */
+int sa_stream_drain(sa_stream_t *s);
+
+/** Return a human readable error */
+const char *sa_strerror(int code);
+
+/* Extensions */
+int
+sa_stream_set_volume_abs(sa_stream_t *s, float vol);
+
+int
+sa_stream_get_volume_abs(sa_stream_t *s, float *vol);
+
+#endif
new file mode 100644
--- /dev/null
+++ b/modules/liboggplay_audio/sydney_audio_alsa.c
@@ -0,0 +1,709 @@
+/* ***** 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 Initial Developer of the Original Code is
+ * CSIRO
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s): Michael Martin
+ *                 Chris Double (chris.double@double.co.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 <stdlib.h>
+#include <pthread.h>
+#include <alsa/asoundlib.h>
+#include "sydney_audio.h"
+
+/* ALSA implementation based heavily on sydney_audio_mac.c */
+
+/*
+ * The audio interface is based on a "pull" I/O model, which means you
+ * can't just provide a data buffer and tell the audio device to play; you must
+ * register a callback and provide data as the device asks for it. To support
+ * sydney audio's "write-to-play" style interface, we have to buffer up the
+ * data as it arrives and feed it to the callback as required.
+ *
+ * This is handled by a simple linked list of buffers; data is always written
+ * to the tail and read from the head. Each buffer tracks the start and end
+ * positions of its contained data. Buffers are allocated when the tail buffer
+ * fills, and freed when the head buffer empties. There is always at least one
+ * buffer allocated.
+ *
+ *       s   e      s      e      s  e            + data read
+ *    +++#####  ->  ########  ->  ####----        # data written
+ *    ^                           ^               - empty
+ *    bl_head                     bl_tail
+ */
+
+typedef struct sa_buf sa_buf;
+struct sa_buf {
+  unsigned int      size;
+  unsigned int      start;
+  unsigned int      end;
+  sa_buf          * next;
+  unsigned char     data[0];
+};
+
+struct sa_stream {
+  snd_pcm_t*        output_unit;
+  pthread_t         thread_id;
+  pthread_mutex_t   mutex;
+  char              playing;
+  int64_t           bytes_played;
+
+  /* audio format info */
+  unsigned int      rate;
+  unsigned int      n_channels;
+  unsigned int      bytes_per_ch;
+
+  /* buffer list */
+  sa_buf          * bl_head;
+  sa_buf          * bl_tail;
+  int               n_bufs;
+};
+
+
+/*
+ * Use a default buffer size with enough room for one second of audio,
+ * assuming stereo data at 44.1kHz with 32 bits per channel, and impose
+ * a generous limit on the number of buffers.
+ */
+#define BUF_SIZE    (2 * 44100 * 4)
+#define BUF_LIMIT   5
+
+#if BUF_LIMIT < 2
+#error BUF_LIMIT must be at least 2!
+#endif
+
+static void audio_callback(void* s);
+static sa_buf *new_buffer(void);
+
+
+/*
+ * -----------------------------------------------------------------------------
+ * Startup and shutdown functions
+ * -----------------------------------------------------------------------------
+ */
+
+int
+sa_stream_create_pcm(
+  sa_stream_t      ** _s,
+  const char        * client_name,
+  sa_mode_t           mode,
+  sa_pcm_format_t     format,
+  unsigned  int       rate,
+  unsigned  int       n_channels
+) {
+  sa_stream_t   * s = 0;
+
+  /*
+   * Make sure we return a NULL stream pointer on failure.
+   */
+  if (_s == NULL) {
+    return SA_ERROR_INVALID;
+  }
+  *_s = NULL;
+
+  if (mode != SA_MODE_WRONLY) {
+    return SA_ERROR_NOT_SUPPORTED;
+  }
+  if (format != SA_PCM_FORMAT_S16_LE) {
+    return SA_ERROR_NOT_SUPPORTED;
+  }
+
+  /*
+   * Allocate the instance and required resources.
+   */
+  if ((s = malloc(sizeof(sa_stream_t))) == NULL) {
+    return SA_ERROR_OOM;
+  }
+  if ((s->bl_head = new_buffer()) == NULL) {
+    free(s);
+    return SA_ERROR_OOM;
+  }
+  if (pthread_mutex_init(&s->mutex, NULL) != 0) {
+    free(s->bl_head);
+    free(s);
+    return SA_ERROR_SYSTEM;
+  }
+
+  s->output_unit  = NULL;
+  s->thread_id    = 0;
+  s->playing      = 0;
+  s->bytes_played = 0;
+  s->rate         = rate;
+  s->n_channels   = n_channels;
+  s->bytes_per_ch = 2;
+  s->bl_tail      = s->bl_head;
+  s->n_bufs       = 1;
+
+  *_s = s;
+  return SA_SUCCESS;
+}
+
+
+int
+sa_stream_open(sa_stream_t *s) {
+
+  if (s == NULL) {
+    return SA_ERROR_NO_INIT;
+  }
+  if (s->output_unit != NULL) {
+    return SA_ERROR_INVALID;
+  }
+
+  if (snd_pcm_open(&s->output_unit, 
+		   "default", 
+		   SND_PCM_STREAM_PLAYBACK, 
+		   0) < 0) {
+    return SA_ERROR_NO_DEVICE;
+  }
+  
+  if (snd_pcm_set_params(s->output_unit,
+			 SND_PCM_FORMAT_S16_LE,
+			 SND_PCM_ACCESS_RW_INTERLEAVED,
+			 s->n_channels,
+			 s->rate,
+			 1,
+			 0) < 0) {
+    snd_pcm_close(s->output_unit);
+    s->output_unit = NULL;
+    return SA_ERROR_NOT_SUPPORTED;
+  }
+
+  return SA_SUCCESS;
+}
+
+
+int
+sa_stream_destroy(sa_stream_t *s) {
+  int result = SA_SUCCESS;
+
+  if (s == NULL) {
+    return SA_SUCCESS;
+  }
+
+  pthread_mutex_lock(&s->mutex);
+
+  /*
+   * This causes the thread sending data to ALSA to stop
+   */
+  s->thread_id = 0;
+
+  /*
+   * Shut down the audio output device.
+   */
+  if (s->output_unit != NULL) {
+    if (s->playing && snd_pcm_close(s->output_unit) < 0) {
+      result = SA_ERROR_SYSTEM;
+    }
+  }
+
+  pthread_mutex_unlock(&s->mutex);
+
+  /*
+   * Release resources.
+   */
+  if (pthread_mutex_destroy(&s->mutex) != 0) {
+    result = SA_ERROR_SYSTEM;
+  }
+  while (s->bl_head != NULL) {
+    sa_buf  * next = s->bl_head->next;
+    free(s->bl_head);
+    s->bl_head = next;
+  }
+  free(s);
+
+  return result;
+}
+
+
+
+/*
+ * -----------------------------------------------------------------------------
+ * Data read and write functions
+ * -----------------------------------------------------------------------------
+ */
+
+int
+sa_stream_write(sa_stream_t *s, const void *data, size_t nbytes) {
+  int result = SA_SUCCESS;
+
+  if (s == NULL || s->output_unit == NULL) {
+    return SA_ERROR_NO_INIT;
+  }
+  if (nbytes == 0) {
+    return SA_SUCCESS;
+  }
+
+  pthread_mutex_lock(&s->mutex);
+
+  /*
+   * Append the new data to the end of our buffer list.
+   */
+  while (1) {
+    unsigned int avail = s->bl_tail->size - s->bl_tail->end;
+
+    if (nbytes <= avail) {
+
+      /*
+       * The new data will fit into the current tail buffer, so
+       * just copy it in and we're done.
+       */
+      memcpy(s->bl_tail->data + s->bl_tail->end, data, nbytes);
+      s->bl_tail->end += nbytes;
+      break;
+
+    } else {
+
+      /*
+       * Copy what we can into the tail and allocate a new buffer
+       * for the rest.
+       */
+      memcpy(s->bl_tail->data + s->bl_tail->end, data, avail);
+      s->bl_tail->end += avail;
+      data = ((unsigned char *)data) + avail;
+      nbytes -= avail;
+
+      /* 
+       * If we still have data left to copy but we've hit the limit of
+       * allowable buffer allocations, we need to spin for a bit to allow
+       * the audio callback function to slurp some more data up.
+       */
+      if (nbytes > 0 && s->n_bufs == BUF_LIMIT) {
+#ifdef TIMING_TRACE
+        printf("#");  /* too much audio data */
+#endif
+        if (!s->playing) {
+          /*
+           * We haven't even started playing yet! That means the
+           * BUF_SIZE/BUF_LIMIT values are too low... Not much we can
+           * do here; spinning won't help because the audio callback
+           * hasn't been enabled yet. Oh well, error time.
+           */
+          printf("Too much audio data received before audio device enabled!\n");
+          result = SA_ERROR_SYSTEM;
+          break;
+        }
+        while (s->n_bufs == BUF_LIMIT) {
+          struct timespec ts = {0, 1000000};
+          pthread_mutex_unlock(&s->mutex);
+          nanosleep(&ts, NULL);
+          pthread_mutex_lock(&s->mutex);
+        }
+      }
+
+      /* 
+       * Allocate a new tail buffer, and go 'round again to fill it up.
+       */
+      if ((s->bl_tail->next = new_buffer()) == NULL) {
+        result = SA_ERROR_OOM;
+        break;
+      }
+      s->n_bufs++;
+      s->bl_tail = s->bl_tail->next;
+    
+    } /* if (nbytes <= avail), else */
+
+  } /* while (1) */
+
+  pthread_mutex_unlock(&s->mutex);
+
+  /*
+   * Once we have our first block of audio data, enable the audio callback
+   * function. This doesn't need to be protected by the mutex, because
+   * s->playing is not used in the audio callback thread, and it's probably
+   * better not to be inside the lock when we enable the audio callback.
+   */
+  if (!s->playing) {
+    s->playing = 1;
+    if (pthread_create(&s->thread_id, NULL, (void *)audio_callback, s) != 0) {
+      result = SA_ERROR_SYSTEM;
+    }
+  }
+
+  return result;
+}
+
+
+static void audio_callback(void* data)
+{
+  sa_stream_t* s = (sa_stream_t*)data;
+  snd_pcm_uframes_t buffer_size;
+  snd_pcm_uframes_t period_size;
+  unsigned int bytes_per_frame = s->n_channels * s->bytes_per_ch;
+  char* buffer = 0;
+
+#ifdef TIMING_TRACE
+  printf(".");  /* audio read 'tick' */
+#endif
+
+  snd_pcm_get_params(s->output_unit, &buffer_size, &period_size);
+ 
+  buffer = malloc(period_size * bytes_per_frame);
+ 
+  while(1) {
+   char* dst = buffer;
+   unsigned int bytes_to_copy   = period_size * bytes_per_frame;
+   snd_pcm_sframes_t frames;
+
+   pthread_mutex_lock(&s->mutex);
+   if (!s->thread_id)
+     break;
+
+    /*
+     * Consume data from the start of the buffer list.
+     */
+    while (1) {
+      unsigned int avail = s->bl_head->end - s->bl_head->start;
+      assert(s->bl_head->start <= s->bl_head->end);
+
+      if (avail >= bytes_to_copy) {
+	/*
+	 * We have all we need in the head buffer, so just grab it and go.
+	 */
+	memcpy(dst, s->bl_head->data + s->bl_head->start, bytes_to_copy);
+	s->bl_head->start += bytes_to_copy;
+	s->bytes_played += bytes_to_copy;
+	break;
+	
+      } else {
+	sa_buf* next = 0;
+	/*
+	 * Copy what we can from the head and move on to the next buffer.
+	 */
+	memcpy(dst, s->bl_head->data + s->bl_head->start, avail);
+	s->bl_head->start += avail;
+	dst += avail;
+	bytes_to_copy -= avail;
+	s->bytes_played += avail;
+
+	/*
+	 * We want to free the now-empty buffer, but not if it's also the
+	 * current tail. If it is the tail, we don't have enough data to fill
+	 * the destination buffer, so we'll just zero it out and give up.
+	 */
+	next = s->bl_head->next;
+	if (next == NULL) {
+#ifdef TIMING_TRACE
+	  printf("!");  /* not enough audio data */
+#endif
+	  memset(dst, 0, bytes_to_copy);
+	  break;
+	}
+	free(s->bl_head);
+	s->bl_head = next;
+	s->n_bufs--;
+	
+      } /* if (avail >= bytes_to_copy), else */
+      
+    } /* while (1) */
+    
+    pthread_mutex_unlock(&s->mutex);
+    
+    frames = snd_pcm_writei(s->output_unit, buffer, period_size);
+    if (frames < 0) {
+      frames = snd_pcm_recover(s->output_unit, frames, 1);
+      if (frames < 0) {
+	printf("snc_pcm_recover error: %s\n", snd_strerror(frames));
+      }
+      if(frames > 0 && frames < period_size)
+	printf("short write (expected %d, wrote %d)\n", (int)period_size, (int)frames);;
+    }
+  }
+  free(buffer);
+}
+
+
+
+/*
+ * -----------------------------------------------------------------------------
+ * General query and support functions
+ * -----------------------------------------------------------------------------
+ */
+
+int
+sa_stream_get_write_size(sa_stream_t *s, size_t *size) {
+  sa_buf  * b;
+  size_t    used = 0;
+
+  if (s == NULL || s->output_unit == NULL) {
+    return SA_ERROR_NO_INIT;
+  }
+
+  pthread_mutex_lock(&s->mutex);
+
+  /*
+   * Sum up the used portions of our buffers and subtract that from
+   * the pre-defined max allowed allocation.
+   */
+  for (b = s->bl_head; b != NULL; b = b->next) {
+    used += b->end - b->start;
+  }
+  *size = BUF_SIZE * BUF_LIMIT - used;
+
+  pthread_mutex_unlock(&s->mutex);
+  return SA_SUCCESS;
+}
+
+
+int
+sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos) {
+
+  if (s == NULL || s->output_unit == NULL) {
+    return SA_ERROR_NO_INIT;
+  }
+  if (position != SA_POSITION_WRITE_SOFTWARE) {
+    return SA_ERROR_NOT_SUPPORTED;
+  }
+
+  pthread_mutex_lock(&s->mutex);
+  *pos = s->bytes_played;
+  pthread_mutex_unlock(&s->mutex);
+  return SA_SUCCESS;
+}
+
+
+int
+sa_stream_pause(sa_stream_t *s) {
+
+  if (s == NULL || s->output_unit == NULL) {
+    return SA_ERROR_NO_INIT;
+  }
+
+  pthread_mutex_lock(&s->mutex);
+#if 0 /* TODO */
+  AudioOutputUnitStop(s->output_unit);
+#endif
+  pthread_mutex_unlock(&s->mutex);
+  return SA_SUCCESS;
+}
+
+
+int
+sa_stream_resume(sa_stream_t *s) {
+
+  if (s == NULL || s->output_unit == NULL) {
+    return SA_ERROR_NO_INIT;
+  }
+
+  pthread_mutex_lock(&s->mutex);
+
+  /*
+   * The audio device resets its mSampleTime counter after pausing,
+   * so we need to clear our tracking value to keep that in sync.
+   */
+  s->bytes_played = 0;
+#if 0 /* TODO */
+  AudioOutputUnitStart(s->output_unit);
+#endif
+  pthread_mutex_unlock(&s->mutex);
+  return SA_SUCCESS;
+}
+
+
+static sa_buf *
+new_buffer(void) {
+  sa_buf  * b = malloc(sizeof(sa_buf) + BUF_SIZE);
+  if (b != NULL) {
+    b->size  = BUF_SIZE;
+    b->start = 0;
+    b->end   = 0;
+    b->next  = NULL;
+  }
+  return b;
+}
+
+
+
+/*
+ * -----------------------------------------------------------------------------
+ * Extension functions
+ * -----------------------------------------------------------------------------
+ */
+
+int
+sa_stream_set_volume_abs(sa_stream_t *s, float vol) {
+  snd_mixer_t* mixer = 0;
+  snd_mixer_elem_t* elem = 0;
+  if (s == NULL || s->output_unit == NULL) {
+    return SA_ERROR_NO_INIT;
+  }
+
+  if (snd_mixer_open(&mixer, 0) < 0) {
+    return SA_ERROR_SYSTEM;
+  }
+
+  if (snd_mixer_attach(mixer, "default") < 0) {
+    snd_mixer_close(mixer);
+    return SA_ERROR_SYSTEM;
+  }
+
+  if (snd_mixer_selem_register(mixer, NULL, NULL) < 0) {
+    snd_mixer_close(mixer);
+    return SA_ERROR_SYSTEM;
+  }
+
+  if (snd_mixer_load(mixer) < 0) {
+    snd_mixer_close(mixer);
+    return SA_ERROR_SYSTEM;
+  }
+
+#if 0
+  snd_mixer_elem_t* elem = 0;
+  for (elem = snd_mixer_first_elem(mixer); elem != NULL; elem = snd_mixer_elem_next(elem)) {
+    if (snd_mixer_selem_has_playback_volume(elem)) {
+      printf("Playback %s\n", snd_mixer_selem_get_name(elem));
+    }
+    else {
+      printf("No Playback: %s\n", snd_mixer_selem_get_name(elem));
+    }
+  }
+#endif
+  elem = snd_mixer_first_elem(mixer);
+  if (elem && snd_mixer_selem_has_playback_volume(elem)) {
+    long min = 0;
+    long max = 0;
+    if (snd_mixer_selem_get_playback_volume_range(elem, &min, &max) >= 0) {
+      snd_mixer_selem_set_playback_volume_all(elem, (max-min)*vol + min);
+    } 
+  }
+  snd_mixer_close(mixer);
+
+  return SA_SUCCESS;
+}
+
+
+int
+sa_stream_get_volume_abs(sa_stream_t *s, float *vol) {
+  snd_mixer_t* mixer = 0;
+  snd_mixer_elem_t* elem = 0;
+  long value = 0;
+
+  if (s == NULL || s->output_unit == NULL) {
+    return SA_ERROR_NO_INIT;
+  }
+
+  if (snd_mixer_open(&mixer, 0) < 0) {
+    return SA_ERROR_SYSTEM;
+  }
+
+  if (snd_mixer_attach(mixer, "default") < 0) {
+    snd_mixer_close(mixer);
+    return SA_ERROR_SYSTEM;
+  }
+
+  if (snd_mixer_selem_register(mixer, NULL, NULL) < 0) {
+    snd_mixer_close(mixer);
+    return SA_ERROR_SYSTEM;
+  }
+
+  if (snd_mixer_load(mixer) < 0) {
+    snd_mixer_close(mixer);
+    return SA_ERROR_SYSTEM;
+  }
+
+  elem = snd_mixer_first_elem(mixer);
+  if (elem && snd_mixer_selem_get_playback_volume(elem, 0, &value) >= 0) {
+    long min = 0;
+    long max = 0;
+    if (snd_mixer_selem_get_playback_volume_range(elem, &min, &max) >= 0) {
+      *vol = (float)(value-min)/(float)(max-min);
+    } 
+  }
+  snd_mixer_close(mixer);
+
+  return SA_SUCCESS;
+}
+
+
+
+/*
+ * -----------------------------------------------------------------------------
+ * Unsupported functions
+ * -----------------------------------------------------------------------------
+ */
+#define UNSUPPORTED(func)   func { return SA_ERROR_NOT_SUPPORTED; }
+
+UNSUPPORTED(int sa_stream_create_opaque(sa_stream_t **s, const char *client_name, sa_mode_t mode, const char *codec))
+UNSUPPORTED(int sa_stream_set_write_lower_watermark(sa_stream_t *s, size_t size))
+UNSUPPORTED(int sa_stream_set_read_lower_watermark(sa_stream_t *s, size_t size))
+UNSUPPORTED(int sa_stream_set_write_upper_watermark(sa_stream_t *s, size_t size))
+UNSUPPORTED(int sa_stream_set_read_upper_watermark(sa_stream_t *s, size_t size))
+UNSUPPORTED(int sa_stream_set_channel_map(sa_stream_t *s, const sa_channel_t map[], unsigned int n))
+UNSUPPORTED(int sa_stream_set_xrun_mode(sa_stream_t *s, sa_xrun_mode_t mode))
+UNSUPPORTED(int sa_stream_set_non_interleaved(sa_stream_t *s, int enable))
+UNSUPPORTED(int sa_stream_set_dynamic_rate(sa_stream_t *s, int enable))
+UNSUPPORTED(int sa_stream_set_driver(sa_stream_t *s, const char *driver))
+UNSUPPORTED(int sa_stream_start_thread(sa_stream_t *s, sa_event_callback_t callback))
+UNSUPPORTED(int sa_stream_stop_thread(sa_stream_t *s))
+UNSUPPORTED(int sa_stream_change_device(sa_stream_t *s, const char *device_name))
+UNSUPPORTED(int sa_stream_change_read_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
+UNSUPPORTED(int sa_stream_change_write_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
+UNSUPPORTED(int sa_stream_change_rate(sa_stream_t *s, unsigned int rate))
+UNSUPPORTED(int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *data, size_t size))
+UNSUPPORTED(int sa_stream_change_user_data(sa_stream_t *s, const void *value))
+UNSUPPORTED(int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction))
+UNSUPPORTED(int sa_stream_set_adjust_nchannels(sa_stream_t *s, sa_adjust_t direction))
+UNSUPPORTED(int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction))
+UNSUPPORTED(int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction))
+UNSUPPORTED(int sa_stream_get_mode(sa_stream_t *s, sa_mode_t *access_mode))
+UNSUPPORTED(int sa_stream_get_codec(sa_stream_t *s, char *codec, size_t *size))
+UNSUPPORTED(int sa_stream_get_pcm_format(sa_stream_t *s, sa_pcm_format_t *format))
+UNSUPPORTED(int sa_stream_get_rate(sa_stream_t *s, unsigned int *rate))
+UNSUPPORTED(int sa_stream_get_nchannels(sa_stream_t *s, int *nchannels))
+UNSUPPORTED(int sa_stream_get_user_data(sa_stream_t *s, void **value))
+UNSUPPORTED(int sa_stream_get_write_lower_watermark(sa_stream_t *s, size_t *size))
+UNSUPPORTED(int sa_stream_get_read_lower_watermark(sa_stream_t *s, size_t *size))
+UNSUPPORTED(int sa_stream_get_write_upper_watermark(sa_stream_t *s, size_t *size))
+UNSUPPORTED(int sa_stream_get_read_upper_watermark(sa_stream_t *s, size_t *size))
+UNSUPPORTED(int sa_stream_get_channel_map(sa_stream_t *s, sa_channel_t map[], unsigned int *n))
+UNSUPPORTED(int sa_stream_get_xrun_mode(sa_stream_t *s, sa_xrun_mode_t *mode))
+UNSUPPORTED(int sa_stream_get_non_interleaved(sa_stream_t *s, int *enabled))
+UNSUPPORTED(int sa_stream_get_dynamic_rate(sa_stream_t *s, int *enabled))
+UNSUPPORTED(int sa_stream_get_driver(sa_stream_t *s, char *driver_name, size_t *size))
+UNSUPPORTED(int sa_stream_get_device(sa_stream_t *s, char *device_name, size_t *size))
+UNSUPPORTED(int sa_stream_get_read_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
+UNSUPPORTED(int sa_stream_get_write_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
+UNSUPPORTED(int sa_stream_get_meta_data(sa_stream_t *s, const char *name, void*data, size_t *size))
+UNSUPPORTED(int sa_stream_get_adjust_rate(sa_stream_t *s, sa_adjust_t *direction))
+UNSUPPORTED(int sa_stream_get_adjust_nchannels(sa_stream_t *s, sa_adjust_t *direction))
+UNSUPPORTED(int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction))
+UNSUPPORTED(int sa_stream_get_adjust_watermarks(sa_stream_t *s, sa_adjust_t *direction))
+UNSUPPORTED(int sa_stream_get_state(sa_stream_t *s, sa_state_t *state))
+UNSUPPORTED(int sa_stream_get_event_error(sa_stream_t *s, sa_error_t *error))
+UNSUPPORTED(int sa_stream_get_event_notify(sa_stream_t *s, sa_notify_t *notify))
+UNSUPPORTED(int sa_stream_read(sa_stream_t *s, void *data, size_t nbytes))
+UNSUPPORTED(int sa_stream_read_ni(sa_stream_t *s, unsigned int channel, void *data, size_t nbytes))
+UNSUPPORTED(int sa_stream_write_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes))
+UNSUPPORTED(int sa_stream_pwrite(sa_stream_t *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
+UNSUPPORTED(int sa_stream_pwrite_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
+UNSUPPORTED(int sa_stream_get_read_size(sa_stream_t *s, size_t *size))
+UNSUPPORTED(int sa_stream_drain(sa_stream_t *s))
+
+const char *sa_strerror(int code) { return NULL; }
+
new file mode 100644
--- /dev/null
+++ b/modules/liboggplay_audio/sydney_audio_mac.c
@@ -0,0 +1,696 @@
+/* ***** 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 Initial Developer of the Original Code is
+ * CSIRO
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s): Michael Martin
+ *
+ * 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 <AudioUnit/AudioUnit.h>
+#include "sydney_audio.h"
+
+/*
+ * The Mac's audio interface is based on a "pull" I/O model, which means you
+ * can't just provide a data buffer and tell the audio device to play; you must
+ * register a callback and provide data as the device asks for it. To support
+ * sydney audio's "write-to-play" style interface, we have to buffer up the
+ * data as it arrives and feed it to the callback as required.
+ *
+ * This is handled by a simple linked list of buffers; data is always written
+ * to the tail and read from the head. Each buffer tracks the start and end
+ * positions of its contained data. Buffers are allocated when the tail buffer
+ * fills, and freed when the head buffer empties. There is always at least one
+ * buffer allocated.
+ *
+ *       s   e      s      e      s  e            + data read
+ *    +++#####  ->  ########  ->  ####----        # data written
+ *    ^                           ^               - empty
+ *    bl_head                     bl_tail
+ */
+
+typedef struct sa_buf sa_buf;
+struct sa_buf {
+  unsigned int      size;
+  unsigned int      start;
+  unsigned int      end;
+  sa_buf          * next;
+  unsigned char     data[0];
+};
+
+struct sa_stream {
+  AudioUnit         output_unit;
+  pthread_mutex_t   mutex;
+  bool              playing;
+  int64_t           bytes_played;
+
+  /* audio format info */
+  unsigned int      rate;
+  unsigned int      n_channels;
+  unsigned int      bytes_per_ch;
+
+  /* buffer list */
+  sa_buf          * bl_head;
+  sa_buf          * bl_tail;
+  int               n_bufs;
+};
+
+
+/*
+ * Use a default buffer size with enough room for one second of audio,
+ * assuming stereo data at 44.1kHz with 32 bits per channel, and impose
+ * a generous limit on the number of buffers.
+ */
+#define BUF_SIZE    (2 * 44100 * 4)
+#define BUF_LIMIT   5
+
+#if BUF_LIMIT < 2
+#error BUF_LIMIT must be at least 2!
+#endif
+
+
+static OSStatus audio_callback(void *arg, AudioUnitRenderActionFlags *action_flags,
+  const AudioTimeStamp *time_stamp, UInt32 bus_num, UInt32 n_frames, AudioBufferList *data);
+
+static sa_buf *new_buffer(void);
+
+
+/*
+ * -----------------------------------------------------------------------------
+ * Startup and shutdown functions
+ * -----------------------------------------------------------------------------
+ */
+
+int
+sa_stream_create_pcm(
+  sa_stream_t      ** _s,
+  const char        * client_name,
+  sa_mode_t           mode,
+  sa_pcm_format_t     format,
+  unsigned  int       rate,
+  unsigned  int       n_channels
+) {
+
+  /*
+   * Make sure we return a NULL stream pointer on failure.
+   */
+  if (_s == NULL) {
+    return SA_ERROR_INVALID;
+  }
+  *_s = NULL;
+
+  if (mode != SA_MODE_WRONLY) {
+    return SA_ERROR_NOT_SUPPORTED;
+  }
+  if (format != SA_PCM_FORMAT_S16_LE) {
+    return SA_ERROR_NOT_SUPPORTED;
+  }
+
+  /*
+   * Allocate the instance and required resources.
+   */
+  sa_stream_t   * s;
+  if ((s = malloc(sizeof(sa_stream_t))) == NULL) {
+    return SA_ERROR_OOM;
+  }
+  if ((s->bl_head = new_buffer()) == NULL) {
+    free(s);
+    return SA_ERROR_OOM;
+  }
+  if (pthread_mutex_init(&s->mutex, NULL) != 0) {
+    free(s->bl_head);
+    free(s);
+    return SA_ERROR_SYSTEM;
+  }
+
+  s->output_unit  = NULL;
+  s->playing      = FALSE;
+  s->bytes_played = 0;
+  s->rate         = rate;
+  s->n_channels   = n_channels;
+  s->bytes_per_ch = 2;
+  s->bl_tail      = s->bl_head;
+  s->n_bufs       = 1;
+
+  *_s = s;
+  return SA_SUCCESS;
+}
+
+
+int
+sa_stream_open(sa_stream_t *s) {
+
+  if (s == NULL) {
+    return SA_ERROR_NO_INIT;
+  }
+  if (s->output_unit != NULL) {
+    return SA_ERROR_INVALID;
+  }
+
+  /*
+   * Open the default audio output unit.
+   */
+  ComponentDescription desc;
+  desc.componentType         = kAudioUnitType_Output;
+  desc.componentSubType      = kAudioUnitSubType_DefaultOutput;
+  desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+  desc.componentFlags        = 0;
+  desc.componentFlagsMask    = 0;
+
+  Component comp = FindNextComponent(NULL, &desc);
+  if (comp == NULL) {
+    return SA_ERROR_NO_DEVICE;
+  }
+
+  if (OpenAComponent(comp, &s->output_unit) != noErr) {
+    return SA_ERROR_NO_DEVICE;
+  }
+
+  /*
+   * Set up the render callback used to feed audio data into the output unit.
+   */
+  AURenderCallbackStruct input;
+  input.inputProc       = audio_callback;
+  input.inputProcRefCon = s;
+  if (AudioUnitSetProperty(s->output_unit, kAudioUnitProperty_SetRenderCallback,
+      kAudioUnitScope_Input, 0, &input, sizeof(input)) != 0) {
+    return SA_ERROR_SYSTEM;
+  }
+
+  /*
+   * Set up the format description for our audio data. Apple uses the
+   * following terminology:
+   *
+   * sample = a single data value for one channel
+   * frame  = a set of samples that includes one sample for each channel
+   * packet = the smallest indivisible block of audio data; for uncompressed
+   *          audio (which is what we have), this is one frame
+   * rate   = the number of complete frames per second
+   *
+   * Note that this definition of frame differs from, well, pretty much everyone
+   * else's. See this really long link for more info:
+   *
+   * http://developer.apple.com/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/Reference/reference.html#//apple_ref/c/tdef/AudioStreamBasicDescription
+   */
+  AudioStreamBasicDescription fmt;
+  fmt.mFormatID         = kAudioFormatLinearPCM;
+  fmt.mFormatFlags      = kLinearPCMFormatFlagIsSignedInteger |
+#ifdef __BIG_ENDIAN__
+                          kLinearPCMFormatFlagIsBigEndian |
+#endif
+                          kLinearPCMFormatFlagIsPacked;
+  fmt.mSampleRate       = s->rate;
+  fmt.mChannelsPerFrame = s->n_channels;
+  fmt.mBitsPerChannel   = s->bytes_per_ch * 8;
+  fmt.mFramesPerPacket  = 1;  /* uncompressed audio */
+  fmt.mBytesPerFrame    = fmt.mChannelsPerFrame * fmt.mBitsPerChannel / 8;
+  fmt.mBytesPerPacket   = fmt.mBytesPerFrame * fmt.mFramesPerPacket;
+
+  /*
+   * We're feeding data in to the output bus of the audio system, so we set
+   * the format description on the input scope of the device, using the very
+   * obvious element value of 0 to indicate the output bus.
+   *
+   * http://developer.apple.com/technotes/tn2002/tn2091.html
+   */
+  if (AudioUnitSetProperty(s->output_unit, kAudioUnitProperty_StreamFormat,
+      kAudioUnitScope_Input, 0, &fmt, sizeof(AudioStreamBasicDescription)) != 0) {
+    return SA_ERROR_NOT_SUPPORTED;
+  }
+
+  if (AudioUnitInitialize(s->output_unit) != 0) {
+    return SA_ERROR_SYSTEM;
+  }
+
+  return SA_SUCCESS;
+}
+
+
+int
+sa_stream_destroy(sa_stream_t *s) {
+
+  if (s == NULL) {
+    return SA_SUCCESS;
+  }
+
+  pthread_mutex_lock(&s->mutex);
+
+  /*
+   * Shut down the audio output device.
+   */
+  int result = SA_SUCCESS;
+  if (s->output_unit != NULL) {
+    if (s->playing && AudioOutputUnitStop(s->output_unit) != 0) {
+      result = SA_ERROR_SYSTEM;
+    }
+    if (AudioUnitUninitialize(s->output_unit) != 0) {
+      result = SA_ERROR_SYSTEM;
+    }
+    if (CloseComponent(s->output_unit) != noErr) {
+      result = SA_ERROR_SYSTEM;
+    }
+  }
+
+  pthread_mutex_unlock(&s->mutex);
+
+  /*
+   * Release resources.
+   */
+  if (pthread_mutex_destroy(&s->mutex) != 0) {
+    result = SA_ERROR_SYSTEM;
+  }
+  while (s->bl_head != NULL) {
+    sa_buf  * next = s->bl_head->next;
+    free(s->bl_head);
+    s->bl_head = next;
+  }
+  free(s);
+
+  return result;
+}
+
+
+
+/*
+ * -----------------------------------------------------------------------------
+ * Data read and write functions
+ * -----------------------------------------------------------------------------
+ */
+
+int
+sa_stream_write(sa_stream_t *s, const void *data, size_t nbytes) {
+
+  if (s == NULL || s->output_unit == NULL) {
+    return SA_ERROR_NO_INIT;
+  }
+  if (nbytes == 0) {
+    return SA_SUCCESS;
+  }
+
+  pthread_mutex_lock(&s->mutex);
+
+  /*
+   * Append the new data to the end of our buffer list.
+   */
+  int result = SA_SUCCESS;
+  while (1) {
+    unsigned int avail = s->bl_tail->size - s->bl_tail->end;
+
+    if (nbytes <= avail) {
+
+      /*
+       * The new data will fit into the current tail buffer, so
+       * just copy it in and we're done.
+       */
+      memcpy(s->bl_tail->data + s->bl_tail->end, data, nbytes);
+      s->bl_tail->end += nbytes;
+      break;
+
+    } else {
+
+      /*
+       * Copy what we can into the tail and allocate a new buffer
+       * for the rest.
+       */
+      memcpy(s->bl_tail->data + s->bl_tail->end, data, avail);
+      s->bl_tail->end += avail;
+      data = ((unsigned char *)data) + avail;
+      nbytes -= avail;
+
+      /* 
+       * If we still have data left to copy but we've hit the limit of
+       * allowable buffer allocations, we need to spin for a bit to allow
+       * the audio callback function to slurp some more data up.
+       */
+      if (nbytes > 0 && s->n_bufs == BUF_LIMIT) {
+#ifdef TIMING_TRACE
+        printf("#");  /* too much audio data */
+#endif
+        if (!s->playing) {
+          /*
+           * We haven't even started playing yet! That means the
+           * BUF_SIZE/BUF_LIMIT values are too low... Not much we can
+           * do here; spinning won't help because the audio callback
+           * hasn't been enabled yet. Oh well, error time.
+           */
+          printf("Too much audio data received before audio device enabled!\n");
+          result = SA_ERROR_SYSTEM;
+          break;
+        }
+        while (s->n_bufs == BUF_LIMIT) {
+          pthread_mutex_unlock(&s->mutex);
+          struct timespec ts = {0, 1000000};
+          nanosleep(&ts, NULL);
+          pthread_mutex_lock(&s->mutex);
+        }
+      }
+
+      /* 
+       * Allocate a new tail buffer, and go 'round again to fill it up.
+       */
+      if ((s->bl_tail->next = new_buffer()) == NULL) {
+        result = SA_ERROR_OOM;
+        break;
+      }
+      s->n_bufs++;
+      s->bl_tail = s->bl_tail->next;
+    
+    } /* if (nbytes <= avail), else */
+
+  } /* while (1) */
+
+  pthread_mutex_unlock(&s->mutex);
+
+  /*
+   * Once we have our first block of audio data, enable the audio callback
+   * function. This doesn't need to be protected by the mutex, because
+   * s->playing is not used in the audio callback thread, and it's probably
+   * better not to be inside the lock when we enable the audio callback.
+   */
+  if (!s->playing) {
+    s->playing = TRUE;
+    if (AudioOutputUnitStart(s->output_unit) != 0) {
+      result = SA_ERROR_SYSTEM;
+    }
+  }
+
+  return result;
+}
+
+
+static OSStatus
+audio_callback(
+  void                        * arg,
+  AudioUnitRenderActionFlags  * action_flags,
+  const AudioTimeStamp        * time_stamp,
+  UInt32                        bus_num,
+  UInt32                        n_frames,
+  AudioBufferList             * data
+) {
+
+#ifdef TIMING_TRACE
+  printf(".");  /* audio read 'tick' */
+#endif
+
+  /*
+   * We're dealing with interleaved data, so the system should only
+   * have provided one buffer to be filled.
+   */
+  assert(data->mNumberBuffers == 1);
+
+  sa_stream_t     * s = arg;
+
+  pthread_mutex_lock(&s->mutex);
+
+  unsigned char   * dst             = data->mBuffers[0].mData;
+  unsigned int      bytes_per_frame = s->n_channels * s->bytes_per_ch;
+  unsigned int      bytes_to_copy   = n_frames * bytes_per_frame;
+
+  /*
+   * Keep track of the number of bytes we've consumed so far. mSampleTime
+   * is actually the number of *frames* that have been consumed by the
+   * audio output unit so far. I don't know why it's a float.
+   */
+  assert(time_stamp->mFlags & kAudioTimeStampSampleTimeValid);
+  s->bytes_played = (int64_t)time_stamp->mSampleTime * bytes_per_frame;
+
+  /*
+   * Consume data from the start of the buffer list.
+   */
+  while (1) {
+    assert(s->bl_head->start <= s->bl_head->end);
+    unsigned int avail = s->bl_head->end - s->bl_head->start;
+
+    if (avail >= bytes_to_copy) {
+
+      /*
+       * We have all we need in the head buffer, so just grab it and go.
+       */
+      memcpy(dst, s->bl_head->data + s->bl_head->start, bytes_to_copy);
+      s->bl_head->start += bytes_to_copy;
+      break;
+
+    } else {
+
+      /*
+       * Copy what we can from the head and move on to the next buffer.
+       */
+      memcpy(dst, s->bl_head->data + s->bl_head->start, avail);
+      s->bl_head->start += avail;
+      dst += avail;
+      bytes_to_copy -= avail;
+
+      /*
+       * We want to free the now-empty buffer, but not if it's also the
+       * current tail. If it is the tail, we don't have enough data to fill
+       * the destination buffer, so we'll just zero it out and give up.
+       */
+      sa_buf  * next = s->bl_head->next;
+      if (next == NULL) {
+#ifdef TIMING_TRACE
+        printf("!");  /* not enough audio data */
+#endif
+        memset(dst, 0, bytes_to_copy);
+        break;
+      }
+      free(s->bl_head);
+      s->bl_head = next;
+      s->n_bufs--;
+
+    } /* if (avail >= bytes_to_copy), else */
+
+  } /* while (1) */
+
+  pthread_mutex_unlock(&s->mutex);
+  return noErr;
+}
+
+
+
+/*
+ * -----------------------------------------------------------------------------
+ * General query and support functions
+ * -----------------------------------------------------------------------------
+ */
+
+int
+sa_stream_get_write_size(sa_stream_t *s, size_t *size) {
+
+  if (s == NULL || s->output_unit == NULL) {
+    return SA_ERROR_NO_INIT;
+  }
+
+  pthread_mutex_lock(&s->mutex);
+
+  /*
+   * Sum up the used portions of our buffers and subtract that from
+   * the pre-defined max allowed allocation.
+   */
+  sa_buf  * b;
+  size_t    used = 0;
+  for (b = s->bl_head; b != NULL; b = b->next) {
+    used += b->end - b->start;
+  }
+  *size = BUF_SIZE * BUF_LIMIT - used;
+
+  pthread_mutex_unlock(&s->mutex);
+  return SA_SUCCESS;
+}
+
+
+int
+sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos) {
+
+  if (s == NULL || s->output_unit == NULL) {
+    return SA_ERROR_NO_INIT;
+  }
+  if (position != SA_POSITION_WRITE_SOFTWARE) {
+    return SA_ERROR_NOT_SUPPORTED;
+  }
+
+  pthread_mutex_lock(&s->mutex);
+  *pos = s->bytes_played;
+  pthread_mutex_unlock(&s->mutex);
+  return SA_SUCCESS;
+}
+
+
+int
+sa_stream_pause(sa_stream_t *s) {
+
+  if (s == NULL || s->output_unit == NULL) {
+    return SA_ERROR_NO_INIT;
+  }
+
+  pthread_mutex_lock(&s->mutex);
+  AudioOutputUnitStop(s->output_unit);
+  pthread_mutex_unlock(&s->mutex);
+  return SA_SUCCESS;
+}
+
+
+int
+sa_stream_resume(sa_stream_t *s) {
+
+  if (s == NULL || s->output_unit == NULL) {
+    return SA_ERROR_NO_INIT;
+  }
+
+  pthread_mutex_lock(&s->mutex);
+
+  /*
+   * The audio device resets its mSampleTime counter after pausing,
+   * so we need to clear our tracking value to keep that in sync.
+   */
+  s->bytes_played = 0;
+  AudioOutputUnitStart(s->output_unit);
+
+  pthread_mutex_unlock(&s->mutex);
+  return SA_SUCCESS;
+}
+
+
+static sa_buf *
+new_buffer(void) {
+  sa_buf  * b = malloc(sizeof(sa_buf) + BUF_SIZE);
+  if (b != NULL) {
+    b->size  = BUF_SIZE;
+    b->start = 0;
+    b->end   = 0;
+    b->next  = NULL;
+  }
+  return b;
+}
+
+
+
+/*
+ * -----------------------------------------------------------------------------
+ * Extension functions
+ * -----------------------------------------------------------------------------
+ */
+
+int
+sa_stream_set_volume_abs(sa_stream_t *s, float vol) {
+
+  if (s == NULL || s->output_unit == NULL) {
+    return SA_ERROR_NO_INIT;
+  }
+
+  pthread_mutex_lock(&s->mutex);
+  AudioUnitSetParameter(s->output_unit, kHALOutputParam_Volume,
+      kAudioUnitParameterFlag_Output, 0, vol, 0);
+  pthread_mutex_unlock(&s->mutex);
+  return SA_SUCCESS;
+}
+
+
+int
+sa_stream_get_volume_abs(sa_stream_t *s, float *vol) {
+
+  if (s == NULL || s->output_unit == NULL) {
+    return SA_ERROR_NO_INIT;
+  }
+
+  pthread_mutex_lock(&s->mutex);
+  Float32 local_vol = 0;
+  AudioUnitGetParameter(s->output_unit, kHALOutputParam_Volume,
+      kAudioUnitParameterFlag_Output, 0, &local_vol);
+  *vol = local_vol;
+  pthread_mutex_unlock(&s->mutex);
+  return SA_SUCCESS;
+}
+
+
+
+/*
+ * -----------------------------------------------------------------------------
+ * Unsupported functions
+ * -----------------------------------------------------------------------------
+ */
+#define UNSUPPORTED(func)   func { return SA_ERROR_NOT_SUPPORTED; }
+
+UNSUPPORTED(int sa_stream_create_opaque(sa_stream_t **s, const char *client_name, sa_mode_t mode, const char *codec))
+UNSUPPORTED(int sa_stream_set_write_lower_watermark(sa_stream_t *s, size_t size))
+UNSUPPORTED(int sa_stream_set_read_lower_watermark(sa_stream_t *s, size_t size))
+UNSUPPORTED(int sa_stream_set_write_upper_watermark(sa_stream_t *s, size_t size))
+UNSUPPORTED(int sa_stream_set_read_upper_watermark(sa_stream_t *s, size_t size))
+UNSUPPORTED(int sa_stream_set_channel_map(sa_stream_t *s, const sa_channel_t map[], unsigned int n))
+UNSUPPORTED(int sa_stream_set_xrun_mode(sa_stream_t *s, sa_xrun_mode_t mode))
+UNSUPPORTED(int sa_stream_set_non_interleaved(sa_stream_t *s, int enable))
+UNSUPPORTED(int sa_stream_set_dynamic_rate(sa_stream_t *s, int enable))
+UNSUPPORTED(int sa_stream_set_driver(sa_stream_t *s, const char *driver))
+UNSUPPORTED(int sa_stream_start_thread(sa_stream_t *s, sa_event_callback_t callback))
+UNSUPPORTED(int sa_stream_stop_thread(sa_stream_t *s))
+UNSUPPORTED(int sa_stream_change_device(sa_stream_t *s, const char *device_name))
+UNSUPPORTED(int sa_stream_change_read_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
+UNSUPPORTED(int sa_stream_change_write_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
+UNSUPPORTED(int sa_stream_change_rate(sa_stream_t *s, unsigned int rate))
+UNSUPPORTED(int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *data, size_t size))
+UNSUPPORTED(int sa_stream_change_user_data(sa_stream_t *s, const void *value))
+UNSUPPORTED(int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction))
+UNSUPPORTED(int sa_stream_set_adjust_nchannels(sa_stream_t *s, sa_adjust_t direction))
+UNSUPPORTED(int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction))
+UNSUPPORTED(int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction))
+UNSUPPORTED(int sa_stream_get_mode(sa_stream_t *s, sa_mode_t *access_mode))
+UNSUPPORTED(int sa_stream_get_codec(sa_stream_t *s, char *codec, size_t *size))
+UNSUPPORTED(int sa_stream_get_pcm_format(sa_stream_t *s, sa_pcm_format_t *format))
+UNSUPPORTED(int sa_stream_get_rate(sa_stream_t *s, unsigned int *rate))
+UNSUPPORTED(int sa_stream_get_nchannels(sa_stream_t *s, int *nchannels))
+UNSUPPORTED(int sa_stream_get_user_data(sa_stream_t *s, void **value))
+UNSUPPORTED(int sa_stream_get_write_lower_watermark(sa_stream_t *s, size_t *size))
+UNSUPPORTED(int sa_stream_get_read_lower_watermark(sa_stream_t *s, size_t *size))
+UNSUPPORTED(int sa_stream_get_write_upper_watermark(sa_stream_t *s, size_t *size))
+UNSUPPORTED(int sa_stream_get_read_upper_watermark(sa_stream_t *s, size_t *size))
+UNSUPPORTED(int sa_stream_get_channel_map(sa_stream_t *s, sa_channel_t map[], unsigned int *n))
+UNSUPPORTED(int sa_stream_get_xrun_mode(sa_stream_t *s, sa_xrun_mode_t *mode))
+UNSUPPORTED(int sa_stream_get_non_interleaved(sa_stream_t *s, int *enabled))
+UNSUPPORTED(int sa_stream_get_dynamic_rate(sa_stream_t *s, int *enabled))
+UNSUPPORTED(int sa_stream_get_driver(sa_stream_t *s, char *driver_name, size_t *size))
+UNSUPPORTED(int sa_stream_get_device(sa_stream_t *s, char *device_name, size_t *size))
+UNSUPPORTED(int sa_stream_get_read_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
+UNSUPPORTED(int sa_stream_get_write_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
+UNSUPPORTED(int sa_stream_get_meta_data(sa_stream_t *s, const char *name, void*data, size_t *size))
+UNSUPPORTED(int sa_stream_get_adjust_rate(sa_stream_t *s, sa_adjust_t *direction))
+UNSUPPORTED(int sa_stream_get_adjust_nchannels(sa_stream_t *s, sa_adjust_t *direction))
+UNSUPPORTED(int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction))
+UNSUPPORTED(int sa_stream_get_adjust_watermarks(sa_stream_t *s, sa_adjust_t *direction))
+UNSUPPORTED(int sa_stream_get_state(sa_stream_t *s, sa_state_t *state))
+UNSUPPORTED(int sa_stream_get_event_error(sa_stream_t *s, sa_error_t *error))
+UNSUPPORTED(int sa_stream_get_event_notify(sa_stream_t *s, sa_notify_t *notify))
+UNSUPPORTED(int sa_stream_read(sa_stream_t *s, void *data, size_t nbytes))
+UNSUPPORTED(int sa_stream_read_ni(sa_stream_t *s, unsigned int channel, void *data, size_t nbytes))
+UNSUPPORTED(int sa_stream_write_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes))
+UNSUPPORTED(int sa_stream_pwrite(sa_stream_t *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
+UNSUPPORTED(int sa_stream_pwrite_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
+UNSUPPORTED(int sa_stream_get_read_size(sa_stream_t *s, size_t *size))
+UNSUPPORTED(int sa_stream_drain(sa_stream_t *s))
+
+const char *sa_strerror(int code) { return NULL; }
+
new file mode 100644
--- /dev/null
+++ b/modules/liboggplay_audio/sydney_audio_oss.c
@@ -0,0 +1,640 @@
+/* ***** 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 Initial Developer of the Original Code is
+ * CSIRO
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s): Marcin Lubonski 
+ *
+ * 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 "sydney_audio.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/soundcard.h>
+
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#define SA_READ_PERIOD 0 
+#define SA_WRITE_PERIOD 2560 // 40 ms of 16-bit, stereo, 16kHz
+#define SA_READ_BUFFER 0
+#define SA_WRITE_BUFFER 7680 // 3 periods per buffer
+
+// for versions newer than 3.6.1
+#define OSS_VERSION(x, y, z) (x << 16 | y << 8 | z)
+// support only versions newer than 3.6.1
+#define SUPP_OSS_VERSION OSS_VERSION(3,6,1)
+
+#if (SOUND_VERSION >= SUPP_OSS_VERSION)
+
+struct SAAudioHandle_ {
+   char *device_name;
+   int channels;
+   int read_period;
+   int write_period;
+   int read_buffer;
+   int write_buffer;
+   sa_pcm_mode_t rw_mode;
+   sa_pcm_format_t format;
+   int rate;
+   int interleaved;
+
+   int capture_handle;
+   int playback_handle;
+   int readN, writeN;
+   char *stored;
+   int stored_amount;
+   int stored_limit;
+   //int read_fd, write_fd;
+};
+
+/* Implemented API functions */
+/** Normal way to open PCM device */
+int sa_device_create_pcm(SAAudioHandle **_dev, const char *client_name, sa_pcm_mode_t rw_mode, sa_pcm_format_t format, int rate, int channels);
+/** Initialise the device */
+int sa_device_open(SAAudioHandle *dev);
+/** Close/destroy everything */
+int sa_device_close(SAAudioHandle *dev);
+
+/* Soft parameter setup - can only be called before calling open*/
+/** Set write buffer lower mark */
+int sa_device_set_write_lower_watermark(SAAudioHandle *dev, int size);
+/** Set read buffer lower mark */
+int sa_device_set_read_lower_watermark(SAAudioHandle *dev, int size);
+/** Set write buffer upper watermark */
+int sa_device_set_write_upper_watermark(SAAudioHandle *dev, int size);
+/** Set read buffer upper watermark */
+int sa_device_set_read_upper_watermark(SAAudioHandle *dev, int size);
+
+/** volume in hundreths of dB's*/
+int sa_device_change_input_volume(SAAudioHandle *dev, const int *vol);
+/** volume in hundreths of dB's*/
+int sa_device_change_output_volume(SAAudioHandle *dev, const int *vol);
+
+/** Read audio playback position */
+int sa_device_get_position(SAAudioHandle *dev, sa_pcm_index_t ref, int64_t *pos);
+
+/* Blocking I/O calls */
+/** Interleaved playback function */
+int sa_device_write(SAAudioHandle *dev, size_t nbytes, const void *data);
+
+
+/* Implementation-specific functions */
+static int oss_audio_format(sa_pcm_format_t sa_format, int* oss_format);
+//static void sa_print_handle_settings(SAAudioHandle* dev);
+
+/*!
+ * \brief fills in the SAAudioHandle struct
+ * \param SAAudioHandle - encapsulation of a handle to audio device
+ * \param client_name - 
+ * \param rw_mode - requested device access type as in :: sa_pcm_mode_t
+ * \param format - audio format as specified in ::sa_pcm_format_t
+ * \return - Sydney API error as in ::sa_pcm_error_t 
+ */
+int sa_device_create_pcm(SAAudioHandle **_dev, const char *client_name, sa_pcm_mode_t rw_mode, sa_pcm_format_t format, int rate, int channels) {
+	SAAudioHandle* dev = NULL;
+	
+	dev = malloc(sizeof(SAAudioHandle));
+	
+	if (!dev) {
+		return SA_DEVICE_OOM;	
+	}		        
+	dev->channels = channels;
+	dev->format = format;
+	dev->rw_mode = rw_mode;
+	dev->rate = rate;
+	dev->readN = 0;
+	dev->readN = 0;
+	dev->capture_handle = -1;
+	dev->playback_handle = -1;
+	dev->interleaved = 1;
+	dev->read_period = SA_READ_PERIOD;
+	dev->write_period = SA_WRITE_PERIOD;
+	dev->read_buffer = SA_READ_BUFFER;
+	dev->write_buffer = SA_WRITE_BUFFER;
+  dev->device_name = "/dev/dsp";
+  dev->stored = NULL;
+  dev->stored_amount = 0;
+  dev->stored_limit = 0;
+	
+	*_dev = dev;
+	 //sa_print_handle_settings(dev);
+	 return SA_DEVICE_SUCCESS;
+}
+
+/*!
+ * \brief creates and opens selected audio device
+ * \param dev - encapsulated audio device handle
+ * \return - Sydney API error as in ::sa_pcm_error_t  
+ */
+int sa_device_open(SAAudioHandle *dev) {	
+ 	int err;
+	int fmt;
+	int audio_fd = -1;
+
+	if (dev->rw_mode == SA_PCM_WRONLY) {
+		// open the default OSS device
+		dev->device_name = "/dev/dsp"; // replace with a function which returns audio ouput device best matching the settings
+		audio_fd = open(dev->device_name, O_WRONLY, 0);
+		if (audio_fd == -1) {
+		   fprintf(stderr, "Cannot open device: %s\n", dev->device_name);
+		   //assert(0);
+		   return SA_DEVICE_OOM;
+		}
+
+		// set the playback rate
+		if ((err = ioctl(audio_fd, SNDCTL_DSP_SPEED, &(dev->rate))) < 0) {
+		   fprintf(stderr, 
+			"Error setting the audio playback rate [%d]\n", 
+			dev->rate);
+		   //assert(0);
+		   return SA_DEVICE_OOM; 
+		}
+		// set the channel numbers
+		if ((err = ioctl(audio_fd,  SNDCTL_DSP_CHANNELS, 
+			&(dev->channels)))< 0) {
+		   fprintf(stderr, "Error setting audio channels\n");
+		   //assert(0);
+		   return SA_DEVICE_OOM;	
+		} 
+		if ((err = oss_audio_format(dev->format, &fmt)) < 0) {
+		   fprintf(stderr, "Format unknown\n");
+		   //assert(0);
+                   return SA_DEVICE_OOM;	   
+		}
+		printf("Setting format with value %d\n", fmt);
+		if ((err = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &fmt)) < 0 ) {
+		   fprintf(stderr, "Error setting audio format\n");
+		   //assert(0);
+		   return SA_DEVICE_OOM;
+		}
+
+		dev->playback_handle = audio_fd;
+ 		
+	}
+	if (dev->rw_mode == SA_PCM_RDONLY) {
+		return SA_DEVICE_NOT_SUPPORTED;
+	} 
+	if (dev->rw_mode == SA_PCM_RW) {
+		return SA_DEVICE_NOT_SUPPORTED;
+	}
+	fprintf(stderr, "Audio device opened successfully\n");
+	return SA_DEVICE_SUCCESS;
+}
+
+#define WRITE(data,amt)                                       \
+  if ((err = write(dev->playback_handle, data, amt)) < 0) {   \
+    fprintf(stderr, "Error writing data to audio device\n");  \
+    return SA_DEVICE_OOM;                                     \
+  }
+
+/*!
+ * \brief Interleaved write operation
+ * \param dev - device handle
+ * \param nbytes - size of the audio buffer to be written to device
+ * \param data - pointer to the buffer with audio samples
+ * \return 
+ * */
+int sa_device_write(SAAudioHandle *dev, size_t nbytes, const void *_data) {
+	int               err;
+  audio_buf_info    info;
+  int               bytes;
+  char            * data = (char *)_data;
+  /*
+  ioctl(dev->playback_handle, SNDCTL_DSP_GETOSPACE, &info);
+  printf("fragment size: %d, nfrags: %d, free: %d wtw: %d\n", info.fragsize, 
+                  info.fragstotal, info.bytes, nbytes);
+  */
+
+
+
+	if ((dev->playback_handle) > 0) {
+    ioctl(dev->playback_handle, SNDCTL_DSP_GETOSPACE, &info);
+    bytes = info.bytes;
+    if (dev->stored_amount > bytes) {
+      WRITE(dev->stored, bytes);
+      memmove(dev->stored, dev->stored + bytes, dev->stored_amount - bytes);
+      dev->stored_amount -= bytes;
+    } else if (dev->stored_amount > 0) {
+      WRITE(dev->stored, dev->stored_amount);
+      bytes -= dev->stored_amount;
+      dev->stored_amount = 0;
+      if (nbytes < bytes) {
+        WRITE(data, nbytes);
+        return SA_DEVICE_SUCCESS;
+      }
+      WRITE(data, bytes);
+      data += bytes;
+      nbytes -= bytes;
+    } else {
+      if (nbytes < bytes) {
+        WRITE(data, nbytes);
+        return SA_DEVICE_SUCCESS;
+      }
+      WRITE(data, bytes);
+      data += bytes;
+      nbytes -= bytes;
+    }
+
+    if (nbytes > 0) {
+      if (dev->stored_amount + nbytes > dev->stored_limit) {
+        dev->stored = realloc(dev->stored, dev->stored_amount + nbytes);
+      }
+      
+      memcpy(dev->stored + dev->stored_amount, data, nbytes);
+      dev->stored_amount += nbytes;
+    }
+	}
+	return SA_DEVICE_SUCCESS;
+}
+
+#define CLOSE_HANDLE(x) if (x != -1) close(x);
+
+/*!
+ * \brief Closes and destroys allocated resources
+ * \param dev - Sydney Audio device handle
+ * \return Sydney API error as in ::sa_pcm_error_t
+ **/
+int sa_device_close(SAAudioHandle *dev) {
+  int err;
+
+	if (dev != NULL) {
+
+    if (dev->stored_amount > 0) {
+      WRITE(dev->stored, dev->stored_amount);
+    }
+
+    if (dev->stored != NULL) {
+      free(dev->stored);
+    }
+
+    dev->stored = NULL;
+    dev->stored_amount = 0;
+    dev->stored_limit = 0;
+	  
+    CLOSE_HANDLE(dev->playback_handle);
+	  CLOSE_HANDLE(dev->capture_handle);
+
+	  printf("Closing audio device\n");	
+	  free(dev);
+	}
+ 	return SA_DEVICE_SUCCESS;
+}
+
+/** 
+ * \brief 
+ * \param dev
+ * \param size
+ * \return  Sydney API error as in ::sa_pcm_error_t
+ */
+int sa_device_set_write_lower_watermark(SAAudioHandle *dev, int size) {
+   dev->write_period = size;
+   return SA_DEVICE_SUCCESS;
+}
+/** 
+ * \brief
+ * \param dev
+ * \param size
+ * \return  Sydney API error as in ::sa_pcm_error_t
+ */
+int sa_device_set_read_lower_watermark(SAAudioHandle *dev, int size) {
+   dev->read_period = size;
+   return SA_DEVICE_SUCCESS;
+}
+/** 
+ * \brief
+ * \param dev
+ * \param size
+ * \return  Sydney API error as in ::sa_pcm_error_t
+ */
+int sa_device_set_write_upper_watermark(SAAudioHandle *dev, int size) {  
+   dev->write_buffer = size;
+   return SA_DEVICE_SUCCESS;
+}
+
+/** 
+ * \brief
+ * \param dev
+ * \param size
+ * \return  Sydney API error as in ::sa_pcm_error_t
+ */
+int sa_device_set_read_upper_watermark(SAAudioHandle *dev, int size) {
+   dev->read_buffer = size;
+   return SA_DEVICE_SUCCESS;
+}
+
+
+int sa_device_set_xrun_mode(SAAudioHandle *dev, sa_xrun_mode_t mode) {
+   return SA_DEVICE_NOT_SUPPORTED;
+}
+
+
+int sa_device_set_ni(SAAudioHandle *dev) {
+   dev->interleaved = 1;
+   return SA_DEVICE_SUCCESS;
+}
+
+int sa_device_start_thread(SAAudioHandle *dev, sa_device_callback *callback) {
+   return SA_DEVICE_NOT_SUPPORTED;
+}
+
+int sa_device_set_channel_map(SAAudioHandle *dev, const sa_channel_def_t *map) {
+   return SA_DEVICE_NOT_SUPPORTED;
+}
+
+
+int sa_device_change_device(SAAudioHandle *dev, const char *device_name) {
+   return SA_DEVICE_NOT_SUPPORTED;
+}
+
+/*!
+ * \brief volume in hundreths of dB's
+ * \param dev - device handle
+ * \param vol - volume level 
+ * \return Sydney API error as in ::sa_pcm_error_t
+ * */
+int sa_device_change_input_volume(SAAudioHandle *dev, const int *vol) {
+#if SOUND_VERSION >= OSS_VERSION(4,0,0)	
+	int err;
+	if ((err = ioctl(dev->capture_handle, SNDCTL_DSP_SETRECVOL, vol) < 0) {
+	   fpritnf(stderr, "Error setting new recording volume level\n");
+	   //assert(0);
+           return SA_DEVICE_OOM;	   
+	}
+	return SA_DEVICE_SUCCESS;
+#else
+	return SA_DEVICE_NOT_SUPPORTED;
+#endif
+}
+
+/*!
+ * \brief volume in hundreths of dB's
+ * \param dev - device handle
+ * \param vol - volume level
+ * \retrun  Sydney API error as in ::sa_pcm_error_t
+ * */
+int sa_device_change_output_volume(SAAudioHandle *dev, const int *vol) {
+#if SOUND_VERSION >= OSS_VERSION(4,0,0)
+	int err;
+	if ((err = ioctl(dev->playback_handle, SNDCTL_DSP_SETPLAYVOL, vol) < 0){
+
+	fprintf(stderr, "Error setting new playback volume\n");
+	//assert(0);
+	return SA_DEVICE_OOM; 
+        }
+	return SA_DEVICE_SUCCESS;
+#else
+	return SA_DEVICE_NOT_SUPPORTED;
+#endif
+}
+
+int sa_device_change_sampling_rate(SAAudioHandle *dev, int rate) {
+   dev->rate = rate;
+   return SA_DEVICE_SUCCESS;
+}
+
+int sa_device_change_client_name(SAAudioHandle *dev, const char *client_name) {
+   return SA_DEVICE_NOT_SUPPORTED;
+}
+
+int sa_device_change_stream_name(SAAudioHandle *dev, const char *stream_name) {
+   return SA_DEVICE_NOT_SUPPORTED;
+}
+
+int sa_device_change_user_data(SAAudioHandle *dev, void *val) {
+   return SA_DEVICE_NOT_SUPPORTED;
+}
+
+
+/*!
+ * \brief
+ * \param dev
+ * \param rate
+ * \param direction
+ * \return  Sydney API error as in ::sa_pcm_error_t
+ * */
+int sa_device_adjust_rate(SAAudioHandle *dev, int rate, int direction) {
+   return  SA_DEVICE_NOT_SUPPORTED;
+}
+/*!
+ * \brief
+ * \param dev
+ * \param nb_channels
+ * \return  Sydney API error as in ::sa_pcm_error_t
+ * */
+int sa_device_adjust_channels(SAAudioHandle *dev, int nb_channels) {               return SA_DEVICE_NOT_SUPPORTED;
+}
+/** Adjust bit sample format */
+int sa_device_adjust_format(SAAudioHandle *dev, sa_pcm_format_t format, int direction) {
+   return SA_DEVICE_NOT_SUPPORTED;
+}
+
+/** Get current state of the audio device */
+int sa_device_get_state(SAAudioHandle *dev, sa_state_t *running) {
+   return SA_DEVICE_NOT_SUPPORTED;
+}
+
+/** Get current sampling rate */
+int sa_device_get_sampling_rate(SAAudioHandle *dev, int *rate) {
+   return SA_DEVICE_NOT_SUPPORTED;
+}
+
+/** Get number of channels */
+int sa_device_get_nb_channels(SAAudioHandle *dev, int *nb_channels) {
+   return SA_DEVICE_NOT_SUPPORTED;
+}
+
+/** Get format being used */
+int sa_device_get_format(SAAudioHandle *dev, sa_pcm_format_t *format) {
+   return SA_DEVICE_NOT_SUPPORTED;
+}
+
+/** Get opaque pointer associated to the device */
+int sa_device_get_user_data(SAAudioHandle *dev, void **val) {
+   return SA_DEVICE_NOT_SUPPORTED;
+}
+
+/** Obtain the error code */
+int sa_device_get_event_error(SAAudioHandle *dev, sa_pcm_error_t *error) {
+   return SA_DEVICE_NOT_SUPPORTED;
+}
+
+/** Obtain the notification code */
+int sa_device_get_event_notify(SAAudioHandle *dev, sa_pcm_notification_t *notify) {
+   return SA_DEVICE_NOT_SUPPORTED;
+}
+
+/*!
+ * \brief returns the current position of the audio playback capture
+ * \param dev - device handle
+ * \param ref - type of position to be returned by this function see ::sa_pcm_index_t
+ * \param pos - position (in bytes or ms depending on 'ref' value)
+ * \return  Sydney API error as in ::sa_pcm_error_t
+ * */
+int sa_device_get_position(SAAudioHandle *dev, sa_pcm_index_t ref, int64_t *pos)
+{
+   int err;
+   int64_t _pos;
+   int delay;
+   count_info ptr;
+   switch (ref) {
+	 case SA_PCM_WRITE_DELAY:
+	      //int delay;
+	      if ((err = ioctl(dev->playback_handle, 
+			       SNDCTL_DSP_GETODELAY, 
+ 			       &delay)) <0) {
+	      	fprintf(stderr, "Error reading playback buffering delay\n");
+		return SA_DEVICE_OOM;	
+	      };  
+	      _pos = (int64_t)delay;
+	      break;
+         case SA_PCM_WRITE_SOFTWARE_POS:
+              //count_info ptr;
+	      if ((err = ioctl(dev->playback_handle, 
+                               SNDCTL_DSP_GETOPTR, 
+                               &ptr)) <0) {
+                //fprintf(stderr, "Error reading audio playback position\n");
+		return SA_DEVICE_OOM;
+              };
+	      _pos = (int64_t)ptr.bytes;  
+	      break;
+         case SA_PCM_READ_SOFTWARE_POS:
+              //count_info ptr;
+	      if ((err = ioctl(dev->playback_handle, 
+                               SNDCTL_DSP_GETIPTR, 
+                               &ptr)) <0) {
+              	 fprintf(stderr, "Error reading audio capture position\n");
+		 return SA_DEVICE_OOM;
+	      };
+               _pos = (int64_t)ptr.bytes;
+	      break;
+
+	 case SA_PCM_READ_DELAY:
+	 case SA_PCM_READ_HARDWARE_POS:
+	 case SA_PCM_WRITE_HARDWARE_POS:               
+	 case SA_PCM_DUPLEX_DELAY:
+	 default:
+	      return SA_DEVICE_NOT_SUPPORTED;
+	      break;		
+   }
+   (*pos) = _pos;
+   return SA_DEVICE_SUCCESS;
+}
+
+/** Private functions - implementation specific */
+
+/*!
+ * \brief private function mapping Sudney Audio format to OSS formats
+ * \param format - Sydney Audio API specific format
+ * \param - filled by the function with a value for corresponding OSS format
+ * \return - Sydney API error value as in ::sa_pcm_format_t
+ * */
+static int oss_audio_format(sa_pcm_format_t sa_format, int* oss_format) {
+#if SOUND_VERSION >= OSS_VERSION(4,0,0) 	
+	int fmt = AFMT_UNDEF;
+#else
+	int fmt = -1;
+#endif	
+	switch (sa_format) {
+                   case SA_PCM_UINT8:
+			fmt = AFMT_U8;
+			break;
+                   case SA_PCM_ULAW:
+			fmt = AFMT_MU_LAW;
+			break;
+                   case SA_PCM_ALAW:
+			fmt = AFMT_A_LAW;
+			break;
+		   /* 16-bit little endian (LE) format */
+                   case SA_PCM_S16_LE:
+			fmt = AFMT_S16_LE;
+			break;
+		   /* 16-bit big endian (BE) format */
+                   case SA_PCM_S16_BE:
+			fmt = AFMT_S16_BE;
+			break;
+#if SOUND_VERSION >= OSS_VERSION(4,0,0)
+		   /* 24-bit formats (LSB aligned in 32 bit word) */
+                   case SA_PCM_S24_LE:
+			fmt = AFMT_S24_LE;
+			break;
+		   /* 24-bit formats (LSB aligned in 32 bit word) */
+		   case SA_PCM_S24_BE:
+			fmt = AFMT_S24_BE;
+			break;
+		   /* 32-bit format little endian */
+                   case SA_PCM_S32_LE:
+			fmt = AFMT_S32_LE;
+			break;
+		   /* 32-bit format big endian */
+                   case SA_PCM_S32_BE:
+			fmt = AFMT_S32_BE;
+			break; 
+                   case SA_PCM_FLOAT32_NE:
+			fmt = AFMT_FLOAT;
+			break;
+#endif
+		   default:
+			return SA_DEVICE_NOT_SUPPORTED;
+			break;
+
+                }
+	(*oss_format) = fmt;
+	return SA_DEVICE_SUCCESS;
+}
+
+/*
+static void sa_print_handle_settings(SAAudioHandle* dev) {
+    printf(">>>>>>>>>>>> SA Device Handle <<<<<<<<<<<\n"); 
+    printf("[SA Audio] - Device name %s\n", dev->device_name);
+    printf("[SA_Audio] - Number of audio channels %d\n", dev->channels);
+    printf("[SA_Audio] - Read period size %d bytes\n", dev->read_period);
+    printf("[SA_Audio] - Write period size %d bytes\n", dev->write_period);
+    printf("[SA_Audio] - Write buffer size %d bytes\n", dev->write_buffer);
+    printf("[SA_Audio] - Read buffer size %d bytes\n", dev->read_buffer);
+    printf("[SA_Audio] - Read/write mode value %d\n", dev->rw_mode);
+    printf("[SA_Audio] - Audio sample bit format value %d\n", dev->format);
+    printf("[SA_Audio] - Audio playback rate %d\n", dev->rate);
+    if (dev->interleaved) {
+        printf("[SA_Audio] - Processing interleaved audio\n");
+    } else {
+	printf("[SA_Audio] - Processing non-interleaved audio\n");
+    }
+    if ((dev->capture_handle) > 0) {
+    	printf("[SA Audio] - Device opened for capture\n");
+    } 
+    if ((dev->playback_handle) > 0) {
+    	printf("[SA_Audio] - Device opened for playback\n");
+    }	    
+}
+*/
+
+#endif // (SOUND_VERSION > SUPP_OSS_VERSION)
new file mode 100644
--- /dev/null
+++ b/modules/liboggplay_audio/sydney_audio_waveapi.c
@@ -0,0 +1,685 @@
+/* ***** 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 Initial Developer of the Original Code is
+ * CSIRO
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s): Marcin Lubonski 
+ *
+ * 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 "sydney_audio.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <windows.h>
+#include <mmreg.h>
+#include <mmsystem.h>
+#include <math.h>
+
+
+// FIX ME: block size and block should be determined based on the OggPlay offset 
+// for audio track
+#define BLOCK_SIZE  2560
+#define BLOCK_COUNT 32
+#define DEFAULT_DEVICE_NAME "Default WAVE Device"
+#define DEFAULT_DEVICE WAVE_MAPPER
+
+#define VERBOSE_OUTPUT 1
+
+// INFO: if you get weird compile errors make sure there is no extra chars pass '\' 
+#if defined(VERBOSE_OUTPUT)
+#define WAVE_ERROR_VERBOSE(error, message) \
+  switch (error) { \
+    case MMSYSERR_ALLOCATED: \
+      printf("[WAVE API] Device allocation error returned while executing %s\n", message); \
+      break; \
+    case MMSYSERR_BADDEVICEID: \
+      printf("[WAVE API] Wrong device ID error returned while executing %s\n", message); \
+      break; \
+    case MMSYSERR_NODRIVER: \
+      printf("[WAVE API] System driver not present error returned while executing %s\n", message); \
+      break; \
+    case MMSYSERR_INVALHANDLE: \
+      printf("[WAVE API] Invalid device handle error returned while executing %s\n", message); \
+      break; \
+    case MMSYSERR_NOMEM: \
+      printf("[WAVE API] No memory error returned while executing %s\n", message); \
+      break; \
+    case MMSYSERR_NOTSUPPORTED: \
+      printf("[WAVE API] Not supported error returned while executing %s\n", message); \
+      break; \
+    case WAVERR_BADFORMAT: \
+      printf("[WAVE API] Not valid audio format returned while executing %s\n", message); \
+      break; \
+    case WAVERR_SYNC: \
+      printf("[WAVE API] Device synchronous error returned while executing %s\n", message); \
+      break; \
+    default: \
+      printf("[WAVE API] Error while executing %s\n", message); \
+      break; \
+  }
+#else
+#define WAVE_ERROR_VERBOSE(error, message) \
+  do {} while(0)
+#endif
+
+#define HANDLE_WAVE_ERROR(status, location) \
+  if (status != MMSYSERR_NOERROR) { \
+      WAVE_ERROR_VERBOSE(status, location); \
+      return getSAErrorCode(status); \
+  }
+
+#define ERROR_IF_NO_INIT(handle) \
+  if (handle == NULL) { \
+		return SA_ERROR_NO_INIT; \
+	}
+
+/* local implementation of the sa_stream_t type */
+struct sa_stream {   
+  char*           deviceName;
+  UINT				    device;
+  UINT				    channels;
+  UINT				    rate;
+	
+  sa_mode_t			  rwMode;
+  sa_pcm_format_t	format;   
+ 
+  HWAVEOUT			  hWaveOut;
+  HANDLE			    callbackEvent;
+  CRITICAL_SECTION  waveCriticalSection;  
+  WAVEHDR*			  waveBlocks;  
+  volatile int		waveFreeBlockCount;
+  int				      waveCurrentBlock;
+};
+
+
+/** Forward definitions of audio api specific functions */
+int allocateBlocks(int size, int count, WAVEHDR** blocks);
+int freeBlocks(WAVEHDR* blocks);
+int openAudio(sa_stream_t *s);
+int closeAudio(sa_stream_t * s);
+int writeAudio(sa_stream_t *s, LPSTR data, int bytes);
+int getSAErrorCode(int waveErrorCode);
+
+void CALLBACK waveOutProc(HWAVEOUT hWaveOut, UINT uMsg, 
+    DWORD dwInstance, DWORD dwParam1, DWORD dwParam2);
+
+/** Main callback function */
+typedef int (*sa_event_callback_t)(sa_stream_t *s, sa_event_t event);
+
+
+/** Normal way to open a PCM device */
+int sa_stream_create_pcm(sa_stream_t **s, 
+                         const char *client_name, 
+                         sa_mode_t mode, 
+                         sa_pcm_format_t format, 
+                         unsigned int rate, 
+                         unsigned int nchannels) {
+  sa_stream_t * _s = NULL;
+  
+  ERROR_IF_NO_INIT(s);
+  
+  *s = NULL;
+  
+  /* FIX ME: for formats different than PCM extend using WAVEFORMATEXTENSIBLE */
+  if (format != SA_PCM_FORMAT_S16_NE) {
+    return SA_ERROR_NOT_SUPPORTED;
+  }
+
+  if (mode != SA_MODE_WRONLY) {
+    return SA_ERROR_NOT_SUPPORTED;
+  }
+
+  if ((_s = (sa_stream_t*)malloc(sizeof(sa_stream_t))) == NULL) {
+    return SA_ERROR_OOM;
+  }
+   
+  _s->rwMode = mode;
+  _s->format = format;
+  _s->rate = rate;
+  _s->channels = nchannels;
+  _s->deviceName = DEFAULT_DEVICE_NAME;
+  _s->device = DEFAULT_DEVICE;
+
+  *s = _s; 
+  return SA_SUCCESS;
+}
+
+/** Initialise the device */
+int sa_stream_open(sa_stream_t *s) {  
+  int status = SA_SUCCESS;
+
+  ERROR_IF_NO_INIT(s);
+
+  switch (s->rwMode) {
+    case SA_MODE_WRONLY: 
+      status = openAudio(s);
+      break;
+	default:
+      status = SA_ERROR_NOT_SUPPORTED;      
+      break;
+  }    
+  return status;
+}
+
+/** Interleaved playback function */
+int sa_stream_write(sa_stream_t *s, const void *data, size_t nbytes) {
+  int status = SA_SUCCESS;
+
+  ERROR_IF_NO_INIT(s);
+
+  status = writeAudio(s, (LPSTR)data, nbytes);
+
+  return status;
+}
+
+/** Close/destroy everything */
+int sa_stream_destroy(sa_stream_t *s) {
+  int status;
+
+  ERROR_IF_NO_INIT(s);
+  /* close and release all allocated resources */
+  status = closeAudio(s);
+
+  return status;
+}
+
+#define LEFT_CHANNEL_MASK 0x0000FFFF
+#define RIGHT_CHANNEL_MASK 0xFFFF0000
+
+/** 
+ * retrieved volume as an int in a scale from 0x0000 to 0xFFFF
+ * only one value for all channels
+ */
+int sa_stream_get_write_volume(sa_stream_t *s, int32_t vol[], unsigned int *n) {
+  int status;
+	DWORD volume;
+	WORD left;
+	WORD right;
+
+	ERROR_IF_NO_INIT(s);
+  
+	status = waveOutGetVolume(s->hWaveOut, &volume);
+	HANDLE_WAVE_ERROR(status, "reading audio volume level");
+
+	left = volume & LEFT_CHANNEL_MASK;
+	right = (volume & RIGHT_CHANNEL_MASK) >> 16;
+  vol[0] = (int32_t)(left + right /2);	
+
+	return SA_SUCCESS;
+
+}
+
+/** changes volume as an int in a scale from 0x0000 to 0xFFFF*/
+int sa_stream_change_write_volume(sa_stream_t *s, const int32_t vol[], unsigned int n) {
+  int status;
+	DWORD volume;
+	WORD left;
+	WORD right;
+	
+	ERROR_IF_NO_INIT(s);
+	
+  volume = (DWORD)vol[0];
+	left = volume & LEFT_CHANNEL_MASK;	  
+	right = left;	  
+	volume =  (left << 16) | right;	
+	
+	status = waveOutSetVolume(s->hWaveOut, volume);
+	HANDLE_WAVE_ERROR(status, "setting new audio volume level");	
+
+	return SA_SUCCESS;
+
+
+}
+
+/** sync/timing */
+int sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos) {
+	int status;
+  MMTIME  mm;
+
+  ERROR_IF_NO_INIT(s);
+
+  if (position != SA_POSITION_WRITE_HARDWARE) {
+    return SA_ERROR_NOT_SUPPORTED;
+  }
+  // request playback progress in bytes
+  mm.wType = TIME_BYTES;		
+	status = waveOutGetPosition(s->hWaveOut, &mm, sizeof(MMTIME));
+  HANDLE_WAVE_ERROR(status, "reading audio buffer position");
+  *pos = (int64_t)mm.u.cb;
+
+	return SA_SUCCESS;
+}
+
+/* Control/xrun */
+/** Resume playing after a pause */
+int sa_stream_resume(sa_stream_t *s) {
+  int status;  
+  
+  ERROR_IF_NO_INIT(s);
+
+  status = waveOutRestart(s->hWaveOut);
+  HANDLE_WAVE_ERROR(status, "resuming audio playback");
+
+  return SA_SUCCESS;
+}
+/** Pause audio playback (do not empty the buffer) */
+int sa_stream_pause(sa_stream_t *s) {
+  int status;
+
+  ERROR_IF_NO_INIT(s);
+  
+  status = waveOutPause(s->hWaveOut);
+  HANDLE_WAVE_ERROR(status, "resuming audio playback");
+
+  return SA_SUCCESS;
+}
+
+/*
+ * -----------------------------------------------------------------------------
+ * Private WAVE API specific functions
+ * -----------------------------------------------------------------------------
+ */
+
+/** 
+ * \brief - allocate buffer for writing to system WAVE audio device
+ * \param size - size of each audio block
+ * \param cound - number of blocks to be allocated
+ * \param blocks - pointer to the blocks buffer to be allocated
+ * \return - completion status
+ */
+int allocateBlocks(int size, int count, WAVEHDR** blocks)
+{
+  unsigned char* buffer;    
+  int i;    
+  WAVEHDR* headers;
+  DWORD totalBufferSize = (size + sizeof(WAVEHDR)) * count;
+    
+  /* allocate memory on heap for the entire set in one go  */    
+  if((buffer = HeapAlloc(
+     GetProcessHeap(), 
+     HEAP_ZERO_MEMORY, 
+     totalBufferSize
+     )) == NULL) {
+      printf("Memory allocation error\n");
+      return SA_ERROR_OOM;
+    }
+
+  /* and set up the pointers to each bit */
+  headers = *blocks = (WAVEHDR*)buffer;
+  buffer += sizeof(WAVEHDR) * count;
+  for(i = 0; i < count; i++) {    
+	  headers[i].dwBufferLength = size;
+    headers[i].lpData = buffer;
+    buffer += size;
+  }
+    
+  return SA_SUCCESS;
+}
+
+/**
+ * \brief - free allocated audio buffer
+ * \param blocks - pointer to allocated the buffer of audio bloks
+ * \return - completion status
+ */
+int freeBlocks(WAVEHDR* blocks)
+{    
+  if (blocks == NULL) 
+    return SA_ERROR_INVALID;
+
+  /* and this is why allocateBlocks works the way it does */     
+  HeapFree(GetProcessHeap(), 0, blocks);
+  blocks = NULL;
+
+  return SA_SUCCESS;
+}
+
+/** 
+ * \brief - open system default WAVE device
+ * \param s - sydney audio stream handle
+ * \return - completion status
+ */ 
+int openAudio(sa_stream_t *s) {
+  int status;
+  WAVEFORMATEX wfx;    
+  UINT supported = FALSE;
+		  
+  status = allocateBlocks(BLOCK_SIZE, BLOCK_COUNT, &(s->waveBlocks));  
+	HANDLE_WAVE_ERROR(status, "allocating audio buffer blocks");
+  
+  s->waveFreeBlockCount	= BLOCK_COUNT;
+  s->waveCurrentBlock	= 0;  
+  wfx.nSamplesPerSec	= (DWORD)s->rate;	/* sample rate */
+  wfx.wBitsPerSample	= 16;				/* sample size */
+  wfx.nChannels			= s->channels;	/* channels    */
+  wfx.cbSize			= 0;				/* size of _extra_ info */
+  wfx.wFormatTag		= WAVE_FORMAT_PCM;
+  wfx.nBlockAlign		= (wfx.wBitsPerSample * wfx.nChannels) >> 3;
+  wfx.nAvgBytesPerSec	= wfx.nBlockAlign * wfx.nSamplesPerSec;
+
+  supported = waveOutOpen(NULL, WAVE_MAPPER, &wfx, (DWORD_PTR)0, (DWORD_PTR)0, 
+				WAVE_FORMAT_QUERY);
+  if (supported == MMSYSERR_NOERROR) { // audio device opened sucessfully 
+    status = waveOutOpen((LPHWAVEOUT)&(s->hWaveOut), WAVE_MAPPER, &wfx, 
+	  (DWORD_PTR)waveOutProc, (DWORD_PTR)s, CALLBACK_FUNCTION);
+    HANDLE_WAVE_ERROR(status, "opening audio device for playback");
+		printf("Audio device sucessfully opened\n");
+  } 
+  else if (supported == WAVERR_BADFORMAT) {
+    printf("Requested format not supported...\n");
+	  // clean up the memory
+	  freeBlocks(s->waveBlocks);
+    return SA_ERROR_NOT_SUPPORTED;
+  } 
+  else {
+    printf("Error opening default audio device. Exiting...\n");
+	  // clean up the memory
+	  freeBlocks(s->waveBlocks);
+    return SA_ERROR_SYSTEM;
+  }
+  // create notification for data written to a device
+  s->callbackEvent = CreateEvent(0, FALSE, FALSE, 0);
+  // initialise critical section for operations on waveFreeBlockCound variable
+  InitializeCriticalSection(&(s->waveCriticalSection));
+
+  return SA_SUCCESS;
+}
+/**
+ * \brief - closes opened audio device handle
+ * \param s - sydney audio stream handle
+ * \return - completion status
+ */
+int closeAudio(sa_stream_t * s) {
+  int status, i;
+  
+  // reseting audio device and flushing buffers
+  status = waveOutReset(s->hWaveOut);    
+  HANDLE_WAVE_ERROR(status, "resetting audio device");
+  
+  /* wait for all blocks to complete */  
+  while(s->waveFreeBlockCount < BLOCK_COUNT)
+	  Sleep(10);
+
+  /* unprepare any blocks that are still prepared */  
+  for(i = 0; i < s->waveFreeBlockCount; i++) {
+    if(s->waveBlocks[i].dwFlags & WHDR_PREPARED) {
+	    status = waveOutUnprepareHeader(s->hWaveOut, &(s->waveBlocks[i]), sizeof(WAVEHDR));
+      HANDLE_WAVE_ERROR(status, "closing audio device");
+    }
+  }    
+
+  freeBlocks(s->waveBlocks);  
+  status = waveOutClose(s->hWaveOut);    
+  HANDLE_WAVE_ERROR(status, "closing audio device");
+
+  DeleteCriticalSection(&(s->waveCriticalSection));
+  CloseHandle(s->callbackEvent);
+  printf("[audio] audio resources cleanup completed\n");
+  
+  return SA_SUCCESS;
+}
+/**
+ * \brief - writes PCM audio samples to audio device
+ * \param s - valid handle to opened sydney stream
+ * \param data - pointer to memory storing audio samples to be played
+ * \param nsamples - number of samples in the memory pointed by previous parameter
+ * \return - completion status
+ */
+int writeAudio(sa_stream_t *s, LPSTR data, int bytes) {    
+  UINT status;
+  WAVEHDR* current;	  
+  int remain;
+
+  current = &(s->waveBlocks[s->waveCurrentBlock]);
+  
+  while(bytes > 0) {
+    /* first make sure the header we're going to use is unprepared */
+    if(current->dwFlags & WHDR_PREPARED) {      
+        status = waveOutUnprepareHeader(s->hWaveOut, current, sizeof(WAVEHDR));         
+        HANDLE_WAVE_ERROR(status, "preparing audio headers for writing");
+    }
+		  
+    if(bytes < (int)(BLOCK_SIZE - current->dwUser)) {							  	    
+		  memcpy(current->lpData + current->dwUser, data, bytes);
+      current->dwUser += bytes;
+      break;
+    }
+	
+    /* remain is even as BLOCK_SIZE and dwUser are even too */
+    remain = BLOCK_SIZE - current->dwUser;      
+  	memcpy(current->lpData + current->dwUser, data, remain);
+    bytes -= remain;
+    data += remain;
+	  current->dwBufferLength = BLOCK_SIZE;
+	  /* write to audio device */
+    waveOutPrepareHeader(s->hWaveOut, current, sizeof(WAVEHDR));
+	  status = waveOutWrite(s->hWaveOut, current, sizeof(WAVEHDR));      
+    HANDLE_WAVE_ERROR(status, "writing audio to audio device");
+      
+    EnterCriticalSection(&(s->waveCriticalSection));
+    s->waveFreeBlockCount--;
+    LeaveCriticalSection(&(s->waveCriticalSection));
+    /*
+     * wait for a block to become free
+     */
+    while (!(s->waveFreeBlockCount)) {
+        //printf("All audio buffer blocks empty\n");        
+      WaitForSingleObject(s->callbackEvent, INFINITE);
+        //Sleep(10);
+    }		  
+		
+    /*
+     * point to the next block
+     */
+    (s->waveCurrentBlock)++;
+    (s->waveCurrentBlock) %= BLOCK_COUNT;		
+
+    current = &(s->waveBlocks[s->waveCurrentBlock]);
+    current->dwUser = 0;
+  }
+  return SA_SUCCESS;
+}
+
+/**
+ * \brief - audio callback function called when next WAVE header is played by audio device
+ */
+void CALLBACK waveOutProc(
+    HWAVEOUT hWaveOut, 
+    UINT uMsg, 
+    DWORD dwInstance,  
+    DWORD dwParam1,    
+    DWORD dwParam2     
+)
+{
+    /*
+     * pointer to free block counter
+     */
+    sa_stream_t* handle = (sa_stream_t*)dwInstance;
+    /*
+     * ignore calls that occur due to openining and closing the
+     * device.
+     */
+    if(uMsg != WOM_DONE)
+        return;
+
+    EnterCriticalSection(&(handle->waveCriticalSection));
+    (handle->waveFreeBlockCount)++;
+    if ((handle->waveFreeBlockCount) == 1) 
+       SetEvent(handle->callbackEvent);
+    LeaveCriticalSection(&(handle->waveCriticalSection));	
+}
+
+/**
+ * \brief - converts frequently reported WAVE error codes to Sydney audio API codes
+ */
+int getSAErrorCode(int waveErrorCode) {
+  int error = SA_ERROR_NOT_SUPPORTED;
+
+  switch (waveErrorCode) {
+    case MMSYSERR_NOERROR: 
+      error = SA_SUCCESS;
+      break;
+    case MMSYSERR_ALLOCATED: 
+      error = SA_ERROR_SYSTEM;
+      break;
+    case MMSYSERR_BADDEVICEID:
+      error = SA_ERROR_INVALID;
+      break;
+    case MMSYSERR_NODRIVER:
+      error = SA_ERROR_NO_DRIVER;
+      break;
+    case MMSYSERR_NOTSUPPORTED:
+      error = SA_ERROR_NOT_SUPPORTED;
+      break;          
+    case MMSYSERR_NOMEM: 
+      error = SA_ERROR_OOM;
+      break;
+    case MMSYSERR_INVALHANDLE:
+      error = SA_ERROR_INVALID;
+      break;
+    case WAVERR_BADFORMAT: 
+      error = SA_ERROR_NOT_SUPPORTED;
+      break;
+    case WAVERR_SYNC: 
+      error = SA_ERROR_NOT_SUPPORTED;
+      break;    
+  }
+  return error;
+}
+
+
+/*
+ * -----------------------------------------------------------------------------
+ * Functions to be implemented next 
+ * -----------------------------------------------------------------------------
+ */
+
+#define NOT_IMPLEMENTED(func)   func { return SA_ERROR_NOT_SUPPORTED; }
+
+/* "Soft" params */
+NOT_IMPLEMENTED(int sa_stream_set_write_lower_watermark(sa_stream_t *s, size_t size))
+NOT_IMPLEMENTED(int sa_stream_set_read_lower_watermark(sa_stream_t *s, size_t size))
+NOT_IMPLEMENTED(int sa_stream_set_write_upper_watermark(sa_stream_t *s, size_t size))
+NOT_IMPLEMENTED(int sa_stream_set_read_upper_watermark(sa_stream_t *s, size_t size))
+
+/** Set the mapping between channels and the loudspeakers */
+NOT_IMPLEMENTED(int sa_stream_set_channel_map(sa_stream_t *s, const sa_channel_t map[], unsigned int n))
+
+/* Query functions */
+NOT_IMPLEMENTED(int sa_stream_get_mode(sa_stream_t *s, sa_mode_t *access_mode))
+NOT_IMPLEMENTED(int sa_stream_get_pcm_format(sa_stream_t *s, sa_pcm_format_t *format))
+NOT_IMPLEMENTED(int sa_stream_get_rate(sa_stream_t *s, unsigned int *rate))
+NOT_IMPLEMENTED(int sa_stream_get_nchannels(sa_stream_t *s, int *nchannels))
+NOT_IMPLEMENTED(int sa_stream_get_device(sa_stream_t *s, char *device_name, size_t *size))
+NOT_IMPLEMENTED(int sa_stream_get_write_lower_watermark(sa_stream_t *s, size_t *size))
+NOT_IMPLEMENTED(int sa_stream_get_read_lower_watermark(sa_stream_t *s, size_t *size))
+NOT_IMPLEMENTED(int sa_stream_get_write_upper_watermark(sa_stream_t *s, size_t *size))
+NOT_IMPLEMENTED(int sa_stream_get_read_upper_watermark(sa_stream_t *s, size_t *size))
+NOT_IMPLEMENTED(int sa_stream_get_channel_map(sa_stream_t *s, sa_channel_t map[], unsigned int *n))
+
+/*
+ * -----------------------------------------------------------------------------
+ * Unsupported functions
+ * -----------------------------------------------------------------------------
+ */
+#define UNSUPPORTED(func)   func { return SA_ERROR_NOT_SUPPORTED; }
+
+/** Create an opaque (e.g. AC3) codec stream */
+UNSUPPORTED(int sa_stream_create_opaque(sa_stream_t **s, const char *client_name, sa_mode_t mode, const char *codec))
+/** Whether xruns cause the card to reset */
+UNSUPPORTED(int sa_stream_set_xrun_mode(sa_stream_t *s, sa_xrun_mode_t mode))
+/** Set the device to non-interleaved mode */
+UNSUPPORTED(int sa_stream_set_non_interleaved(sa_stream_t *s, int enable))
+/** Require dynamic sample rate */
+UNSUPPORTED(int sa_stream_set_dynamic_rate(sa_stream_t *s, int enable))
+/** Select driver */
+UNSUPPORTED(int sa_stream_set_driver(sa_stream_t *s, const char *driver))
+/** Start callback */
+UNSUPPORTED(int sa_stream_start_thread(sa_stream_t *s, sa_event_callback_t callback))
+/** Stop callback */
+UNSUPPORTED(int sa_stream_stop_thread(sa_stream_t *s))
+/** Change the device connected to the stream */
+UNSUPPORTED(int sa_stream_change_device(sa_stream_t *s, const char *device_name))
+/** volume in hundreths of dB*/
+UNSUPPORTED(int sa_stream_change_read_volume(sa_stream_t *s, const int32_t vol[], unsigned int n))
+/** Change the sampling rate */
+UNSUPPORTED(int sa_stream_change_rate(sa_stream_t *s, unsigned int rate))
+/** Change some meta data that is attached to the stream */
+UNSUPPORTED(int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *data, size_t size))
+/** Associate opaque user data */
+UNSUPPORTED(int sa_stream_change_user_data(sa_stream_t *s, const void *value))
+/* Hardware-related. This is implementation-specific and hardware specific. */
+UNSUPPORTED(int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction))
+UNSUPPORTED(int sa_stream_set_adjust_nchannels(sa_stream_t *s, sa_adjust_t direction))
+UNSUPPORTED(int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction))
+UNSUPPORTED(int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction))
+/* Query functions */
+UNSUPPORTED(int sa_stream_get_codec(sa_stream_t *s, char *codec, size_t *size))
+UNSUPPORTED(int sa_stream_get_user_data(sa_stream_t *s, void **value))
+
+UNSUPPORTED(int sa_stream_get_xrun_mode(sa_stream_t *s, sa_xrun_mode_t *mode))
+UNSUPPORTED(int sa_stream_get_non_interleaved(sa_stream_t *s, int *enabled))
+UNSUPPORTED(int sa_stream_get_dynamic_rate(sa_stream_t *s, int *enabled))
+UNSUPPORTED(int sa_stream_get_driver(sa_stream_t *s, char *driver_name, size_t *size))            
+UNSUPPORTED(int sa_stream_get_read_volume(sa_stream_t *s, int32_t vol[], unsigned int *n))
+UNSUPPORTED(int sa_stream_get_meta_data(sa_stream_t *s, const char *name, void*data, size_t *size))
+UNSUPPORTED(int sa_stream_get_adjust_rate(sa_stream_t *s, sa_adjust_t *direction))
+UNSUPPORTED(int sa_stream_get_adjust_nchannels(sa_stream_t *s, sa_adjust_t *direction))
+UNSUPPORTED(int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction))
+UNSUPPORTED(int sa_stream_get_adjust_watermarks(sa_stream_t *s, sa_adjust_t *direction))
+/** Get current state of the audio device */
+UNSUPPORTED(int sa_stream_get_state(sa_stream_t *s, sa_state_t *state))
+/** Obtain the error code */
+UNSUPPORTED(int sa_stream_get_event_error(sa_stream_t *s, sa_error_t *error))
+/** Obtain the notification code */
+UNSUPPORTED(int sa_stream_get_event_notify(sa_stream_t *s, sa_notify_t *notify))
+
+/* Blocking IO calls */
+/** Interleaved capture function */
+UNSUPPORTED(int sa_stream_read(sa_stream_t *s, void *data, size_t nbytes))
+/** Non-interleaved capture function */
+UNSUPPORTED(int sa_stream_read_ni(sa_stream_t *s, unsigned int channel, void *data, size_t nbytes))
+
+/** Non-interleaved playback function */
+UNSUPPORTED(int sa_stream_write_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes))
+/** Interleaved playback function with seek offset */
+UNSUPPORTED(int sa_stream_pwrite(sa_stream_t *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
+/** Non-interleaved playback function with seek offset */
+UNSUPPORTED(int sa_stream_pwrite_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))
+
+/** Query how much can be read without blocking */
+UNSUPPORTED(int sa_stream_get_read_size(sa_stream_t *s, size_t *size))
+/** Query how much can be written without blocking */
+UNSUPPORTED(int sa_stream_get_write_size(sa_stream_t *s, size_t *size))
+
+/** Block until all audio has been played */
+UNSUPPORTED(int sa_stream_drain(sa_stream_t *s))
+
+/** Return a human readable error */
+const char *sa_strerror(int code);
new file mode 100644
--- /dev/null
+++ b/modules/liboggplay_audio/update.sh
@@ -0,0 +1,5 @@
+# Usage: ./update.sh <browser_plugin_src_directory>
+#
+# Copies the needed files from a directory containing the original
+# browser_plugin source that we need for the Mozilla HTML5 media support.
+cp $1/src/audio/* .
--- a/toolkit/library/Makefile.in
+++ b/toolkit/library/Makefile.in
@@ -200,16 +200,24 @@ ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
 CXXFLAGS	+= $(TK_CFLAGS)
 EXTRA_DSO_LDOPTS += \
 	-framework SystemConfiguration \
 	-framework QuickTime \
 	-framework IOKit \
 	-lcrypto \
 	$(TK_LIBS) \
 	$(NULL)
+
+ifdef MOZ_OGG
+EXTRA_DSO_LDOPTS += \
+	-framework CoreAudio \
+ 	-framework AudioToolbox \
+	-framework AudioUnit \
+	$(NULL)
+endif
 endif
 
 ifdef MOZ_ENABLE_DBUS
 EXTRA_DSO_LDOPTS += $(MOZ_DBUS_GLIB_LIBS)
 endif
 
 ifeq (gtk2,$(MOZ_WIDGET_TOOLKIT))
 EXTRA_DSO_LDOPTS += $(XLDFLAGS) $(XLIBS) $(MOZ_XFT_LIBS) $(MOZ_GTK2_LIBS) $(XT_LIBS) -lgthread-2.0
--- a/toolkit/toolkit-makefiles.sh
+++ b/toolkit/toolkit-makefiles.sh
@@ -277,16 +277,65 @@ MAKEFILES_libpref="
 "
 
 MAKEFILES_libutil="
   modules/libutil/Makefile
   modules/libutil/public/Makefile
   modules/libutil/src/Makefile
 "
 
+MAKEFILES_libvorbis="
+  modules/libvorbis/Makefile
+  modules/libvorbis/lib/Makefile
+  modules/libvorbis/include/Makefile
+  modules/libvorbis/include/vorbis/Makefile
+"
+
+MAKEFILES_libtheora="
+  modules/libtheora/Makefile
+  modules/libtheora/lib/Makefile
+  modules/libtheora/include/Makefile
+  modules/libtheora/include/theora/Makefile
+"
+
+MAKEFILES_liboggz="
+  modules/liboggz/Makefile
+  modules/liboggz/src/Makefile
+  modules/liboggz/src/liboggz/Makefile
+  modules/liboggz/include/Makefile
+  modules/liboggz/include/oggz/Makefile
+"
+
+MAKEFILES_libogg="
+  modules/libogg/Makefile
+  modules/libogg/src/Makefile
+  modules/libogg/include/Makefile
+  modules/libogg/include/ogg/Makefile
+"
+
+MAKEFILES_libfishsound="
+  modules/libfishsound/Makefile
+  modules/libfishsound/src/Makefile
+  modules/libfishsound/src/libfishsound/Makefile
+  modules/libfishsound/include/Makefile
+  modules/libfishsound/include/fishsound/Makefile
+"
+
+MAKEFILES_liboggplay="
+  modules/liboggplay/Makefile
+  modules/liboggplay/src/Makefile
+  modules/liboggplay/src/liboggplay/Makefile
+  modules/liboggplay/include/Makefile
+  modules/liboggplay/include/oggplay/Makefile
+"
+
+MAKEFILES_liboggplay_audio="
+  modules/liboggplay_audio/Makefile
+"
+
 MAKEFILES_oji="
   modules/oji/Makefile
   modules/oji/public/Makefile
   modules/oji/src/Makefile
   plugin/oji/JEP/Makefile
 "
 
 MAKEFILES_plugin="
@@ -1138,8 +1187,20 @@ fi # MOZ_COMPONENTLIB
 if [ "$MOZ_MEDIA" ]; then
  add_makefiles "
    content/media/Makefile
    content/media/video/Makefile
    content/media/video/public/Makefile
    content/media/video/src/Makefile
  "
 fi
+
+if [ "$MOZ_OGG" ]; then
+ add_makefiles "
+   $MAKEFILES_libvorbis
+   $MAKEFILES_libtheora
+   $MAKEFILES_liboggz
+   $MAKEFILES_libogg
+   $MAKEFILES_libfishsound
+   $MAKEFILES_liboggplay
+   $MAKEFILES_liboggplay_audio
+ "
+fi
\ No newline at end of file
--- a/toolkit/toolkit-tiers.mk
+++ b/toolkit/toolkit-tiers.mk
@@ -111,16 +111,28 @@ endif
 ifdef MOZ_RDF
 tier_gecko_dirs += rdf
 endif
 
 ifdef MOZ_JSDEBUGGER
 tier_gecko_dirs += js/jsd
 endif
 
+ifdef MOZ_OGG
+tier_gecko_dirs += \
+		modules/libfishsound \
+		modules/libogg \
+		modules/liboggplay \
+		modules/liboggplay_audio \
+		modules/liboggz \
+		modules/libtheora \
+		modules/libvorbis \
+		$(NULL)
+endif
+
 tier_gecko_dirs	+= \
 		uriloader \
 		modules/libimg \
 		caps \
 		parser/expat \
 		parser/xml \
 		parser/htmlparser \
 		gfx \