wrt-media-client: added WRT media player client plugin
authorKrisztian Litkey <kli@iki.fi>
Wed, 21 Aug 2013 18:03:14 +0000 (21:03 +0300)
committerKrisztian Litkey <kli@iki.fi>
Wed, 21 Aug 2013 18:03:14 +0000 (21:03 +0300)
This plugin, at least in its current primitive form, is only here
temporarily. It registers and relays media playback commands by
emitting a D-Bus signal to a corresponding WRT plugin that catches
the signal and fires JS events for it.

This should be replaced by properly relaying (and extending/changing
as needed) the client protocol to the WRT. Relaying can, but does
not need to use D-Bus. We can have a dedicated WRT-specific plugin
using any socket-based IPC and a private dedicated protocol, but
we also could have, for instance, websocket bindings for the client
protocol and use that from the WRT.

The key thing is that a proper publish/subscribe protocol should be
used, so that the client can get proper notifications about changes
in the state of voice recognition. Currently the WRT (media) client
is not a proper client and for instance it does not know if VR/its
voice focus is active at all.

configure.ac
src/Makefile.am
src/plugins/wrt-media-client/Makefile [new file with mode: 0644]
src/plugins/wrt-media-client/wrt-media-client.c [new file with mode: 0644]

index 9a33941..5ca694c 100644 (file)
@@ -203,6 +203,21 @@ AC_SUBST(GLIB_LIBS)
 AC_SUBST(MURPHY_GLIB_CFLAGS)
 AC_SUBST(MURPHY_GLIB_LIBS)
 
+# Check if WRT media client should be enabled.
+AC_ARG_ENABLE(wrt-client,
+              [  --enable-wrt-client     enable WRT media client plugin],
+             [enable_wrtc=$enableval], [enable_wrtc=yes])
+
+if test "$enable_wrtc" = "yes"; then
+    PKG_CHECK_MODULES(GIO, gio-2.0)
+else
+    AC_MSG_NOTICE([WRT media client plugin is disabled.])
+fi
+
+AM_CONDITIONAL(WRTC_ENABLED, [test "$enable_wrtc" = "yes"])
+AC_SUBST(GIO_CFLAGS)
+AC_SUBST(GIO_LIBS)
+
 # Shave by default.
 SHAVE_INIT([build-aux], [enable])
 
index 5737090..c843047 100644 (file)
@@ -252,6 +252,26 @@ plugin_input_handler_la_LDFLAGS =                          \
 plugin_input_handler_la_LIBADD  =                              \
                $(UDEV_LIBS)
 
+if WRTC_ENABLED
+# WRT media client
+plugin_LTLIBRARIES += plugin-wrt-media-client.la
+
+plugin_wrt_media_client_la_SOURCES =                           \
+               plugins/wrt-media-client/wrt-media-client.c
+
+plugin_wrt_media_client_la_CFLAGS =                            \
+               $(AM_CFLAGS)                                    \
+               $(MURPHY_GLIB_CFLAGS)                           \
+               $(GIO_CFLAGS)
+
+plugin_wrt_media_client_la_LDFLAGS =                           \
+               -module -avoid-version
+
+plugin_wrt_media_client_la_LIBADD =                            \
+               $(MURPHY_GLIB_LIBS)                             \
+               $(GIO_LIBS)
+endif
+
 # cleanup
 clean-local::
        -rm -f *~
diff --git a/src/plugins/wrt-media-client/Makefile b/src/plugins/wrt-media-client/Makefile
new file mode 100644 (file)
index 0000000..adcfe7e
--- /dev/null
@@ -0,0 +1,5 @@
+all:
+       $(MAKE) -C ../.. $@
+
+%:
+       $(MAKE) -C ../.. $(MAKECMDGOALS)
diff --git a/src/plugins/wrt-media-client/wrt-media-client.c b/src/plugins/wrt-media-client/wrt-media-client.c
new file mode 100644 (file)
index 0000000..6cf5cec
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Intel Corporation nor the names of its contributors
+ *     may be used to endorse or promote products derived from this software
+ *     without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include <murphy/common/debug.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/common/glib-glue.h>
+
+#include <gio/gio.h>
+
+#include "src/daemon/plugin.h"
+#include "src/daemon/client.h"
+
+#define WRTC_NAME    "wrt-media-client"
+#define WRTC_DESCR   "A demo WRT media player relay client."
+#define WRTC_AUTHORS "Krisztian Litkey <krisztian.litkey@intel.com>"
+#define WRTC_VERSION "0.0.1"
+
+#define CONFIG_BUS   "wrtc.bus"
+#define CONFIG_PLAY  "wrtc.commands.play"
+#define CONFIG_STOP  "wrtc.commands.stop"
+#define CONFIG_PAUSE "wrtc.commands.pause"
+#define CONFIG_NEXT  "wrtc.commands.next"
+#define CONFIG_PREV  "wrtc.commands.prev"
+
+#define DEFAULT_BUS   "session"
+#define DEFAULT_PLAY  "play music"
+#define DEFAULT_STOP  "stop music"
+#define DEFAULT_PAUSE "pause music"
+#define DEFAULT_NEXT  "play next"
+#define DEFAULT_PREV  "play previous"
+
+typedef enum {
+    CMD_PLAY = 0,
+    CMD_STOP,
+    CMD_PAUSE,
+    CMD_NEXT,
+    CMD_PREV
+} wrtc_cmd_t;
+
+typedef struct {
+    srs_context_t   *srs;                /* SRS runtime context */
+    srs_plugin_t    *self;               /* our plugin instance */
+    srs_client_t    *c;                  /* our SRS client */
+    GDBusConnection *gdbus;              /* D-Bus connection */
+    guint            gnrq;               /* name request ID*/
+    int              name;               /* whether we have the name */
+    struct {
+        const char *bus;
+        const char *play;
+        const char *stop;
+        const char *pause;
+        const char *next;
+        const char *prev;
+    } config;
+} wrtc_t;
+
+
+static int focus_cb(srs_client_t *c, srs_voice_focus_t focus)
+{
+    wrtc_t        *wrtc = (wrtc_t *)c->user_data;
+    srs_context_t *srs = wrtc->srs;
+    const char    *state;
+
+    MRP_UNUSED(wrtc);
+    MRP_UNUSED(srs);
+
+    switch (focus) {
+    case SRS_VOICE_FOCUS_NONE:      state = "no";      break;
+    case SRS_VOICE_FOCUS_SHARED:    state = "shared";    break;
+    case SRS_VOICE_FOCUS_EXCLUSIVE: state = "exclusive"; break;
+    default:                        state = "unknown";
+    }
+
+    mrp_log_info("WRT media client now has %s voice focus.", state);
+
+    return TRUE;
+}
+
+
+static int command_cb(srs_client_t *c, int idx, int ntoken, char **tokens,
+                      uint32_t *start, uint32_t *end, srs_audiobuf_t *audio)
+{
+    static const char *events[] = {
+        [CMD_PLAY]  = "play",
+        [CMD_STOP]  = "stop",
+        [CMD_PAUSE] = "pause",
+        [CMD_NEXT]  = "next",
+        [CMD_PREV]  = "previous",
+    };
+    static int nevent = MRP_ARRAY_SIZE(events);
+
+    wrtc_t          *wrtc = (wrtc_t *)c->user_data;
+    const char      *event;
+    GVariantBuilder *vb;
+    GVariant        *args;
+
+    MRP_UNUSED(start);
+    MRP_UNUSED(end);
+    MRP_UNUSED(audio);
+    MRP_UNUSED(tokens);
+    MRP_UNUSED(ntoken);
+
+    if (!wrtc->name) {
+        mrp_log_error("WRT media client can't relay, got no D-Bus name.");
+
+        return TRUE;
+    }
+
+    if (idx < 0 || idx >= nevent) {
+        mrp_log_error("WRT media client got invalid command #%d.", idx);
+
+        return TRUE;
+    }
+    else
+        event = events[idx];
+
+    mrp_log_info("WRT media client relaying command #%d (%s).", idx, event);
+
+    vb = g_variant_builder_new(G_VARIANT_TYPE("as"));
+    g_variant_builder_add(vb, "s", event);
+    args = g_variant_new("(as)", vb);
+    g_variant_builder_unref(vb);
+
+    g_dbus_connection_emit_signal(wrtc->gdbus, NULL,
+                                  "/srs", "org.tizen.srs", "Result",
+                                  args, NULL);
+    return TRUE;
+}
+
+
+static void wrtc_cleanup(wrtc_t *wrtc);
+
+
+static void name_acquired_cb(GDBusConnection *gdbus,
+                             const gchar *name, gpointer data)
+{
+    wrtc_t *wrtc = (wrtc_t *)data;
+
+    MRP_UNUSED(gdbus);
+
+    mrp_log_info("WRT media client plugin acquired name '%s' on D-Bus.", name);
+
+    wrtc->name = TRUE;
+}
+
+
+static void name_lost_cb(GDBusConnection *gdbus,
+                         const gchar *name, gpointer data)
+{
+    wrtc_t *wrtc = (wrtc_t *)data;
+
+    MRP_UNUSED(gdbus);
+
+    mrp_log_info("WRT media client plugin lost name '%s' on D-Bus.", name);
+
+    wrtc->gnrq = 0;
+    wrtc->name = 0;
+}
+
+
+static int wrtc_setup(wrtc_t *wrtc)
+{
+    static srs_client_ops_t ops = {
+        .notify_focus   = focus_cb,
+        .notify_command = command_cb
+    };
+
+    srs_context_t *srs = wrtc->srs;
+    char          *cmds[] = {
+        (char *)wrtc->config.play,
+        (char *)wrtc->config.stop,
+        (char *)wrtc->config.pause,
+        (char *)wrtc->config.next,
+        (char *)wrtc->config.prev
+    };
+    int         ncmd   = (int)MRP_ARRAY_SIZE(cmds);
+    const char *name   = "wrt-media-client";
+    const char *appcls = "player";
+    const char *id     = "wrt-media-client";
+
+    wrtc->gdbus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
+
+    if (wrtc->gdbus == NULL)
+        return FALSE;
+
+    wrtc->gnrq = g_bus_own_name(G_BUS_TYPE_SESSION, "org.tizen.srs", 0,
+                                NULL, name_acquired_cb, name_lost_cb,
+                                wrtc, NULL);
+
+    wrtc->c = client_create(srs, SRS_CLIENT_TYPE_BUILTIN,
+                            name, appcls, cmds, ncmd, id, &ops, wrtc);
+
+    if (wrtc->c == NULL) {
+        wrtc_cleanup(wrtc);
+
+        return FALSE;
+    }
+
+    client_request_focus(wrtc->c, SRS_VOICE_FOCUS_SHARED);
+
+    return TRUE;
+}
+
+
+static void wrtc_cleanup(wrtc_t *wrtc)
+{
+    if (wrtc->c != NULL)
+        client_destroy(wrtc->c);
+
+    if (wrtc->gnrq != 0)
+        g_bus_unown_name(wrtc->gnrq);
+
+    if (wrtc->gdbus != NULL)
+        g_object_unref(wrtc->gdbus);
+}
+
+
+static int create_wrtc(srs_plugin_t *plugin)
+{
+    wrtc_t *wrtc;
+
+    mrp_debug("creating WRT media client plugin");
+
+    wrtc = mrp_allocz(sizeof(*wrtc));
+
+    if (wrtc != NULL) {
+        wrtc->self = plugin;
+        wrtc->srs  = plugin->srs;
+        plugin->plugin_data = wrtc;
+        return TRUE;
+    }
+    else
+        return FALSE;
+}
+
+
+static int config_wrtc(srs_plugin_t *plugin, srs_cfg_t *settings)
+{
+    wrtc_t *wrtc = (wrtc_t *)plugin->plugin_data;
+    struct {
+        const char **addr;
+        const char  *key;
+        const char  *def;
+    } config[] = {
+        { &wrtc->config.bus  , CONFIG_BUS  , DEFAULT_BUS   },
+        { &wrtc->config.play , CONFIG_PLAY , DEFAULT_PLAY  },
+        { &wrtc->config.stop , CONFIG_STOP , DEFAULT_STOP  },
+        { &wrtc->config.pause, CONFIG_PAUSE, DEFAULT_PAUSE },
+        { &wrtc->config.next , CONFIG_NEXT , DEFAULT_NEXT  },
+        { &wrtc->config.prev , CONFIG_PREV , DEFAULT_PREV  },
+        { NULL, NULL, NULL }
+    }, *cfg;
+
+    mrp_debug("configure WRT media client plugin");
+
+    if (plugin->srs->gl == NULL) {
+        mrp_log_error("The WRT media client plugin requires GMainLoop.");
+        mrp_log_error("Please set the 'gmainloop' config variable true.");
+
+        if (plugin->srs->config_file != NULL)
+            mrp_log_error("You can do this either in the configuration file"
+                          "'%s',\n"
+                          "or by passing -s gmainloop=true on the command line",
+                          plugin->srs->config_file);
+        else
+            mrp_log_error("You can do this by passing the -s gmainloop=true\n"
+                          "command line option.");
+
+        return FALSE;
+    }
+
+    for (cfg = config; cfg->key; cfg++)
+        *cfg->addr = srs_get_string_config(settings, cfg->key, cfg->def);
+
+    mrp_log_info("WRT media client configuration:");
+    mrp_log_info("    D-Bus: '%s'", wrtc->config.bus);
+    mrp_log_info("    play command: '%s'", wrtc->config.play);
+    mrp_log_info("    stop command: '%s'", wrtc->config.stop);
+    mrp_log_info("    pause command: '%s'", wrtc->config.pause);
+    mrp_log_info("    next command: '%s'", wrtc->config.next);
+    mrp_log_info("    prev command: '%s'", wrtc->config.prev);
+
+    return TRUE;
+}
+
+
+static int start_wrtc(srs_plugin_t *plugin)
+{
+    wrtc_t *wrtc = (wrtc_t *)plugin->plugin_data;
+
+    MRP_UNUSED(wrtc);
+
+    mrp_debug("start WRT media client plugin");
+
+    return wrtc_setup(wrtc);
+}
+
+
+static void stop_wrtc(srs_plugin_t *plugin)
+{
+    wrtc_t *wrtc = (wrtc_t *)plugin->plugin_data;
+
+    mrp_debug("stop WRT media client plugin");
+
+    return;
+}
+
+
+static void destroy_wrtc(srs_plugin_t *plugin)
+{
+    srs_context_t *srs  = plugin->srs;
+    wrtc_t        *wrtc = (wrtc_t *)plugin->plugin_data;
+
+    MRP_UNUSED(srs);
+
+    mrp_debug("destroy WRT media client plugin");
+
+    wrtc_cleanup(wrtc);
+    mrp_free(wrtc);
+}
+
+
+SRS_DECLARE_PLUGIN(WRTC_NAME, WRTC_DESCR, WRTC_AUTHORS, WRTC_VERSION,
+                   create_wrtc, config_wrtc, start_wrtc, stop_wrtc,
+                   destroy_wrtc)