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;
}
Esempio n. 6
0
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;
}
Esempio n. 11
0
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;
}
Esempio n. 13
0
/* 
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;
}
Esempio n. 14
0
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;
}