Bug 612799 (2/2) - Remote Audio - Signal errors to the child AudioStream. r=kinetik
authorPaul ADENOT <paul@paul.cx>
Fri, 10 Jun 2011 18:26:06 +0200
changeset 70878 e109c624ae00
parent 70877 9dbd24f893ff
child 70879 635d71b87150
child 70881 e598a0c0eb1c
push id20435
push usereakhgari@mozilla.com
push dateFri, 10 Jun 2011 20:08:22 +0000
treeherdermozilla-central@28217403cd02 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik
bugs612799
milestone7.0a1
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 612799 (2/2) - Remote Audio - Signal errors to the child AudioStream. r=kinetik
content/media/nsAudioStream.cpp
content/media/nsAudioStream.h
dom/ipc/AudioChild.cpp
dom/ipc/AudioChild.h
dom/ipc/AudioParent.cpp
dom/ipc/AudioParent.h
dom/ipc/PAudio.ipdl
--- a/content/media/nsAudioStream.cpp
+++ b/content/media/nsAudioStream.cpp
@@ -84,20 +84,20 @@ class nsAudioStreamLocal : public nsAudi
 {
  public:
   NS_DECL_ISUPPORTS
 
   ~nsAudioStreamLocal();
   nsAudioStreamLocal();
 
   nsresult Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat);
-  void Shutdown();
+  nsresult Shutdown();
   nsresult Write(const void* aBuf, PRUint32 aCount, PRBool aBlocking);
   PRUint32 Available();
-  void SetVolume(double aVolume);
+  nsresult SetVolume(double aVolume);
   nsresult Drain();
   void Pause();
   void Resume();
   PRInt64 GetPosition();
   PRInt64 GetSampleOffset();
   PRBool IsPaused();
   PRInt32 GetMinWriteSamples();
 
@@ -128,20 +128,20 @@ class nsAudioStreamRemote : public nsAud
 {
  public:
   NS_DECL_ISUPPORTS
 
   nsAudioStreamRemote();
   ~nsAudioStreamRemote();
 
   nsresult Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat);
-  void Shutdown();
+  nsresult Shutdown();
   nsresult Write(const void* aBuf, PRUint32 aCount, PRBool aBlocking);
   PRUint32 Available();
-  void SetVolume(double aVolume);
+  nsresult SetVolume(double aVolume);
   nsresult Drain();
   void Pause();
   void Resume();
   PRInt64 GetPosition();
   PRInt64 GetSampleOffset();
   PRBool IsPaused();
   PRInt32 GetMinWriteSamples();
 
@@ -405,17 +405,18 @@ nsAudioStreamLocal::nsAudioStreamLocal()
 
 nsAudioStreamLocal::~nsAudioStreamLocal()
 {
   Shutdown();
 }
 
 NS_IMPL_THREADSAFE_ISUPPORTS0(nsAudioStreamLocal)
 
-nsresult nsAudioStreamLocal::Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat)
+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, 
@@ -435,24 +436,31 @@ nsresult nsAudioStreamLocal::Init(PRInt3
     PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStreamLocal: sa_stream_open error"));
     return NS_ERROR_FAILURE;
   }
   mInError = PR_FALSE;
 
   return NS_OK;
 }
 
-void nsAudioStreamLocal::Shutdown()
+nsresult
+nsAudioStreamLocal::Shutdown()
 {
   if (!mAudioHandle)
-    return;
+    return NS_ERROR_FAILURE;
 
-  sa_stream_destroy(static_cast<sa_stream_t*>(mAudioHandle));
+  if (sa_stream_destroy(static_cast<sa_stream_t*>(mAudioHandle)));
+  {
+    PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStreamLocal: sa_stream_destroy error"));
+    mInError = true;
+    return NS_ERROR_FAILURE;
+  }
   mAudioHandle = nsnull;
   mInError = PR_TRUE;
+  return NS_OK;
 }
 
 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");
 
@@ -542,27 +550,30 @@ PRUint32 nsAudioStreamLocal::Available()
 
   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 nsAudioStreamLocal::SetVolume(double aVolume)
+nsresult
+nsAudioStreamLocal::SetVolume(double 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, ("nsAudioStreamLocal: sa_stream_set_volume_abs error"));
     mInError = PR_TRUE;
+    return NS_ERROR_FAILURE;
   }
 #else
   mVolume = aVolume;
 #endif
+  return NS_OK;
 }
 
 nsresult nsAudioStreamLocal::Drain()
 {
   NS_ASSERTION(!mPaused, "Don't drain audio when paused, it won't finish!");
 
   if (mInError)
     return NS_ERROR_FAILURE;
@@ -687,32 +698,33 @@ nsAudioStreamRemote::Init(PRInt32 aNumCh
     }
   }
 
   nsCOMPtr<nsIRunnable> event = new AudioInitEvent(this);
   NS_DispatchToMainThread(event, NS_DISPATCH_SYNC);
   return NS_OK;
 }
 
-void
+nsresult
 nsAudioStreamRemote::Shutdown()
 {
   if (!mAudioChild)
-    return;
+    return NS_ERROR_FAILURE;
   nsCOMPtr<nsIRunnable> event = new AudioShutdownEvent(mAudioChild);
   NS_DispatchToMainThread(event);
   mAudioChild = nsnull;
+  return NS_OK;
 }
 
 nsresult
 nsAudioStreamRemote::Write(const void* aBuf,
                            PRUint32 aCount,
                            PRBool aBlocking)
 {
-  if (!mAudioChild)
+  if (!mAudioChild || mAudioChild->IsInError())
     return NS_ERROR_FAILURE;
   nsCOMPtr<nsIRunnable> event = new AudioWriteEvent(mAudioChild,
                                                     aBuf,
                                                     aCount,
                                                     mBytesPerSample);
   NS_DispatchToMainThread(event);
   return NS_OK;
 }
@@ -727,51 +739,52 @@ PRInt32 nsAudioStreamRemote::GetMinWrite
 {
   if (!mAudioChild)
     return -1;
   nsCOMPtr<nsIRunnable> event = new AudioMinWriteSampleEvent(mAudioChild);
   NS_DispatchToMainThread(event);
   return mAudioChild->WaitForMinWriteSample();
 }
 
-void
+nsresult
 nsAudioStreamRemote::SetVolume(double aVolume)
 {
-  if (!mAudioChild)
-    return;
+  if (!mAudioChild || mAudioChild->IsInError())
+    return NS_ERROR_FAILURE;
   nsCOMPtr<nsIRunnable> event = new AudioSetVolumeEvent(mAudioChild, aVolume);
   NS_DispatchToMainThread(event);
+  return NS_OK;
 }
 
 nsresult
 nsAudioStreamRemote::Drain()
 {
   if (!mAudioChild)
     return NS_ERROR_FAILURE;
   nsCOMPtr<nsIRunnable> event = new AudioDrainEvent(mAudioChild);
   NS_DispatchToMainThread(event);
   return mAudioChild->WaitForDrain();
 }
 
 void
 nsAudioStreamRemote::Pause()
 {
+  if (!mAudioChild || mAudioChild->IsInError())
+    return;
   mPaused = PR_TRUE;
-  if (!mAudioChild)
-    return;
   nsCOMPtr<nsIRunnable> event = new AudioPauseEvent(mAudioChild, PR_TRUE);
   NS_DispatchToMainThread(event);
 }
 
 void
 nsAudioStreamRemote::Resume()
 {
+  if (!mAudioChild || mAudioChild->IsInError())
+    return;
   mPaused = PR_FALSE;
-  if (!mAudioChild)
-    return;
   nsCOMPtr<nsIRunnable> event = new AudioPauseEvent(mAudioChild, PR_FALSE);
   NS_DispatchToMainThread(event);
 }
 
 PRInt64 nsAudioStreamRemote::GetPosition()
 {
   PRInt64 sampleOffset = GetSampleOffset();
   if (sampleOffset >= 0) {
--- a/content/media/nsAudioStream.h
+++ b/content/media/nsAudioStream.h
@@ -74,33 +74,33 @@ public:
   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).
   virtual nsresult Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat) = 0;
 
   // Closes the stream. All future use of the stream is an error.
-  virtual void Shutdown() = 0;
+  virtual nsresult 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.
   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.
   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).
-  virtual void SetVolume(double aVolume) = 0;
+  virtual nsresult SetVolume(double aVolume) = 0;
 
   // Block until buffered audio data has been consumed.
   virtual nsresult Drain() = 0;
 
   // Pause audio playback
   virtual void Pause() = 0;
 
   // Resume audio playback
--- a/dom/ipc/AudioChild.cpp
+++ b/dom/ipc/AudioChild.cpp
@@ -82,16 +82,23 @@ AudioChild::RecvDrainDone(const nsresult
 
   if (status == NS_OK)
     mDrained = PR_TRUE;
 
   mAudioReentrantMonitor.NotifyAll();
   return true;
 }
 
+bool
+AudioChild::RecvEnteringErrorState()
+{
+  mInError = PR_TRUE;
+  return true;
+}
+
 PRInt32
 AudioChild::WaitForMinWriteSample()
 {
   ReentrantMonitorAutoEnter mon(mAudioReentrantMonitor);
   // -2 : initial value
   while (mMinWriteSample == -2 && mIPCOpen)
     mAudioReentrantMonitor.Wait();
   return mMinWriteSample;
--- a/dom/ipc/AudioChild.h
+++ b/dom/ipc/AudioChild.h
@@ -50,32 +50,35 @@ class AudioChild : public PAudioChild
 {
  public:
     NS_IMETHOD_(nsrefcnt) AddRef();
     NS_IMETHOD_(nsrefcnt) Release();
 
     AudioChild();
     virtual ~AudioChild();
     virtual bool RecvSampleOffsetUpdate(const PRInt64&, const PRInt64&);
+    virtual bool RecvEnteringErrorState();
     virtual bool RecvDrainDone(const nsresult& status);
     virtual PRInt32 WaitForMinWriteSample();
     virtual bool RecvMinWriteSampleDone(const PRInt32& sampleCount);
     virtual nsresult WaitForDrain();
     virtual void ActorDestroy(ActorDestroyReason);
 
     PRInt64 GetLastKnownSampleOffset();
     PRInt64 GetLastKnownSampleOffsetTime();
 
     PRBool IsIPCOpen() { return mIPCOpen; };
+    PRBool IsInError() { return mInError; };
  private:
     nsAutoRefCnt mRefCnt;
     NS_DECL_OWNINGTHREAD
     PRInt64 mLastSampleOffset, mLastSampleOffsetTime;
     PRInt32 mMinWriteSample;
     mozilla::ReentrantMonitor mAudioReentrantMonitor;
     PRPackedBool mIPCOpen;
     PRPackedBool mDrained;
+    PRPackedBool mInError;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/ipc/AudioParent.cpp
+++ b/dom/ipc/AudioParent.cpp
@@ -43,31 +43,34 @@
 
 // C++ file contents
 namespace mozilla {
 namespace dom {
 
 class AudioWriteEvent : public nsRunnable
 {
  public:
-  AudioWriteEvent(nsAudioStream* owner, nsCString data, PRUint32 count)
+  AudioWriteEvent(AudioParent* parent, nsAudioStream* owner, nsCString data, PRUint32 count)
   {
+    mParent = parent;
     mOwner = owner;
     mData  = data;
     mCount = count;
   }
 
   NS_IMETHOD Run()
   {
-    mOwner->Write(mData.get(), mCount, true);
+    if (mOwner->Write(mData.get(), mCount, true) != NS_OK) 
+      mParent->EnteringErrorState();
     return NS_OK;
   }
 
  private:
     nsRefPtr<nsAudioStream> mOwner;
+    nsRefPtr<AudioParent> mParent;
     nsCString mData;
     PRUint32  mCount;
 };
 
 class AudioPauseEvent : public nsRunnable
 {
  public:
   AudioPauseEvent(nsAudioStream* owner, PRBool aPause)
@@ -211,17 +214,17 @@ AudioParent::Notify(nsITimer* timer)
 
 bool
 AudioParent::RecvWrite(
         const nsCString& data,
         const PRUint32& count)
 {
   if (!mStream)
     return false;
-  nsCOMPtr<nsIRunnable> event = new AudioWriteEvent(mStream, data, count);
+  nsCOMPtr<nsIRunnable> event = new AudioWriteEvent(this, mStream, data, count);
   nsCOMPtr<nsIThread> thread = mStream->GetThread();
   thread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
   return true;
 }
 
 bool
 AudioParent::RecvSetVolume(const float& aVolume)
 {
@@ -278,16 +281,23 @@ AudioParent::RecvResume()
 bool
 AudioParent::RecvShutdown()
 {
   Shutdown();
   unused << PAudioParent::Send__delete__(this);
   return true;
 }
 
+void
+AudioParent::EnteringErrorState()
+{
+  if (mIPCOpen)
+    PAudioParent::SendEnteringErrorState();
+}
+
 bool
 AudioParent::SendMinWriteSampleDone(PRInt32 minSamples)
 {
   if (mIPCOpen)
     return PAudioParent::SendMinWriteSampleDone(minSamples);
   return true;
 }
 
@@ -304,16 +314,17 @@ AudioParent::AudioParent(PRInt32 aNumCha
 {
   mStream = nsAudioStream::AllocateStream();
   NS_ASSERTION(mStream, "AudioStream allocation failed.");
   if (NS_FAILED(mStream->Init(aNumChannels,
                               aRate,
                               (nsAudioStream::SampleFormat) aFormat))) {
       NS_WARNING("AudioStream initialization failed.");
       mStream = nsnull;
+      EnteringErrorState();
       return;
   }
 
   mTimer = do_CreateInstance("@mozilla.org/timer;1");
   mTimer->InitWithCallback(this, 1000, nsITimer::TYPE_REPEATING_SLACK);
 }
 
 AudioParent::~AudioParent()
--- a/dom/ipc/AudioParent.h
+++ b/dom/ipc/AudioParent.h
@@ -81,16 +81,18 @@ class AudioParent : public PAudioParent,
 
     virtual bool
     SendDrainDone(nsresult status);
 
     AudioParent(PRInt32 aNumChannels, PRInt32 aRate, PRInt32 aFormat);
     virtual ~AudioParent();
     virtual void ActorDestroy(ActorDestroyReason);
 
+    void EnteringErrorState();
+
     nsRefPtr<nsAudioStream> mStream;
     nsCOMPtr<nsITimer> mTimer;
 
 private:
     void Shutdown();
 
     PRPackedBool mIPCOpen;
 };
--- a/dom/ipc/PAudio.ipdl
+++ b/dom/ipc/PAudio.ipdl
@@ -61,13 +61,14 @@ parent:
 
  child:
 
   __delete__();
 
   SampleOffsetUpdate(PRInt64 offset, PRInt64 time);
   MinWriteSampleDone(PRInt32 sampleCount);
   DrainDone(nsresult status);
-
+ 
+  EnteringErrorState();
 };
 
 } // namespace dom
 } // namespace mozilla