static guint
gst_openal_sink_delay (GstAudioSink * asink)
{
    GstOpenALSink *openal = GST_OPENAL_SINK (asink);
    ALint queued, state, offset, delay;
    ALCcontext *old;

    if (!openal->context)
        return 0;

    GST_OPENAL_SINK_LOCK (openal);
    old = pushContext (openal->context);

    delay = 0;
    alGetSourcei (openal->sID, AL_BUFFERS_QUEUED, &queued);
    /* Order here is important. If the offset is queried after the state and an
     * underrun occurs in between the two calls, it can end up with a 0 offset
     * in a playing state, incorrectly reporting a len*queued/bps delay. */
    alGetSourcei (openal->sID, AL_BYTE_OFFSET, &offset);
    alGetSourcei (openal->sID, AL_SOURCE_STATE, &state);

    /* Note: state=stopped is an underrun, meaning all buffers are processed
     * and there's no delay when writing the next buffer. Pre-buffering is
     * state=initial, which will introduce a delay while writing. */
    if (checkALError () == AL_NO_ERROR && state != AL_STOPPED)
        delay = ((queued * openal->bID_length) - offset) / openal->bytes_per_sample;

    popContext (old, openal->context);
    GST_OPENAL_SINK_UNLOCK (openal);

    return delay;
}
static void
gst_openal_sink_reset (GstAudioSink * audiosink)
{
  GstOpenALSink *sink = GST_OPENAL_SINK (audiosink);
  ALCcontext *old;

  GST_OPENAL_SINK_LOCK (sink);
  old = pushContext (sink->default_context);

  sink->write_reset = AL_TRUE;
  alSourceStop (sink->default_source);
  alSourceRewind (sink->default_source);
  alSourcei (sink->default_source, AL_BUFFER, 0);
  checkALError ();

  popContext (old, sink->default_context);
  GST_OPENAL_SINK_UNLOCK (sink);
}
static void
gst_openal_sink_reset (GstAudioSink * asink)
{
    GstOpenALSink *openal = GST_OPENAL_SINK (asink);
    ALCcontext *old;

    GST_OPENAL_SINK_LOCK (openal);
    old = pushContext (openal->context);

    openal->write_reset = AL_TRUE;
    alSourceStop (openal->sID);
    alSourceRewind (openal->sID);
    alSourcei (openal->sID, AL_BUFFER, 0);
    checkALError ();

    popContext (old, openal->context);
    GST_OPENAL_SINK_UNLOCK (openal);
}
static guint
gst_openal_sink_delay (GstAudioSink * audiosink)
{
  GstOpenALSink *sink = GST_OPENAL_SINK (audiosink);
  ALint queued, state, offset, delay;
  ALCcontext *old;

  if (!sink->default_context)
    return 0;

  GST_OPENAL_SINK_LOCK (sink);
  old = pushContext (sink->default_context);

  delay = 0;
  alGetSourcei (sink->default_source, AL_BUFFERS_QUEUED, &queued);
  /* Order here is important. If the offset is queried after the state and an
   * underrun occurs in between the two calls, it can end up with a 0 offset
   * in a playing state, incorrectly reporting a len*queued/bps delay. */
  alGetSourcei (sink->default_source, AL_BYTE_OFFSET, &offset);
  alGetSourcei (sink->default_source, AL_SOURCE_STATE, &state);

  /* Note: state=stopped is an underrun, meaning all buffers are processed
   * and there's no delay when writing the next buffer. Pre-buffering is
   * state=initial, which will introduce a delay while writing. */
  if (checkALError () == AL_NO_ERROR && state != AL_STOPPED)
    delay =
        ((queued * sink->buffer_length) -
        offset) / sink->bytes_per_sample / sink->channels / GST_MSECOND;

  popContext (old, sink->default_context);
  GST_OPENAL_SINK_UNLOCK (sink);

  if (G_UNLIKELY (delay < 0)) {
    /* make sure we never return a negative delay */
    GST_WARNING_OBJECT (openal_debug, "negative delay");
    delay = 0;
  }

  return delay;
}
static gint
gst_openal_sink_write (GstAudioSink * audiosink, gpointer data, guint length)
{
  GstOpenALSink *sink = GST_OPENAL_SINK (audiosink);
  ALint processed, queued, state;
  ALCcontext *old;
  gulong rest_us;

  g_assert (length == sink->buffer_length);

  old = pushContext (sink->default_context);

  rest_us =
      (guint64) (sink->buffer_length / sink->bytes_per_sample) *
      G_USEC_PER_SEC / sink->rate / sink->channels;
  do {
    alGetSourcei (sink->default_source, AL_SOURCE_STATE, &state);
    alGetSourcei (sink->default_source, AL_BUFFERS_QUEUED, &queued);
    alGetSourcei (sink->default_source, AL_BUFFERS_PROCESSED, &processed);
    if (checkALError () != AL_NO_ERROR) {
      GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL),
          ("Source state error detected"));
      length = 0;
      goto out_nolock;
    }

    if (processed > 0 || queued < sink->buffer_count)
      break;
    if (state != AL_PLAYING)
      alSourcePlay (sink->default_source);
    g_usleep (rest_us);
  }
  while (1);

  GST_OPENAL_SINK_LOCK (sink);
  if (sink->write_reset != AL_FALSE) {
    sink->write_reset = AL_FALSE;
    length = 0;
    goto out;
  }

  queued -= processed;
  while (processed-- > 0) {
    ALuint bid;
    alSourceUnqueueBuffers (sink->default_source, 1, &bid);
  }
  if (state == AL_STOPPED) {
    /* "Restore" from underruns (not actually needed, but it keeps delay
     * calculations correct while rebuffering) */
    alSourceRewind (sink->default_source);
  }

  alBufferData (sink->buffers[sink->buffer_idx], sink->format,
      data, sink->buffer_length, sink->rate);
  alSourceQueueBuffers (sink->default_source, 1,
      &sink->buffers[sink->buffer_idx]);
  sink->buffer_idx = (sink->buffer_idx + 1) % sink->buffer_count;
  queued++;

  if (state != AL_PLAYING && queued == sink->buffer_count)
    alSourcePlay (sink->default_source);

  if (checkALError () != AL_NO_ERROR) {
    GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL),
        ("Source queue error detected"));
    goto out;
  }

out:
  GST_OPENAL_SINK_UNLOCK (sink);
out_nolock:
  popContext (old, sink->default_context);
  return length;
}