2008-12-23 Brian Cameron <[email protected]> VERMILLION_106a
authoryippi
Tue, 23 Dec 2008 22:15:59 +0000
changeset 14806 21af01944306
parent 14805 786d1403a6d2
child 14807 6825801aa2f6
2008-12-23 Brian Cameron <[email protected]> * patches/libcanberra-02-gstreamer.diff: Update patch again based on upstream discussions.
ChangeLog
patches/libcanberra-02-gstreamer.diff
--- a/ChangeLog	Tue Dec 23 10:02:08 2008 +0000
+++ b/ChangeLog	Tue Dec 23 22:15:59 2008 +0000
@@ -1,3 +1,8 @@
+2008-12-23  Brian Cameron  <[email protected]>
+
+	* patches/libcanberra-02-gstreamer.diff:  Update patch again based
+	  on upstream discussions.
+
 2008-12-23  Darren Kenny  <[email protected]>
 
 	* base-specs/nwam-manager.spec:
--- 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) {