--- a/patches/libcanberra-02-gstreamer.diff Tue Dec 23 10:02:08 2008 +0000
+++ b/patches/libcanberra-02-gstreamer.diff Tue Dec 23 22:15:59 2008 +0000
@@ -1,29 +1,280 @@
-diff -pruN libcanberra-0.10/configure.ac libcanberra-0.10-mod/configure.ac
---- libcanberra-0.10/configure.ac 2008-10-06 17:10:29.000000000 +0300
-+++ libcanberra-0.10-mod/configure.ac 2008-11-14 15:17:12.000000000 +0200
-@@ -303,8 +303,8 @@ else
- HAVE_GSTREAMER=0
- fi
+diff --git a/src/Makefile.am b/src/Makefile.am
+index d1b4d89..4f27deb 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -38,7 +38,8 @@ include_HEADERS = \
+ canberra.h
+
+ noinst_PROGRAMS = \
+- test-canberra
++ test-canberra \
++ $(GTK_NOINST)
+
+ libcanberra_la_SOURCES = \
+ canberra.h \
+@@ -283,6 +284,7 @@ gtkmodule_LTLIBRARIES = \
+
+ bin_PROGRAMS = \
+ canberra-gtk-play
++GTK_NOINST = canberra-gstreamer-test
+
+ libcanberra_gtk_la_SOURCES = \
+ canberra-gtk.h \
+@@ -315,6 +317,15 @@ canberra_gtk_play_LDADD = \
+ canberra_gtk_play_CFLAGS = \
+ $(GTK_CFLAGS)
--AC_SUBST(GSTREAMER_CFLAGS)
--AC_SUBST(GSTREAMER_LIBS)
-+AC_SUBST(GST_CFLAGS)
-+AC_SUBST(GST_LIBS)
-
- ### Null output (optional) ####
-
---- libcanberra-0.10/src/gstreamer.c-orig 2008-12-20 19:50:52.598441000 -0600
-+++ libcanberra-0.10-mod/src/gstreamer.c 2008-12-20 19:47:31.744422000 -0600
-@@ -50,6 +50,8 @@ struct outstanding {
++canberra_gstreamer_test_SOURCES = \
++ canberra-gstreamer-test.c
++canberra_gstreamer_test_LDADD = \
++ $(GTK_LIBS) \
++ libcanberra.la \
++ libcanberra-gtk.la
++canberra_gstreamer_test_CFLAGS = \
++ $(GTK_CFLAGS)
++
+ EXTRA_DIST += \
+ libcanberra-login-sound.desktop.in \
+ libcanberra-logout-sound.sh.in
+diff --git a/src/canberra-gstreamer-test.c b/src/canberra-gstreamer-test.c
+new file mode 100644
+index 0000000..83daa3b
+--- /dev/null
++++ b/src/canberra-gstreamer-test.c
+@@ -0,0 +1,196 @@
++/***
++ This file is part of libcanberra.
++
++ Copyright 2008 Lennart Poettering
++
++ libcanberra 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.
++
++ libcanberra 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 libcanberra. If not, see
++ <http://www.gnu.org/licenses/>.
++***/
++
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include <gtk/gtk.h>
++#include <canberra-gtk.h>
++#include <locale.h>
++
++static int ret = 0;
++static ca_proplist *proplist = NULL;
++static int n_loops = 2;
++static int n_playing = 0;
++static ca_context *ca_ctx;
++
++static void callback(ca_context *c, uint32_t id, int error, void *userdata);
++
++static gboolean idle_quit(gpointer userdata) {
++ gtk_main_quit();
++ return FALSE;
++}
++
++static gboolean idle_cancel(gpointer userdata) {
++ int r;
++ guint id = GPOINTER_TO_UINT(userdata);
++
++ g_print("Cancelling id %u\n", id);
++ r = ca_context_cancel(ca_ctx, id);
++ if (r < 0)
++ g_printerr("Failed to cancel sound: %s\n", ca_strerror(r));
++
++ return FALSE;
++}
++
++static gboolean idle_play(gpointer userdata) {
++ int r;
++ guint id = GPOINTER_TO_UINT(userdata);
++
++ if (n_loops < 2)
++ return FALSE;
++
++ n_loops--;
++
++ g_print ("Starting playback id %u\n", id);
++ r = ca_context_play_full(ca_ctx, id, proplist, callback, userdata);
++
++ if (r < 0) {
++ g_printerr("Failed to play sound: %s\n", ca_strerror(r));
++ ret = 1;
++ if (n_playing == 0)
++ gtk_main_quit();
++ }
++
++ return FALSE;
++}
++
++static void callback(ca_context *c, uint32_t id, int error, void *userdata) {
++ g_print ("Playback done. id %u, err %d\n", id, error);
++ n_playing--;
++
++ if (error < 0) {
++ g_printerr("Error received for id %u: %s\n", id, ca_strerror(error));
++ ret = 1;
++ } else if (n_loops > 1) {
++ /* So, why don't we call ca_context_play_full() here directly?
++ -- Because the context this callback is called from is
++ explicitly documented as undefined and no libcanberra function
++ may be called from it. */
++
++ n_playing++;
++ g_idle_add(idle_play, userdata);
++ return;
++ }
++
++ /* So, why don't we call gtk_main_quit() here directly? -- Because
++ * otherwise we might end up with a small race condition: this
++ * callback might get called before the main loop actually started
++ * running */
++ if (n_playing == 0)
++ g_idle_add(idle_quit, NULL);
++}
++
++int main (int argc, char *argv[]) {
++ GOptionContext *oc;
++ static gchar *event_id = NULL, *filename = NULL, *event_description = NULL, *cache_control = NULL;
++ int r;
++ static gboolean version = FALSE;
++
++ static const GOptionEntry options[] = {
++ { "id", 'i', 0, G_OPTION_ARG_STRING, &event_id, "Event sound identifier", "STRING" },
++ { "file", 'f', 0, G_OPTION_ARG_STRING, &filename, "Play file", "PATH" },
++ { "description", 'd', 0, G_OPTION_ARG_STRING, &event_description, "Event sound description", "STRING" },
++ { "cache-control", 'c', 0, G_OPTION_ARG_STRING, &cache_control, "Cache control (permanent, volatile, never)", "STRING" },
++ { "loop", 'l', 0, G_OPTION_ARG_INT, &n_loops, "Loop how many times (detault: 10)", "INTEGER" },
++ { "version", 'v', 0, G_OPTION_ARG_NONE, &version, "Display version number and quit", NULL },
++ { NULL, 0, 0, 0, NULL, NULL, NULL }
++ };
++
++ setlocale(LC_ALL, "");
++
++ g_type_init();
++ g_thread_init(NULL);
++
++ oc = g_option_context_new("- canberra-gstreamer-test");
++ g_option_context_add_main_entries(oc, options, NULL);
++ g_option_context_add_group(oc, gtk_get_option_group(TRUE));
++ g_option_context_set_help_enabled(oc, TRUE);
++ g_option_context_parse(oc, &argc, &argv, NULL);
++ g_option_context_free(oc);
++
++ if (version) {
++ g_print("canberra-gstreamer-test from %s\n", PACKAGE_STRING);
++ return 0;
++ }
++
++ if (!event_id && !filename) {
++ g_printerr("No event id or file specified.\n");
++ return 1;
++ }
++
++ ret = ca_context_create(&ca_ctx);
++ if (ret) {
++ g_printerr("Failed to create libcanberra context: %s\n",
++ ca_strerror(ret));
++ return 1;
++ }
++
++ ca_context_change_props(ca_ctx,
++ CA_PROP_APPLICATION_NAME, "canberra-gstreamer-test",
++ CA_PROP_APPLICATION_ID, "org.freedesktop.libcanberra.GStreamerTest",
++ NULL);
++
++ ret = ca_context_set_driver(ca_ctx, "gstreamer");
++ if (ret) {
++ g_printerr("Failed to GStreamer backend: %s\n",
++ ca_strerror(ret));
++ return 1;
++ }
++
++ ca_proplist_create(&proplist);
++
++ if (event_id)
++ ca_proplist_sets(proplist, CA_PROP_EVENT_ID, event_id);
++
++ if (filename)
++ ca_proplist_sets(proplist, CA_PROP_MEDIA_FILENAME, filename);
++
++ if (cache_control)
++ ca_proplist_sets(proplist, CA_PROP_CANBERRA_CACHE_CONTROL, cache_control);
++
++ if (event_description)
++ ca_proplist_sets(proplist, CA_PROP_EVENT_DESCRIPTION, event_description);
++
++ g_print ("Starting playback id %u\n", 1);
++ n_playing++;
++ r = ca_context_play_full(ca_ctx, 1, proplist, callback, GUINT_TO_POINTER(1));
++
++ if (r < 0) {
++ g_printerr("Failed to play sound: %s\n", ca_strerror(r));
++ ret = 1;
++ goto finish;
++ }
++
++ n_playing++;
++ g_timeout_add(8000, idle_play, GUINT_TO_POINTER(2));
++ /* Also schedule cancellation of one of the playbacks after 15 sec */
++ g_timeout_add(20000, idle_cancel, GUINT_TO_POINTER(1));
++ gtk_main();
++
++finish:
++
++ ca_proplist_destroy(proplist);
++
++ ca_context_destroy(ca_ctx);
++
++ return ret;
++}
+diff --git a/src/gstreamer.c b/src/gstreamer.c
+index f008849..f9cc177 100644
+--- a/src/gstreamer.c
++++ b/src/gstreamer.c
+@@ -46,6 +46,7 @@ struct outstanding {
+ CA_LLIST_FIELDS(struct outstanding);
+ ca_bool_t dead;
+ uint32_t id;
++ int err;
+ ca_finish_callback_t callback;
void *userdata;
GstElement *pipeline;
- struct ca_context *context;
-+ pthread_cond_t cond;
-+ pthread_mutex_t mutex;
+@@ -54,26 +55,38 @@ struct outstanding {
+
+ struct private {
+ ca_theme_data *theme;
+- ca_mutex *outstanding_mutex;
+ ca_bool_t signal_semaphore;
+ sem_t semaphore;
++
++ GstBus *mgr_bus;
++
++ /* Everything below protected by the outstanding_mutex */
++ ca_mutex *outstanding_mutex;
++ ca_bool_t mgr_thread_running;
+ ca_bool_t semaphore_allocated;
+ CA_LLIST_HEAD(struct outstanding, outstanding);
};
- struct private {
-@@ -68,12 +70,15 @@ static void outstanding_free(struct outs
+ #define PRIVATE(c) ((struct private *) ((c)->private))
+
++static void* thread_func(void *userdata);
++static void send_eos_msg(struct outstanding *out, int err);
++static void send_mgr_exit_msg (struct private *p);
++
+ static void outstanding_free(struct outstanding *o) {
+ GstBus *bus;
ca_assert(o);
@@ -43,31 +294,168 @@
ca_free(o);
}
-@@ -141,14 +146,13 @@ int driver_destroy(ca_context *c) {
- if (out->callback)
- out->callback(c, out->id, CA_ERROR_DESTROYED, out->userdata);
+@@ -81,6 +94,7 @@ static void outstanding_free(struct outstanding *o) {
+ int driver_open(ca_context *c) {
+ GError *error = NULL;
+ struct private *p;
++ pthread_t thread;
+
+ ca_return_val_if_fail(c, CA_ERROR_INVALID);
+ ca_return_val_if_fail(!PRIVATE(c), CA_ERROR_INVALID);
+@@ -95,6 +109,7 @@ int driver_open(ca_context *c) {
+
+ if (!(p = ca_new0(struct private, 1)))
+ return CA_ERROR_OOM;
++ c->private = p;
+
+ if (!(p->outstanding_mutex = ca_mutex_new())) {
+ driver_destroy(c);
+@@ -105,10 +120,21 @@ int driver_open(ca_context *c) {
+ driver_destroy(c);
+ return CA_ERROR_OOM;
+ }
+-
+ p->semaphore_allocated = TRUE;
-- out = out->next;
+- c->private = p;
++ p->mgr_bus = gst_bus_new();
++ if (p->mgr_bus == NULL) {
++ driver_destroy(c);
++ return CA_ERROR_OOM;
++ }
++ gst_bus_set_flushing(p->mgr_bus, FALSE);
++
++ /* Give a reference to the bus to the mgr thread */
++ if (pthread_create(&thread, NULL, thread_func, p) < 0) {
++ driver_destroy(c);
++ return CA_ERROR_OOM;
++ }
++ p->mgr_thread_running = TRUE;
+
+ return CA_SUCCESS;
+ }
+@@ -128,33 +154,18 @@ int driver_destroy(ca_context *c) {
+ /* Tell all player threads to terminate */
+ out = p->outstanding;
+ while (out) {
+- GstElement *pipeline;
-
- ca_mutex_unlock(p->outstanding_mutex);
+- if (out->dead) {
+- out = out->next;
+- continue;
+- }
+-
+- pipeline = out->pipeline;
+- out->dead = TRUE;
+-
+- if (out->callback)
+- out->callback(c, out->id, CA_ERROR_DESTROYED, out->userdata);
+-
++ if (!out->dead)
++ send_eos_msg(out, CA_ERROR_DESTROYED);
+ out = out->next;
+-
+- ca_mutex_unlock(p->outstanding_mutex);
-
- gst_element_set_state(pipeline, GST_STATE_NULL);
- gst_object_unref(GST_OBJECT(pipeline));
-
-+ pthread_mutex_lock(&out->mutex);
-+ pthread_cond_signal(&out->cond);
-+ pthread_mutex_unlock(&out->mutex);
- ca_mutex_lock(p->outstanding_mutex);
-+
-+ out = out->next;
+- ca_mutex_lock(p->outstanding_mutex);
}
- if (p->semaphore_allocated) {
-@@ -242,16 +246,9 @@ static GstBusSyncReply bus_cb(GstBus *bu
- if (!out->dead && out->callback)
- out->callback(out->context, out->id, err, out->userdata);
+- if (p->semaphore_allocated) {
+- /* Now wait until all players are destroyed */
++ /* Now that we've sent EOS for all pending players, append a
++ * message to wait for the mgr thread to exit */
++ if (p->mgr_thread_running && p->semaphore_allocated) {
++ send_mgr_exit_msg(p);
++
+ p->signal_semaphore = TRUE;
+- while (p->outstanding) {
++ while (p->mgr_thread_running) {
+ ca_mutex_unlock(p->outstanding_mutex);
+ sem_wait(&p->semaphore);
+ ca_mutex_lock(p->outstanding_mutex);
+@@ -165,6 +176,9 @@ int driver_destroy(ca_context *c) {
+ ca_mutex_free(p->outstanding_mutex);
+ }
+
++ if (p->mgr_bus)
++ g_object_unref(p->mgr_bus);
++
+ if (p->theme)
+ ca_theme_data_free(p->theme);
+
+@@ -194,7 +208,24 @@ int driver_change_props(ca_context *c, ca_proplist *changed, ca_proplist *merged
+ return CA_SUCCESS;
+ }
-- ca_mutex_lock(p->outstanding_mutex);
+-static GstBusSyncReply bus_cb(GstBus *bus, GstMessage *message, gpointer data) {
++static void
++send_eos_msg(struct outstanding *out, int err) {
++ struct private *p;
++ GstMessage *m;
++ GstStructure *s;
++
++ out->dead = TRUE;
++ out->err = err;
++
++ p = PRIVATE(out->context);
++ s = gst_structure_new("application/eos", "info", G_TYPE_POINTER, out, NULL);
++ m = gst_message_new_application (GST_OBJECT (out->pipeline), s);
++
++ gst_bus_post (p->mgr_bus, m);
++}
++
++static GstBusSyncReply
++bus_cb(GstBus *bus, GstMessage *message, gpointer data) {
+ int err;
+ struct outstanding *out;
+ struct private *p;
+@@ -208,52 +239,28 @@ static GstBusSyncReply bus_cb(GstBus *bus, GstMessage *message, gpointer data) {
+
+ switch (GST_MESSAGE_TYPE(message)) {
+ /* for all elements */
+- case GST_MESSAGE_UNKNOWN:
+- return GST_BUS_DROP;
+ case GST_MESSAGE_ERROR:
+ err = CA_ERROR_SYSTEM;
+ break;
+- /* only from bin */
+ case GST_MESSAGE_EOS:
++ /* only respect EOS from the toplevel pipeline */
+ if (GST_OBJECT(out->pipeline) != GST_MESSAGE_SRC(message))
+- return GST_BUS_DROP;
++ return GST_BUS_PASS;
+
+ err = CA_SUCCESS;
+ break;
+- case GST_MESSAGE_STATE_CHANGED: {
+- GstState pending;
+-
+- if (GST_OBJECT(out->pipeline) != GST_MESSAGE_SRC(message))
+- return GST_BUS_DROP;
+-
+- gst_message_parse_state_changed(message, NULL, NULL, &pending);
+- /* g_debug (gst_element_state_get_name (pending)); */
+-
+- if (pending == GST_STATE_NULL || pending == GST_STATE_VOID_PENDING)
+- return GST_BUS_PASS;
+- else
+- return GST_BUS_DROP;
+- break;
+- }
+ default:
+- return GST_BUS_DROP;
++ return GST_BUS_PASS;
+ }
+
+- if (!out->dead && out->callback)
+- out->callback(out->context, out->id, err, out->userdata);
+-
++ /* Bin finished playback: ask the manager thread to shut it
++ * down, since we can't from the sync message handler */
+ ca_mutex_lock(p->outstanding_mutex);
-
- CA_LLIST_REMOVE(struct outstanding, p->outstanding, out);
-
@@ -76,62 +464,106 @@
-
- outstanding_free(out);
-
-- ca_mutex_unlock(p->outstanding_mutex);
-+ pthread_mutex_lock(&out->mutex);
-+ pthread_cond_signal(&out->cond);
-+ pthread_mutex_unlock(&out->mutex);
++ if (!out->dead)
++ send_eos_msg(out, err);
+ ca_mutex_unlock(p->outstanding_mutex);
- return GST_BUS_DROP;
+- return GST_BUS_DROP;
++ return GST_BUS_PASS;
}
-@@ -310,23 +307,60 @@ static void on_pad_added(GstElement *ele
+
+ struct ca_sound_file {
+@@ -310,11 +317,87 @@ static void on_pad_added(GstElement *element, GstPad *pad, gboolean arg1, gpoint
gst_caps_unref(caps);
}
++static void
++send_mgr_exit_msg (struct private *p) {
++ GstMessage *m;
++ GstStructure *s;
++
++ s = gst_structure_new("application/mgr-exit", NULL);
++ m = gst_message_new_application (NULL, s);
++
++ gst_bus_post (p->mgr_bus, m);
++}
++
++/* Global manager thread that shuts down GStreamer pipelines when ordered */
+static void* thread_func(void *userdata) {
-+ struct outstanding *out = userdata;
-+ struct private *p;
-+
-+ p = PRIVATE(out->context);
++ struct private *p = userdata;
++ GstBus *bus = g_object_ref(p->mgr_bus);
+
+ pthread_detach(pthread_self());
+
-+ /* Wait until we receive EOS -> also close and destroy needs to get here. */
-+ pthread_mutex_lock(&out->mutex);
-+ pthread_cond_wait (&out->cond, &out->mutex);
++ /* Pop messages from the manager bus until we see an exit command */
++ do {
++ GstMessage *m = gst_bus_timed_pop(bus, GST_CLOCK_TIME_NONE);
++ const GstStructure *s;
++ const GValue *v;
++ struct outstanding *out;
++
++ if (m == NULL)
++ break;
++ if (GST_MESSAGE_TYPE(m) != GST_MESSAGE_APPLICATION) {
++ gst_message_unref (m);
++ break;
++ }
++
++ s = gst_message_get_structure(m);
++ if (gst_structure_has_name(s, "application/mgr-exit")) {
++ gst_message_unref (m);
++ break;
++ }
+
-+ ca_mutex_lock(p->outstanding_mutex);
++ /* Otherwise, this must be an EOS message for an outstanding pipe */
++ ca_assert(gst_structure_has_name(s, "application/eos"));
++ v = gst_structure_get_value(s, "info");
++ ca_assert(v);
++ out = g_value_get_pointer(v);
++ ca_assert(out);
+
-+ /* Set pipeline back to NULL to close things */
-+ if (gst_element_set_state(out->pipeline,
-+ GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE) {
-+ goto fail;
-+ }
-+
-+ CA_LLIST_REMOVE(struct outstanding, p->outstanding, out);
++ /* Set pipeline back to NULL to close things. By the time this
++ * completes, we can be sure bus_cb won't be called */
++ if (gst_element_set_state(out->pipeline, GST_STATE_NULL) ==
++ GST_STATE_CHANGE_FAILURE) {
++ gst_message_unref (m);
++ break;
++ }
++ if (out->callback)
++ out->callback(out->context, out->id, out->err, out->userdata);
+
-+ if (!p->outstanding && p->signal_semaphore)
-+ sem_post(&p->semaphore);
++ ca_mutex_lock(p->outstanding_mutex);
++ CA_LLIST_REMOVE(struct outstanding, p->outstanding, out);
++ outstanding_free(out);
++ ca_mutex_unlock(p->outstanding_mutex);
++
++ gst_message_unref(m);
++ } while (TRUE);
+
-+ outstanding_free(out);
++ /* Signal the semaphore and exit */
++ ca_mutex_lock(p->outstanding_mutex);
++ if (p->signal_semaphore)
++ sem_post(&p->semaphore);
++ p->mgr_thread_running = FALSE;
++ ca_mutex_unlock(p->outstanding_mutex);
+
-+fail:
-+ ca_mutex_unlock(p->outstanding_mutex);
-+ pthread_mutex_unlock(&out->mutex);
++ gst_bus_set_flushing(bus, TRUE);
++ g_object_unref (bus);
+ return NULL;
+}
+
++
int driver_play(ca_context *c, uint32_t id, ca_proplist *proplist, ca_finish_callback_t cb, void *userdata) {
struct private *p;
- struct outstanding *out = NULL;
+ struct outstanding *out;
ca_sound_file *f;
- GstElement *decodebin, *sink, *audioconvert, *audioresample, *bin;
+- GstElement *decodebin, *sink, *audioconvert, *audioresample, *bin;
++ GstElement *decodebin, *sink, *audioconvert, *audioresample, *abin;
GstBus *bus;
GstPad *audiopad;
-+ pthread_t thread;
int ret;
-
- ca_return_val_if_fail(c, CA_ERROR_INVALID);
+@@ -323,10 +406,13 @@ int driver_play(ca_context *c, uint32_t id, ca_proplist *proplist, ca_finish_cal
ca_return_val_if_fail(proplist, CA_ERROR_INVALID);
ca_return_val_if_fail(!userdata || cb, CA_ERROR_INVALID);
@@ -142,14 +574,17 @@
-
+ audioconvert = NULL;
+ audioresample = NULL;
-+ bin = NULL;
++ abin = NULL;
p = PRIVATE(c);
if ((ret = ca_lookup_sound_with_callback(&f, ca_gst_sound_file_open, NULL, &p->theme, c->props, proplist)) < 0)
-@@ -345,6 +379,19 @@ int driver_play(ca_context *c, uint32_t
+@@ -344,43 +430,55 @@ int driver_play(ca_context *c, uint32_t id, ca_proplist *proplist, ca_finish_cal
+ || !(decodebin = gst_element_factory_make("decodebin2", NULL))
|| !(audioconvert = gst_element_factory_make("audioconvert", NULL))
|| !(audioresample = gst_element_factory_make("audioresample", NULL))
- || !(sink = gst_element_factory_make("autoaudiosink", NULL))) {
+- || !(sink = gst_element_factory_make("autoaudiosink", NULL))) {
++ || !(sink = gst_element_factory_make("autoaudiosink", NULL))
++ || !(abin = gst_bin_new ("audiobin"))) {
+
+ /* At this point, if there is a failure, free each plugin separately. */
+ if (out->pipeline != NULL)
@@ -162,53 +597,81 @@
+ g_object_unref(audioresample);
+ if (sink != NULL)
+ g_object_unref(sink);
++ if (abin != NULL)
++ g_object_unref(abin);
++
++ ca_free(out);
+
ret = CA_ERROR_OOM;
goto fail;
}
-@@ -361,11 +408,8 @@ int driver_play(ca_context *c, uint32_t
- f->fdsrc, decodebin, NULL);
- if (!gst_element_link(f->fdsrc, decodebin)) {
+- bin = gst_bin_new("audiobin");
+-
+- g_signal_connect(decodebin, "new-decoded-pad", G_CALLBACK (on_pad_added), bin);
+-
+ bus = gst_pipeline_get_bus(GST_PIPELINE (out->pipeline));
+ gst_bus_set_sync_handler(bus, bus_cb, out);
+ gst_object_unref(bus);
+
+- gst_bin_add_many(GST_BIN (out->pipeline),
+- f->fdsrc, decodebin, NULL);
+-
+- if (!gst_element_link(f->fdsrc, decodebin)) {
- f->fdsrc = NULL;
- decodebin = NULL;
- audioconvert = NULL;
- audioresample = NULL;
- sink = NULL;
+- goto fail;
+- }
+-
+- gst_bin_add_many(GST_BIN (bin), audioconvert, audioresample, sink, NULL);
++ g_signal_connect(decodebin, "new-decoded-pad",
++ G_CALLBACK (on_pad_added), abin);
++ gst_bin_add_many(GST_BIN (abin), audioconvert, audioresample, sink, NULL);
+ gst_element_link_many(audioconvert, audioresample, sink, NULL);
+
+ audiopad = gst_element_get_pad(audioconvert, "sink");
+- gst_element_add_pad(bin, gst_ghost_pad_new("sink", audiopad));
+-
++ gst_element_add_pad(abin, gst_ghost_pad_new("sink", audiopad));
+ gst_object_unref(audiopad);
+
+- gst_bin_add(GST_BIN (out->pipeline), bin);
++ gst_bin_add_many(GST_BIN (out->pipeline),
++ f->fdsrc, decodebin, abin, NULL);
++ if (!gst_element_link(f->fdsrc, decodebin)) {
++ /* Bin now owns the fdsrc... */
++ f->fdsrc = NULL;
++
+ outstanding_free(out);
+ ret = CA_ERROR_OOM;
- goto fail;
- }
-
-@@ -379,8 +423,6 @@ int driver_play(ca_context *c, uint32_t
-
- gst_bin_add(GST_BIN (out->pipeline), bin);
++ goto fail;
++ }
++ /* Bin now owns the fdsrc... */
++ f->fdsrc = NULL;
- decodebin = NULL;
- sink = NULL;
ca_free(f);
f = NULL;
-@@ -388,11 +430,17 @@ int driver_play(ca_context *c, uint32_t
+@@ -388,11 +486,8 @@ int driver_play(ca_context *c, uint32_t id, ca_proplist *proplist, ca_finish_cal
CA_LLIST_PREPEND(struct outstanding, p->outstanding, out);
ca_mutex_unlock(p->outstanding_mutex);
- if (gst_element_set_state(out->pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
-+ if (pthread_create(&thread, NULL, thread_func, out) < 0) {
- ca_mutex_lock(p->outstanding_mutex);
- CA_LLIST_REMOVE(struct outstanding, p->outstanding, out);
- ca_mutex_unlock(p->outstanding_mutex);
-+ outstanding_free(out);
-+ ret = CA_ERROR_OOM;
-+ goto fail;
-+ }
-
+- ca_mutex_lock(p->outstanding_mutex);
+- CA_LLIST_REMOVE(struct outstanding, p->outstanding, out);
+- ca_mutex_unlock(p->outstanding_mutex);
+-
+ if (gst_element_set_state(out->pipeline,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
ret = CA_ERROR_NOTAVAILABLE;
goto fail;
}
-@@ -406,17 +454,6 @@ fail:
+@@ -406,17 +501,6 @@ fail:
if (f)
ca_free(f);
@@ -226,16 +689,17 @@
return ret;
}
-@@ -431,15 +468,15 @@ int driver_cancel(ca_context *c, uint32_
+@@ -431,31 +515,34 @@ int driver_cancel(ca_context *c, uint32_t id) {
ca_mutex_lock(p->outstanding_mutex);
- for (out = p->outstanding; out; out = out->next) {
+- GstElement *pipeline;
+ for (out = p->outstanding; out;/* out = out->next*/) {
- GstElement *pipeline;
++ struct outstanding *next;
- if (out->id != id)
-+ if (out->id != id || out->pipeline == NULL)
++ if (out->id != id || out->pipeline == NULL || out->dead == TRUE)
+ {
+ out = out->next;
continue;
@@ -245,21 +709,34 @@
-
+ }
+
++ if (gst_element_set_state(out->pipeline, GST_STATE_NULL) ==
++ GST_STATE_CHANGE_FAILURE) {
++ goto error;
++ }
if (out->callback)
out->callback(c, out->id, CA_ERROR_CANCELED, out->userdata);
-
-@@ -447,10 +484,11 @@ int driver_cancel(ca_context *c, uint32_
- out->dead = TRUE;
-
- ca_mutex_unlock(p->outstanding_mutex);
+-
+- pipeline = out->pipeline;
+- out->dead = TRUE;
+-
+- ca_mutex_unlock(p->outstanding_mutex);
- gst_element_set_state(out->pipeline, GST_STATE_NULL);
-+ pthread_mutex_lock(&out->mutex);
-+ pthread_cond_signal(&out->cond);
-+ pthread_mutex_unlock(&out->mutex);
-+ out = out->next;
- ca_mutex_lock(p->outstanding_mutex);
+- ca_mutex_lock(p->outstanding_mutex);
-
- gst_object_unref(pipeline);
++ next = out->next;
++ CA_LLIST_REMOVE(struct outstanding, p->outstanding, out);
++ outstanding_free(out);
++ out = next;
}
ca_mutex_unlock(p->outstanding_mutex);
+
+ return CA_SUCCESS;
++
++error:
++ ca_mutex_unlock(p->outstanding_mutex);
++ return CA_ERROR_SYSTEM;
+ }
+
+ int driver_cache(ca_context *c, ca_proplist *proplist) {