static gboolean gst_directsound_ring_buffer_start (GstRingBuffer * buf) { GstDirectSoundRingBuffer * dsoundbuffer; HANDLE hThread; dsoundbuffer = GST_DIRECTSOUND_RING_BUFFER (buf); GST_DEBUG ("Starting RingBuffer"); GST_DSOUND_LOCK (dsoundbuffer); hThread = CreateThread (NULL, 256 * 1024 /* Stack size: 256k */, gst_directsound_write_proc, buf, CREATE_SUSPENDED, NULL); if (!hThread) { GST_DSOUND_UNLOCK (dsoundbuffer); GST_WARNING ("gst_directsound_ring_buffer_start: CreateThread"); return FALSE; } dsoundbuffer->hThread = hThread; dsoundbuffer->should_run = TRUE; gst_directsound_set_volume (dsoundbuffer->pDSB8, dsoundbuffer->volume); if (G_UNLIKELY (!SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL))) GST_WARNING ("gst_directsound_ring_buffer_start: Failed to set thread priority."); ResumeThread (hThread); GST_DSOUND_UNLOCK (dsoundbuffer); return TRUE; }
static gboolean gst_directsound_ring_buffer_stop (GstRingBuffer * buf) { HRESULT hr; DWORD ret; HANDLE hThread; GstDirectSoundRingBuffer * dsoundbuffer; dsoundbuffer = GST_DIRECTSOUND_RING_BUFFER (buf); GST_DEBUG ("Stopping RingBuffer"); GST_DSOUND_LOCK (dsoundbuffer); dsoundbuffer->should_run = FALSE; if (dsoundbuffer->pDSB8) { hr = IDirectSoundBuffer8_Stop (dsoundbuffer->pDSB8); if (G_UNLIKELY (FAILED(hr))) { GST_DSOUND_UNLOCK (dsoundbuffer); GST_WARNING ("gst_directsound_ring_buffer_stop: IDirectSoundBuffer8_Stop, hr = %X", (unsigned int) hr); return FALSE; } } hThread = dsoundbuffer->hThread; if (dsoundbuffer->suspended && ResumeThread (hThread) != -1) { dsoundbuffer->suspended = FALSE; } else { GST_DSOUND_UNLOCK (dsoundbuffer); GST_WARNING ("gst_directsound_ring_buffer_stop: ResumeThread failed."); return FALSE; } GST_DSOUND_UNLOCK (dsoundbuffer); /* wait without lock held */ ret = WaitForSingleObject (hThread, 5000); if (G_UNLIKELY (ret == WAIT_TIMEOUT)) { GST_WARNING ("gst_directsound_ring_buffer_stop: Failed to wait for thread shutdown. (%u)", (unsigned int) ret); return FALSE; } GST_DSOUND_LOCK (dsoundbuffer); CloseHandle (dsoundbuffer->hThread); dsoundbuffer->hThread = NULL; GST_DSOUND_UNLOCK (dsoundbuffer); return TRUE; }
static gboolean gst_directsound_ring_buffer_release (GstRingBuffer * buf) { GstDirectSoundRingBuffer * dsoundbuffer; dsoundbuffer = GST_DIRECTSOUND_RING_BUFFER (buf); /* first we have to ensure our ring buffer is stopped */ gst_directsound_ring_buffer_stop (buf); GST_DSOUND_LOCK (dsoundbuffer); /* release secondary DirectSound buffer */ if (dsoundbuffer->pDSB8) { IDirectSoundBuffer8_Release (dsoundbuffer->pDSB8); dsoundbuffer->pDSB8 = NULL; } gst_buffer_unref (buf->data); buf->data = NULL; GST_DSOUND_UNLOCK (dsoundbuffer); return TRUE; }
static gboolean gst_directsound_ring_buffer_resume (GstRingBuffer * buf) { GstDirectSoundRingBuffer *dsoundbuffer; dsoundbuffer = GST_DIRECTSOUND_RING_BUFFER (buf); GST_DEBUG ("Resuming RingBuffer"); GST_DSOUND_LOCK (dsoundbuffer); if (G_LIKELY (dsoundbuffer->suspended) && ResumeThread (dsoundbuffer->hThread) != -1) { dsoundbuffer->suspended = FALSE; } else { GST_DSOUND_UNLOCK (dsoundbuffer); GST_WARNING ("gst_directsound_ring_buffer_resume: ResumeThread failed."); return FALSE; } GST_DSOUND_UNLOCK (dsoundbuffer); return TRUE; }
static gboolean gst_directsound_src_unprepare (GstAudioSrc * asrc) { GstDirectSoundSrc *dsoundsrc; GST_DEBUG_OBJECT (asrc, "unpreparing directsoundsrc"); dsoundsrc = GST_DIRECTSOUND_SRC (asrc); GST_DSOUND_LOCK (dsoundsrc); /* Stop capturing */ IDirectSoundCaptureBuffer_Stop (dsoundsrc->pDSBSecondary); /* Release buffer */ IDirectSoundCaptureBuffer_Release (dsoundsrc->pDSBSecondary); GST_DSOUND_UNLOCK (dsoundsrc); return TRUE; }
static void gst_directsound_src_reset (GstAudioSrc * asrc) { GstDirectSoundSrc *dsoundsrc; LPVOID pLockedBuffer = NULL; DWORD dwSizeBuffer = 0; GST_DEBUG_OBJECT (asrc, "reset directsoundsrc"); dsoundsrc = GST_DIRECTSOUND_SRC (asrc); GST_DSOUND_LOCK (dsoundsrc); dsoundsrc->reset_while_sleeping = TRUE; /* Interrupt read sleep if required */ if (dsoundsrc->read_wait_clock_id != NULL) gst_clock_id_unschedule (dsoundsrc->read_wait_clock_id); if (dsoundsrc->pDSBSecondary) { /*stop capturing */ HRESULT hRes = IDirectSoundCaptureBuffer_Stop (dsoundsrc->pDSBSecondary); /*reset position */ /* hRes = IDirectSoundCaptureBuffer_SetCurrentPosition (dsoundsrc->pDSBSecondary, 0); */ /*reset the buffer */ hRes = IDirectSoundCaptureBuffer_Lock (dsoundsrc->pDSBSecondary, dsoundsrc->current_circular_offset, dsoundsrc->buffer_size, pLockedBuffer, &dwSizeBuffer, NULL, NULL, 0L); if (SUCCEEDED (hRes)) { memset (pLockedBuffer, 0, dwSizeBuffer); hRes = IDirectSoundCaptureBuffer_Unlock (dsoundsrc->pDSBSecondary, pLockedBuffer, dwSizeBuffer, NULL, 0); } dsoundsrc->current_circular_offset = 0; } GST_DSOUND_UNLOCK (dsoundsrc); }
static void gst_directsound_src_reset (GstAudioSrc * asrc) { GstDirectSoundSrc *dsoundsrc; LPVOID pLockedBuffer = NULL; DWORD dwSizeBuffer = 0; GST_DEBUG_OBJECT (asrc, "reset directsoundsrc"); dsoundsrc = GST_DIRECTSOUND_SRC (asrc); #if 0 IDirectSoundCaptureBuffer_Stop (dsoundsrc->pDSBSecondary); #endif GST_DSOUND_LOCK (dsoundsrc); if (dsoundsrc->pDSBSecondary) { /*stop capturing */ HRESULT hRes = IDirectSoundCaptureBuffer_Stop (dsoundsrc->pDSBSecondary); /*reset position */ /* hRes = IDirectSoundCaptureBuffer_SetCurrentPosition (dsoundsrc->pDSBSecondary, 0); */ /*reset the buffer */ hRes = IDirectSoundCaptureBuffer_Lock (dsoundsrc->pDSBSecondary, dsoundsrc->current_circular_offset, dsoundsrc->buffer_size, pLockedBuffer, &dwSizeBuffer, NULL, NULL, 0L); if (SUCCEEDED (hRes)) { memset (pLockedBuffer, 0, dwSizeBuffer); hRes = IDirectSoundCaptureBuffer_Unlock (dsoundsrc->pDSBSecondary, pLockedBuffer, dwSizeBuffer, NULL, 0); } dsoundsrc->current_circular_offset = 0; } GST_DSOUND_UNLOCK (dsoundsrc); }
static void gst_directsound_sink_reset (GstAudioSink * asink) { GstDirectSoundSink *dsoundsink; LPVOID pLockedBuffer = NULL; DWORD dwSizeBuffer = 0; dsoundsink = GST_DIRECTSOUND_SINK (asink); GST_DSOUND_LOCK (dsoundsink); if (dsoundsink->pDSBSecondary) { /*stop playing */ HRESULT hRes = IDirectSoundBuffer_Stop (dsoundsink->pDSBSecondary); /*reset position */ hRes = IDirectSoundBuffer_SetCurrentPosition (dsoundsink->pDSBSecondary, 0); dsoundsink->current_circular_offset = 0; /*reset the buffer */ hRes = IDirectSoundBuffer_Lock (dsoundsink->pDSBSecondary, dsoundsink->current_circular_offset, dsoundsink->buffer_size, &pLockedBuffer, &dwSizeBuffer, NULL, NULL, 0L); if (SUCCEEDED (hRes)) { memset (pLockedBuffer, 0, dwSizeBuffer); hRes = IDirectSoundBuffer_Unlock (dsoundsink->pDSBSecondary, pLockedBuffer, dwSizeBuffer, NULL, 0); } } dsoundsink->first_buffer_after_reset = TRUE; GST_DSOUND_UNLOCK (dsoundsink); }
static gboolean gst_directsound_ring_buffer_pause (GstRingBuffer * buf) { HRESULT hr = S_OK; GstDirectSoundRingBuffer * dsoundbuffer; dsoundbuffer = GST_DIRECTSOUND_RING_BUFFER (buf); GST_DEBUG ("Pausing RingBuffer"); GST_DSOUND_LOCK (dsoundbuffer); if (dsoundbuffer->pDSB8) { hr = IDirectSoundBuffer8_Stop (dsoundbuffer->pDSB8); } if (G_LIKELY (!dsoundbuffer->suspended)) { if (G_UNLIKELY(SuspendThread (dsoundbuffer->hThread) == -1)) GST_WARNING ("gst_directsound_ring_buffer_pause: SuspendThread failed."); else dsoundbuffer->suspended = TRUE; } GST_DSOUND_UNLOCK (dsoundbuffer); /* in the unlikely event that a device was reconfigured, we can consider * ourselves stopped even though the stop call failed */ if (G_UNLIKELY (FAILED(hr)) && G_UNLIKELY(hr != DIRECTSOUND_ERROR_DEVICE_RECONFIGURED) && G_UNLIKELY(hr != DIRECTSOUND_ERROR_DEVICE_NO_DRIVER)) { GST_WARNING ("gst_directsound_ring_buffer_pause: IDirectSoundBuffer8_Stop, hr = %X", (unsigned int) hr); return FALSE; } return TRUE; }
/* return number of readed bytes */ static guint gst_directsound_src_read (GstAudioSrc * asrc, gpointer data, guint length, GstClockTime * timestamp) { GstDirectSoundSrc *dsoundsrc; HRESULT hRes; /* Result for windows functions */ DWORD dwCurrentCaptureCursor = 0; DWORD dwBufferSize = 0; LPVOID pLockedBuffer1 = NULL; LPVOID pLockedBuffer2 = NULL; DWORD dwSizeBuffer1 = 0; DWORD dwSizeBuffer2 = 0; DWORD dwStatus = 0; GST_DEBUG_OBJECT (asrc, "reading directsoundsrc"); dsoundsrc = GST_DIRECTSOUND_SRC (asrc); GST_DSOUND_LOCK (dsoundsrc); /* Get current buffer status */ hRes = IDirectSoundCaptureBuffer_GetStatus (dsoundsrc->pDSBSecondary, &dwStatus); /* Starting capturing if not already */ if (!(dwStatus & DSCBSTATUS_CAPTURING)) { hRes = IDirectSoundCaptureBuffer_Start (dsoundsrc->pDSBSecondary, DSCBSTART_LOOPING); // Sleep (dsoundsrc->latency_time/1000); GST_DEBUG_OBJECT (asrc, "capture started"); } // calculate_buffersize: while (length > dwBufferSize) { Sleep (dsoundsrc->latency_time / 1000); hRes = IDirectSoundCaptureBuffer_GetCurrentPosition (dsoundsrc->pDSBSecondary, &dwCurrentCaptureCursor, NULL); /* calculate the buffer */ if (dwCurrentCaptureCursor < dsoundsrc->current_circular_offset) { dwBufferSize = dsoundsrc->buffer_size - (dsoundsrc->current_circular_offset - dwCurrentCaptureCursor); } else { dwBufferSize = dwCurrentCaptureCursor - dsoundsrc->current_circular_offset; } } // while (... /* Lock the buffer */ hRes = IDirectSoundCaptureBuffer_Lock (dsoundsrc->pDSBSecondary, dsoundsrc->current_circular_offset, length, &pLockedBuffer1, &dwSizeBuffer1, &pLockedBuffer2, &dwSizeBuffer2, 0L); /* Copy buffer data to another buffer */ if (hRes == DS_OK) { memcpy (data, pLockedBuffer1, dwSizeBuffer1); } /* ...and if something is in another buffer */ if (pLockedBuffer2 != NULL) { memcpy (((guchar *) data + dwSizeBuffer1), pLockedBuffer2, dwSizeBuffer2); } dsoundsrc->current_circular_offset += dwSizeBuffer1 + dwSizeBuffer2; dsoundsrc->current_circular_offset %= dsoundsrc->buffer_size; IDirectSoundCaptureBuffer_Unlock (dsoundsrc->pDSBSecondary, pLockedBuffer1, dwSizeBuffer1, pLockedBuffer2, dwSizeBuffer2); GST_DSOUND_UNLOCK (dsoundsrc); /* return length (readed data size in bytes) */ return length; }
static gboolean gst_directsound_src_event (GstBaseSrc * bsrc, GstEvent * event) { HRESULT hr; DWORD dwStatus; //DWORD dwSizeBuffer = 0; //LPVOID pLockedBuffer = NULL; GstDirectSoundSrc * dsoundsrc; dsoundsrc = GST_DIRECTSOUND_SRC (bsrc); GST_BASE_SRC_CLASS (parent_class)->event (bsrc, event); /* no buffer, no event to process */ if (!dsoundsrc->dsoundbuffer) return TRUE; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_START: GST_DSOUND_LOCK (dsoundsrc->dsoundbuffer); dsoundsrc->dsoundbuffer->flushing = TRUE; GST_DSOUND_UNLOCK (dsoundsrc->dsoundbuffer); break; case GST_EVENT_FLUSH_STOP: GST_DSOUND_LOCK (dsoundsrc->dsoundbuffer); dsoundsrc->dsoundbuffer->flushing = FALSE; if (dsoundsrc->dsoundbuffer->pDSCB8) { hr = IDirectSoundCaptureBuffer8_GetStatus (dsoundsrc->dsoundbuffer->pDSCB8, &dwStatus); if (FAILED(hr)) { GST_DSOUND_UNLOCK (dsoundsrc->dsoundbuffer); GST_WARNING("gst_directsound_src_event: IDirectSoundCaptureBuffer8_GetStatus, hr = %X", (unsigned int) hr); return FALSE; } if (!(dwStatus & DSCBSTATUS_CAPTURING)) { // ###: capture api doesn't support _SetCurrentPosition. commenting // out for now. #if 0 /* reset position */ hr = IDirectSoundBuffer8_SetCurrentPosition (dsoundsink->dsoundbuffer->pDSB8, 0); dsoundsink->dsoundbuffer->buffer_circular_offset = 0; /* reset the buffer */ hr = IDirectSoundBuffer8_Lock (dsoundsink->dsoundbuffer->pDSB8, dsoundsink->dsoundbuffer->buffer_circular_offset, 0L, &pLockedBuffer, &dwSizeBuffer, NULL, NULL, DSBLOCK_ENTIREBUFFER); if (SUCCEEDED (hr)) { memset (pLockedBuffer, 0, dwSizeBuffer); hr = IDirectSoundBuffer8_Unlock (dsoundsink->dsoundbuffer->pDSB8, pLockedBuffer, dwSizeBuffer, NULL, 0); if (FAILED(hr)) { GST_DSOUND_UNLOCK (dsoundsink->dsoundbuffer); GST_WARNING("gst_directsound_sink_event: IDirectSoundBuffer8_Unlock, hr = %X", (unsigned int) hr); return FALSE; } } else { GST_DSOUND_UNLOCK (dsoundsink->dsoundbuffer); GST_WARNING ( "gst_directsound_sink_event: IDirectSoundBuffer8_Lock, hr = %X", (unsigned int) hr); return FALSE; } #endif } } GST_DSOUND_UNLOCK (dsoundsrc->dsoundbuffer); break; default: break; } return TRUE; }
static guint gst_directsound_sink_write (GstAudioSink * asink, gpointer data, guint length) { GstDirectSoundSink *dsoundsink; DWORD dwStatus; HRESULT hRes; LPVOID pLockedBuffer1 = NULL, pLockedBuffer2 = NULL; DWORD dwSizeBuffer1, dwSizeBuffer2; DWORD dwCurrentPlayCursor; dsoundsink = GST_DIRECTSOUND_SINK (asink); /* Fix endianness */ if (dsoundsink->buffer_format == GST_IEC958) _swab (data, data, length); GST_DSOUND_LOCK (dsoundsink); /* get current buffer status */ hRes = IDirectSoundBuffer_GetStatus (dsoundsink->pDSBSecondary, &dwStatus); /* get current play cursor position */ hRes = IDirectSoundBuffer_GetCurrentPosition (dsoundsink->pDSBSecondary, &dwCurrentPlayCursor, NULL); if (SUCCEEDED (hRes) && (dwStatus & DSBSTATUS_PLAYING)) { DWORD dwFreeBufferSize; calculate_freesize: /* calculate the free size of the circular buffer */ if (dwCurrentPlayCursor < dsoundsink->current_circular_offset) dwFreeBufferSize = dsoundsink->buffer_size - (dsoundsink->current_circular_offset - dwCurrentPlayCursor); else dwFreeBufferSize = dwCurrentPlayCursor - dsoundsink->current_circular_offset; if (length >= dwFreeBufferSize) { Sleep (100); hRes = IDirectSoundBuffer_GetCurrentPosition (dsoundsink->pDSBSecondary, &dwCurrentPlayCursor, NULL); hRes = IDirectSoundBuffer_GetStatus (dsoundsink->pDSBSecondary, &dwStatus); if (SUCCEEDED (hRes) && (dwStatus & DSBSTATUS_PLAYING)) goto calculate_freesize; else { dsoundsink->first_buffer_after_reset = FALSE; GST_DSOUND_UNLOCK (dsoundsink); return 0; } } } if (dwStatus & DSBSTATUS_BUFFERLOST) { hRes = IDirectSoundBuffer_Restore (dsoundsink->pDSBSecondary); /*need a loop waiting the buffer is restored?? */ dsoundsink->current_circular_offset = 0; } hRes = IDirectSoundBuffer_Lock (dsoundsink->pDSBSecondary, dsoundsink->current_circular_offset, length, &pLockedBuffer1, &dwSizeBuffer1, &pLockedBuffer2, &dwSizeBuffer2, 0L); if (SUCCEEDED (hRes)) { // Write to pointers without reordering. memcpy (pLockedBuffer1, data, dwSizeBuffer1); if (pLockedBuffer2 != NULL) memcpy (pLockedBuffer2, (LPBYTE) data + dwSizeBuffer1, dwSizeBuffer2); // Update where the buffer will lock (for next time) dsoundsink->current_circular_offset += dwSizeBuffer1 + dwSizeBuffer2; dsoundsink->current_circular_offset %= dsoundsink->buffer_size; /* Circular buffer */ hRes = IDirectSoundBuffer_Unlock (dsoundsink->pDSBSecondary, pLockedBuffer1, dwSizeBuffer1, pLockedBuffer2, dwSizeBuffer2); } /* if the buffer was not in playing state yet, call play on the buffer except if this buffer is the fist after a reset (base class call reset and write a buffer when setting the sink to pause) */ if (!(dwStatus & DSBSTATUS_PLAYING) && dsoundsink->first_buffer_after_reset == FALSE) { hRes = IDirectSoundBuffer_Play (dsoundsink->pDSBSecondary, 0, 0, DSBPLAY_LOOPING); } dsoundsink->first_buffer_after_reset = FALSE; GST_DSOUND_UNLOCK (dsoundsink); return length; }
/* return number of readed bytes */ static guint gst_directsound_src_read (GstAudioSrc * asrc, gpointer data, guint length, GstClockTime * timestamp) { GstDirectSoundSrc *dsoundsrc; guint64 sleep_time_ms, sleep_until; GstClockID clock_id; HRESULT hRes; /* Result for windows functions */ DWORD dwCurrentCaptureCursor = 0; DWORD dwBufferSize = 0; LPVOID pLockedBuffer1 = NULL; LPVOID pLockedBuffer2 = NULL; DWORD dwSizeBuffer1 = 0; DWORD dwSizeBuffer2 = 0; DWORD dwStatus = 0; GST_DEBUG_OBJECT (asrc, "reading directsoundsrc"); dsoundsrc = GST_DIRECTSOUND_SRC (asrc); GST_DSOUND_LOCK (dsoundsrc); /* Get current buffer status */ hRes = IDirectSoundCaptureBuffer_GetStatus (dsoundsrc->pDSBSecondary, &dwStatus); if (FAILED (hRes)) { GST_DSOUND_UNLOCK (dsoundsrc); return -1; } /* Starting capturing if not already */ if (!(dwStatus & DSCBSTATUS_CAPTURING)) { hRes = IDirectSoundCaptureBuffer_Start (dsoundsrc->pDSBSecondary, DSCBSTART_LOOPING); GST_INFO_OBJECT (asrc, "capture started"); } /* Loop till the source has produced bytes equal to or greater than @length. * * DirectSound has a notification-based API that uses Windows CreateEvent() * + WaitForSingleObject(), but it is completely useless for live streams. * * 1. You must schedule all events before starting capture * 2. The events are all fired exactly once * 3. You cannot schedule new events while a capture is running * 4. You cannot stop/schedule/start either * * This means you cannot use the API while doing live looped capture and we * must resort to this. * * However, this is almost as efficient as event-based capture since it's ok * to consistently overwait by a fixed amount; the extra bytes will just end * up being used in the next call, and the extra latency will be constant. */ while (TRUE) { hRes = IDirectSoundCaptureBuffer_GetCurrentPosition (dsoundsrc->pDSBSecondary, &dwCurrentCaptureCursor, NULL); if (FAILED (hRes)) { GST_DSOUND_UNLOCK (dsoundsrc); return -1; } /* calculate the size of the buffer that's been captured while accounting * for wrap-arounds */ if (dwCurrentCaptureCursor < dsoundsrc->current_circular_offset) { dwBufferSize = dsoundsrc->buffer_size - (dsoundsrc->current_circular_offset - dwCurrentCaptureCursor); } else { dwBufferSize = dwCurrentCaptureCursor - dsoundsrc->current_circular_offset; } if (dwBufferSize >= length) { /* Yay, we got all the data we need */ break; } else { GST_DEBUG_OBJECT (asrc, "not enough data, got %lu (want at least %u)", dwBufferSize, length); /* If we didn't get enough data, sleep for a proportionate time */ sleep_time_ms = gst_util_uint64_scale (dsoundsrc->latency_time, length - dwBufferSize, length * 1000); /* Make sure we don't run in a tight loop unnecessarily */ sleep_time_ms = MAX (sleep_time_ms, 10); /* Sleep using gst_clock_id_wait() so that we can be interrupted */ sleep_until = gst_clock_get_time (dsoundsrc->system_clock) + sleep_time_ms * GST_MSECOND; /* Setup the clock id wait */ if (G_UNLIKELY (dsoundsrc->read_wait_clock_id == NULL || gst_clock_single_shot_id_reinit (dsoundsrc->system_clock, dsoundsrc->read_wait_clock_id, sleep_until) == FALSE)) { if (dsoundsrc->read_wait_clock_id != NULL) gst_clock_id_unref (dsoundsrc->read_wait_clock_id); dsoundsrc->read_wait_clock_id = gst_clock_new_single_shot_id (dsoundsrc->system_clock, sleep_until); } clock_id = dsoundsrc->read_wait_clock_id; dsoundsrc->reset_while_sleeping = FALSE; GST_DEBUG_OBJECT (asrc, "waiting %" G_GUINT64_FORMAT "ms for more data", sleep_time_ms); GST_DSOUND_UNLOCK (dsoundsrc); gst_clock_id_wait (clock_id, NULL); GST_DSOUND_LOCK (dsoundsrc); if (dsoundsrc->reset_while_sleeping == TRUE) { GST_DEBUG_OBJECT (asrc, "reset while sleeping, cancelled read"); GST_DSOUND_UNLOCK (dsoundsrc); return -1; } } } GST_DEBUG_OBJECT (asrc, "Got enough data: %lu bytes (wanted at least %u)", dwBufferSize, length); /* Lock the buffer and read only the first @length bytes. Keep the rest in * the capture buffer for the next read. */ hRes = IDirectSoundCaptureBuffer_Lock (dsoundsrc->pDSBSecondary, dsoundsrc->current_circular_offset, length, &pLockedBuffer1, &dwSizeBuffer1, &pLockedBuffer2, &dwSizeBuffer2, 0L); /* NOTE: We now assume that dwSizeBuffer1 + dwSizeBuffer2 == length since the * API is supposed to guarantee that */ /* Copy buffer data to another buffer */ if (hRes == DS_OK) { memcpy (data, pLockedBuffer1, dwSizeBuffer1); } /* ...and if something is in another buffer */ if (pLockedBuffer2 != NULL) { memcpy (((guchar *) data + dwSizeBuffer1), pLockedBuffer2, dwSizeBuffer2); } dsoundsrc->current_circular_offset += dwSizeBuffer1 + dwSizeBuffer2; dsoundsrc->current_circular_offset %= dsoundsrc->buffer_size; IDirectSoundCaptureBuffer_Unlock (dsoundsrc->pDSBSecondary, pLockedBuffer1, dwSizeBuffer1, pLockedBuffer2, dwSizeBuffer2); GST_DSOUND_UNLOCK (dsoundsrc); /* We always read exactly @length data */ return length; }
static DWORD WINAPI gst_directsound_write_proc (LPVOID lpParameter) { GstRingBuffer * buf; GstDirectSoundRingBuffer * dsoundbuffer; HRESULT hr; DWORD dwStatus; LPVOID pLockedBuffer1 = NULL, pLockedBuffer2 = NULL; DWORD dwSizeBuffer1 = 0, dwSizeBuffer2 = 0; DWORD dwCurrentPlayCursor = 0; gint64 freeBufferSize = 0; guint8 * readptr = NULL; gint readseg = 0; guint len = 0; gint retries = 0; gboolean flushing = FALSE; gboolean should_run = TRUE; gboolean error = FALSE; buf = (GstRingBuffer *) lpParameter; dsoundbuffer = GST_DIRECTSOUND_RING_BUFFER (buf); do { GST_DSOUND_LOCK (dsoundbuffer); if (dsoundbuffer->flushing || !dsoundbuffer->pDSB8) { GST_DSOUND_UNLOCK (dsoundbuffer); goto complete; } GST_DSOUND_UNLOCK (dsoundbuffer); restore_buffer: /* get current buffer status */ GST_DSOUND_LOCK (dsoundbuffer); hr = IDirectSoundBuffer8_GetStatus (dsoundbuffer->pDSB8, &dwStatus); GST_DSOUND_UNLOCK (dsoundbuffer); if (dwStatus & DSBSTATUS_BUFFERLOST) { GST_DEBUG ("Buffer was lost, attempting to restore"); GST_DSOUND_LOCK (dsoundbuffer); hr = IDirectSoundBuffer8_Restore (dsoundbuffer->pDSB8); GST_DSOUND_UNLOCK (dsoundbuffer); /* restore may fail again, ensure we restore the * buffer before we continue */ if (FAILED(hr) && hr == DSERR_BUFFERLOST) { if (retries++ < MAX_LOST_RETRIES) { GST_DEBUG ("Unable to restore, trying again"); goto restore_buffer; } else { GST_ELEMENT_ERROR (dsoundbuffer->dsoundsink, RESOURCE, FAILED, ("%ls.", DXGetErrorDescription9W(hr)), ("gst_directsound_write_proc: IDirectSoundBuffer8_Restore, hr = %X", (unsigned int) hr)); goto complete; } } } /* get current play cursor and write cursor positions */ GST_DSOUND_LOCK (dsoundbuffer); hr = IDirectSoundBuffer8_GetCurrentPosition (dsoundbuffer->pDSB8, &dwCurrentPlayCursor, NULL); GST_DSOUND_UNLOCK (dsoundbuffer); if (G_UNLIKELY (FAILED(hr))) { /* try and reopen the default directsound device */ if (hr == DIRECTSOUND_ERROR_DEVICE_RECONFIGURED) { /* we have to wait a while for the sound device removal to actually * be processed before attempting to reopen the device. Yes, this sucks */ Sleep (2000); GST_DSOUND_LOCK (dsoundbuffer); IDirectSoundBuffer8_Release (dsoundbuffer->pDSB8); dsoundbuffer->pDSB8 = NULL; GST_DSOUND_UNLOCK (dsoundbuffer); if (gst_directsound_ring_buffer_close_device (buf) && gst_directsound_ring_buffer_open_device (buf) && gst_directsound_create_buffer (buf) ) { dsoundbuffer->buffer_write_offset = 0; goto restore_buffer; } } /* only trigger an error if we're not already in an error state */ if (FAILED(hr) && !error) { GST_ELEMENT_ERROR (dsoundbuffer->dsoundsink, RESOURCE, FAILED, ("%ls.", DXGetErrorDescription9W(hr)), ("gst_directsound_write_proc: IDirectSoundBuffer8_GetCurrentPosition, hr = %X", (unsigned int) hr)); error = TRUE; goto complete; } } GST_LOG ("Current Play Cursor: %u Current Write Offset: %d", (unsigned int) dwCurrentPlayCursor, dsoundbuffer->buffer_write_offset); /* calculate the free size of the circular buffer */ GST_DSOUND_LOCK (dsoundbuffer); if (dwCurrentPlayCursor <= dsoundbuffer->buffer_write_offset) freeBufferSize = dsoundbuffer->buffer_size - (dsoundbuffer->buffer_write_offset - dwCurrentPlayCursor); else freeBufferSize = dwCurrentPlayCursor - dsoundbuffer->buffer_write_offset; GST_DSOUND_UNLOCK (dsoundbuffer); if (!gst_ring_buffer_prepare_read (buf, &readseg, &readptr, &len)) goto complete; len -= dsoundbuffer->segoffset; GST_LOG ("Size of segment to write: %d Free buffer size: %lld", len, freeBufferSize); /* If we can't write this into directsound because we don't have enough * space, then start playback if we're currently paused. Then, sleep * for a little while to wait until space is available */ if (len >= freeBufferSize) { if (!(dwStatus & DSBSTATUS_PLAYING)) { GST_DSOUND_LOCK (dsoundbuffer); hr = IDirectSoundBuffer8_Play (dsoundbuffer->pDSB8, 0, 0, DSBPLAY_LOOPING); GST_DSOUND_UNLOCK (dsoundbuffer); if (FAILED(hr)) { GST_WARNING ("gst_directsound_write_proc: IDirectSoundBuffer8_Play, hr = %X", (unsigned int) hr); } } goto complete; } /* lock it */ GST_DSOUND_LOCK (dsoundbuffer); hr = IDirectSoundBuffer8_Lock (dsoundbuffer->pDSB8, dsoundbuffer->buffer_write_offset, len, &pLockedBuffer1, &dwSizeBuffer1, &pLockedBuffer2, &dwSizeBuffer2, 0L); /* copy chunks */ if (SUCCEEDED (hr)) { if (len <= dwSizeBuffer1) { memcpy (pLockedBuffer1, (LPBYTE) readptr + dsoundbuffer->segoffset, len); } else { memcpy (pLockedBuffer1, (LPBYTE) readptr + dsoundbuffer->segoffset, dwSizeBuffer1); memcpy (pLockedBuffer2, (LPBYTE) readptr + dsoundbuffer->segoffset + dwSizeBuffer1, len - dwSizeBuffer1); } IDirectSoundBuffer8_Unlock (dsoundbuffer->pDSB8, pLockedBuffer1, dwSizeBuffer1, pLockedBuffer2, dwSizeBuffer2); } else { GST_WARNING ("gst_directsound_write_proc: IDirectSoundBuffer8_Lock, hr = %X", (unsigned int) hr); } /* update tracking data */ dsoundbuffer->segoffset += dwSizeBuffer1 + (len - dwSizeBuffer1); dsoundbuffer->buffer_write_offset += dwSizeBuffer1 + (len - dwSizeBuffer1); dsoundbuffer->buffer_write_offset %= dsoundbuffer->buffer_size; GST_DSOUND_UNLOCK (dsoundbuffer); freeBufferSize -= dwSizeBuffer1 + (len - dwSizeBuffer1); GST_LOG ("DirectSound Buffer1 Data Size: %u DirectSound Buffer2 Data Size: %u", (unsigned int) dwSizeBuffer1, (unsigned int) dwSizeBuffer2); GST_LOG ("Free buffer size: %lld", freeBufferSize); /* check if we read a whole segment */ GST_DSOUND_LOCK (dsoundbuffer); if (dsoundbuffer->segoffset == dsoundbuffer->segsize) { GST_DSOUND_UNLOCK (dsoundbuffer); /* advance to next segment */ gst_ring_buffer_clear (buf, readseg); gst_ring_buffer_advance (buf, 1); GST_DSOUND_LOCK (dsoundbuffer); dsoundbuffer->segoffset = 0; } GST_DSOUND_UNLOCK (dsoundbuffer); complete: GST_DSOUND_LOCK (dsoundbuffer); should_run = dsoundbuffer->should_run; flushing = dsoundbuffer->flushing; retries = 0; GST_DSOUND_UNLOCK (dsoundbuffer); /* it's extremely important to sleep in without the lock! */ if (len >= freeBufferSize || flushing || error) Sleep (dsoundbuffer->min_sleep_time); } while(should_run); return 0; }