virtual HRESULT RenderAudioSamples (bool preroll)
  {
    guint8 *ptr;
    gint seg;
    gint len;
    gint bpf;
    guint written, written_sum;
    HRESULT res;

    GST_LOG_OBJECT (m_ringbuffer->sink, "Writing audio samples (preroll: %d)",
        preroll);

    if (!gst_audio_ring_buffer_prepare_read (GST_AUDIO_RING_BUFFER_CAST
            (m_ringbuffer), &seg, &ptr, &len)) {
      GST_WARNING_OBJECT (m_ringbuffer->sink, "No segment available");
      return E_FAIL;
    }

    bpf =
        GST_AUDIO_INFO_BPF (&GST_AUDIO_RING_BUFFER_CAST (m_ringbuffer)->
        spec.info);
    len /= bpf;
    GST_LOG_OBJECT (m_ringbuffer->sink,
        "Write audio samples: %p size %d segment: %d", ptr, len, seg);

    written_sum = 0;
    do {
      res =
          m_ringbuffer->output->output->ScheduleAudioSamples (ptr, len,
          0, 0, &written);
      len -= written;
      ptr += written * bpf;
      written_sum += written;
    } while (len > 0 && res == S_OK);

    GST_LOG_OBJECT (m_ringbuffer->sink, "Wrote %u samples: 0x%08x", written_sum,
        res);

    gst_audio_ring_buffer_clear (GST_AUDIO_RING_BUFFER_CAST (m_ringbuffer),
        seg);
    gst_audio_ring_buffer_advance (GST_AUDIO_RING_BUFFER_CAST (m_ringbuffer),
        1);

    return res;
  }
/* HALOutput AudioUnit will request fairly arbitrarily-sized chunks
 * of data, not of a fixed size. So, we keep track of where in
 * the current ringbuffer segment we are, and only advance the segment
 * once we've read the whole thing */
static OSStatus
gst_osx_audio_sink_io_proc (GstOsxAudioRingBuffer * buf,
    AudioUnitRenderActionFlags * ioActionFlags,
    const AudioTimeStamp * inTimeStamp,
    UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * bufferList)
{
  guint8 *readptr;
  gint readseg;
  gint len;
  gint stream_idx = buf->core_audio->stream_idx;
  gint remaining = bufferList->mBuffers[stream_idx].mDataByteSize;
  gint offset = 0;

  while (remaining) {
    if (!gst_audio_ring_buffer_prepare_read (GST_AUDIO_RING_BUFFER (buf),
            &readseg, &readptr, &len))
      return 0;

    len -= buf->segoffset;

    if (len > remaining)
      len = remaining;

    memcpy ((char *) bufferList->mBuffers[stream_idx].mData + offset,
        readptr + buf->segoffset, len);

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

    if ((gint) buf->segoffset == GST_AUDIO_RING_BUFFER (buf)->spec.segsize) {
      /* clear written samples */
      gst_audio_ring_buffer_clear (GST_AUDIO_RING_BUFFER (buf), readseg);

      /* we wrote one segment */
      gst_audio_ring_buffer_advance (GST_AUDIO_RING_BUFFER (buf), 1);

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

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

  channels = GST_AUDIO_INFO_CHANNELS (&buf->spec.info);

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

  if (gst_audio_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_audio_ring_buffer_clear (buf, readseg);

    /* we wrote one segment */
    gst_audio_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;
  }
}
/* 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 (GstAudioRingBuffer * buf)
{
    GstAudioSink *sink;
    GstAudioSinkClass *csink;
    GstAudioSinkRingBuffer *abuf = GST_AUDIO_SINK_RING_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_AUDIO_SINK_RING_BUFFER_SIGNAL (buf);
    GST_OBJECT_UNLOCK (abuf);

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

    message = gst_message_new_stream_status (GST_OBJECT_CAST (buf),
              GST_STREAM_STATUS_TYPE_ENTER, GST_ELEMENT_CAST (sink));
    g_value_init (&val, GST_TYPE_G_THREAD);
    g_value_set_boxed (&val, g_thread_self ());
    gst_message_set_stream_status_object (message, &val);
    g_value_unset (&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_audio_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_audio_ring_buffer_clear (buf, readseg);

            /* we wrote one segment */
            gst_audio_ring_buffer_advance (buf, 1);
        } else {
            GST_OBJECT_LOCK (abuf);
            if (!abuf->running)
                goto stop_running;
            if (G_UNLIKELY (g_atomic_int_get (&buf->state) ==
                            GST_AUDIO_RING_BUFFER_STATE_STARTED)) {
                GST_OBJECT_UNLOCK (abuf);
                continue;
            }
            GST_DEBUG_OBJECT (sink, "signal wait");
            GST_AUDIO_SINK_RING_BUFFER_SIGNAL (buf);
            GST_DEBUG_OBJECT (sink, "wait for action");
            GST_AUDIO_SINK_RING_BUFFER_WAIT (buf);
            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));
        g_value_init (&val, GST_TYPE_G_THREAD);
        g_value_set_boxed (&val, g_thread_self ());
        gst_message_set_stream_status_object (message, &val);
        g_value_unset (&val);
        GST_DEBUG_OBJECT (sink, "posting LEAVE stream status");
        gst_element_post_message (GST_ELEMENT_CAST (sink), message);
        return;
    }
}