Пример #1
0
OSStatus
gst_osx_audio_sink_io_proc (AudioDeviceID inDevice,
    const AudioTimeStamp * inNow, const AudioBufferList * inInputData,
    const AudioTimeStamp * inInputTime, AudioBufferList * outOutputData,
    const AudioTimeStamp * inOutputTime, void *inClientData)
{
  GstOsxRingBuffer *buf = GST_OSX_RING_BUFFER (inClientData);

  guint8 *readptr;
  gint readseg;
  gint len;

  if (gst_ring_buffer_prepare_read (GST_RING_BUFFER (buf), &readseg, &readptr,
          &len)) {
    outOutputData->mBuffers[0].mDataByteSize = len;
    memcpy ((char *) outOutputData->mBuffers[0].mData, readptr, len);

    /* clear written samples */
    gst_ring_buffer_clear (GST_RING_BUFFER (buf), readseg);

    /* we wrote one segment */
    gst_ring_buffer_advance (GST_RING_BUFFER (buf), 1);
  }
  return 0;
}
Пример #2
0
/* this is the callback of jack. This should be RT-safe.
 * Writes samples from the jack input port's buffer to the gst ring buffer.
 */
static int
jack_process_cb (jack_nframes_t nframes, void *arg)
{
  GstJackAudioSrc *src;
  GstRingBuffer *buf;
  GstJackRingBuffer *abuf;
  gint len, givenLen;
  guint8 *writeptr, *dataStart;
  gint writeseg;
  gint channels, i, j;
  sample_t **buffers, *data;

  buf = GST_RING_BUFFER_CAST (arg);
  abuf = GST_JACK_RING_BUFFER_CAST (arg);
  src = GST_JACK_AUDIO_SRC (GST_OBJECT_PARENT (buf));

  channels = buf->spec.channels;
  len = sizeof (sample_t) * nframes * channels;

  /* alloc pointers to samples */
  buffers = g_alloca (sizeof (sample_t *) * channels);
  data = g_alloca (len);

  /* get input buffers */
  for (i = 0; i < channels; i++)
    buffers[i] = (sample_t *) jack_port_get_buffer (src->ports[i], nframes);

  //writeptr = data; 
  dataStart = (guint8 *) data;

  /* the samples in the jack input buffers have to be interleaved into the 
   * ringbuffer 
   */

  for (i = 0; i < nframes; ++i)
    for (j = 0; j < channels; ++j)
      *data++ = buffers[j][i];

  if (gst_ring_buffer_prepare_read (buf, &writeseg, &writeptr, &givenLen)) {
    memcpy (writeptr, (char *) dataStart, givenLen);

    GST_DEBUG ("copy %d frames: %p, %d bytes, %d channels", nframes, writeptr,
        len / channels, channels);

    /* clear written samples in the ringbuffer */
    // gst_ring_buffer_clear(buf, 0);

    /* we wrote one segment */
    gst_ring_buffer_advance (buf, 1);
  }
  return 0;
}
Пример #3
0
/* this is the callback of jack. This should be RT-safe.
 * Writes samples from the jack input port's buffer to the gst ring buffer.
 */
static int
jack_process_cb (jack_nframes_t nframes, void *arg)
{
  GstJackAudioSrc *src;
  GstRingBuffer *buf;
  gint len;
  guint8 *writeptr;
  gint writeseg;
  gint channels, i, j, flen;
  sample_t *data;

  buf = GST_RING_BUFFER_CAST (arg);
  src = GST_JACK_AUDIO_SRC (GST_OBJECT_PARENT (buf));

  channels = buf->spec.channels;

  /* get input buffers */
  for (i = 0; i < channels; i++)
    src->buffers[i] =
        (sample_t *) jack_port_get_buffer (src->ports[i], nframes);

  if (gst_ring_buffer_prepare_read (buf, &writeseg, &writeptr, &len)) {
    flen = len / channels;

    /* the number of samples must be exactly the segment size */
    if (nframes * sizeof (sample_t) != flen)
      goto wrong_size;

    /* the samples in the jack input buffers have to be interleaved into the
     * ringbuffer */
    data = (sample_t *) writeptr;
    for (i = 0; i < nframes; ++i)
      for (j = 0; j < channels; ++j)
        *data++ = src->buffers[j][i];

    GST_DEBUG ("copy %d frames: %p, %d bytes, %d channels", nframes, writeptr,
        len / channels, channels);

    /* we wrote one segment */
    gst_ring_buffer_advance (buf, 1);
  }
  return 0;

  /* ERRORS */
wrong_size:
  {
    GST_ERROR_OBJECT (src, "nbytes (%d) != flen (%d)",
        (gint) (nframes * sizeof (sample_t)), flen);
    return 1;
  }
}
Пример #4
0
static OSStatus
gst_osx_audio_src_io_proc (GstOsxRingBuffer * buf,
    AudioUnitRenderActionFlags * ioActionFlags,
    const AudioTimeStamp * inTimeStamp,
    UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * bufferList)
{
  OSStatus status;
  guint8 *writeptr;
  gint writeseg;
  gint len;
  gint remaining;
  gint offset = 0;

  status = AudioUnitRender (buf->core_audio->audiounit, ioActionFlags,
      inTimeStamp, inBusNumber, inNumberFrames, buf->core_audio->recBufferList);

  if (status) {
    GST_WARNING_OBJECT (buf, "AudioUnitRender returned %d", (int) status);
    return status;
  }

  remaining = buf->core_audio->recBufferList->mBuffers[0].mDataByteSize;

  while (remaining) {
    if (!gst_ring_buffer_prepare_read (GST_RING_BUFFER (buf),
            &writeseg, &writeptr, &len))
      return 0;

    len -= buf->segoffset;

    if (len > remaining)
      len = remaining;

    memcpy (writeptr + buf->segoffset,
        (char *) buf->core_audio->recBufferList->mBuffers[0].mData + offset,
        len);

    buf->segoffset += len;
    offset += len;
    remaining -= len;

    if ((gint) buf->segoffset == GST_RING_BUFFER (buf)->spec.segsize) {
      /* we wrote one segment */
      gst_ring_buffer_advance (GST_RING_BUFFER (buf), 1);

      buf->segoffset = 0;
    }
  }
  return 0;
}
Пример #5
0
/* this is the callback of jack. This should RT-safe.
 */
static int
jack_process_cb (jack_nframes_t nframes, void *arg)
{
  GstJackAudioSink *sink;
  GstRingBuffer *buf;
  gint readseg, len;
  guint8 *readptr;
  gint i, j, flen, channels;
  sample_t *data;

  buf = GST_RING_BUFFER_CAST (arg);
  sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf));

  channels = buf->spec.channels;

  /* get target buffers */
  for (i = 0; i < channels; i++) {
    sink->buffers[i] =
        (sample_t *) jack_port_get_buffer (sink->ports[i], nframes);
  }

  if (gst_ring_buffer_prepare_read (buf, &readseg, &readptr, &len)) {
    flen = len / channels;

    /* the number of samples must be exactly the segment size */
    if (nframes * sizeof (sample_t) != flen)
      goto wrong_size;

    GST_DEBUG_OBJECT (sink, "copy %d frames: %p, %d bytes, %d channels",
        nframes, readptr, flen, channels);
    data = (sample_t *) readptr;

    /* the samples in the ringbuffer have the channels interleaved, we need to
     * deinterleave into the jack target buffers */
    for (i = 0; i < nframes; i++) {
      for (j = 0; j < channels; j++) {
        sink->buffers[j][i] = *data++;
      }
    }

    /* clear written samples in the ringbuffer */
    gst_ring_buffer_clear (buf, readseg);

    /* we wrote one segment */
    gst_ring_buffer_advance (buf, 1);
  } else {
    GST_DEBUG_OBJECT (sink, "write %d frames silence", nframes);
    /* We are not allowed to read from the ringbuffer, write silence to all
     * jack output buffers */
    for (i = 0; i < channels; i++) {
      memset (sink->buffers[i], 0, nframes * sizeof (sample_t));
    }
  }
  return 0;

  /* ERRORS */
wrong_size:
  {
    GST_ERROR_OBJECT (sink, "nbytes (%d) != flen (%d)",
        (gint) (nframes * sizeof (sample_t)), flen);
    return 1;
  }
}
Пример #6
0
/* this internal thread does nothing else but write samples to the audio device.
 * It will write each segment in the ringbuffer and will update the play
 * pointer.
 * The start/stop methods control the thread.
 */
static void
audioringbuffer_thread_func (GstRingBuffer * buf)
{
  GstAudioSink *sink;
  GstAudioSinkClass *csink;
  GstAudioRingBuffer *abuf = GST_AUDIORING_BUFFER_CAST (buf);
  WriteFunc writefunc;
  GstMessage *message;
  GValue val = { 0 };

  sink = GST_AUDIO_SINK (GST_OBJECT_PARENT (buf));
  csink = GST_AUDIO_SINK_GET_CLASS (sink);

  GST_DEBUG_OBJECT (sink, "enter thread");

  GST_OBJECT_LOCK (abuf);
  GST_DEBUG_OBJECT (sink, "signal wait");
  GST_AUDIORING_BUFFER_SIGNAL (buf);
  GST_OBJECT_UNLOCK (abuf);

  writefunc = csink->write;
  if (writefunc == NULL)
    goto no_function;

  g_value_init (&val, G_TYPE_POINTER);
  g_value_set_pointer (&val, sink->thread);
  message = gst_message_new_stream_status (GST_OBJECT_CAST (buf),
      GST_STREAM_STATUS_TYPE_ENTER, GST_ELEMENT_CAST (sink));
  gst_message_set_stream_status_object (message, &val);
  GST_DEBUG_OBJECT (sink, "posting ENTER stream status");
  gst_element_post_message (GST_ELEMENT_CAST (sink), message);

  while (TRUE) {
    gint left, len;
    guint8 *readptr;
    gint readseg;

    /* buffer must be started */
    if (gst_ring_buffer_prepare_read (buf, &readseg, &readptr, &len)) {
      gint written;

      left = len;
      do {
        written = writefunc (sink, readptr, left);
        GST_LOG_OBJECT (sink, "transfered %d bytes of %d from segment %d",
            written, left, readseg);
        if (written < 0 || written > left) {
          /* might not be critical, it e.g. happens when aborting playback */
          GST_WARNING_OBJECT (sink,
              "error writing data in %s (reason: %s), skipping segment (left: %d, written: %d)",
              GST_DEBUG_FUNCPTR_NAME (writefunc),
              (errno > 1 ? g_strerror (errno) : "unknown"), left, written);
          break;
        }
        left -= written;
        readptr += written;
      } while (left > 0);

      /* clear written samples */
      gst_ring_buffer_clear (buf, readseg);

      /* we wrote one segment */
      gst_ring_buffer_advance (buf, 1);
    } else {
      GST_OBJECT_LOCK (abuf);
      if (!abuf->running)
        goto stop_running;
      GST_DEBUG_OBJECT (sink, "signal wait");
      GST_AUDIORING_BUFFER_SIGNAL (buf);
      GST_DEBUG_OBJECT (sink, "wait for action");
#ifndef GSTREAMER_LITE
      GST_AUDIORING_BUFFER_WAIT (buf);
#else // GSTREAMER_LITE
      // In same cases we may have condition when we waiting here for ring buffer to start,
      // while ring buffer is started and data is available. So, lets use wait with timeout
      // and recheck if we good to go. wait_segment() will start ring buffer when data is available.
      {
          GTimeVal timeout;
          g_get_current_time(&timeout);
          g_time_val_add(&timeout, 100000); // 100 millisecond
          GST_AUDIORING_BUFFER_TIMED_WAIT (buf, &timeout);
      }
#endif // GSTREAMER_LITE
      GST_DEBUG_OBJECT (sink, "got signal");
      if (!abuf->running)
        goto stop_running;
      GST_DEBUG_OBJECT (sink, "continue running");
      GST_OBJECT_UNLOCK (abuf);
    }
  }

  /* Will never be reached */
  g_assert_not_reached ();
  return;

  /* ERROR */
no_function:
  {
    GST_DEBUG_OBJECT (sink, "no write function, exit thread");
    return;
  }
stop_running:
  {
    GST_OBJECT_UNLOCK (abuf);
    GST_DEBUG_OBJECT (sink, "stop running, exit thread");
    message = gst_message_new_stream_status (GST_OBJECT_CAST (buf),
        GST_STREAM_STATUS_TYPE_LEAVE, GST_ELEMENT_CAST (sink));
    gst_message_set_stream_status_object (message, &val);
    GST_DEBUG_OBJECT (sink, "posting LEAVE stream status");
    gst_element_post_message (GST_ELEMENT_CAST (sink), message);
    return;
  }
}
Пример #7
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;
}
Пример #8
0
/* this internal thread does nothing else but read samples from the audio device.
 * It will read each segment in the ringbuffer and will update the play
 * pointer. 
 * The start/stop methods control the thread.
 */
static void
audioringbuffer_thread_func (GstRingBuffer * buf)
{
  GstAudioSrc *src;
  GstAudioSrcClass *csrc;
  GstAudioRingBuffer *abuf = GST_AUDIORING_BUFFER (buf);
  ReadFunc readfunc;
  GstMessage *message;
  GValue val = { 0 };

  src = GST_AUDIO_SRC (GST_OBJECT_PARENT (buf));
  csrc = GST_AUDIO_SRC_GET_CLASS (src);

  GST_DEBUG_OBJECT (src, "enter thread");

  readfunc = csrc->read;
  if (readfunc == NULL)
    goto no_function;

  /* FIXME: maybe we should at least use a custom pointer type here? */
  g_value_init (&val, G_TYPE_POINTER);
  g_value_set_pointer (&val, src->thread);
  message = gst_message_new_stream_status (GST_OBJECT_CAST (buf),
      GST_STREAM_STATUS_TYPE_ENTER, GST_ELEMENT_CAST (src));
  gst_message_set_stream_status_object (message, &val);
  GST_DEBUG_OBJECT (src, "posting ENTER stream status");
  gst_element_post_message (GST_ELEMENT_CAST (src), message);

  while (TRUE) {
    gint left, len;
    guint8 *readptr;
    gint readseg;

    if (gst_ring_buffer_prepare_read (buf, &readseg, &readptr, &len)) {
      gint read;

      left = len;
      do {
        read = readfunc (src, readptr, left);
        GST_LOG_OBJECT (src, "transfered %d bytes of %d to segment %d", read,
            left, readseg);
        if (read < 0 || read > left) {
          GST_WARNING_OBJECT (src,
              "error reading data %d (reason: %s), skipping segment", read,
              g_strerror (errno));
          break;
        }
        left -= read;
        readptr += read;
      } while (left > 0);

      /* we read one segment */
      gst_ring_buffer_advance (buf, 1);
    } else {
      GST_OBJECT_LOCK (abuf);
      if (!abuf->running)
        goto stop_running;
      GST_DEBUG_OBJECT (src, "signal wait");
      GST_AUDIORING_BUFFER_SIGNAL (buf);
      GST_DEBUG_OBJECT (src, "wait for action");
      GST_AUDIORING_BUFFER_WAIT (buf);
      GST_DEBUG_OBJECT (src, "got signal");
      if (!abuf->running)
        goto stop_running;
      GST_DEBUG_OBJECT (src, "continue running");
      GST_OBJECT_UNLOCK (abuf);
    }
  }

  /* Will never be reached */
  g_assert_not_reached ();
  return;

  /* ERROR */
no_function:
  {
    GST_DEBUG ("no write function, exit thread");
    return;
  }
stop_running:
  {
    GST_OBJECT_UNLOCK (abuf);
    GST_DEBUG ("stop running, exit thread");
    message = gst_message_new_stream_status (GST_OBJECT_CAST (buf),
        GST_STREAM_STATUS_TYPE_LEAVE, GST_ELEMENT_CAST (src));
    gst_message_set_stream_status_object (message, &val);
    GST_DEBUG_OBJECT (src, "posting LEAVE stream status");
    gst_element_post_message (GST_ELEMENT_CAST (src), message);
    return;
  }
}