Bug 599089 - Remote Audio to parent process. r=kinetik a=blocking-fennec
authorDoug Turner <dougt@dougt.org>
Tue, 16 Nov 2010 20:14:19 -0800
changeset 57627 908443327ad31bd8cb22f5e6a8f44eab83b65db6
parent 57626 384f1f7127dfb7245c881eb2ce066ea0bbf59cd9
child 57628 6dd7e2f976395e1b863cf9da376c6e23bf424ebc
push id17015
push userdougt@mozilla.com
push dateWed, 17 Nov 2010 04:25:31 +0000
treeherdermozilla-central@0f43632752fe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik, blocking-fennec
bugs599089
milestone2.0b8pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 599089 - Remote Audio to parent process. r=kinetik a=blocking-fennec
content/html/content/public/nsHTMLMediaElement.h
content/html/content/src/nsHTMLAudioElement.cpp
content/media/Makefile.in
content/media/nsAudioStream.cpp
content/media/nsAudioStream.h
content/media/nsBuiltinDecoderStateMachine.cpp
content/media/nsBuiltinDecoderStateMachine.h
content/media/wave/nsWaveDecoder.cpp
dom/ipc/AudioChild.cpp
dom/ipc/AudioChild.h
dom/ipc/AudioParent.cpp
dom/ipc/AudioParent.h
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/Makefile.in
dom/ipc/PAudio.ipdl
dom/ipc/PContent.ipdl
dom/ipc/ipdl.mk
--- a/content/html/content/public/nsHTMLMediaElement.h
+++ b/content/html/content/public/nsHTMLMediaElement.h
@@ -593,17 +593,17 @@ protected:
 
   nsRefPtr<gfxASurface> mPrintSurface;
 
   // Reference to the source element last returned by GetNextSource().
   // This is the child source element which we're trying to load from.
   nsCOMPtr<nsIContent> mSourceLoadCandidate;
 
   // An audio stream for writing audio directly from JS.
-  nsAutoPtr<nsAudioStream> mAudioStream;
+  nsRefPtr<nsAudioStream> mAudioStream;
 
   // PR_TRUE if MozAudioAvailable events can be safely dispatched, based on
   // a media and element same-origin check.
   PRBool mAllowAudioData;
 
   // If true then we have begun downloading the media content.
   // Set to false when completed, or not yet started.
   PRPackedBool mBegun;
--- a/content/html/content/src/nsHTMLAudioElement.cpp
+++ b/content/html/content/src/nsHTMLAudioElement.cpp
@@ -163,17 +163,17 @@ nsHTMLAudioElement::MozSetup(PRUint32 aC
   if (0 == aChannels) {
     return NS_ERROR_FAILURE;
   }
 
   if (mAudioStream) {
     mAudioStream->Shutdown();
   }
 
-  mAudioStream = new nsAudioStream();
+  mAudioStream = nsAudioStream::AllocateStream();
   nsresult rv = mAudioStream->Init(aChannels, aRate,
                                    nsAudioStream::FORMAT_FLOAT32);
   if (NS_FAILED(rv)) {
     mAudioStream->Shutdown();
     mAudioStream = nsnull;
     return rv;
   }
 
--- a/content/media/Makefile.in
+++ b/content/media/Makefile.in
@@ -93,14 +93,16 @@ PARALLEL_DIRS += webm
 endif
 
 ifdef ENABLE_TESTS
 PARALLEL_DIRS += test
 endif
 
 FORCE_STATIC_LIB = 1
 
+include $(topsrcdir)/config/config.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
 
 INCLUDES += \
   -I$(srcdir)/../base/src \
   -I$(srcdir)/../html/content/src \
   $(NULL)
--- a/content/media/nsAudioStream.cpp
+++ b/content/media/nsAudioStream.cpp
@@ -30,107 +30,381 @@
  * 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 ***** */
+
+#ifdef MOZ_IPC
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/PAudioChild.h"
+#include "mozilla/dom/AudioChild.h"
+#include "nsXULAppAPI.h"
+using namespace mozilla::dom;
+#endif
+
 #include <stdio.h>
 #include <math.h>
 #include "prlog.h"
 #include "prmem.h"
 #include "nsAutoPtr.h"
 #include "nsAudioStream.h"
 #include "nsAlgorithm.h"
 extern "C" {
 #include "sydneyaudio/sydney_audio.h"
 }
 #include "mozilla/TimeStamp.h"
+#include "nsThreadUtils.h"
 
 #if defined(XP_MACOSX)
 #define SA_PER_STREAM_VOLUME 1
 #endif
 
 using mozilla::TimeStamp;
 
 #ifdef PR_LOGGING
 PRLogModuleInfo* gAudioStreamLog = nsnull;
 #endif
 
+#ifdef MOZ_IPC
+static nsIThread *gAudioPlaybackThread = nsnull;
+#endif
+
 #define FAKE_BUFFER_SIZE 176400
 #define MILLISECONDS_PER_SECOND 1000
 
+class nsAudioStreamLocal : public nsAudioStream
+{
+ public:
+  NS_DECL_ISUPPORTS
+
+  ~nsAudioStreamLocal();
+  nsAudioStreamLocal();
+  
+  nsresult Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat);
+  void Shutdown();
+  nsresult Write(const void* aBuf, PRUint32 aCount, PRBool aBlocking);
+  PRUint32 Available();
+  void SetVolume(float aVolume);
+  void Drain();
+  void Pause();
+  void Resume();
+  PRInt64 GetPosition();
+  PRInt64 GetSampleOffset();
+  PRBool IsPaused();
+
+ private:
+
+  double mVolume;
+  void* mAudioHandle;
+  int mRate;
+  int mChannels;
+
+  SampleFormat mFormat;
+
+  // When a Write() request is made, and the number of samples
+  // requested to be written exceeds the buffer size of the audio
+  // backend, the remaining samples are stored in this variable. They
+  // will be written on the next Write() request.
+  nsTArray<short> mBufferOverflow;
+
+  // PR_TRUE if this audio stream is paused.
+  PRPackedBool mPaused;
+
+  // PR_TRUE if this stream has encountered an error.
+  PRPackedBool mInError;
+
+};
+
+#ifdef MOZ_IPC
+class nsAudioStreamRemote : public nsAudioStream
+{
+ public:
+  NS_DECL_ISUPPORTS
+
+  nsAudioStreamRemote();
+  ~nsAudioStreamRemote();
+  
+  nsresult Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat);
+  void Shutdown();
+  nsresult Write(const void* aBuf, PRUint32 aCount, PRBool aBlocking);
+  PRUint32 Available();
+  void SetVolume(float aVolume);
+  void Drain();
+  void Pause();
+  void Resume();
+  PRInt64 GetPosition();
+  PRInt64 GetSampleOffset();
+  PRBool IsPaused();
+
+  AudioChild* mAudioChild;
+
+  SampleFormat mFormat;
+  int mRate;
+  int mChannels;
+  // PR_TRUE if this audio stream is paused.
+  PRPackedBool mPaused;
+
+  PRInt32 mBytesPerSample;
+
+  friend class AudioInitEvent;
+  friend class AudioShutdownEvent;
+  friend class AudioWriteEvent;
+  friend class AudioSetVolumeEvent;
+  friend class AudioPauseEvent;
+  friend class AudioDrainEvent;
+  friend class AudioGetSampleEvent;
+};
+
+class AudioInitEvent : public nsRunnable
+{
+ public:
+  AudioInitEvent(nsAudioStreamRemote* owner)
+  {
+    mOwner = owner;
+  }
+
+  NS_IMETHOD Run()
+  {
+    ContentChild * cpc = ContentChild::GetSingleton();
+    NS_ASSERTION(cpc, "Content Protocol is NULL!");
+    mOwner->mAudioChild =  static_cast<AudioChild*> (cpc->SendPAudioConstructor(mOwner->mChannels,
+                                                                                mOwner->mRate,
+                                                                                mOwner->mFormat));
+    return NS_OK;
+  }
+  
+  nsRefPtr<nsAudioStreamRemote> mOwner;
+};
+
+class AudioShutdownEvent : public nsRunnable
+{
+ public:
+  AudioShutdownEvent(nsAudioStreamRemote* owner)
+  {
+    mOwner = owner;
+  }
+
+  NS_IMETHOD Run()
+  {
+    if (mOwner->mAudioChild) {
+      PAudioChild::Send__delete__(mOwner->mAudioChild);
+      mOwner->mAudioChild = nsnull;
+    }
+    mOwner = nsnull;
+    return NS_OK;
+  }
+  nsRefPtr<nsAudioStreamRemote> mOwner;
+};
+
+class AudioWriteEvent : public nsRunnable
+{
+ public:
+  AudioWriteEvent(nsAudioStreamRemote* owner,
+                  const void* aBuf,
+                  PRUint32 aNumberOfSamples,
+                  PRUint32 aBytesPerSample)
+  {    
+    mOwner = owner;
+    mBytesPerSample = aBytesPerSample;
+    mBuffer.Assign((const char*)aBuf, aNumberOfSamples*aBytesPerSample);
+  }
+
+  NS_IMETHOD Run()
+  {
+    if (!mOwner->mAudioChild)
+      return NS_OK;
+
+    mOwner->mAudioChild->SendWrite(mBuffer,
+                                   mBuffer.Length() / mBytesPerSample);
+    return NS_OK;
+  }
+
+  nsRefPtr<nsAudioStreamRemote> mOwner;
+  nsCString mBuffer;
+  PRUint32 mBytesPerSample;
+};
+
+class AudioSetVolumeEvent : public nsRunnable
+{
+ public:
+  AudioSetVolumeEvent(nsAudioStreamRemote* owner, float volume)
+  {
+    mOwner = owner;
+    mVolume = volume;
+  }
+
+  NS_IMETHOD Run()
+  {
+    if (!mOwner->mAudioChild)
+      return NS_OK;
+
+    mOwner->mAudioChild->SendSetVolume(mVolume);
+    return NS_OK;
+  }
+  
+  nsRefPtr<nsAudioStreamRemote> mOwner;
+  float mVolume;
+};
+
+class AudioDrainEvent : public nsRunnable
+{
+ public:
+  AudioDrainEvent(nsAudioStreamRemote* owner)
+  {
+    mOwner = owner;
+  }
+
+  NS_IMETHOD Run()
+  {
+    if (!mOwner->mAudioChild)
+      return NS_OK;
+
+    mOwner->mAudioChild->SendDrain();
+    return NS_OK;
+  }
+  
+  nsRefPtr<nsAudioStreamRemote> mOwner;
+};
+
+
+class AudioPauseEvent : public nsRunnable
+{
+ public:
+  AudioPauseEvent(nsAudioStreamRemote* owner, PRBool pause)
+  {
+    mOwner = owner;
+    mPause = pause;
+  }
+
+  NS_IMETHOD Run()
+  {
+    if (!mOwner->mAudioChild)
+      return NS_OK;
+
+    if (mPause)
+      mOwner->mAudioChild->SendPause();
+    else
+      mOwner->mAudioChild->SendResume();
+
+    return NS_OK;
+  }
+  
+  nsRefPtr<nsAudioStreamRemote> mOwner;
+  PRBool mPause;
+};
+
+
+#endif // MOZ_IPC
+
+
 void nsAudioStream::InitLibrary()
 {
 #ifdef PR_LOGGING
   gAudioStreamLog = PR_NewLogModule("nsAudioStream");
 #endif
+
+#ifdef MOZ_IPC
+  // We only need this thread in the main process.
+  if (XRE_GetProcessType() == GeckoProcessType_Default) {
+      NS_NewThread(&gAudioPlaybackThread);
+  }
+#endif
 }
 
 void nsAudioStream::ShutdownLibrary()
 {
+#ifdef MOZ_IPC
+  NS_IF_RELEASE(gAudioPlaybackThread);
+#endif
 }
 
-nsAudioStream::nsAudioStream() :
+
+nsIThread *
+nsAudioStream::GetGlobalThread()
+{
+#ifdef MOZ_IPC
+  NS_IF_ADDREF(gAudioPlaybackThread);
+  return gAudioPlaybackThread;
+#else
+  return nsnull;
+#endif
+}
+
+nsAudioStream* nsAudioStream::AllocateStream()
+{
+  nsAudioStream* result = nsnull;
+
+#ifdef MOZ_IPC
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    return new nsAudioStreamRemote();
+  }
+#endif
+  return new nsAudioStreamLocal();
+}
+
+nsAudioStreamLocal::nsAudioStreamLocal() :
   mVolume(1.0),
   mAudioHandle(0),
   mRate(0),
   mChannels(0),
   mFormat(FORMAT_S16_LE),
   mPaused(PR_FALSE),
   mInError(PR_FALSE)
 {
 }
 
-nsAudioStream::~nsAudioStream()
-{
-  Shutdown();
-}
+nsAudioStreamLocal::~nsAudioStreamLocal(){}
 
-nsresult nsAudioStream::Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat)
+NS_IMPL_THREADSAFE_ISUPPORTS0(nsAudioStreamLocal)
+
+nsresult nsAudioStreamLocal::Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat)
 {
   mRate = aRate;
   mChannels = aNumChannels;
   mFormat = aFormat;
+
   if (sa_stream_create_pcm(reinterpret_cast<sa_stream_t**>(&mAudioHandle),
                            NULL, 
                            SA_MODE_WRONLY, 
                            SA_PCM_FORMAT_S16_NE,
                            aRate,
                            aNumChannels) != SA_SUCCESS) {
     mAudioHandle = nsnull;
     mInError = PR_TRUE;
-    PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_create_pcm error"));
+    PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStreamLocal: sa_stream_create_pcm error"));
     return NS_ERROR_FAILURE;
   }
   
   if (sa_stream_open(static_cast<sa_stream_t*>(mAudioHandle)) != SA_SUCCESS) {
     sa_stream_destroy(static_cast<sa_stream_t*>(mAudioHandle));
     mAudioHandle = nsnull;
     mInError = PR_TRUE;
-    PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_open error"));
+    PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStreamLocal: sa_stream_open error"));
     return NS_ERROR_FAILURE;
   }
   mInError = PR_FALSE;
+
   return NS_OK;
 }
 
-void nsAudioStream::Shutdown()
+void nsAudioStreamLocal::Shutdown()
 {
-  if (!mAudioHandle) 
+  if (!mAudioHandle)
     return;
 
   sa_stream_destroy(static_cast<sa_stream_t*>(mAudioHandle));
   mAudioHandle = nsnull;
   mInError = PR_TRUE;
 }
 
-nsresult nsAudioStream::Write(const void* aBuf, PRUint32 aCount, PRBool aBlocking)
+nsresult nsAudioStreamLocal::Write(const void* aBuf, PRUint32 aCount, PRBool aBlocking)
 {
   NS_ABORT_IF_FALSE(aCount % mChannels == 0,
                     "Buffer size must be divisible by channel count");
   NS_ASSERTION(!mPaused, "Don't write audio when paused, you'll block");
 
   if (mInError)
     return NS_ERROR_FAILURE;
 
@@ -194,100 +468,99 @@ nsresult nsAudioStream::Write(const void
         count = available;
       }
     }
 
     if (sa_stream_write(static_cast<sa_stream_t*>(mAudioHandle),
                         s_data.get(),
                         count * sizeof(short)) != SA_SUCCESS)
     {
-      PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_write error"));
+      PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStreamLocal: sa_stream_write error"));
       mInError = PR_TRUE;
       return NS_ERROR_FAILURE;
     }
   }
   return NS_OK;
 }
 
-PRUint32 nsAudioStream::Available()
+PRUint32 nsAudioStreamLocal::Available()
 {
   // If the audio backend failed to open, lie and say we'll accept some
   // data.
   if (mInError)
     return FAKE_BUFFER_SIZE;
 
   size_t s = 0; 
   if (sa_stream_get_write_size(static_cast<sa_stream_t*>(mAudioHandle), &s) != SA_SUCCESS)
     return 0;
 
   return s / sizeof(short);
 }
 
-void nsAudioStream::SetVolume(float aVolume)
+void nsAudioStreamLocal::SetVolume(float aVolume)
 {
   NS_ASSERTION(aVolume >= 0.0 && aVolume <= 1.0, "Invalid volume");
 #if defined(SA_PER_STREAM_VOLUME)
   if (sa_stream_set_volume_abs(static_cast<sa_stream_t*>(mAudioHandle), aVolume) != SA_SUCCESS) {
-    PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_set_volume_abs error"));
+    PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStreamLocal: sa_stream_set_volume_abs error"));
     mInError = PR_TRUE;
   }
 #else
   mVolume = aVolume;
 #endif
 }
 
-void nsAudioStream::Drain()
+void nsAudioStreamLocal::Drain()
 {
   if (mInError)
     return;
 
   // Write any remaining unwritten sound data in the overflow buffer
   if (!mBufferOverflow.IsEmpty()) {
     if (sa_stream_write(static_cast<sa_stream_t*>(mAudioHandle),
                         mBufferOverflow.Elements(),
                         mBufferOverflow.Length() * sizeof(short)) != SA_SUCCESS)
-      PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_write error"));
+      PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStreamLocal: sa_stream_write error"));
       mInError = PR_TRUE;
       return;
   }
 
   int r = sa_stream_drain(static_cast<sa_stream_t*>(mAudioHandle));
   if (r != SA_SUCCESS && r != SA_ERROR_INVALID) {
-    PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_drain error"));
+    PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStreamLocal: sa_stream_drain error"));
     mInError = PR_TRUE;
   }
 }
 
-void nsAudioStream::Pause()
+void nsAudioStreamLocal::Pause()
 {
   if (mInError)
     return;
   mPaused = PR_TRUE;
   sa_stream_pause(static_cast<sa_stream_t*>(mAudioHandle));
 }
 
-void nsAudioStream::Resume()
+void nsAudioStreamLocal::Resume()
 {
   if (mInError)
     return;
   mPaused = PR_FALSE;
   sa_stream_resume(static_cast<sa_stream_t*>(mAudioHandle));
 }
 
-PRInt64 nsAudioStream::GetPosition()
+PRInt64 nsAudioStreamLocal::GetPosition()
 {
   PRInt64 sampleOffset = GetSampleOffset();
   if (sampleOffset >= 0) {
     return ((MILLISECONDS_PER_SECOND * sampleOffset) / mRate / mChannels);
   }
-
   return -1;
 }
 
-PRInt64 nsAudioStream::GetSampleOffset()
+PRInt64 nsAudioStreamLocal::GetSampleOffset()
 {
   if (mInError) {
     return -1;
   }
  
   sa_position_t positionType = SA_POSITION_WRITE_SOFTWARE;
 #if defined(XP_WIN)
   positionType = SA_POSITION_WRITE_HARDWARE;
@@ -295,8 +568,146 @@ PRInt64 nsAudioStream::GetSampleOffset()
   PRInt64 position = 0;
   if (sa_stream_get_position(static_cast<sa_stream_t*>(mAudioHandle),
                              positionType, &position) == SA_SUCCESS) {
     return position / sizeof(short);
   }
 
   return -1;
 }
+
+PRBool nsAudioStreamLocal::IsPaused()
+{
+  return mPaused;
+}
+
+#ifdef MOZ_IPC
+
+nsAudioStreamRemote::nsAudioStreamRemote()
+ : mAudioChild(NULL),
+   mFormat(FORMAT_S16_LE),
+   mRate(0),
+   mChannels(0),
+   mPaused(PR_FALSE),
+   mBytesPerSample(1)
+{}
+
+nsAudioStreamRemote::~nsAudioStreamRemote()
+{}
+
+NS_IMPL_THREADSAFE_ISUPPORTS0(nsAudioStreamRemote)
+
+nsresult 
+nsAudioStreamRemote::Init(PRInt32 aNumChannels,
+                          PRInt32 aRate,
+                          SampleFormat aFormat)
+{
+  mRate = aRate;
+  mChannels = aNumChannels;
+  mFormat = aFormat;
+
+  switch (mFormat) {
+    case FORMAT_U8: {
+      mBytesPerSample = sizeof(PRUint8);
+      break;
+    }
+    case FORMAT_S16_LE: {
+      mBytesPerSample = sizeof(short);
+      break;
+    }
+    case FORMAT_FLOAT32: {
+      mBytesPerSample = sizeof(float);
+    }
+  }
+
+  nsCOMPtr<nsIRunnable> event = new AudioInitEvent(this);
+  NS_DispatchToMainThread(event);
+  return NS_OK;
+}
+
+void
+nsAudioStreamRemote::Shutdown()
+{
+  nsCOMPtr<nsIRunnable> event = new AudioShutdownEvent(this);
+  NS_DispatchToMainThread(event);
+}
+
+nsresult
+nsAudioStreamRemote::Write(const void* aBuf,
+                           PRUint32 aCount,
+                           PRBool aBlocking)
+{
+  nsCOMPtr<nsIRunnable> event = new AudioWriteEvent(this,
+                                                    aBuf,
+                                                    aCount,
+                                                    mBytesPerSample);
+  NS_DispatchToMainThread(event);
+  return NS_OK;
+}
+
+PRUint32
+nsAudioStreamRemote::Available()
+{
+  return FAKE_BUFFER_SIZE;
+}
+
+void
+nsAudioStreamRemote::SetVolume(float aVolume)
+{
+  nsCOMPtr<nsIRunnable> event = new AudioSetVolumeEvent(this, aVolume);
+  NS_DispatchToMainThread(event);
+}
+
+void
+nsAudioStreamRemote::Drain()
+{
+  nsCOMPtr<nsIRunnable> event = new AudioDrainEvent(this);
+  NS_DispatchToMainThread(event, NS_DISPATCH_SYNC);
+}
+ 
+void
+nsAudioStreamRemote::Pause()
+{
+  mPaused = PR_TRUE;
+  nsCOMPtr<nsIRunnable> event = new AudioPauseEvent(this, PR_TRUE);
+  NS_DispatchToMainThread(event);
+}
+
+void
+nsAudioStreamRemote::Resume()
+{
+  mPaused = PR_FALSE;
+  nsCOMPtr<nsIRunnable> event = new AudioPauseEvent(this, PR_FALSE);
+  NS_DispatchToMainThread(event);
+}
+
+PRInt64 nsAudioStreamRemote::GetPosition()
+{
+  PRInt64 sampleOffset = GetSampleOffset();
+  if (sampleOffset >= 0) {
+    return ((MILLISECONDS_PER_SECOND * sampleOffset) / mRate / mChannels);
+  }
+  return 0;
+}
+
+PRInt64
+nsAudioStreamRemote::GetSampleOffset()
+{
+  if(!mAudioChild)
+    return 0;
+
+  PRInt64 offset = mAudioChild->GetLastKnownSampleOffset();
+  if (offset == -1)
+    return 0;
+
+  PRInt64 time   = mAudioChild->GetLastKnownSampleOffsetTime();
+  PRInt64 result = offset + (mRate * mChannels * (PR_IntervalNow() - time) / MILLISECONDS_PER_SECOND);
+
+  return result;
+}
+
+PRBool
+nsAudioStreamRemote::IsPaused()
+{
+  return mPaused;
+}
+
+#endif // MOZ_IPC
--- a/content/media/nsAudioStream.h
+++ b/content/media/nsAudioStream.h
@@ -34,99 +34,86 @@
  * 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(nsAudioStream_h_)
 #define nsAudioStream_h_
 
 #include "nscore.h"
-#include "prlog.h"
-#include "nsTArray.h"
+#include "nsISupportsImpl.h"
+#include "nsIThread.h"
 
-extern PRLogModuleInfo* gAudioStreamLog;
+class nsAudioStream : public nsISupports
+{
+public:
 
-class nsAudioStream 
-{
- public:
   enum SampleFormat
   {
     FORMAT_U8,
     FORMAT_S16_LE,
     FORMAT_FLOAT32
   };
 
   // Initialize Audio Library. Some Audio backends require initializing the
   // library before using it. 
   static void InitLibrary();
 
   // Shutdown Audio Library. Some Audio backends require shutting down the
   // library after using it.
   static void ShutdownLibrary();
 
-  nsAudioStream();
-  ~nsAudioStream();
+  // Thread, usually for MOZ_IPC handling, that is shared between audio streams.
+  // This may return null in the child process
+  static nsIThread *GetGlobalThread();
+
+  // AllocateStream will return either a local stream or a remoted stream
+  // depending on where you call it from.  If MOZ_IPC is enabled, and you
+  // call this from a child process, you may recieve an implementation which
+  // forwards to a compositing process.
+  static nsAudioStream* AllocateStream();
 
   // 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, SampleFormat aFormat);
+  virtual nsresult Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat) = 0;
 
   // Closes the stream. All future use of the stream is an error.
-  void Shutdown();
+  virtual void Shutdown() = 0;
 
   // Write sound data to the audio hardware.  aBuf is an array of samples in
   // the format specified by mFormat of length aCount.  aCount should be
   // evenly divisible by the number of channels in this audio stream.
   // When aBlocking is PR_TRUE, we'll block until the write has completed,
   // otherwise we'll buffer any data we can't write immediately, and write
   // it in a later call.
-  nsresult Write(const void* aBuf, PRUint32 aCount, PRBool aBlocking);
+  virtual nsresult Write(const void* aBuf, PRUint32 aCount, PRBool aBlocking) = 0;
 
   // Return the number of sound samples that can be written to the audio device
   // without blocking.
-  PRUint32 Available();
+  virtual PRUint32 Available() = 0;
 
   // Set the current volume of the audio playback. This is a value from
   // 0 (meaning muted) to 1 (meaning full volume).
-  void SetVolume(float aVolume);
+  virtual void SetVolume(float aVolume) = 0;
 
   // Block until buffered audio data has been consumed.
-  void Drain();
+  virtual void Drain() = 0;
 
   // Pause audio playback
-  void Pause();
+  virtual void Pause() = 0;
 
   // Resume audio playback
-  void Resume();
+  virtual void Resume() = 0;
 
   // Return the position in milliseconds of the sample being played by the
   // audio hardware.
-  PRInt64 GetPosition();
+  virtual PRInt64 GetPosition() = 0;
 
   // Return the position, measured in samples played since the start, by
   // the audio hardware.
-  PRInt64 GetSampleOffset();
+  virtual PRInt64 GetSampleOffset() = 0;
 
   // Returns PR_TRUE when the audio stream is paused.
-  PRBool IsPaused() { return mPaused; }
-
- private:
-  double mVolume;
-  void* mAudioHandle;
-  int mRate;
-  int mChannels;
-
-  SampleFormat mFormat;
+  virtual PRBool IsPaused() = 0;
+};
 
-  // When a Write() request is made, and the number of samples
-  // requested to be written exceeds the buffer size of the audio
-  // backend, the remaining samples are stored in this variable. They
-  // will be written on the next Write() request.
-  nsTArray<short> mBufferOverflow;
-
-  // PR_TRUE if this audio stream is paused.
-  PRPackedBool mPaused;
-
-  // PR_TRUE if this stream has encountered an error.
-  PRPackedBool mInError;
-};
 #endif
--- a/content/media/nsBuiltinDecoderStateMachine.cpp
+++ b/content/media/nsBuiltinDecoderStateMachine.cpp
@@ -156,16 +156,22 @@ nsBuiltinDecoderStateMachine::nsBuiltinD
   mEventManager(aDecoder)
 {
   MOZ_COUNT_CTOR(nsBuiltinDecoderStateMachine);
 }
 
 nsBuiltinDecoderStateMachine::~nsBuiltinDecoderStateMachine()
 {
   MOZ_COUNT_DTOR(nsBuiltinDecoderStateMachine);
+
+  if (mAudioStream) {
+    MonitorAutoEnter mon(mDecoder->GetMonitor());
+    mAudioStream->Shutdown();
+    mAudioStream = nsnull;
+  }
 }
 
 PRBool nsBuiltinDecoderStateMachine::HasFutureAudio() const {
   mDecoder->GetMonitor().AssertCurrentThreadIn();
   NS_ASSERTION(HasAudio(), "Should only call HasFutureAudio() when we have audio");
   // We've got audio ready to play if:
   // 1. We've not completed playback of audio, and
   // 2. we either have more than the threshold of decoded audio available, or
@@ -606,17 +612,17 @@ void nsBuiltinDecoderStateMachine::Start
     MonitorAutoEnter audioMon(mAudioMonitor);
     if (mAudioStream) {
       // We have an audiostream, so it must have been paused the last time
       // StopPlayback() was called.
       mAudioStream->Resume();
     } else {
       // No audiostream, create one.
       const nsVideoInfo& info = mReader->GetInfo();
-      mAudioStream = new nsAudioStream();
+      mAudioStream = nsAudioStream::AllocateStream();
       mAudioStream->Init(info.mAudioChannels,
                          info.mAudioRate,
                          MOZ_SOUND_DATA_FORMAT);
       mAudioStream->SetVolume(mVolume);
     }
   }
   mPlayStartTime = TimeStamp::Now();
   mDecoder->GetMonitor().NotifyAll();
--- a/content/media/nsBuiltinDecoderStateMachine.h
+++ b/content/media/nsBuiltinDecoderStateMachine.h
@@ -421,17 +421,17 @@ protected:
   // Position to seek to in milliseconds when the seek state transition occurs.
   // The decoder monitor lock must be obtained before reading or writing
   // this value. Accessed on main and state machine thread.
   PRInt64 mSeekTime;
 
   // The audio stream resource. Used on the state machine, audio, and main
   // threads. You must hold the mAudioMonitor, and must NOT hold the decoder
   // monitor when using the audio stream!
-  nsAutoPtr<nsAudioStream> mAudioStream;
+  nsRefPtr<nsAudioStream> mAudioStream;
 
   // The reader, don't call its methods with the decoder monitor held.
   // This is created in the play state machine's constructor, and destroyed
   // in the play state machine's destructor.
   nsAutoPtr<nsBuiltinDecoderReader> mReader;
 
   // The time of the current frame in milliseconds. This is referenced from
   // 0 which is the initial playback position. Set by the state machine
--- a/content/media/wave/nsWaveDecoder.cpp
+++ b/content/media/wave/nsWaveDecoder.cpp
@@ -290,17 +290,17 @@ private:
   // the state machine.  The stream is threadsafe, but is only used on the
   // playback thread except for create, open, and cancel, which are called
   // from the main thread.
   nsMediaStream* mStream;
 
   // Our audio stream.  Created on demand when entering playback state.  It
   // is destroyed when seeking begins and will not be reinitialized until
   // playback resumes, so it is possible for this to be null.
-  nsAutoPtr<nsAudioStream> mAudioStream;
+  nsRefPtr<nsAudioStream> mAudioStream;
 
   // Maximum time to spend waiting for data during buffering.
   TimeDuration mBufferingWait;
 
   // Machine time that buffering began, used with mBufferingWait to time out
   // buffering.
   TimeStamp mBufferingStart;
 
@@ -901,17 +901,17 @@ nsWaveStateMachine::ChangeState(State aS
 #endif
   mState = aState;
   monitor.NotifyAll();
 }
 
 void
 nsWaveStateMachine::OpenAudioStream()
 {
-  mAudioStream = new nsAudioStream();
+  mAudioStream = nsAudioStream::AllocateStream();
   if (!mAudioStream) {
     LOG(PR_LOG_ERROR, ("Could not create audio stream"));
   } else {
     NS_ABORT_IF_FALSE(mMetadataValid,
                       "Attempting to initialize audio stream with invalid metadata");
     mAudioStream->Init(mChannels, mSampleRate, mSampleFormat);
     mAudioStream->SetVolume(mInitialVolume);
   }
new file mode 100644
--- /dev/null
+++ b/dom/ipc/AudioChild.cpp
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
+/* ***** 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 Audio IPC
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Doug Turner <dougt@mozilla.com>
+ *
+ * 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 "mozilla/dom/AudioChild.h"
+
+namespace mozilla {
+namespace dom {
+
+AudioChild::AudioChild()
+  : mLastSampleOffset(-1),
+    mLastSampleOffsetTime(0)
+{
+  MOZ_COUNT_CTOR(AudioChild);
+}
+
+AudioChild::~AudioChild()
+{
+  MOZ_COUNT_DTOR(AudioChild);
+}
+
+bool
+AudioChild::RecvSampleOffsetUpdate(const PRInt64& offset,
+                                   const PRInt64& time)
+{
+  mLastSampleOffset = offset;
+  mLastSampleOffsetTime = time;
+  return true;
+}
+
+PRInt64
+AudioChild::GetLastKnownSampleOffset()
+{
+  return mLastSampleOffset;
+}
+
+PRInt64
+AudioChild::GetLastKnownSampleOffsetTime()
+{
+  return mLastSampleOffsetTime;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/ipc/AudioChild.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
+/* ***** 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 Audio IPC
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Doug Turner <dougt@mozilla.com>
+ *
+ * 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 ***** */
+
+#ifndef mozilla_dom_AudioChild_h
+#define mozilla_dom_AudioChild_h
+
+#include "mozilla/dom/PAudioChild.h"
+
+namespace mozilla {
+namespace dom {
+
+class AudioChild : public PAudioChild
+{
+ public:
+    AudioChild();
+    virtual ~AudioChild();
+    virtual bool RecvSampleOffsetUpdate(const PRInt64&, const PRInt64&);
+    
+    PRInt64 GetLastKnownSampleOffset();
+    PRInt64 GetLastKnownSampleOffsetTime();
+ private:
+    PRInt64 mLastSampleOffset, mLastSampleOffsetTime;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/dom/ipc/AudioParent.cpp
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
+/* ***** 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 Audio IPC
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Doug Turner <dougt@mozilla.com>
+ *
+ * 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 "mozilla/dom/AudioParent.h"
+#include "nsThreadUtils.h"
+
+// C++ file contents
+namespace mozilla {
+namespace dom {
+
+class AudioWriteEvent : public nsRunnable
+{
+ public:
+  AudioWriteEvent(nsAudioStream* owner, nsCString data, PRUint32 count)
+  {
+    mOwner = owner;
+    mData  = data;
+    mCount = count;
+  }
+
+  NS_IMETHOD Run()
+  {
+    mOwner->Write(mData.get(), mCount, true);
+    return NS_OK;
+  }
+
+ private:
+    nsRefPtr<nsAudioStream> mOwner;
+    nsCString mData;
+    PRUint32  mCount;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(AudioParent, nsITimerCallback)
+
+nsresult
+AudioParent::Notify(nsITimer* timer)
+{
+  if (!mStream) {
+    timer->Cancel();
+    return NS_ERROR_FAILURE;
+  }
+
+  PRInt64 offset = mStream->GetSampleOffset();
+  SendSampleOffsetUpdate(offset, PR_IntervalNow());
+  return NS_OK;
+}
+bool
+AudioParent::RecvWrite(
+        const nsCString& data,
+        const PRUint32& count)
+{
+  nsCOMPtr<nsIRunnable> event = new AudioWriteEvent(mStream, data, count);
+  nsCOMPtr<nsIThread> thread = nsAudioStream::GetGlobalThread();
+  thread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
+  return true;
+}
+    
+bool
+AudioParent::RecvSetVolume(const float& aVolume)
+{
+  if (mStream)
+    mStream->SetVolume(aVolume);
+  return true;
+}
+
+bool
+AudioParent::RecvDrain()
+{
+  if (mStream)
+    mStream->Drain();
+  return true;
+}
+
+bool
+AudioParent::RecvPause()
+{
+  if (mStream)
+    mStream->Pause();
+  return true;
+}
+
+bool
+AudioParent::RecvResume()
+{
+  if (mStream)
+    mStream->Resume();
+  return true;
+}
+
+bool
+AudioParent::Recv__delete__()
+{
+  if (mStream) {
+    mStream->Shutdown();
+    mStream = nsnull;
+  }
+
+  if (mTimer) {
+    mTimer->Cancel();
+    mTimer = nsnull;
+  }
+  return true;
+}
+
+AudioParent::AudioParent(PRInt32 aNumChannels, PRInt32 aRate, PRInt32 aFormat)
+{
+  mStream = nsAudioStream::AllocateStream();
+  if (mStream)
+    mStream->Init(aNumChannels,
+                  aRate,
+                  (nsAudioStream::SampleFormat) aFormat);
+  if (!mStream)
+    return; 
+
+  mTimer = do_CreateInstance("@mozilla.org/timer;1");
+  mTimer->InitWithCallback(this, 1000, nsITimer::TYPE_REPEATING_SLACK);
+}
+
+AudioParent::~AudioParent()
+{
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/ipc/AudioParent.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
+/* ***** 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 Audio IPC
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Doug Turner <dougt@mozilla.com>
+ *
+ * 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 ***** */
+
+#ifndef mozilla_dom_AudioParent_h
+#define mozilla_dom_AudioParent_h
+
+#include "mozilla/dom/PAudioParent.h"
+#include "nsAudioStream.h"
+#include "nsITimer.h"
+
+namespace mozilla {
+namespace dom {
+class AudioParent : public PAudioParent, public nsITimerCallback
+{
+ public:
+
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSITIMERCALLBACK
+
+    virtual bool
+    RecvWrite(
+            const nsCString& data,
+            const PRUint32& count);
+
+    virtual bool
+    RecvSetVolume(const float& aVolume);
+
+    virtual bool
+    RecvDrain();
+
+    virtual bool
+    RecvPause();
+
+    virtual bool
+    RecvResume();
+
+    virtual bool
+    Recv__delete__();
+
+    AudioParent(PRInt32 aNumChannels, PRInt32 aRate, PRInt32 aFormat);
+    virtual ~AudioParent();
+
+    nsRefPtr<nsAudioStream> mStream;
+    nsCOMPtr<nsITimer> mTimer;
+
+};
+} // namespace dom
+} // namespace mozilla
+
+#endif
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -42,23 +42,26 @@
 #endif
 
 #ifdef MOZ_WIDGET_QT
 #include "nsQAppInstance.h"
 #endif
 
 #include "ContentChild.h"
 #include "TabChild.h"
+#include "AudioChild.h"
 
 #include "mozilla/ipc/TestShellChild.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/ipc/XPCShellEnvironment.h"
 #include "mozilla/jsipc/PContextWrapperChild.h"
 #include "mozilla/dom/ExternalHelperAppChild.h"
 
+#include "nsAudioStream.h"
+
 #include "nsIObserverService.h"
 #include "nsTObserverArray.h"
 #include "nsIObserver.h"
 #include "nsIPrefService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsXULAppAPI.h"
 #include "nsWeakReference.h"
 #include "nsIScriptError.h"
@@ -282,16 +285,32 @@ ContentChild::DeallocPTestShell(PTestShe
 
 bool
 ContentChild::RecvPTestShellConstructor(PTestShellChild* actor)
 {
     actor->SendPContextWrapperConstructor()->SendPObjectWrapperConstructor(true);
     return true;
 }
 
+PAudioChild*
+ContentChild::AllocPAudio(const PRInt32& numChannels,
+                          const PRInt32& rate,
+                          const PRInt32& format)
+{
+    PAudioChild *child = new AudioChild();
+    return child;
+}
+
+bool
+ContentChild::DeallocPAudio(PAudioChild* doomed)
+{
+    delete doomed;
+    return true;
+}
+
 PNeckoChild* 
 ContentChild::AllocPNecko()
 {
     return new NeckoChild();
 }
 
 bool 
 ContentChild::DeallocPNecko(PNeckoChild* necko)
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -78,16 +78,21 @@ public:
 
     virtual PBrowserChild* AllocPBrowser(const PRUint32& aChromeFlags);
     virtual bool DeallocPBrowser(PBrowserChild*);
 
     virtual PTestShellChild* AllocPTestShell();
     virtual bool DeallocPTestShell(PTestShellChild*);
     virtual bool RecvPTestShellConstructor(PTestShellChild*);
 
+    virtual PAudioChild* AllocPAudio(const PRInt32&,
+                                     const PRInt32&,
+                                     const PRInt32&);
+    virtual bool DeallocPAudio(PAudioChild*);
+
     virtual PNeckoChild* AllocPNecko();
     virtual bool DeallocPNecko(PNeckoChild*);
 
     virtual PExternalHelperAppChild *AllocPExternalHelperApp(
             const IPC::URI& uri,
             const nsCString& aMimeContentType,
             const nsCString& aContentDisposition,
             const bool& aForceSave,
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -61,16 +61,17 @@
 #include "nsCExternalHandlerService.h"
 #include "nsFrameMessageManager.h"
 #include "nsIAlertsService.h"
 #include "nsToolkitCompsCID.h"
 #include "nsIDOMGeoGeolocation.h"
 #include "nsIConsoleService.h"
 #include "nsIScriptError.h"
 #include "nsConsoleMessage.h"
+#include "AudioParent.h"
 
 #ifdef MOZ_PERMISSIONS
 #include "nsPermissionManager.h"
 #endif
 
 #include "mozilla/dom/ExternalHelperAppParent.h"
 #include "nsAccelerometer.h"
 
@@ -356,16 +357,34 @@ ContentParent::AllocPTestShell()
 }
 
 bool
 ContentParent::DeallocPTestShell(PTestShellParent* shell)
 {
   delete shell;
   return true;
 }
+ 
+PAudioParent*
+ContentParent::AllocPAudio(const PRInt32& numChannels,
+                           const PRInt32& rate,
+                           const PRInt32& format)
+{
+    AudioParent *parent = new AudioParent(numChannels, rate, format);
+    parent->AddRef();
+    return parent;
+}
+
+bool
+ContentParent::DeallocPAudio(PAudioParent* doomed)
+{
+    AudioParent *parent = static_cast<AudioParent*>(doomed);
+    NS_RELEASE(parent);
+    return true;
+}
 
 PNeckoParent* 
 ContentParent::AllocPNecko()
 {
     return new NeckoParent();
 }
 
 bool 
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -112,16 +112,21 @@ private:
     virtual ~ContentParent();
 
     virtual PBrowserParent* AllocPBrowser(const PRUint32& aChromeFlags);
     virtual bool DeallocPBrowser(PBrowserParent* frame);
 
     virtual PTestShellParent* AllocPTestShell();
     virtual bool DeallocPTestShell(PTestShellParent* shell);
 
+    virtual PAudioParent* AllocPAudio(const PRInt32&,
+                                     const PRInt32&,
+                                     const PRInt32&);
+    virtual bool DeallocPAudio(PAudioParent*);
+
     virtual PNeckoParent* AllocPNecko();
     virtual bool DeallocPNecko(PNeckoParent* necko);
 
     virtual PExternalHelperAppParent* AllocPExternalHelperApp(
             const IPC::URI& uri,
             const nsCString& aMimeContentType,
             const nsCString& aContentDisposition,
             const bool& aForceSave,
--- a/dom/ipc/Makefile.in
+++ b/dom/ipc/Makefile.in
@@ -47,24 +47,28 @@ LIBXUL_LIBRARY = 1
 FORCE_STATIC_LIB = 1
 EXPORT_LIBRARY = 1
 
 EXPORTS = TabMessageUtils.h PCOMContentPermissionRequestChild.h
 
 EXPORTS_NAMESPACES = mozilla/dom
 
 EXPORTS_mozilla/dom = \
+  AudioChild.h \
+  AudioParent.h \
   ContentChild.h \
   ContentParent.h \
   ContentProcess.h \
   TabParent.h \
   TabChild.h \
   $(NULL)
 
 CPPSRCS = \
+  AudioChild.cpp \
+  AudioParent.cpp \
   ContentProcess.cpp \
   ContentParent.cpp \
   ContentChild.cpp \
   TabParent.cpp \
   TabChild.cpp \
   TabMessageUtils.cpp \
   $(NULL)
 
new file mode 100644
--- /dev/null
+++ b/dom/ipc/PAudio.ipdl
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
+/* ***** 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 Audio IPC
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Doug Turner <dougt@mozilla.com>
+ *
+ * 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 protocol PContent;
+
+namespace mozilla {
+namespace dom {
+
+sync protocol PAudio
+{
+  manager PContent;
+
+parent:
+
+  __delete__();
+
+  Write(nsCString data, PRUint32 count);
+
+  SetVolume(float aVolume);
+
+  sync Drain();
+
+  Pause();
+  Resume();
+
+ child:
+
+  SampleOffsetUpdate(PRInt64 offset, PRInt64 time);
+
+
+};
+
+} // namespace dom
+} // namespace mozilla
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -31,16 +31,17 @@
  * 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 protocol PAudio;
 include protocol PBrowser;
 include protocol PTestShell;
 include protocol PNecko;
 include protocol PExternalHelperApp;
 
 include "mozilla/chrome/RegistryMessageUtils.h";
 include "mozilla/net/NeckoMessageUtils.h";
 
@@ -56,16 +57,17 @@ using OverrideMapping;
 using IPC::URI;
 using IPC::Permission;
 
 namespace mozilla {
 namespace dom {
 
 rpc protocol PContent
 {
+    manages PAudio;
     manages PBrowser;
     manages PTestShell;
     manages PNecko;
     manages PExternalHelperApp;
 
 child:
     PBrowser(PRUint32 chromeFlags);
 
@@ -87,16 +89,18 @@ child:
     // nsIPermissionManager messages
     AddPermission(Permission permission);
 
     AccelerationChanged(double x, double y, double z);
 
 parent:
     PNecko();
 
+    PAudio(PRInt32 aNumChannels, PRInt32 aRate, PRInt32 aFormat);
+
     // Services remoting
 
     async StartVisitedQuery(URI uri);
     async VisitURI(URI uri, URI referrer, PRUint32 flags);
     async SetURITitle(URI uri, nsString title);
     
     // filepicker remoting
     sync ShowFilePicker(PRInt16 mode, PRInt16 selectedType, 
--- a/dom/ipc/ipdl.mk
+++ b/dom/ipc/ipdl.mk
@@ -30,14 +30,15 @@
 # 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 *****
 
 IPDLSRCS = \
+  PAudio.ipdl \
   PBrowser.ipdl \
   PContent.ipdl \
   PContentDialog.ipdl \
   PDocumentRenderer.ipdl \
   PContentPermissionRequest.ipdl \
   $(NULL)