Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
static void
gst_openal_sink_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GstOpenALSink *sink = GST_OPENAL_SINK (object);

  switch (prop_id) {
    case PROP_DEVICE:
      g_free (sink->device_name);
      sink->device_name = g_value_dup_string (value);
      if (sink->probed_caps)
        gst_caps_unref (sink->probed_caps);
      sink->probed_caps = NULL;
      break;
    case PROP_USER_DEVICE:
      if (!sink->default_device)
        sink->user_device = g_value_get_pointer (value);
      break;
    case PROP_USER_CONTEXT:
      if (!sink->default_device)
        sink->user_context = g_value_get_pointer (value);
      break;
    case PROP_USER_SOURCE:
      if (!sink->default_device)
        sink->user_source = g_value_get_uint (value);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}
Ejemplo n.º 3
0
static gboolean
gst_openal_sink_unprepare (GstAudioSink * asink)
{
    GstOpenALSink *openal = GST_OPENAL_SINK (asink);
    ALCcontext *old;

    if (!openal->context)
        return TRUE;

    old = pushContext (openal->context);

    alSourceStop (openal->sID);
    alSourcei (openal->sID, AL_BUFFER, 0);

    if (!openal->custom_sID)
        alDeleteSources (1, &openal->sID);
    openal->sID = 0;

    alDeleteBuffers (openal->bID_count, openal->bIDs);
    g_free (openal->bIDs);
    openal->bIDs = NULL;
    openal->bID_idx = 0;
    openal->bID_count = 0;
    openal->bID_length = 0;

    checkALError ();
    popContext (old, openal->context);
    if (!openal->custom_ctx)
        alcDestroyContext (openal->context);
    openal->context = NULL;

    return TRUE;
}
Ejemplo n.º 4
0
static gboolean
gst_openal_sink_open (GstAudioSink * audiosink)
{
  GstOpenALSink *sink = GST_OPENAL_SINK (audiosink);

  if (sink->user_device) {
    ALCint value = -1;
    alcGetIntegerv (sink->user_device, ALC_ATTRIBUTES_SIZE, 1, &value);
    if (value > 0) {
      if (!sink->user_context
          || alcGetContextsDevice (sink->user_context) == sink->user_device)
        sink->default_device = sink->user_device;
    }
  } else if (sink->user_context)
    sink->default_device = alcGetContextsDevice (sink->user_context);
  else
    sink->default_device = alcOpenDevice (sink->device_name);
  if (!sink->default_device) {
    GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE,
        ("Could not open device."), GST_ALC_ERROR (sink->default_device));
    return FALSE;
  }

  return TRUE;
}
Ejemplo n.º 5
0
static gboolean
gst_openal_sink_unprepare (GstAudioSink * audiosink)
{
  GstOpenALSink *sink = GST_OPENAL_SINK (audiosink);
  ALCcontext *old;

  if (!sink->default_context)
    return TRUE;

  old = pushContext (sink->default_context);

  alSourceStop (sink->default_source);
  alSourcei (sink->default_source, AL_BUFFER, 0);

  if (!sink->user_source)
    alDeleteSources (1, &sink->default_source);
  sink->default_source = 0;

  alDeleteBuffers (sink->buffer_count, sink->buffers);
  g_free (sink->buffers);
  sink->buffers = NULL;
  sink->buffer_idx = 0;
  sink->buffer_count = 0;
  sink->buffer_length = 0;

  checkALError ();
  popContext (old, sink->default_context);
  if (!sink->user_context)
    alcDestroyContext (sink->default_context);
  sink->default_context = NULL;

  return TRUE;
}
Ejemplo n.º 6
0
static gboolean
gst_openal_sink_open (GstAudioSink * asink)
{
    GstOpenALSink *openal = GST_OPENAL_SINK (asink);

    if (openal->custom_dev) {
        ALCint val = -1;
        alcGetIntegerv (openal->custom_dev, ALC_ATTRIBUTES_SIZE, 1, &val);
        if (val > 0) {
            if (!openal->custom_ctx ||
                    alcGetContextsDevice (openal->custom_ctx) == openal->custom_dev)
                openal->device = openal->custom_dev;
        }
    } else if (openal->custom_ctx)
        openal->device = alcGetContextsDevice (openal->custom_ctx);
    else
        openal->device = alcOpenDevice (openal->devname);
    if (!openal->device) {
        GST_ELEMENT_ERROR (openal, RESOURCE, OPEN_WRITE,
                           ("Could not open audio device for playback."),
                           GST_ALC_ERROR (openal->device));
        return FALSE;
    }

    return TRUE;
}
Ejemplo n.º 7
0
static GstCaps *
gst_openal_sink_getcaps (GstBaseSink * bsink)
{
    GstOpenALSink *sink = GST_OPENAL_SINK (bsink);
    GstCaps *caps;

    if (sink->device == NULL) {
        GstPad *pad = GST_BASE_SINK_PAD (bsink);
        caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
    } else if (sink->probed_caps)
        caps = gst_caps_copy (sink->probed_caps);
    else {
        if (sink->context)
            caps = gst_openal_helper_probe_caps (sink->context);
        else if (sink->custom_ctx)
            caps = gst_openal_helper_probe_caps (sink->custom_ctx);
        else {
            ALCcontext *ctx = alcCreateContext (sink->device, NULL);
            if (ctx) {
                caps = gst_openal_helper_probe_caps (ctx);
                alcDestroyContext (ctx);
            } else {
                GST_ELEMENT_WARNING (sink, RESOURCE, FAILED,
                                     ("Could not create temporary context."),
                                     GST_ALC_ERROR (sink->device));
                caps = NULL;
            }
        }

        if (caps && !gst_caps_is_empty (caps))
            sink->probed_caps = gst_caps_copy (caps);
    }

    return caps;
}
Ejemplo n.º 8
0
static void
gst_openal_sink_dispose (GObject * object)
{
  GstOpenALSink *sink = GST_OPENAL_SINK (object);

  if (sink->probed_caps)
    gst_caps_unref (sink->probed_caps);
  sink->probed_caps = NULL;

  G_OBJECT_CLASS (gst_openal_sink_parent_class)->dispose (object);
}
Ejemplo n.º 9
0
static void
gst_openal_sink_finalize (GObject * object)
{
  GstOpenALSink *sink = GST_OPENAL_SINK (object);

  g_free (sink->device_name);
  sink->device_name = NULL;
  g_mutex_clear (&sink->openal_lock);

  G_OBJECT_CLASS (gst_openal_sink_parent_class)->finalize (object);
}
Ejemplo n.º 10
0
static GstCaps *
gst_openal_sink_getcaps (GstBaseSink * basesink, GstCaps * filter)
{
  GstOpenALSink *sink = GST_OPENAL_SINK (basesink);
  GstCaps *caps;

  if (sink->default_device == NULL) {
    GstPad *pad = GST_BASE_SINK_PAD (basesink);
    GstCaps *tcaps = gst_pad_get_pad_template_caps (pad);
    caps = gst_caps_copy (tcaps);
    gst_caps_unref (tcaps);
  } else if (sink->probed_caps)
    caps = gst_caps_copy (sink->probed_caps);
  else {
    if (sink->default_context)
      caps = gst_openal_helper_probe_caps (sink->default_context);
    else if (sink->user_context)
      caps = gst_openal_helper_probe_caps (sink->user_context);
    else {
      ALCcontext *context = alcCreateContext (sink->default_device, NULL);
      if (context) {
        caps = gst_openal_helper_probe_caps (context);
        alcDestroyContext (context);
      } else {
        GST_ELEMENT_WARNING (sink, RESOURCE, FAILED,
            ("Could not create temporary context."),
            GST_ALC_ERROR (sink->default_device));
        caps = NULL;
      }
    }

    if (caps && !gst_caps_is_empty (caps))
      sink->probed_caps = gst_caps_copy (caps);
  }

  if (filter) {
    GstCaps *intersection;

    intersection =
        gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
    return intersection;
  } else {
    return caps;
  }
}
Ejemplo n.º 11
0
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);
}
Ejemplo n.º 12
0
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);
}
Ejemplo n.º 13
0
static gboolean
gst_openal_sink_close (GstAudioSink * asink)
{
    GstOpenALSink *openal = GST_OPENAL_SINK (asink);

    if (!openal->custom_dev && !openal->custom_ctx) {
        if (alcCloseDevice (openal->device) == ALC_FALSE) {
            GST_ELEMENT_ERROR (openal, RESOURCE, CLOSE,
                               ("Could not close audio device."), GST_ALC_ERROR (openal->device));
            return FALSE;
        }
    }
    openal->device = NULL;

    if (openal->probed_caps)
        gst_caps_unref (openal->probed_caps);
    openal->probed_caps = NULL;

    return TRUE;
}
Ejemplo n.º 14
0
static gboolean
gst_openal_sink_close (GstAudioSink * audiosink)
{
  GstOpenALSink *sink = GST_OPENAL_SINK (audiosink);

  if (!sink->user_device && !sink->user_context) {
    if (alcCloseDevice (sink->default_device) == ALC_FALSE) {
      GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE,
          ("Could not close device."), GST_ALC_ERROR (sink->default_device));
      return FALSE;
    }
  }
  sink->default_device = NULL;

  if (sink->probed_caps)
    gst_caps_unref (sink->probed_caps);
  sink->probed_caps = NULL;

  return TRUE;
}
Ejemplo n.º 15
0
static void
gst_openal_sink_get_property (GObject * object, guint prop_id, GValue * value,
    GParamSpec * pspec)
{
  GstOpenALSink *sink = GST_OPENAL_SINK (object);
  const ALCchar *device_name = sink->device_name;
  ALCdevice *device = sink->default_device;
  ALCcontext *context = sink->default_context;
  ALuint source = sink->default_source;

  switch (prop_id) {
    case PROP_DEVICE_NAME:
      device_name = "";
      if (device)
        device_name = alcGetString (device, ALC_DEVICE_SPECIFIER);
      /* fall-through */
    case PROP_DEVICE:
      g_value_set_string (value, device_name);
      break;
    case PROP_USER_DEVICE:
      if (!device)
        device = sink->user_device;
      g_value_set_pointer (value, device);
      break;
    case PROP_USER_CONTEXT:
      if (!context)
        context = sink->user_context;
      g_value_set_pointer (value, context);
      break;
    case PROP_USER_SOURCE:
      if (!source)
        source = sink->user_source;
      g_value_set_uint (value, source);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}
Ejemplo n.º 16
0
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;
}
Ejemplo n.º 17
0
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;
}
Ejemplo n.º 18
0
static gboolean
gst_openal_sink_prepare (GstAudioSink * audiosink,
    GstAudioRingBufferSpec * spec)
{
  GstOpenALSink *sink = GST_OPENAL_SINK (audiosink);
  ALCcontext *context, *old;

  if (sink->default_context && !gst_openal_sink_unprepare (audiosink))
    return FALSE;

  if (sink->user_context)
    context = sink->user_context;
  else {
    ALCint attribs[3] = { 0, 0, 0 };

    /* Don't try to change the playback frequency of an app's device */
    if (!sink->user_device) {
      attribs[0] = ALC_FREQUENCY;
      attribs[1] = GST_AUDIO_INFO_RATE (&spec->info);
      attribs[2] = 0;
    }

    context = alcCreateContext (sink->default_device, attribs);
    if (!context) {
      GST_ELEMENT_ERROR (sink, RESOURCE, FAILED,
          ("Unable to prepare device."), GST_ALC_ERROR (sink->default_device));
      return FALSE;
    }
  }

  old = pushContext (context);

  if (sink->user_source) {
    if (!sink->user_context || !alIsSource (sink->user_source)) {
      GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND, (NULL),
          ("Invalid source specified for context"));
      goto fail;
    }
    sink->default_source = sink->user_source;
  } else {
    ALuint source;

    alGenSources (1, &source);
    if (checkALError () != AL_NO_ERROR) {
      GST_ELEMENT_ERROR (sink, RESOURCE, NO_SPACE_LEFT, (NULL),
          ("Unable to generate source"));
      goto fail;
    }
    sink->default_source = source;
  }

  gst_openal_sink_parse_spec (sink, spec);
  if (sink->format == AL_NONE) {
    GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL),
        ("Unable to get type %d, format %d, and %d channels", spec->type,
            GST_AUDIO_INFO_FORMAT (&spec->info),
            GST_AUDIO_INFO_CHANNELS (&spec->info)));
    goto fail;
  }

  sink->buffers = g_malloc (sink->buffer_count * sizeof (*sink->buffers));
  if (!sink->buffers) {
    GST_ELEMENT_ERROR (sink, RESOURCE, FAILED, ("Out of memory."),
        ("Unable to allocate buffers"));
    goto fail;
  }

  alGenBuffers (sink->buffer_count, sink->buffers);
  if (checkALError () != AL_NO_ERROR) {
    GST_ELEMENT_ERROR (sink, RESOURCE, NO_SPACE_LEFT, (NULL),
        ("Unable to generate %d buffers", sink->buffer_count));
    goto fail;
  }
  sink->buffer_idx = 0;

  popContext (old, context);
  sink->default_context = context;
  return TRUE;

fail:
  if (!sink->user_source && sink->default_source)
    alDeleteSources (1, &sink->default_source);
  sink->default_source = 0;

  g_free (sink->buffers);
  sink->buffers = NULL;
  sink->buffer_count = 0;
  sink->buffer_length = 0;

  popContext (old, context);
  if (!sink->user_context)
    alcDestroyContext (context);
  return FALSE;
}
Ejemplo n.º 19
0
static gboolean
gst_openal_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec)
{
    GstOpenALSink *openal = GST_OPENAL_SINK (asink);
    ALCcontext *ctx, *old;

    if (openal->context && !gst_openal_sink_unprepare (asink))
        return FALSE;

    if (openal->custom_ctx)
        ctx = openal->custom_ctx;
    else {
        ALCint attribs[3] = { 0, 0, 0 };

        /* Don't try to change the playback frequency of an app's device */
        if (!openal->custom_dev) {
            attribs[0] = ALC_FREQUENCY;
            attribs[1] = spec->rate;
            attribs[2] = 0;
        }

        ctx = alcCreateContext (openal->device, attribs);
        if (!ctx) {
            GST_ELEMENT_ERROR (openal, RESOURCE, FAILED,
                               ("Unable to prepare device."), GST_ALC_ERROR (openal->device));
            return FALSE;
        }
    }

    old = pushContext (ctx);

    if (openal->custom_sID) {
        if (!openal->custom_ctx || !alIsSource (openal->custom_sID)) {
            GST_ELEMENT_ERROR (openal, RESOURCE, NOT_FOUND, (NULL),
                               ("Invalid source ID specified for context"));
            goto fail;
        }
        openal->sID = openal->custom_sID;
    } else {
        ALuint sourceID;

        alGenSources (1, &sourceID);
        if (checkALError () != AL_NO_ERROR) {
            GST_ELEMENT_ERROR (openal, RESOURCE, NO_SPACE_LEFT, (NULL),
                               ("Unable to generate source"));
            goto fail;
        }
        openal->sID = sourceID;
    }

    gst_openal_sink_parse_spec (openal, spec);
    if (openal->format == AL_NONE) {
        GST_ELEMENT_ERROR (openal, RESOURCE, SETTINGS, (NULL),
                           ("Unable to get type %d, format %d, and %d channels",
                            spec->type, spec->format, spec->channels));
        goto fail;
    }

    openal->bIDs = g_malloc (openal->bID_count * sizeof (*openal->bIDs));
    if (!openal->bIDs) {
        GST_ELEMENT_ERROR (openal, RESOURCE, FAILED, ("Out of memory."),
                           ("Unable to allocate buffer IDs"));
        goto fail;
    }

    alGenBuffers (openal->bID_count, openal->bIDs);
    if (checkALError () != AL_NO_ERROR) {
        GST_ELEMENT_ERROR (openal, RESOURCE, NO_SPACE_LEFT, (NULL),
                           ("Unable to generate %d buffers", openal->bID_count));
        goto fail;
    }
    openal->bID_idx = 0;

    popContext (old, ctx);
    openal->context = ctx;
    return TRUE;

fail:
    if (!openal->custom_sID && openal->sID)
        alDeleteSources (1, &openal->sID);
    openal->sID = 0;

    g_free (openal->bIDs);
    openal->bIDs = NULL;
    openal->bID_count = 0;
    openal->bID_length = 0;

    popContext (old, ctx);
    if (!openal->custom_ctx)
        alcDestroyContext (ctx);
    return FALSE;
}