Bug 564734 - Ensure all audio is queued before draining on win32. r=kinetik
authorChris Pearce <chris@pearce.org.nz>
Thu, 13 May 2010 12:59:42 +1200
changeset 42253 3cf79d49588ef1a0f88a73ec0efe1a8444d76a66
parent 42252 fdef1b2f86adaca96a9c08002fd5bdbbe4a6f817
child 42254 2f2cff1c9bf2d0fe88237585b10fa0012d752d2b
push idunknown
push userunknown
push dateunknown
reviewerskinetik
bugs564734
milestone1.9.3a5pre
Bug 564734 - Ensure all audio is queued before draining on win32. r=kinetik
media/libsydneyaudio/README_MOZILLA
media/libsydneyaudio/bug564734-win32-drain.patch
media/libsydneyaudio/src/sydney_audio_waveapi.c
media/libsydneyaudio/update.sh
--- a/media/libsydneyaudio/README_MOZILLA
+++ b/media/libsydneyaudio/README_MOZILLA
@@ -24,8 +24,11 @@ a failed attempt to open the stream.
 
 bug495558_alsa_endian.patch adds support for big endian ALSA platforms.
 
 bug526411_latency.patch: reduce requested latency to 250ms to match OGGPLAY_AUDIO_OFFSET.
 
 sydney_aix.patch: Bug 499266 - add support for AIX 5.x
 
 bug562488_oss_destroy_crash.patch: Fix crash in OSS backend when destroying stream.
+
+bug564734-win32-drain.patch: Ensure we can drain audio stream when there's less
+than one block's worth of data left to be written. Also ensure that data is played.
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/media/libsydneyaudio/bug564734-win32-drain.patch
@@ -0,0 +1,149 @@
+diff --git a/media/libsydneyaudio/src/sydney_audio_waveapi.c b/media/libsydneyaudio/src/sydney_audio_waveapi.c
+--- a/media/libsydneyaudio/src/sydney_audio_waveapi.c
++++ b/media/libsydneyaudio/src/sydney_audio_waveapi.c
+@@ -121,16 +121,17 @@ struct sa_stream {
+ };
+ 
+ 
+ /** 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 writeBlock(sa_stream_t *s, WAVEHDR* current);
+ 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);
+ 
+ /** Normal way to open a PCM device */
+ int sa_stream_create_pcm(sa_stream_t **s, 
+@@ -320,20 +321,32 @@ int sa_stream_pause(sa_stream_t *s) {
+   
+   status = waveOutPause(s->hWaveOut);
+   HANDLE_WAVE_ERROR(status, "resuming audio playback");
+ 
+   s->playing = 0;
+ 
+   return SA_SUCCESS;
+ }
++
+ /** Block until all audio has been played */
+ int sa_stream_drain(sa_stream_t *s) {
++  int status;
++  WAVEHDR* current;
++
+   ERROR_IF_NO_INIT(s);
+-  
++
++  current = &(s->waveBlocks[s->waveCurrentBlock]);
++  if (current->dwUser) {
++    /* We've got pending audio which hasn't been written, we must write it to
++       the hardware, else it will never be played. */
++    status = writeBlock(s, current);
++    HANDLE_WAVE_ERROR(status, "writing audio to audio device");
++  }
++
+   if (!s->playing) {
+     return SA_ERROR_INVALID;
+   }
+ 
+   /* wait for all blocks to complete */
+   EnterCriticalSection(&(s->waveCriticalSection));
+   while(s->waveFreeBlockCount < BLOCK_COUNT) {
+     LeaveCriticalSection(&(s->waveCriticalSection));
+@@ -502,16 +515,48 @@ int closeAudio(sa_stream_t * s) {
+ 
+   s->playing = 0;
+ 
+   DeleteCriticalSection(&(s->waveCriticalSection));
+   CloseHandle(s->callbackEvent);
+   
+   return result;
+ }
++
++/**
++ * \brief - writes a WAVEHDR block of PCM audio samples to hardware.
++ * \param s - valid handle to opened sydney stream
++ * \param current - pointer to WAVEHDR storing audio samples to be played
++ * \return - completion status
++ */
++int writeBlock(sa_stream_t *s, WAVEHDR* current) {
++  int status;
++  ERROR_IF_NO_INIT(s);
++
++  current->dwBufferLength = current->dwUser;
++  /* 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));
++
++  /*
++   * point to the next block
++   */
++  (s->waveCurrentBlock)++;
++  (s->waveCurrentBlock) %= BLOCK_COUNT;		
++
++  s->playing = 1;
++
++  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) {    
+@@ -536,40 +581,27 @@ int writeAudio(sa_stream_t *s, LPSTR dat
+ 		  
+     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;      
++    remain = BLOCK_SIZE - current->dwUser;
+   	memcpy(current->lpData + current->dwUser, data, remain);
++    current->dwUser += 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));      
++
++    status = writeBlock(s, current);
+     HANDLE_WAVE_ERROR(status, "writing audio to audio device");
+-      
+-    EnterCriticalSection(&(s->waveCriticalSection));
+-    s->waveFreeBlockCount--;
+-    LeaveCriticalSection(&(s->waveCriticalSection));
+-
+-    /*
+-     * point to the next block
+-     */
+-    (s->waveCurrentBlock)++;
+-    (s->waveCurrentBlock) %= BLOCK_COUNT;		
+ 
+     current = &(s->waveBlocks[s->waveCurrentBlock]);
+     current->dwUser = 0;
+-
+-    s->playing = 1;
+   }
+   return SA_SUCCESS;
+ }
+ 
+ /**
+  * \brief - audio callback function called when next WAVE header is played by audio device
+  */
+ void CALLBACK waveOutProc(
--- a/media/libsydneyaudio/src/sydney_audio_waveapi.c
+++ b/media/libsydneyaudio/src/sydney_audio_waveapi.c
@@ -121,16 +121,17 @@ struct sa_stream {
 };
 
 
 /** 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 writeBlock(sa_stream_t *s, WAVEHDR* current);
 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);
 
 /** Normal way to open a PCM device */
 int sa_stream_create_pcm(sa_stream_t **s, 
@@ -320,20 +321,32 @@ int sa_stream_pause(sa_stream_t *s) {
   
   status = waveOutPause(s->hWaveOut);
   HANDLE_WAVE_ERROR(status, "resuming audio playback");
 
   s->playing = 0;
 
   return SA_SUCCESS;
 }
+
 /** Block until all audio has been played */
 int sa_stream_drain(sa_stream_t *s) {
+  int status;
+  WAVEHDR* current;
+
   ERROR_IF_NO_INIT(s);
-  
+
+  current = &(s->waveBlocks[s->waveCurrentBlock]);
+  if (current->dwUser) {
+    /* We've got pending audio which hasn't been written, we must write it to
+       the hardware, else it will never be played. */
+    status = writeBlock(s, current);
+    HANDLE_WAVE_ERROR(status, "writing audio to audio device");
+  }
+
   if (!s->playing) {
     return SA_ERROR_INVALID;
   }
 
   /* wait for all blocks to complete */
   EnterCriticalSection(&(s->waveCriticalSection));
   while(s->waveFreeBlockCount < BLOCK_COUNT) {
     LeaveCriticalSection(&(s->waveCriticalSection));
@@ -502,16 +515,48 @@ int closeAudio(sa_stream_t * s) {
 
   s->playing = 0;
 
   DeleteCriticalSection(&(s->waveCriticalSection));
   CloseHandle(s->callbackEvent);
   
   return result;
 }
+
+/**
+ * \brief - writes a WAVEHDR block of PCM audio samples to hardware.
+ * \param s - valid handle to opened sydney stream
+ * \param current - pointer to WAVEHDR storing audio samples to be played
+ * \return - completion status
+ */
+int writeBlock(sa_stream_t *s, WAVEHDR* current) {
+  int status;
+  ERROR_IF_NO_INIT(s);
+
+  current->dwBufferLength = current->dwUser;
+  /* 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));
+
+  /*
+   * point to the next block
+   */
+  (s->waveCurrentBlock)++;
+  (s->waveCurrentBlock) %= BLOCK_COUNT;		
+
+  s->playing = 1;
+
+  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) {    
@@ -536,40 +581,27 @@ int writeAudio(sa_stream_t *s, LPSTR dat
 		  
     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;      
+    remain = BLOCK_SIZE - current->dwUser;
   	memcpy(current->lpData + current->dwUser, data, remain);
+    current->dwUser += 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));      
+
+    status = writeBlock(s, current);
     HANDLE_WAVE_ERROR(status, "writing audio to audio device");
-      
-    EnterCriticalSection(&(s->waveCriticalSection));
-    s->waveFreeBlockCount--;
-    LeaveCriticalSection(&(s->waveCriticalSection));
-
-    /*
-     * point to the next block
-     */
-    (s->waveCurrentBlock)++;
-    (s->waveCurrentBlock) %= BLOCK_COUNT;		
 
     current = &(s->waveBlocks[s->waveCurrentBlock]);
     current->dwUser = 0;
-
-    s->playing = 1;
   }
   return SA_SUCCESS;
 }
 
 /**
  * \brief - audio callback function called when next WAVE header is played by audio device
  */
 void CALLBACK waveOutProc(
--- a/media/libsydneyaudio/update.sh
+++ b/media/libsydneyaudio/update.sh
@@ -10,8 +10,9 @@ patch -p4 <include-CoreServices.patch
 patch -p4 <sydney_os2_base.patch
 patch -p4 <sydney_os2_moz.patch
 patch -p3 <bug495794_closeAudio.patch
 patch -p3 <bug495558_alsa_endian.patch
 patch -p3 <bug525401_drain_deadlock.patch
 patch -p3 <bug526411_latency.patch
 patch -p3 <sydney_aix.patch
 patch -p3 <bug562488_oss_destroy_crash.patch
+patch -p3 <bug564734-win32-drain.patch