2 * Copyright (c) 2012, Intel Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of Intel Corporation nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #include <murphy/common/log.h>
34 #include <murphy/common/debug.h>
35 #include <murphy/common/dbus-libdbus.h>
37 #include "srs/daemon/plugin.h"
38 #include "srs/daemon/client.h"
40 #include "dbus-config.h"
42 #define PLUGIN_NAME "dbus-client"
43 #define PLUGIN_DESCR "A D-Bus client plugin for SRS."
44 #define PLUGIN_AUTHORS "Krisztian Litkey <kli@iki.fi>"
45 #define PLUGIN_VERSION "0.0.1"
47 #define BUS_CONFIG "dbus.address"
48 #define BUS_DEFAULT "session"
50 #define MAX_COMMANDS 256
52 static int register_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data);
53 static int unregister_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
55 static int focus_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data);
56 static int render_voice_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
58 static int cancel_voice_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
60 static int query_voices_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
63 static int focus_notify(srs_client_t *c, srs_voice_focus_t focus);
64 static int command_notify(srs_client_t *c, int idx, int ntoken, char **tokens,
65 uint32_t *start, uint32_t *end,
66 srs_audiobuf_t *audio);
67 static int voice_notify(srs_client_t *c, srs_voice_event_t *event);
69 #define reply_error simple_reply
70 #define reply_register simple_reply
71 #define reply_unregister simple_reply
72 #define reply_focus simple_reply
73 #define reply_cancel simple_reply
76 srs_plugin_t *self; /* our plugin instance */
77 const char *address; /* bus address */
78 mrp_dbus_t *dbus; /* bus we're on */
82 static void dbusif_cleanup(dbusif_t *bus);
85 static int dbusif_setup(dbusif_t *bus)
87 srs_context_t *srs = bus->self->srs;
88 const char *path, *iface, *method;
89 int (*cb)(mrp_dbus_t *, mrp_dbus_msg_t *, void *);
91 mrp_debug("setting up client D-BUS interface (%s)", bus->address);
93 bus->dbus = mrp_dbus_get(srs->ml, bus->address, NULL);
95 if (bus->dbus != NULL) {
96 path = SRS_CLIENT_PATH;
97 iface = SRS_CLIENT_INTERFACE;
99 method = SRS_CLIENT_REGISTER;
101 if (!mrp_dbus_export_method(bus->dbus, path, iface, method, cb, bus)) {
102 mrp_log_error("Failed to register D-BUS '%s' method.", method);
106 method = SRS_CLIENT_UNREGISTER;
108 if (!mrp_dbus_export_method(bus->dbus, path, iface, method, cb, bus)) {
109 mrp_log_error("Failed to register D-BUS '%s' method.", method);
113 method = SRS_CLIENT_REQUEST_FOCUS;
115 if (!mrp_dbus_export_method(bus->dbus, path, iface, method, cb, bus)) {
116 mrp_log_error("Failed to register D-BUS '%s' method.", method);
120 method = SRS_CLIENT_RENDER_VOICE;
121 cb = render_voice_req;
122 if (!mrp_dbus_export_method(bus->dbus, path, iface, method, cb, bus)) {
123 mrp_log_error("Failed to register D-BUS '%s' method.", method);
127 method = SRS_CLIENT_CANCEL_VOICE;
128 cb = cancel_voice_req;
129 if (!mrp_dbus_export_method(bus->dbus, path, iface, method, cb, bus)) {
130 mrp_log_error("Failed to register D-BUS '%s' method.", method);
134 method = SRS_CLIENT_QUERY_VOICES;
135 cb = query_voices_req;
136 if (!mrp_dbus_export_method(bus->dbus, path, iface, method, cb, bus)) {
137 mrp_log_error("Failed to register D-BUS '%s' method.", method);
141 if (!mrp_dbus_acquire_name(bus->dbus, SRS_CLIENT_SERVICE, NULL)) {
142 mrp_log_error("Failed to acquire D-BUS name '%s'.",
149 mrp_log_error("Failed to connect to D-BUS (%s).", bus->address);
161 static void dbusif_cleanup(dbusif_t *bus)
163 srs_context_t *srs = bus->self->srs;
164 const char *path, *iface, *method;
165 int (*cb)(mrp_dbus_t *, mrp_dbus_msg_t *, void *);
167 mrp_debug("cleaning up client D-BUS interface");
169 if (bus->dbus != NULL) {
170 mrp_dbus_release_name(bus->dbus, SRS_CLIENT_SERVICE, NULL);
172 path = SRS_CLIENT_PATH;
173 iface = SRS_CLIENT_INTERFACE;
175 method = SRS_CLIENT_REGISTER;
177 mrp_dbus_remove_method(bus->dbus, path, iface, method, cb, bus);
179 method = SRS_CLIENT_UNREGISTER;
181 mrp_dbus_remove_method(bus->dbus, path, iface, method, cb, bus);
183 method = SRS_CLIENT_REQUEST_FOCUS;
185 mrp_dbus_remove_method(bus->dbus, path, iface, method, cb, bus);
187 method = SRS_CLIENT_RENDER_VOICE;
188 cb = render_voice_req;
189 mrp_dbus_remove_method(bus->dbus, path, iface, method, cb, bus);
191 method = SRS_CLIENT_CANCEL_VOICE;
192 cb = cancel_voice_req;
193 mrp_dbus_remove_method(bus->dbus, path, iface, method, cb, bus);
195 method = SRS_CLIENT_QUERY_VOICES;
196 cb = query_voices_req;
197 mrp_dbus_remove_method(bus->dbus, path, iface, method, cb, bus);
199 mrp_dbus_unref(bus->dbus);
205 static void name_change_cb(mrp_dbus_t *dbus, const char *name, int running,
206 const char *owner, void *user_data)
208 dbusif_t *bus = (dbusif_t *)user_data;
209 srs_context_t *srs = bus->self->srs;
214 mrp_debug("D-BUS client %s %s", name, running ? "up" : "down");
217 c = client_lookup_by_id(srs, name);
220 mrp_log_info("client %s disconnected from D-BUS", name);
222 mrp_dbus_forget_name(dbus, name, name_change_cb, bus);
228 static void simple_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *req, int errcode,
234 mrp_dbus_reply(dbus, req, MRP_DBUS_TYPE_INVALID);
237 mrp_dbus_reply_error(dbus, req, MRP_DBUS_ERROR_FAILED, errmsg,
238 MRP_DBUS_TYPE_INT32, &error,
239 MRP_DBUS_TYPE_INVALID);
244 static void reply_render(mrp_dbus_t *dbus, mrp_dbus_msg_t *req, uint32_t id)
246 mrp_dbus_reply(dbus, req, MRP_DBUS_TYPE_UINT32, &id,
247 MRP_DBUS_TYPE_INVALID);
251 static char *clear_non_us_ascii(char *s)
255 for (p = s; *p; p++) {
264 static void reply_voice_query(mrp_dbus_t *dbus, mrp_dbus_msg_t *req, int nactor,
265 srs_voice_actor_t *actors)
267 srs_voice_actor_t *a;
268 char *voices[nactor], **v;
269 char *lang[nactor], **ml;
270 char *dialect[nactor], **sl;
271 char *gender[nactor], **g;
272 char *description[nactor], **d;
282 for (i = 0; i < nactor; i++, a++, v++, ml++, sl++, g++, d++) {
285 *sl = a->dialect ? a->dialect : "";
286 *g = a->gender == SRS_VOICE_GENDER_MALE ? "male" : "female";
289 * XXX TODO: this is a hack is currently needed for festival
290 * which can feed us voice descriptions that are not UTF-8
291 * (and consequently not 7-bit ASCII either).
293 *d = clear_non_us_ascii(a->description);
295 printf("* description: %s\n", *d);
304 mrp_dbus_reply(dbus, req,
305 MRP_DBUS_TYPE_UINT32, &n,
306 MRP_DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &v , n,
307 MRP_DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &ml, n,
308 MRP_DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &sl, n,
309 MRP_DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &g , n,
310 MRP_DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &d , n,
311 MRP_DBUS_TYPE_INVALID);
315 static int parse_commands(mrp_dbus_msg_t *msg, char **commands, int ncommand)
320 while (n < ncommand - 1) {
321 if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, commands + n))
331 static int parse_register(mrp_dbus_msg_t *req, const char **id,
332 const char **name, const char **appclass,
333 char ***commands, int *ncommand, const char **errmsg)
338 *id = mrp_dbus_msg_sender(req);
341 *errmsg = "failed to parse register message";
345 if (!mrp_dbus_msg_read_basic(req, MRP_DBUS_TYPE_STRING, name))
348 if (!mrp_dbus_msg_read_basic(req, MRP_DBUS_TYPE_STRING, appclass))
351 if (mrp_dbus_msg_read_array(req, MRP_DBUS_TYPE_STRING, &cmds, &ncmd)) {
354 *ncommand = (int)ncmd;
361 *errmsg = "malformed register message";
366 static int register_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *req, void *user_data)
368 static srs_client_ops_t ops = {
369 .notify_focus = focus_notify,
370 .notify_command = command_notify,
371 .notify_render = voice_notify,
374 dbusif_t *bus = (dbusif_t *)user_data;
375 srs_context_t *srs = bus->self->srs;
376 const char *id, *name, *appcls, *errmsg;
381 ncmd = MRP_ARRAY_SIZE(cmds);
382 err = parse_register(req, &id, &name, &appcls, &cmds, &ncmd, &errmsg);
385 reply_register(dbus, req, err, errmsg);
390 mrp_debug("got register request from %s", id);
392 c = client_create(srs, SRS_CLIENT_TYPE_EXTERNAL, name, appcls, cmds, ncmd,
396 if (mrp_dbus_follow_name(dbus, id, name_change_cb, bus)) {
403 errmsg = "failed to track DBUS name";
408 errmsg = "failed to register client";
411 reply_register(dbus, req, err, errmsg);
416 static int parse_unregister(mrp_dbus_msg_t *req, const char **id,
419 *id = mrp_dbus_msg_sender(req);
424 *errmsg = "failed to determine client id";
430 static int unregister_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *req,
433 dbusif_t *bus = (dbusif_t *)user_data;
434 srs_context_t *srs = bus->self->srs;
435 const char *id, *errmsg;
439 err = parse_unregister(req, &id, &errmsg);
442 mrp_debug("got unregister request from %s", id);
444 c = client_lookup_by_id(srs, id);
447 mrp_dbus_forget_name(dbus, c->id, name_change_cb, bus);
449 reply_unregister(dbus, req, 0, NULL);
452 reply_unregister(dbus, req, 1, "you don't exist, go away");
455 reply_unregister(dbus, req, err, errmsg);
461 static int parse_focus(mrp_dbus_msg_t *req, const char **id, int *focus,
466 *id = mrp_dbus_msg_sender(req);
469 if (mrp_dbus_msg_read_basic(req, MRP_DBUS_TYPE_STRING, &type)) {
470 if (!strcmp(type, "none"))
471 *focus = SRS_VOICE_FOCUS_NONE;
472 else if (!strcmp(type, "shared" ))
473 *focus = SRS_VOICE_FOCUS_SHARED;
474 else if (!strcmp(type, "exclusive"))
475 *focus = SRS_VOICE_FOCUS_EXCLUSIVE;
477 *errmsg = "invalid voice focus requested";
484 *errmsg = "malformed voice focus request";
487 *errmsg = "failed to determine client id";
493 static int focus_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *req, void *user_data)
495 dbusif_t *bus = (dbusif_t *)user_data;
496 srs_context_t *srs = bus->self->srs;
497 const char *id, *errmsg;
501 err = parse_focus(req, &id, &focus, &errmsg);
504 mrp_debug("got 0x%x focus request from %s", focus, id);
506 c = client_lookup_by_id(srs, id);
509 if (client_request_focus(c, focus))
510 reply_focus(dbus, req, 0, NULL);
512 reply_focus(dbus, req, 1, "focus request failed");
515 reply_focus(dbus, req, 1, "you don't exist, go away");
518 reply_focus(dbus, req, err, errmsg);
524 static int focus_notify(srs_client_t *c, srs_voice_focus_t focus)
526 dbusif_t *bus = (dbusif_t *)c->user_data;
527 srs_context_t *srs = c->srs;
528 const char *dest = c->id;
529 const char *path = SRS_CLIENT_PATH;
530 const char *iface = SRS_CLIENT_INTERFACE;
531 const char *sig = SRS_CLIENT_NOTIFY_FOCUS;
535 case SRS_VOICE_FOCUS_NONE: state = "none"; break;
536 case SRS_VOICE_FOCUS_SHARED: state = "shared"; break;
537 case SRS_VOICE_FOCUS_EXCLUSIVE: state = "exclusive"; break;
538 default: return FALSE;
541 return mrp_dbus_signal(bus->dbus, dest, path, iface, sig,
542 MRP_DBUS_TYPE_STRING, state, MRP_DBUS_TYPE_INVALID);
546 static int command_notify(srs_client_t *c, int idx, int ntoken, char **tokens,
547 uint32_t *start, uint32_t *end, srs_audiobuf_t *audio)
549 dbusif_t *bus = (dbusif_t *)c->user_data;
550 srs_context_t *srs = c->srs;
551 const char *dest = c->id;
552 const char *path = SRS_CLIENT_PATH;
553 const char *iface = SRS_CLIENT_INTERFACE;
554 const char *sig = SRS_CLIENT_NOTIFY_COMMAND;
556 char buf[1024], *cmd, *p, *t;
568 for (i = 0; i < ntoken; i++) {
569 n = snprintf(p, l, "%s%s", t, tokens[i]);
579 return mrp_dbus_signal(bus->dbus, dest, path, iface, sig,
580 MRP_DBUS_TYPE_STRING, cmd, MRP_DBUS_TYPE_INVALID);
584 static int voice_notify(srs_client_t *c, srs_voice_event_t *event)
586 dbusif_t *bus = (dbusif_t *)c->user_data;
587 srs_context_t *srs = c->srs;
588 const char *dest = c->id;
589 const char *path = SRS_CLIENT_PATH;
590 const char *iface = SRS_CLIENT_INTERFACE;
591 const char *sig = SRS_CLIENT_NOTIFY_VOICE;
597 switch (event->type) {
598 case SRS_VOICE_EVENT_STARTED: type = "started" ; goto send;
599 case SRS_VOICE_EVENT_COMPLETED: type = "completed"; goto send;
600 case SRS_VOICE_EVENT_TIMEOUT: type = "timeout" ; goto send;
601 case SRS_VOICE_EVENT_ABORTED: type = "aborted" ; goto send;
603 return mrp_dbus_signal(bus->dbus, dest, path, iface, sig,
604 MRP_DBUS_TYPE_UINT32, &event->id,
605 MRP_DBUS_TYPE_STRING, type,
606 MRP_DBUS_TYPE_INVALID);
608 case SRS_VOICE_EVENT_PROGRESS:
610 pcnt = event->data.progress.pcnt;
611 msec = event->data.progress.msec;
613 return mrp_dbus_signal(bus->dbus, dest, path, iface, sig,
614 MRP_DBUS_TYPE_UINT32, &event->id,
615 MRP_DBUS_TYPE_STRING, type,
616 MRP_DBUS_TYPE_DOUBLE, &pcnt,
617 MRP_DBUS_TYPE_UINT32, &msec,
618 MRP_DBUS_TYPE_INVALID);
626 static int parse_render_voice(mrp_dbus_msg_t *req, const char **id,
627 const char **msg, const char **voice,
628 int *timeout, int *notify_events,
636 *id = mrp_dbus_msg_sender(req);
641 if (!mrp_dbus_msg_read_basic(req, MRP_DBUS_TYPE_STRING, msg) ||
642 !mrp_dbus_msg_read_basic(req, MRP_DBUS_TYPE_STRING, voice) ||
643 !mrp_dbus_msg_read_basic(req, MRP_DBUS_TYPE_INT32 , &to) ||
644 !mrp_dbus_msg_read_array(req, MRP_DBUS_TYPE_STRING,
645 (void **)&events, &nevent)) {
646 *errmsg = "malformed voice render message";
654 for (i = 0; i < nevent; i++) {
657 if (!strcmp(e, SRS_CLIENT_VOICE_STARTED))
658 *notify_events |= SRS_VOICE_MASK_STARTED;
659 else if (!strcmp(e, SRS_CLIENT_VOICE_PROGRESS))
660 *notify_events |= SRS_VOICE_MASK_PROGRESS;
661 else if (!strcmp(e, SRS_CLIENT_VOICE_COMPLETED))
662 *notify_events |= SRS_VOICE_MASK_COMPLETED;
663 else if (!strcmp(e, SRS_CLIENT_VOICE_TIMEOUT))
664 *notify_events |= SRS_VOICE_MASK_TIMEOUT;
665 else if (!strcmp(e, SRS_CLIENT_VOICE_ABORTED))
666 *notify_events |= SRS_VOICE_MASK_ABORTED;
668 *errmsg = "invalid event";
678 static int render_voice_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *req,
681 dbusif_t *bus = (dbusif_t *)user_data;
682 srs_context_t *srs = bus->self->srs;
683 const char *id, *msg, *voice, *errmsg;
684 int timeout, events, err;
688 err = parse_render_voice(req, &id, &msg, &voice, &timeout, &events,
692 reply_error(dbus, req, err, errmsg);
697 c = client_lookup_by_id(srs, id);
700 reply_error(dbus, req, 1, "you don't exists, go away");
705 reqid = client_render_voice(c, msg, voice, timeout, events);
707 if (reqid != SRS_VOICE_INVALID)
708 reply_render(dbus, req, reqid);
710 reply_error(dbus, req, 1, "voice render request failed");
716 static int parse_cancel_voice(mrp_dbus_msg_t *req, const char **id,
717 uint32_t *reqid, const char **errmsg)
720 *id = mrp_dbus_msg_sender(req);
725 if (!mrp_dbus_msg_read_basic(req, MRP_DBUS_TYPE_UINT32, &reqid)) {
726 *errmsg = "malformed voice render message";
735 static int cancel_voice_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *req,
738 dbusif_t *bus = (dbusif_t *)user_data;
739 srs_context_t *srs = bus->self->srs;
740 const char *id, *errmsg;
745 err = parse_cancel_voice(req, &id, &reqid, &errmsg);
748 reply_cancel(dbus, req, err, errmsg);
753 c = client_lookup_by_id(srs, id);
756 reply_cancel(dbus, req, 1, "you don't exists, go away");
761 client_cancel_voice(c, reqid);
762 reply_cancel(dbus, req, 0, NULL);
768 static int parse_voice_query(mrp_dbus_msg_t *req, const char **id,
771 *id = mrp_dbus_msg_sender(req);
776 if (!mrp_dbus_msg_read_basic(req, MRP_DBUS_TYPE_STRING, lang))
783 static int query_voices_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *req,
786 dbusif_t *bus = (dbusif_t *)user_data;
787 srs_context_t *srs = bus->self->srs;
792 srs_voice_actor_t *actors;
795 err = parse_voice_query(req, &id, &lang);
798 reply_cancel(dbus, req, err, "internal error");
803 c = client_lookup_by_id(srs, id);
806 reply_error(dbus, req, 1, "you don't exists, go away");
811 nactor = client_query_voices(c, lang, &actors);
814 reply_error(dbus, req, 1, "voice actor query failed");
816 reply_voice_query(dbus, req, nactor, actors);
818 client_free_queried_voices(actors);
824 static int create_dbusif(srs_plugin_t *plugin)
828 mrp_debug("creating D-Bus client interface plugin");
830 bus = mrp_allocz(sizeof(*bus));
834 plugin->plugin_data = bus;
842 static int config_dbusif(srs_plugin_t *plugin, srs_cfg_t *settings)
844 dbusif_t *bus = (dbusif_t *)plugin->plugin_data;
846 MRP_UNUSED(settings);
848 mrp_debug("configure D-Bus client interface plugin");
850 bus->address = srs_get_string_config(settings, BUS_CONFIG, BUS_DEFAULT);
852 mrp_log_info("Client interface D-Bus address: '%s'", bus->address);
854 return dbusif_setup(bus);
858 static int start_dbusif(srs_plugin_t *plugin)
860 dbusif_t *bus = (dbusif_t *)plugin->plugin_data;
864 mrp_debug("start D-Bus client interface plugin");
870 static void stop_dbusif(srs_plugin_t *plugin)
872 dbusif_t *bus = (dbusif_t *)plugin->plugin_data;
874 mrp_debug("stop D-Bus client interface plugin");
880 static void destroy_dbusif(srs_plugin_t *plugin)
882 srs_context_t *srs = plugin->srs;
883 dbusif_t *dbus = (dbusif_t *)plugin->plugin_data;
887 mrp_debug("destroy D-Bus client interface plugin");
889 dbusif_cleanup(dbus);
894 SRS_DECLARE_PLUGIN(PLUGIN_NAME, PLUGIN_DESCR, PLUGIN_AUTHORS, PLUGIN_VERSION,
895 create_dbusif, config_dbusif, start_dbusif, stop_dbusif,