|
Packit |
3ae693 |
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/***
|
|
Packit |
3ae693 |
This file is part of libcanberra.
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
Copyright 2008 Nokia Corporation and/or its subsidiary(-ies).
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
Author: Marc-Andre Lureau <marc-andre.lureau@nokia.com>
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
libcanberra is free software; you can redistribute it and/or modify
|
|
Packit |
3ae693 |
it under the terms of the GNU Lesser General Public License as
|
|
Packit |
3ae693 |
published by the Free Software Foundation, either version 2.1 of the
|
|
Packit |
3ae693 |
License, or (at your option) any later version.
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
libcanberra is distributed in the hope that it will be useful, but
|
|
Packit |
3ae693 |
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
3ae693 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
3ae693 |
Lesser General Public License for more details.
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
You should have received a copy of the GNU Lesser General Public
|
|
Packit |
3ae693 |
License along with libcanberra. If not, see
|
|
Packit |
3ae693 |
<http://www.gnu.org/licenses/>.
|
|
Packit |
3ae693 |
***/
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
#ifdef HAVE_CONFIG_H
|
|
Packit |
3ae693 |
#include <config.h>
|
|
Packit |
3ae693 |
#endif
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
#include <errno.h>
|
|
Packit |
3ae693 |
#include <stdlib.h>
|
|
Packit |
3ae693 |
#include <pthread.h>
|
|
Packit |
3ae693 |
#include <semaphore.h>
|
|
Packit |
3ae693 |
#include <sys/types.h>
|
|
Packit |
3ae693 |
#include <sys/stat.h>
|
|
Packit |
3ae693 |
#include <fcntl.h>
|
|
Packit |
3ae693 |
#include <unistd.h>
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
#include <gst/gst.h>
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
#include "canberra.h"
|
|
Packit |
3ae693 |
#include "common.h"
|
|
Packit |
3ae693 |
#include "driver.h"
|
|
Packit |
3ae693 |
#include "llist.h"
|
|
Packit |
3ae693 |
#include "read-sound-file.h"
|
|
Packit |
3ae693 |
#include "sound-theme-spec.h"
|
|
Packit |
3ae693 |
#include "malloc.h"
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
struct outstanding {
|
|
Packit |
3ae693 |
CA_LLIST_FIELDS(struct outstanding);
|
|
Packit |
3ae693 |
ca_bool_t dead;
|
|
Packit |
3ae693 |
uint32_t id;
|
|
Packit |
3ae693 |
int err;
|
|
Packit |
3ae693 |
ca_finish_callback_t callback;
|
|
Packit |
3ae693 |
void *userdata;
|
|
Packit |
3ae693 |
GstElement *pipeline;
|
|
Packit |
3ae693 |
struct ca_context *context;
|
|
Packit |
3ae693 |
};
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
struct private {
|
|
Packit |
3ae693 |
ca_theme_data *theme;
|
|
Packit |
3ae693 |
ca_bool_t signal_semaphore;
|
|
Packit |
3ae693 |
sem_t semaphore;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
GstBus *mgr_bus;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* Everything below protected by the outstanding_mutex */
|
|
Packit |
3ae693 |
ca_mutex *outstanding_mutex;
|
|
Packit |
3ae693 |
ca_bool_t mgr_thread_running;
|
|
Packit |
3ae693 |
ca_bool_t semaphore_allocated;
|
|
Packit |
3ae693 |
CA_LLIST_HEAD(struct outstanding, outstanding);
|
|
Packit |
3ae693 |
};
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
#define PRIVATE(c) ((struct private *) ((c)->private))
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
static void* thread_func(void *userdata);
|
|
Packit |
3ae693 |
static void send_eos_msg(struct outstanding *out, int err);
|
|
Packit |
3ae693 |
static void send_mgr_exit_msg (struct private *p);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
static void outstanding_free(struct outstanding *o) {
|
|
Packit |
3ae693 |
GstBus *bus;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_assert(o);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (o->pipeline) {
|
|
Packit |
3ae693 |
bus = gst_pipeline_get_bus(GST_PIPELINE (o->pipeline));
|
|
Packit |
3ae693 |
if (bus != NULL) {
|
|
Packit |
3ae693 |
gst_bus_set_sync_handler(bus, NULL, NULL, NULL);
|
|
Packit |
3ae693 |
gst_object_unref(bus);
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
gst_object_unref(GST_OBJECT(o->pipeline));
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_free(o);
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_open(ca_context *c) {
|
|
Packit |
3ae693 |
GError *error = NULL;
|
|
Packit |
3ae693 |
struct private *p;
|
|
Packit |
3ae693 |
pthread_t thread;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(!PRIVATE(c), CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(!c->driver || ca_streq(c->driver, "gstreamer"), CA_ERROR_NODRIVER);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
gst_init_check(NULL, NULL, &error);
|
|
Packit |
3ae693 |
if (error != NULL) {
|
|
Packit |
3ae693 |
g_warning("gst_init: %s ", error->message);
|
|
Packit |
3ae693 |
g_error_free(error);
|
|
Packit |
3ae693 |
return CA_ERROR_INVALID;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (!(p = ca_new0(struct private, 1)))
|
|
Packit |
3ae693 |
return CA_ERROR_OOM;
|
|
Packit |
3ae693 |
c->private = p;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (!(p->outstanding_mutex = ca_mutex_new())) {
|
|
Packit |
3ae693 |
driver_destroy(c);
|
|
Packit |
3ae693 |
return CA_ERROR_OOM;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (sem_init(&p->semaphore, 0, 0) < 0) {
|
|
Packit |
3ae693 |
driver_destroy(c);
|
|
Packit |
3ae693 |
return CA_ERROR_OOM;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
p->semaphore_allocated = TRUE;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
p->mgr_bus = gst_bus_new();
|
|
Packit |
3ae693 |
if (p->mgr_bus == NULL) {
|
|
Packit |
3ae693 |
driver_destroy(c);
|
|
Packit |
3ae693 |
return CA_ERROR_OOM;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
gst_bus_set_flushing(p->mgr_bus, FALSE);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* Give a reference to the bus to the mgr thread */
|
|
Packit |
3ae693 |
if (pthread_create(&thread, NULL, thread_func, p) < 0) {
|
|
Packit |
3ae693 |
driver_destroy(c);
|
|
Packit |
3ae693 |
return CA_ERROR_OOM;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
p->mgr_thread_running = TRUE;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return CA_SUCCESS;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_destroy(ca_context *c) {
|
|
Packit |
3ae693 |
struct private *p;
|
|
Packit |
3ae693 |
struct outstanding *out;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(PRIVATE(c), CA_ERROR_STATE);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
p = PRIVATE(c);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (p->outstanding_mutex) {
|
|
Packit |
3ae693 |
ca_mutex_lock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* Tell all player threads to terminate */
|
|
Packit |
3ae693 |
out = p->outstanding;
|
|
Packit |
3ae693 |
while (out) {
|
|
Packit |
3ae693 |
if (!out->dead)
|
|
Packit |
3ae693 |
send_eos_msg(out, CA_ERROR_DESTROYED);
|
|
Packit |
3ae693 |
out = out->next;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* Now that we've sent EOS for all pending players, append a
|
|
Packit |
3ae693 |
* message to wait for the mgr thread to exit */
|
|
Packit |
3ae693 |
if (p->mgr_thread_running && p->semaphore_allocated) {
|
|
Packit |
3ae693 |
send_mgr_exit_msg(p);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
p->signal_semaphore = TRUE;
|
|
Packit |
3ae693 |
while (p->mgr_thread_running) {
|
|
Packit |
3ae693 |
ca_mutex_unlock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
sem_wait(&p->semaphore);
|
|
Packit |
3ae693 |
ca_mutex_lock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_mutex_unlock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
ca_mutex_free(p->outstanding_mutex);
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (p->mgr_bus)
|
|
Packit |
3ae693 |
g_object_unref(p->mgr_bus);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (p->theme)
|
|
Packit |
3ae693 |
ca_theme_data_free(p->theme);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (p->semaphore_allocated)
|
|
Packit |
3ae693 |
sem_destroy(&p->semaphore);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_free(p);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* no gst_deinit(), see doc */
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return CA_SUCCESS;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_change_device(ca_context *c, const char *device) {
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(PRIVATE(c), CA_ERROR_STATE);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return CA_SUCCESS;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_change_props(ca_context *c, ca_proplist *changed, ca_proplist *merged) {
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(changed, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(merged, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(PRIVATE(c), CA_ERROR_STATE);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return CA_SUCCESS;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
static void
|
|
Packit |
3ae693 |
send_eos_msg(struct outstanding *out, int err) {
|
|
Packit |
3ae693 |
struct private *p;
|
|
Packit |
3ae693 |
GstMessage *m;
|
|
Packit |
3ae693 |
GstStructure *s;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
out->dead = TRUE;
|
|
Packit |
3ae693 |
out->err = err;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
p = PRIVATE(out->context);
|
|
Packit |
3ae693 |
s = gst_structure_new("application/eos", "info", G_TYPE_POINTER, out, NULL);
|
|
Packit |
3ae693 |
m = gst_message_new_application (GST_OBJECT (out->pipeline), s);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
gst_bus_post (p->mgr_bus, m);
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
static GstBusSyncReply
|
|
Packit |
3ae693 |
bus_cb(GstBus *bus, GstMessage *message, gpointer data) {
|
|
Packit |
3ae693 |
int err;
|
|
Packit |
3ae693 |
struct outstanding *out;
|
|
Packit |
3ae693 |
struct private *p;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_return_val_if_fail(bus, GST_BUS_DROP);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(message, GST_BUS_DROP);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(data, GST_BUS_DROP);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
out = data;
|
|
Packit |
3ae693 |
p = PRIVATE(out->context);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
switch (GST_MESSAGE_TYPE(message)) {
|
|
Packit |
3ae693 |
/* for all elements */
|
|
Packit |
3ae693 |
case GST_MESSAGE_ERROR:
|
|
Packit |
3ae693 |
err = CA_ERROR_SYSTEM;
|
|
Packit |
3ae693 |
break;
|
|
Packit |
3ae693 |
case GST_MESSAGE_EOS:
|
|
Packit |
3ae693 |
/* only respect EOS from the toplevel pipeline */
|
|
Packit |
3ae693 |
if (GST_OBJECT(out->pipeline) != GST_MESSAGE_SRC(message))
|
|
Packit |
3ae693 |
return GST_BUS_PASS;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
err = CA_SUCCESS;
|
|
Packit |
3ae693 |
break;
|
|
Packit |
3ae693 |
default:
|
|
Packit |
3ae693 |
return GST_BUS_PASS;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* Bin finished playback: ask the manager thread to shut it
|
|
Packit |
3ae693 |
* down, since we can't from the sync message handler */
|
|
Packit |
3ae693 |
ca_mutex_lock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
if (!out->dead)
|
|
Packit |
3ae693 |
send_eos_msg(out, err);
|
|
Packit |
3ae693 |
ca_mutex_unlock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return GST_BUS_PASS;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
struct ca_sound_file {
|
|
Packit |
3ae693 |
GstElement *fdsrc;
|
|
Packit |
3ae693 |
};
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
static int ca_gst_sound_file_open(ca_sound_file **_f, const char *fn) {
|
|
Packit |
3ae693 |
int fd;
|
|
Packit |
3ae693 |
ca_sound_file *f;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_return_val_if_fail(_f, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(fn, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((fd = open(fn, O_RDONLY)) == -1)
|
|
Packit |
3ae693 |
return errno == ENOENT ? CA_ERROR_NOTFOUND : CA_ERROR_SYSTEM;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (!(f = ca_new0(ca_sound_file, 1))) {
|
|
Packit |
3ae693 |
close(fd);
|
|
Packit |
3ae693 |
return CA_ERROR_OOM;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (!(f->fdsrc = gst_element_factory_make("fdsrc", NULL))) {
|
|
Packit |
3ae693 |
close(fd);
|
|
Packit |
3ae693 |
ca_free(f);
|
|
Packit |
3ae693 |
return CA_ERROR_OOM;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
g_object_set(GST_OBJECT(f->fdsrc), "fd", fd, NULL);
|
|
Packit |
3ae693 |
*_f = f;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return CA_SUCCESS;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
static void on_pad_added(GstElement *element, GstPad *pad, gboolean arg1, gpointer data)
|
|
Packit |
3ae693 |
{
|
|
Packit |
3ae693 |
GstStructure *structure;
|
|
Packit |
3ae693 |
GstElement *sinkelement;
|
|
Packit |
3ae693 |
GstCaps *caps;
|
|
Packit |
3ae693 |
GstPad *vpad;
|
|
Packit |
3ae693 |
const char *type;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
sinkelement = GST_ELEMENT(data);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
caps = gst_pad_query_caps(pad, NULL);
|
|
Packit |
3ae693 |
if (gst_caps_is_empty(caps) || gst_caps_is_any(caps)) {
|
|
Packit |
3ae693 |
gst_caps_unref(caps);
|
|
Packit |
3ae693 |
return;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
structure = gst_caps_get_structure(caps, 0);
|
|
Packit |
3ae693 |
type = gst_structure_get_name(structure);
|
|
Packit |
3ae693 |
if (g_str_has_prefix(type, "audio/x-raw") == TRUE) {
|
|
Packit |
3ae693 |
vpad = gst_element_get_static_pad(sinkelement, "sink");
|
|
Packit |
3ae693 |
gst_pad_link(pad, vpad);
|
|
Packit |
3ae693 |
gst_object_unref(vpad);
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
gst_caps_unref(caps);
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
static void
|
|
Packit |
3ae693 |
send_mgr_exit_msg (struct private *p) {
|
|
Packit |
3ae693 |
GstMessage *m;
|
|
Packit |
3ae693 |
GstStructure *s;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
s = gst_structure_new("application/mgr-exit", NULL);
|
|
Packit |
3ae693 |
m = gst_message_new_application (NULL, s);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
gst_bus_post (p->mgr_bus, m);
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* Global manager thread that shuts down GStreamer pipelines when ordered */
|
|
Packit |
3ae693 |
static void* thread_func(void *userdata) {
|
|
Packit |
3ae693 |
struct private *p = userdata;
|
|
Packit |
3ae693 |
GstBus *bus = g_object_ref(p->mgr_bus);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
pthread_detach(pthread_self());
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* Pop messages from the manager bus until we see an exit command */
|
|
Packit |
3ae693 |
do {
|
|
Packit |
3ae693 |
GstMessage *m = gst_bus_timed_pop(bus, GST_CLOCK_TIME_NONE);
|
|
Packit |
3ae693 |
const GstStructure *s;
|
|
Packit |
3ae693 |
const GValue *v;
|
|
Packit |
3ae693 |
struct outstanding *out;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (m == NULL)
|
|
Packit |
3ae693 |
break;
|
|
Packit |
3ae693 |
if (GST_MESSAGE_TYPE(m) != GST_MESSAGE_APPLICATION) {
|
|
Packit |
3ae693 |
gst_message_unref (m);
|
|
Packit |
3ae693 |
break;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
s = gst_message_get_structure(m);
|
|
Packit |
3ae693 |
if (gst_structure_has_name(s, "application/mgr-exit")) {
|
|
Packit |
3ae693 |
gst_message_unref (m);
|
|
Packit |
3ae693 |
break;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* Otherwise, this must be an EOS message for an outstanding pipe */
|
|
Packit |
3ae693 |
ca_assert(gst_structure_has_name(s, "application/eos"));
|
|
Packit |
3ae693 |
v = gst_structure_get_value(s, "info");
|
|
Packit |
3ae693 |
ca_assert(v);
|
|
Packit |
3ae693 |
out = g_value_get_pointer(v);
|
|
Packit |
3ae693 |
ca_assert(out);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* Set pipeline back to NULL to close things. By the time this
|
|
Packit |
3ae693 |
* completes, we can be sure bus_cb won't be called */
|
|
Packit |
3ae693 |
if (gst_element_set_state(out->pipeline, GST_STATE_NULL) ==
|
|
Packit |
3ae693 |
GST_STATE_CHANGE_FAILURE) {
|
|
Packit |
3ae693 |
gst_message_unref (m);
|
|
Packit |
3ae693 |
break;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
if (out->callback)
|
|
Packit |
3ae693 |
out->callback(out->context, out->id, out->err, out->userdata);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_mutex_lock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
CA_LLIST_REMOVE(struct outstanding, p->outstanding, out);
|
|
Packit |
3ae693 |
outstanding_free(out);
|
|
Packit |
3ae693 |
ca_mutex_unlock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
gst_message_unref(m);
|
|
Packit |
3ae693 |
} while (TRUE);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* Signal the semaphore and exit */
|
|
Packit |
3ae693 |
ca_mutex_lock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
if (p->signal_semaphore)
|
|
Packit |
3ae693 |
sem_post(&p->semaphore);
|
|
Packit |
3ae693 |
p->mgr_thread_running = FALSE;
|
|
Packit |
3ae693 |
ca_mutex_unlock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
gst_bus_set_flushing(bus, TRUE);
|
|
Packit |
3ae693 |
g_object_unref (bus);
|
|
Packit |
3ae693 |
return NULL;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_play(ca_context *c, uint32_t id, ca_proplist *proplist, ca_finish_callback_t cb, void *userdata) {
|
|
Packit |
3ae693 |
struct private *p;
|
|
Packit |
3ae693 |
struct outstanding *out;
|
|
Packit |
3ae693 |
ca_sound_file *f;
|
|
Packit |
3ae693 |
GstElement *decodebin, *sink, *audioconvert, *audioresample, *abin;
|
|
Packit |
3ae693 |
GstBus *bus;
|
|
Packit |
3ae693 |
GstPad *audiopad;
|
|
Packit |
3ae693 |
int ret;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(proplist, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(!userdata || cb, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
out = NULL;
|
|
Packit |
3ae693 |
f = NULL;
|
|
Packit |
3ae693 |
sink = NULL;
|
|
Packit |
3ae693 |
decodebin = NULL;
|
|
Packit |
3ae693 |
audioconvert = NULL;
|
|
Packit |
3ae693 |
audioresample = NULL;
|
|
Packit |
3ae693 |
abin = NULL;
|
|
Packit |
3ae693 |
p = PRIVATE(c);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if ((ret = ca_lookup_sound_with_callback(&f, ca_gst_sound_file_open, NULL, &p->theme, c->props, proplist)) < 0)
|
|
Packit |
3ae693 |
goto fail;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (!(out = ca_new0(struct outstanding, 1)))
|
|
Packit |
3ae693 |
return CA_ERROR_OOM;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
out->id = id;
|
|
Packit |
3ae693 |
out->callback = cb;
|
|
Packit |
3ae693 |
out->userdata = userdata;
|
|
Packit |
3ae693 |
out->context = c;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (!(out->pipeline = gst_pipeline_new(NULL))
|
|
Packit |
3ae693 |
|| !(decodebin = gst_element_factory_make("decodebin2", NULL))
|
|
Packit |
3ae693 |
|| !(audioconvert = gst_element_factory_make("audioconvert", NULL))
|
|
Packit |
3ae693 |
|| !(audioresample = gst_element_factory_make("audioresample", NULL))
|
|
Packit |
3ae693 |
|| !(sink = gst_element_factory_make("autoaudiosink", NULL))
|
|
Packit |
3ae693 |
|| !(abin = gst_bin_new ("audiobin"))) {
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
/* At this point, if there is a failure, free each plugin separately. */
|
|
Packit |
3ae693 |
if (out->pipeline != NULL)
|
|
Packit |
3ae693 |
g_object_unref (out->pipeline);
|
|
Packit |
3ae693 |
if (decodebin != NULL)
|
|
Packit |
3ae693 |
g_object_unref(decodebin);
|
|
Packit |
3ae693 |
if (audioconvert != NULL)
|
|
Packit |
3ae693 |
g_object_unref(audioconvert);
|
|
Packit |
3ae693 |
if (audioresample != NULL)
|
|
Packit |
3ae693 |
g_object_unref(audioresample);
|
|
Packit |
3ae693 |
if (sink != NULL)
|
|
Packit |
3ae693 |
g_object_unref(sink);
|
|
Packit |
3ae693 |
if (abin != NULL)
|
|
Packit |
3ae693 |
g_object_unref(abin);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_free(out);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ret = CA_ERROR_OOM;
|
|
Packit |
3ae693 |
goto fail;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
bus = gst_pipeline_get_bus(GST_PIPELINE (out->pipeline));
|
|
Packit |
3ae693 |
gst_bus_set_sync_handler(bus, bus_cb, out, NULL);
|
|
Packit |
3ae693 |
gst_object_unref(bus);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
g_signal_connect(decodebin, "new-decoded-pad",
|
|
Packit |
3ae693 |
G_CALLBACK (on_pad_added), abin);
|
|
Packit |
3ae693 |
gst_bin_add_many(GST_BIN (abin), audioconvert, audioresample, sink, NULL);
|
|
Packit |
3ae693 |
gst_element_link_many(audioconvert, audioresample, sink, NULL);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
audiopad = gst_element_get_static_pad(audioconvert, "sink");
|
|
Packit |
3ae693 |
gst_element_add_pad(abin, gst_ghost_pad_new("sink", audiopad));
|
|
Packit |
3ae693 |
gst_object_unref(audiopad);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
gst_bin_add_many(GST_BIN (out->pipeline),
|
|
Packit |
3ae693 |
f->fdsrc, decodebin, abin, NULL);
|
|
Packit |
3ae693 |
if (!gst_element_link(f->fdsrc, decodebin)) {
|
|
Packit |
3ae693 |
/* Bin now owns the fdsrc... */
|
|
Packit |
3ae693 |
f->fdsrc = NULL;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
outstanding_free(out);
|
|
Packit |
3ae693 |
ret = CA_ERROR_OOM;
|
|
Packit |
3ae693 |
goto fail;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
/* Bin now owns the fdsrc... */
|
|
Packit |
3ae693 |
f->fdsrc = NULL;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_free(f);
|
|
Packit |
3ae693 |
f = NULL;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_mutex_lock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
CA_LLIST_PREPEND(struct outstanding, p->outstanding, out);
|
|
Packit |
3ae693 |
ca_mutex_unlock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (gst_element_set_state(out->pipeline,
|
|
Packit |
3ae693 |
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
|
|
Packit |
3ae693 |
ret = CA_ERROR_NOTAVAILABLE;
|
|
Packit |
3ae693 |
goto fail;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return CA_SUCCESS;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
fail:
|
|
Packit |
3ae693 |
if (f && f->fdsrc)
|
|
Packit |
3ae693 |
gst_object_unref(f->fdsrc);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (f)
|
|
Packit |
3ae693 |
ca_free(f);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return ret;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_cancel(ca_context *c, uint32_t id) {
|
|
Packit |
3ae693 |
struct private *p;
|
|
Packit |
3ae693 |
struct outstanding *out = NULL;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(PRIVATE(c), CA_ERROR_STATE);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
p = PRIVATE(c);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_mutex_lock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
for (out = p->outstanding; out;/* out = out->next*/) {
|
|
Packit |
3ae693 |
struct outstanding *next;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (out->id != id || out->pipeline == NULL || out->dead == TRUE) {
|
|
Packit |
3ae693 |
out = out->next;
|
|
Packit |
3ae693 |
continue;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (gst_element_set_state(out->pipeline, GST_STATE_NULL) ==
|
|
Packit |
3ae693 |
GST_STATE_CHANGE_FAILURE)
|
|
Packit |
3ae693 |
goto error;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (out->callback)
|
|
Packit |
3ae693 |
out->callback(c, out->id, CA_ERROR_CANCELED, out->userdata);
|
|
Packit |
3ae693 |
next = out->next;
|
|
Packit |
3ae693 |
CA_LLIST_REMOVE(struct outstanding, p->outstanding, out);
|
|
Packit |
3ae693 |
outstanding_free(out);
|
|
Packit |
3ae693 |
out = next;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_mutex_unlock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return CA_SUCCESS;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
error:
|
|
Packit |
3ae693 |
ca_mutex_unlock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
return CA_ERROR_SYSTEM;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_cache(ca_context *c, ca_proplist *proplist) {
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(proplist, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(PRIVATE(c), CA_ERROR_STATE);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return CA_ERROR_NOTSUPPORTED;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
int driver_playing(ca_context *c, uint32_t id, int *playing) {
|
|
Packit |
3ae693 |
struct private *p;
|
|
Packit |
3ae693 |
struct outstanding *out;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(c->private, CA_ERROR_STATE);
|
|
Packit |
3ae693 |
ca_return_val_if_fail(playing, CA_ERROR_INVALID);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
p = PRIVATE(c);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
*playing = 0;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_mutex_lock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
for (out = p->outstanding; out; out = out->next) {
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
if (out->id != id || out->pipeline == NULL || out->dead == TRUE)
|
|
Packit |
3ae693 |
continue;
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
*playing = 1;
|
|
Packit |
3ae693 |
break;
|
|
Packit |
3ae693 |
}
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
ca_mutex_unlock(p->outstanding_mutex);
|
|
Packit |
3ae693 |
|
|
Packit |
3ae693 |
return CA_SUCCESS;
|
|
Packit |
3ae693 |
}
|