pulse: remove pulseaudiosink helper bin

This is causing us lots of headaches in 0.10 and needs to be done
differently and properly in 0.11. playbin or decodebin should
reconfigure themselves based on reconfigure events, for example.
This commit is contained in:
Tim-Philipp Müller 2011-12-25 22:17:53 +00:00
parent 2799bcd32e
commit ff74718616
6 changed files with 16 additions and 1002 deletions

View file

@ -9,21 +9,6 @@
<package>GStreamer Good Plug-ins git</package>
<origin>Unknown package origin</origin>
<elements>
<element>
<name>pulseaudiosink</name>
<longname>Bin wrapping pulsesink</longname>
<class>Sink/Audio/Bin</class>
<description>Correctly handles sink changes when streaming compressed formats to pulsesink</description>
<author>Arun Raghavan &lt;arun.raghavan@collabora.co.uk&gt;</author>
<pads>
<caps>
<name>sink</name>
<direction>sink</direction>
<presence>always</presence>
<details>audio/x-raw-int, endianness=(int){ 1234, 4321 }, signed=(boolean)true, width=(int)16, depth=(int)16, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 32 ]; audio/x-raw-float, endianness=(int){ 1234, 4321 }, width=(int)32, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 32 ]; audio/x-raw-int, endianness=(int){ 1234, 4321 }, signed=(boolean)true, width=(int)32, depth=(int)32, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 32 ]; audio/x-raw-int, signed=(boolean)false, width=(int)8, depth=(int)8, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 32 ]; audio/x-alaw, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 32 ]; audio/x-mulaw, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 32 ]; audio/x-raw-int, endianness=(int){ 1234, 4321 }, signed=(boolean)true, width=(int)24, depth=(int)24, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 32 ]; audio/x-raw-int, endianness=(int){ 1234, 4321 }, signed=(boolean)true, width=(int)32, depth=(int)24, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 32 ]; audio/x-ac3, framed=(boolean)true; audio/x-eac3, framed=(boolean)true; audio/x-dts, framed=(boolean)true, block-size=(int){ 512, 1024, 2048 }; audio/mpeg, mpegversion=(int)1, mpegaudioversion=(int)[ 1, 2 ], parsed=(boolean)true</details>
</caps>
</pads>
</element>
<element>
<name>pulsemixer</name>
<longname>PulseAudio Mixer</longname>
@ -64,4 +49,4 @@
</pads>
</element>
</elements>
</plugin>
</plugin>

View file

@ -7,7 +7,6 @@ libgstpulse_la_SOURCES = \
pulsemixertrack.c \
pulseprobe.c \
pulsesink.c \
pulseaudiosink.c \
pulsesrc.c \
pulseutil.c

View file

@ -49,12 +49,6 @@ plugin_init (GstPlugin * plugin)
GST_TYPE_PULSESRC))
return FALSE;
/* FIXME 0.11: this helper bin sink should just go away, reconfiguration
* should be handled using reconfigure events */
if (!gst_element_register (plugin, "pulseaudiosink", GST_RANK_PRIMARY + 11,
GST_TYPE_PULSE_AUDIO_SINK))
return FALSE;
if (!gst_element_register (plugin, "pulsemixer", GST_RANK_NONE,
GST_TYPE_PULSEMIXER))
return FALSE;

View file

@ -1,960 +0,0 @@
/*-*- Mode: C; c-basic-offset: 2 -*-*/
/* GStreamer pulseaudio plugin
*
* Copyright (c) 2011 Intel Corporation
* 2011 Collabora
* 2011 Arun Raghavan <arun.raghavan@collabora.co.uk>
* 2011 Sebastian Dröge <sebastian.droege@collabora.co.uk>
*
* gst-pulse is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* gst-pulse is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with gst-pulse; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
/**
* SECTION:element-pulseaudiosink
* @see_also: pulsesink, pulsesrc, pulsemixer
*
* This element outputs audio to a
* <ulink href="http://www.pulseaudio.org">PulseAudio sound server</ulink> via
* the @pulsesink element. It transparently takes care of passing compressed
* format as-is if the sink supports it, decoding if necessary, and changes
* to supported formats at runtime.
*
* <refsect2>
* <title>Example pipelines</title>
* |[
* gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisdec ! pulseaudiosink
* ]| Decode and play an Ogg/Vorbis file.
* |[
* gst-launch -v filesrc location=test.mp3 ! mp3parse ! pulseaudiosink stream-properties="props,media.title=test"
* ]| Play an MP3 file on a sink that supports decoding directly, plug in a
* decoder if/when required.
* </refsect2>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/* FIXME 0.11: pulseaudiosink helper bin must die */
#include <gst/pbutils/pbutils.h>
#include <gst/gst-i18n-plugin.h>
#include <gst/audio/gstaudioiec61937.h>
#include "pulsesink.h"
GST_DEBUG_CATEGORY (pulseaudiosink_debug);
#define GST_CAT_DEFAULT (pulseaudiosink_debug)
#define GST_PULSE_AUDIO_SINK_LOCK(obj) G_STMT_START { \
GST_LOG_OBJECT (obj, \
"locking from thread %p", \
g_thread_self ()); \
g_mutex_lock (GST_PULSE_AUDIO_SINK_CAST(obj)->lock); \
GST_LOG_OBJECT (obj, \
"locked from thread %p", \
g_thread_self ()); \
} G_STMT_END
#define GST_PULSE_AUDIO_SINK_UNLOCK(obj) G_STMT_START { \
GST_LOG_OBJECT (obj, \
"unlocking from thread %p", \
g_thread_self ()); \
g_mutex_unlock (GST_PULSE_AUDIO_SINK_CAST(obj)->lock); \
} G_STMT_END
typedef struct
{
GstBin parent;
GMutex *lock;
GstPad *sinkpad;
GstPad *sink_proxypad;
GstPadEventFunction sinkpad_old_eventfunc;
GstPadEventFunction proxypad_old_eventfunc;
GstPulseSink *psink;
GstElement *dbin;
GstSegment segment;
guint event_probe_id;
gulong pad_added_id;
guint block_probe_id;
gboolean format_lost;
} GstPulseAudioSink;
typedef struct
{
GstBinClass parent_class;
guint n_prop_own;
guint n_prop_total;
} GstPulseAudioSinkClass;
static void gst_pulse_audio_sink_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void gst_pulse_audio_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_pulse_audio_sink_dispose (GObject * object);
static gboolean gst_pulse_audio_sink_src_event (GstPad * pad,
GstObject * parent, GstEvent * event);
static gboolean gst_pulse_audio_sink_sink_event (GstPad * pad,
GstObject * parent, GstEvent * event);
static gboolean gst_pulse_audio_sink_sink_query (GstPad * pad,
GstObject * parent, GstQuery * query);
static gboolean gst_pulse_audio_sink_sink_acceptcaps (GstPulseAudioSink * pbin,
GstPad * pad, GstCaps * caps);
static GstStateChangeReturn gst_pulse_audio_sink_change_state (GstElement *
element, GstStateChange transition);
static gboolean gst_pulse_audio_sink_set_caps (GstPulseAudioSink * pbin,
GstCaps * caps);
#define gst_pulse_audio_sink_parent_class parent_class
G_DEFINE_TYPE (GstPulseAudioSink, gst_pulse_audio_sink, GST_TYPE_BIN);
static GstStaticPadTemplate sink_template =
GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
GST_STATIC_CAPS (PULSE_SINK_TEMPLATE_CAPS));
static GParamSpec *
param_spec_copy (GParamSpec * spec)
{
const char *name, *nick, *blurb;
GParamFlags flags;
name = g_param_spec_get_name (spec);
nick = g_param_spec_get_nick (spec);
blurb = g_param_spec_get_blurb (spec);
flags = spec->flags;
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_BOOLEAN) {
return g_param_spec_boolean (name, nick, blurb,
G_PARAM_SPEC_BOOLEAN (spec)->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_BOXED) {
return g_param_spec_boxed (name, nick, blurb, spec->value_type, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_CHAR) {
GParamSpecChar *cspec = G_PARAM_SPEC_CHAR (spec);
return g_param_spec_char (name, nick, blurb, cspec->minimum,
cspec->maximum, cspec->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_DOUBLE) {
GParamSpecDouble *dspec = G_PARAM_SPEC_DOUBLE (spec);
return g_param_spec_double (name, nick, blurb, dspec->minimum,
dspec->maximum, dspec->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_ENUM) {
return g_param_spec_enum (name, nick, blurb, spec->value_type,
G_PARAM_SPEC_ENUM (spec)->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_FLAGS) {
return g_param_spec_flags (name, nick, blurb, spec->value_type,
G_PARAM_SPEC_ENUM (spec)->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_FLOAT) {
GParamSpecFloat *fspec = G_PARAM_SPEC_FLOAT (spec);
return g_param_spec_double (name, nick, blurb, fspec->minimum,
fspec->maximum, fspec->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_GTYPE) {
return g_param_spec_gtype (name, nick, blurb,
G_PARAM_SPEC_GTYPE (spec)->is_a_type, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_INT) {
GParamSpecInt *ispec = G_PARAM_SPEC_INT (spec);
return g_param_spec_int (name, nick, blurb, ispec->minimum,
ispec->maximum, ispec->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_INT64) {
GParamSpecInt64 *ispec = G_PARAM_SPEC_INT64 (spec);
return g_param_spec_int64 (name, nick, blurb, ispec->minimum,
ispec->maximum, ispec->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_LONG) {
GParamSpecLong *lspec = G_PARAM_SPEC_LONG (spec);
return g_param_spec_long (name, nick, blurb, lspec->minimum,
lspec->maximum, lspec->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_OBJECT) {
return g_param_spec_object (name, nick, blurb, spec->value_type, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_PARAM) {
return g_param_spec_param (name, nick, blurb, spec->value_type, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_POINTER) {
return g_param_spec_pointer (name, nick, blurb, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_STRING) {
return g_param_spec_string (name, nick, blurb,
G_PARAM_SPEC_STRING (spec)->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_UCHAR) {
GParamSpecUChar *cspec = G_PARAM_SPEC_UCHAR (spec);
return g_param_spec_uchar (name, nick, blurb, cspec->minimum,
cspec->maximum, cspec->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_UINT) {
GParamSpecUInt *ispec = G_PARAM_SPEC_UINT (spec);
return g_param_spec_uint (name, nick, blurb, ispec->minimum,
ispec->maximum, ispec->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_UINT64) {
GParamSpecUInt64 *ispec = G_PARAM_SPEC_UINT64 (spec);
return g_param_spec_uint64 (name, nick, blurb, ispec->minimum,
ispec->maximum, ispec->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_ULONG) {
GParamSpecULong *lspec = G_PARAM_SPEC_ULONG (spec);
return g_param_spec_ulong (name, nick, blurb, lspec->minimum,
lspec->maximum, lspec->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_UNICHAR) {
return g_param_spec_unichar (name, nick, blurb,
G_PARAM_SPEC_UNICHAR (spec)->default_value, flags);
}
if (G_PARAM_SPEC_TYPE (spec) == G_TYPE_PARAM_VARIANT) {
GParamSpecVariant *vspec = G_PARAM_SPEC_VARIANT (spec);
return g_param_spec_variant (name, nick, blurb, vspec->type,
vspec->default_value, flags);
}
g_warning ("Unknown param type %ld for '%s'",
(long) G_PARAM_SPEC_TYPE (spec), name);
g_assert_not_reached ();
}
static void
gst_pulse_audio_sink_class_init (GstPulseAudioSinkClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GstElementClass *element_class = (GstElementClass *) klass;
GstPulseSinkClass *psink_class =
GST_PULSESINK_CLASS (g_type_class_ref (GST_TYPE_PULSESINK));
GParamSpec **specs;
guint n, i, j;
GST_DEBUG_CATEGORY_INIT (pulseaudiosink_debug, "pulseaudiosink", 0,
"Bin that wraps pulsesink for handling compressed formats");
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&sink_template));
gst_element_class_set_details_simple (element_class,
"Bin wrapping pulsesink", "Sink/Audio/Bin",
"Correctly handles sink changes when streaming compressed formats to "
"pulsesink", "Arun Raghavan <arun.raghavan@collabora.co.uk>");
gobject_class->get_property = gst_pulse_audio_sink_get_property;
gobject_class->set_property = gst_pulse_audio_sink_set_property;
gobject_class->dispose = gst_pulse_audio_sink_dispose;
element_class->change_state =
GST_DEBUG_FUNCPTR (gst_pulse_audio_sink_change_state);
/* Find out how many properties we already have */
specs = g_object_class_list_properties (gobject_class, &klass->n_prop_own);
g_free (specs);
/* Proxy pulsesink's properties */
specs = g_object_class_list_properties (G_OBJECT_CLASS (psink_class), &n);
for (i = 0, j = klass->n_prop_own; i < n; i++) {
if (g_object_class_find_property (gobject_class,
g_param_spec_get_name (specs[i]))) {
/* We already inherited this property from a parent, skip */
j--;
} else {
g_object_class_install_property (gobject_class, i + j + 1,
param_spec_copy (specs[i]));
}
}
klass->n_prop_total = i + j;
g_free (specs);
g_type_class_unref (psink_class);
}
static GstPad *
get_proxypad (GstPad * sinkpad)
{
GstIterator *iter = NULL;
GValue res = { 0 };
GstPad *proxypad = NULL;
iter = gst_pad_iterate_internal_links (sinkpad);
if (iter) {
if (gst_iterator_next (iter, &res) == GST_ITERATOR_OK) {
proxypad = g_value_dup_object (&res);
g_value_reset (&res);
}
gst_iterator_free (iter);
}
return proxypad;
}
static void
post_missing_element_message (GstPulseAudioSink * pbin, const gchar * name)
{
GstMessage *msg;
msg = gst_missing_element_message_new (GST_ELEMENT_CAST (pbin), name);
gst_element_post_message (GST_ELEMENT_CAST (pbin), msg);
}
static void
notify_cb (GObject * selector, GParamSpec * pspec, GstPulseAudioSink * pbin)
{
g_object_notify (G_OBJECT (pbin), g_param_spec_get_name (pspec));
}
static void
gst_pulse_audio_sink_init (GstPulseAudioSink * pbin)
{
GstPulseAudioSinkClass *klass =
GST_PULSE_AUDIO_SINK_CLASS (G_OBJECT_GET_CLASS (pbin));
GstPad *pad = NULL;
GParamSpec **specs;
GString *prop;
guint i;
pbin->lock = g_mutex_new ();
gst_segment_init (&pbin->segment, GST_FORMAT_UNDEFINED);
pbin->psink = GST_PULSESINK (gst_element_factory_make ("pulsesink",
"pulseaudiosink-sink"));
g_assert (pbin->psink != NULL);
if (!gst_bin_add (GST_BIN (pbin), GST_ELEMENT (pbin->psink))) {
GST_ERROR_OBJECT (pbin, "Failed to add pulsesink to bin");
goto error;
}
pad = gst_element_get_static_pad (GST_ELEMENT (pbin->psink), "sink");
pbin->sinkpad = gst_ghost_pad_new_from_template ("sink", pad,
gst_static_pad_template_get (&sink_template));
pbin->sinkpad_old_eventfunc = GST_PAD_EVENTFUNC (pbin->sinkpad);
gst_pad_set_event_function (pbin->sinkpad,
GST_DEBUG_FUNCPTR (gst_pulse_audio_sink_sink_event));
gst_pad_set_query_function (pbin->sinkpad,
GST_DEBUG_FUNCPTR (gst_pulse_audio_sink_sink_query));
gst_element_add_pad (GST_ELEMENT (pbin), pbin->sinkpad);
if (!(pbin->sink_proxypad = get_proxypad (pbin->sinkpad)))
GST_ERROR_OBJECT (pbin, "Failed to get proxypad of srcpad");
else {
pbin->proxypad_old_eventfunc = GST_PAD_EVENTFUNC (pbin->sink_proxypad);
gst_pad_set_event_function (pbin->sink_proxypad,
GST_DEBUG_FUNCPTR (gst_pulse_audio_sink_src_event));
}
/* Now proxy all the notify::* signals */
specs = g_object_class_list_properties (G_OBJECT_CLASS (klass), &i);
prop = g_string_sized_new (30);
for (i--; i >= klass->n_prop_own; i--) {
g_string_printf (prop, "notify::%s", g_param_spec_get_name (specs[i]));
g_signal_connect (pbin->psink, prop->str, G_CALLBACK (notify_cb), pbin);
}
g_string_free (prop, TRUE);
g_free (specs);
pbin->format_lost = FALSE;
out:
if (pad)
gst_object_unref (pad);
return;
error:
if (pbin->psink)
gst_object_unref (pbin->psink);
goto out;
}
static void
gst_pulse_audio_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (object);
GstPulseAudioSinkClass *klass =
GST_PULSE_AUDIO_SINK_CLASS (G_OBJECT_GET_CLASS (object));
g_return_if_fail (prop_id <= klass->n_prop_total);
g_object_set_property (G_OBJECT (pbin->psink), g_param_spec_get_name (pspec),
value);
}
static void
gst_pulse_audio_sink_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (object);
GstPulseAudioSinkClass *klass =
GST_PULSE_AUDIO_SINK_CLASS (G_OBJECT_GET_CLASS (object));
g_return_if_fail (prop_id <= klass->n_prop_total);
g_object_get_property (G_OBJECT (pbin->psink), g_param_spec_get_name (pspec),
value);
}
static void
gst_pulse_audio_sink_free_dbin (GstPulseAudioSink * pbin)
{
g_signal_handler_disconnect (pbin->dbin, pbin->pad_added_id);
gst_element_set_state (pbin->dbin, GST_STATE_NULL);
gst_bin_remove (GST_BIN (pbin), pbin->dbin);
pbin->dbin = NULL;
}
static void
gst_pulse_audio_sink_dispose (GObject * object)
{
GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (object);
if (pbin->lock) {
g_mutex_free (pbin->lock);
pbin->lock = NULL;
}
if (pbin->sink_proxypad) {
gst_object_unref (pbin->sink_proxypad);
pbin->sink_proxypad = NULL;
}
if (pbin->dbin) {
g_signal_handler_disconnect (pbin->dbin, pbin->pad_added_id);
pbin->dbin = NULL;
}
pbin->sinkpad = NULL;
pbin->psink = NULL;
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static gboolean
gst_pulse_audio_sink_update_sinkpad (GstPulseAudioSink * pbin, GstPad * sinkpad)
{
gboolean ret;
ret = gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (pbin->sinkpad), sinkpad);
if (!ret)
GST_WARNING_OBJECT (pbin, "Could not update ghostpad target");
return ret;
}
static void
distribute_running_time (GstElement * element, const GstSegment * segment)
{
GstEvent *event;
GstPad *pad;
pad = gst_element_get_static_pad (element, "sink");
/* FIXME: Some decoders collect newsegments and send them out at once, making
* them lose accumulator events (and thus making dbin_event_probe() hard to
* do right if we're sending these as well. We can get away with not sending
* these at the moment, but this should be fixed! */
#if 0
if (segment->accum) {
event = gst_event_new_new_segment_full (FALSE, segment->rate,
segment->applied_rate, segment->format, 0, segment->accum, 0);
gst_pad_send_event (pad, event);
}
#endif
/* TODO review this copy, see if it can be avoided */
event = gst_event_new_segment (gst_segment_copy (segment));
gst_pad_send_event (pad, event);
gst_object_unref (pad);
}
static GstPadProbeReturn
dbin_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer data)
{
GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (data);
if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
GST_DEBUG_OBJECT (pbin, "Got newsegment - dropping");
pbin->event_probe_id = 0;
return GST_PAD_PROBE_REMOVE;
}
return GST_PAD_PROBE_OK;
}
static void
pad_added_cb (GstElement * dbin, GstPad * pad, gpointer * data)
{
GstPulseAudioSink *pbin;
GstPad *sinkpad = NULL;
pbin = GST_PULSE_AUDIO_SINK (data);
sinkpad = gst_element_get_static_pad (GST_ELEMENT (pbin->psink), "sink");
GST_PULSE_AUDIO_SINK_LOCK (pbin);
if (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK)
GST_ERROR_OBJECT (pbin, "Failed to link decodebin to pulsesink");
else
GST_DEBUG_OBJECT (pbin, "Linked new pad to pulsesink");
GST_PULSE_AUDIO_SINK_UNLOCK (pbin);
gst_object_unref (sinkpad);
}
/* Called with pbin lock held */
static void
gst_pulse_audio_sink_add_dbin (GstPulseAudioSink * pbin)
{
GstPad *sinkpad = NULL;
g_assert (pbin->dbin == NULL);
pbin->dbin = gst_element_factory_make ("decodebin", "pulseaudiosink-dbin");
if (!pbin->dbin) {
post_missing_element_message (pbin, "decodebin");
GST_ELEMENT_WARNING (pbin, CORE, MISSING_PLUGIN,
(_("Missing element '%s' - check your GStreamer installation."),
"decodebin"), ("audio playback might fail"));
goto out;
}
if (!gst_bin_add (GST_BIN (pbin), pbin->dbin)) {
GST_ERROR_OBJECT (pbin, "Failed to add decodebin to bin");
goto out;
}
pbin->pad_added_id = g_signal_connect (pbin->dbin, "pad-added",
G_CALLBACK (pad_added_cb), pbin);
if (!gst_element_sync_state_with_parent (pbin->dbin)) {
GST_ERROR_OBJECT (pbin, "Failed to set decodebin to parent state");
goto out;
}
/* Trap the newsegment events that we feed the decodebin and discard them */
sinkpad = gst_element_get_static_pad (GST_ELEMENT (pbin->psink), "sink");
if (pbin->event_probe_id == 0)
pbin->event_probe_id =
gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
dbin_event_probe, gst_object_ref (pbin),
(GDestroyNotify) gst_object_unref);
gst_object_unref (sinkpad);
sinkpad = NULL;
GST_DEBUG_OBJECT (pbin, "Distributing running time to decodebin");
distribute_running_time (pbin->dbin, &pbin->segment);
sinkpad = gst_element_get_static_pad (pbin->dbin, "sink");
gst_pulse_audio_sink_update_sinkpad (pbin, sinkpad);
out:
if (sinkpad)
gst_object_unref (sinkpad);
}
static void
update_eac3_alignment (GstPulseAudioSink * pbin)
{
GstCaps *caps = gst_pad_peer_query_caps (pbin->sinkpad, NULL);
GstStructure *st;
if (!caps)
return;
st = gst_caps_get_structure (caps, 0);
if (g_str_equal (gst_structure_get_name (st), "audio/x-eac3")) {
GstStructure *event_st = gst_structure_new ("ac3parse-set-alignment",
"alignment", G_TYPE_STRING, pbin->dbin ? "frame" : "iec61937", NULL);
if (!gst_pad_push_event (pbin->sinkpad,
gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, event_st)))
GST_WARNING_OBJECT (pbin->sinkpad, "Could not update alignment");
}
gst_caps_unref (caps);
}
static GstPadProbeReturn
proxypad_blocked_cb (GstPad * pad, GstPadProbeInfo * info, gpointer data)
{
GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (data);
GstCaps *caps;
GstPad *sinkpad = NULL;
GST_DEBUG_OBJECT (pbin, "blocked");
GST_PULSE_AUDIO_SINK_LOCK (pbin);
if (!pbin->format_lost) {
sinkpad = gst_element_get_static_pad (GST_ELEMENT (pbin->psink), "sink");
if (gst_pad_has_current_caps (pbin->sinkpad)) {
/* See if we already got caps on our sinkpad */
caps = gst_pad_get_current_caps (pbin->sinkpad);
} else {
/* We haven't, so get caps from upstream */
caps = gst_pad_query_caps (pad, NULL);
}
if (gst_pad_query_accept_caps (sinkpad, caps)) {
if (pbin->dbin) {
GST_DEBUG_OBJECT (pbin, "Removing decodebin");
gst_pulse_audio_sink_free_dbin (pbin);
gst_pulse_audio_sink_update_sinkpad (pbin, sinkpad);
} else {
GST_DEBUG_OBJECT (pbin, "Doing nothing");
gst_pad_send_event (sinkpad, gst_event_new_caps (caps));
}
gst_caps_unref (caps);
gst_object_unref (sinkpad);
goto done;
}
/* pulsesink doesn't accept the incoming caps, so add a decodebin
* (potentially after removing the existing once, since decodebin can't
* renegotiate). */
} else {
/* Format lost, proceed to try plugging a decodebin */
pbin->format_lost = FALSE;
}
if (pbin->dbin != NULL) {
/* decodebin doesn't support reconfiguration, so throw this one away and
* create a new one. */
gst_pulse_audio_sink_free_dbin (pbin);
}
GST_DEBUG_OBJECT (pbin, "Adding decodebin");
gst_pulse_audio_sink_add_dbin (pbin);
done:
update_eac3_alignment (pbin);
pbin->block_probe_id = 0;
GST_PULSE_AUDIO_SINK_UNLOCK (pbin);
return GST_PAD_PROBE_REMOVE;
}
static gboolean
gst_pulse_audio_sink_src_event (GstPad * pad, GstObject * parent,
GstEvent * event)
{
GstPulseAudioSink *pbin = NULL;
GstPad *ghostpad = NULL;
gboolean ret = FALSE;
ghostpad = GST_PAD_CAST (parent);
if (G_UNLIKELY (!ghostpad)) {
GST_WARNING_OBJECT (pad, "Could not get ghostpad");
goto out;
}
pbin = GST_PULSE_AUDIO_SINK (gst_pad_get_parent (ghostpad));
if (G_UNLIKELY (!pbin)) {
GST_WARNING_OBJECT (pad, "Could not get pulseaudiosink");
goto out;
}
if (G_UNLIKELY (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_UPSTREAM) &&
(gst_event_has_name (event, "pulse-format-lost") ||
gst_event_has_name (event, "pulse-sink-changed"))) {
g_return_val_if_fail (pad->mode != GST_PAD_MODE_PULL, FALSE);
GST_PULSE_AUDIO_SINK_LOCK (pbin);
if (gst_event_has_name (event, "pulse-format-lost"))
pbin->format_lost = TRUE;
if (pbin->block_probe_id == 0)
pbin->block_probe_id =
gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
proxypad_blocked_cb, gst_object_ref (pbin),
(GDestroyNotify) gst_object_unref);
GST_PULSE_AUDIO_SINK_UNLOCK (pbin);
ret = TRUE;
} else if (pbin->proxypad_old_eventfunc) {
ret = pbin->proxypad_old_eventfunc (pad, parent, event);
event = NULL;
}
out:
if (pbin)
gst_object_unref (pbin);
if (event)
gst_event_unref (event);
return ret;
}
static gboolean
gst_pulse_audio_sink_sink_event (GstPad * pad, GstObject * parent,
GstEvent * event)
{
GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (parent);
gboolean ret;
gboolean forward = TRUE;
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_CAPS:
{
GstCaps *caps;
gst_event_parse_caps (event, &caps);
ret = gst_pulse_audio_sink_set_caps (pbin, caps);
forward = FALSE;
break;
}
case GST_EVENT_SEGMENT:
{
const GstSegment *segment = NULL;
GST_PULSE_AUDIO_SINK_LOCK (pbin);
gst_event_parse_segment (event, &segment);
GST_DEBUG_OBJECT (pbin, "newsegment: %" GST_SEGMENT_FORMAT, segment);
if (segment->format == GST_FORMAT_TIME) {
/* Store the values for feeding to sub-elements */
gst_segment_copy_into (segment, &pbin->segment);
} else {
GST_WARNING_OBJECT (pbin, "Got a non-TIME format segment");
gst_segment_init (&pbin->segment, GST_FORMAT_TIME);
}
GST_PULSE_AUDIO_SINK_UNLOCK (pbin);
break;
}
case GST_EVENT_FLUSH_STOP:
GST_PULSE_AUDIO_SINK_LOCK (pbin);
gst_segment_init (&pbin->segment, GST_FORMAT_UNDEFINED);
GST_PULSE_AUDIO_SINK_UNLOCK (pbin);
break;
default:
break;
}
if (forward)
ret = pbin->sinkpad_old_eventfunc (pad, parent, event);
else
gst_event_unref (event);
return ret;
}
/* The bin's acceptcaps should be exactly equivalent to a pulsesink that is
* connected to a sink that supports all the formats in template caps. This
* means that upstream will have to have everything possibly upto a parser
* plugged and we plugin a decoder whenever required. */
static gboolean
gst_pulse_audio_sink_sink_acceptcaps (GstPulseAudioSink * pbin, GstPad * pad,
GstCaps * caps)
{
GstAudioRingBufferSpec spec = { 0 };
const GstStructure *st;
GstCaps *pad_caps = NULL;
gboolean ret = FALSE;
pad_caps = gst_pad_query_caps (pad, caps);
if (!pad_caps || gst_caps_is_empty (pad_caps))
goto out;
/* If we've not got fixed caps, creating a stream might fail, so let's just
* return from here with default acceptcaps behaviour */
if (!gst_caps_is_fixed (caps))
goto out;
spec.latency_time = GST_AUDIO_BASE_SINK (pbin->psink)->latency_time;
if (!gst_audio_ring_buffer_parse_caps (&spec, caps))
goto out;
/* Make sure non-raw input is framed (one frame per buffer) and can be
* payloaded */
st = gst_caps_get_structure (caps, 0);
if (!g_str_has_prefix (gst_structure_get_name (st), "audio/x-raw")) {
gboolean framed = FALSE, parsed = FALSE;
gst_structure_get_boolean (st, "framed", &framed);
gst_structure_get_boolean (st, "parsed", &parsed);
if ((!framed && !parsed) || gst_audio_iec61937_frame_size (&spec) <= 0)
goto out;
}
ret = TRUE;
out:
if (pad_caps)
gst_caps_unref (pad_caps);
return ret;
}
static gboolean
gst_pulse_audio_sink_sink_query (GstPad * pad, GstObject * parent,
GstQuery * query)
{
GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (parent);
gboolean ret = FALSE;
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_ACCEPT_CAPS:
{
GstCaps *caps;
gst_query_parse_accept_caps (query, &caps);
ret = gst_pulse_audio_sink_sink_acceptcaps (pbin, pad, caps);
gst_query_set_accept_caps_result (query, ret);
ret = TRUE;
break;
}
default:
ret = gst_pad_query_default (pad, parent, query);
break;
}
return ret;
}
static gboolean
gst_pulse_audio_sink_set_caps (GstPulseAudioSink * pbin, GstCaps * caps)
{
gboolean ret = TRUE;
GST_PULSE_AUDIO_SINK_LOCK (pbin);
GST_DEBUG_OBJECT (pbin, "got caps %" GST_PTR_FORMAT, caps);
if (gst_pad_has_current_caps (pbin->sinkpad)) {
GstCaps *current;
/* See if we already got caps on our sinkpad */
current = gst_pad_get_current_caps (pbin->sinkpad);
ret = gst_caps_is_equal (caps, current);
gst_caps_unref (current);
if (ret)
goto done;
}
if (pbin->block_probe_id == 0)
pbin->block_probe_id =
gst_pad_add_probe (pbin->sink_proxypad,
GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM, proxypad_blocked_cb,
gst_object_ref (pbin), (GDestroyNotify) gst_object_unref);
done:
GST_PULSE_AUDIO_SINK_UNLOCK (pbin);
return ret;
}
static GstStateChangeReturn
gst_pulse_audio_sink_change_state (GstElement * element,
GstStateChange transition)
{
GstPulseAudioSink *pbin = GST_PULSE_AUDIO_SINK (element);
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
/* Nothing to do for upward transitions */
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
GST_PULSE_AUDIO_SINK_LOCK (pbin);
if (pbin->block_probe_id) {
gst_pad_remove_probe (pbin->sink_proxypad, pbin->block_probe_id);
pbin->block_probe_id = 0;
}
GST_PULSE_AUDIO_SINK_UNLOCK (pbin);
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (ret != GST_STATE_CHANGE_SUCCESS) {
GST_DEBUG_OBJECT (pbin, "Base class returned %d on state change", ret);
goto out;
}
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
GST_PULSE_AUDIO_SINK_LOCK (pbin);
gst_segment_init (&pbin->segment, GST_FORMAT_UNDEFINED);
if (pbin->dbin) {
GstPad *pad = gst_element_get_static_pad (GST_ELEMENT (pbin->psink),
"sink");
gst_pulse_audio_sink_free_dbin (pbin);
gst_pulse_audio_sink_update_sinkpad (pbin, pad);
gst_object_unref (pad);
}
GST_PULSE_AUDIO_SINK_UNLOCK (pbin);
break;
default:
break;
}
out:
return ret;
}

View file

@ -426,6 +426,8 @@ gst_pulsering_context_subscribe_cb (pa_context * c,
GST_INFO_OBJECT (psink, "emitting sink-changed");
/* FIXME: send reconfigure event instead and let decodebin/playbin
* handle that. Also take care of ac3 alignment. See "pulse-format-lost" */
renego = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
gst_structure_new_empty ("pulse-sink-changed"));
@ -742,9 +744,22 @@ gst_pulsering_stream_event_cb (pa_stream * p, const char *name,
g_free (psink->device);
psink->device = g_strdup (pa_proplist_gets (pl, "device"));
/* FIXME: send reconfigure event instead and let decodebin/playbin
* handle that. Also take care of ac3 alignment */
renego = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
gst_structure_new_empty ("pulse-format-lost"));
#if 0
if (g_str_equal (gst_structure_get_name (st), "audio/x-eac3")) {
GstStructure *event_st = gst_structure_new ("ac3parse-set-alignment",
"alignment", G_TYPE_STRING, pbin->dbin ? "frame" : "iec61937", NULL);
if (!gst_pad_push_event (pbin->sinkpad,
gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, event_st)))
GST_WARNING_OBJECT (pbin->sinkpad, "Could not update alignment");
}
#endif
if (!gst_pad_push_event (GST_BASE_SINK (psink)->sinkpad, renego)) {
/* Nobody handled the format change - emit an error */
GST_ELEMENT_ERROR (psink, STREAM, FORMAT, ("Sink format changed"),
@ -1938,8 +1953,6 @@ done:
pa_threaded_mainloop_signal (mainloop, 0);
}
/* NOTE: If you're making changes here, see if pulseaudiosink acceptcaps also
* needs to be changed accordingly. */
static gboolean
gst_pulsesink_query_acceptcaps (GstPulseSink * psink, GstCaps * caps)
{

View file

@ -121,23 +121,6 @@ GType gst_pulsesink_get_type (void);
_PULSE_SINK_CAPS_COMMON \
_PULSE_SINK_CAPS_1_0
/* FIXME 0.11: pulseaudiosink helper bin must die */
#define GST_TYPE_PULSE_AUDIO_SINK \
(gst_pulse_audio_sink_get_type())
#define GST_PULSE_AUDIO_SINK(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PULSE_AUDIO_SINK,GstPulseAudioSink))
#define GST_PULSE_AUDIO_SINK_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PULSE_AUDIO_SINK,GstPulseAudioSinkClass))
#define GST_IS_PULSE_AUDIO_SINK(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PULSE_AUDIO_SINK))
#define GST_IS_PULSE_AUDIO_SINK_CLASS(obj) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PULSE_AUDIO_SINK))
#define GST_PULSE_AUDIO_SINK_CAST(obj) \
((GstPulseAudioSink *)(obj))
GType gst_pulse_audio_sink_get_type (void);
G_END_DECLS
#endif /* __GST_PULSESINK_H__ */