2 * Copyright (c) 2012, 2013, 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/debug.h>
34 #include <murphy/common/log.h>
35 #include <murphy/common/mm.h>
36 #include <murphy/common/list.h>
37 #include <murphy/common/transport.h>
38 #include <murphy/common/native-types.h>
40 #include "srs/daemon/plugin.h"
41 #include "srs/daemon/client.h"
43 #include "native-messages.h"
44 #include "native-config.h"
46 #define PLUGIN_NAME "native-client"
47 #define PLUGIN_DESCR "Native client plugin for SRS."
48 #define PLUGIN_AUTHORS "Krisztian Litkey <kli@iki.fi>"
49 #define PLUGIN_VERSION "0.0.1"
53 * server runtime context
57 srs_plugin_t *self; /* our plugin instance */
58 const char *address; /* our transport address */
59 int sock; /* or existing transport socket */
60 mrp_transport_t *lt; /* transport we listen on */
61 mrp_list_hook_t clients; /* connected clients */
62 int next_id; /* next client id */
71 srs_client_t *c; /* associated SRS client */
72 server_t *s; /* server runtime context */
73 mrp_transport_t *t; /* transport towards this client */
74 mrp_list_hook_t hook; /* to list of native clients */
75 int id; /* client id */
79 static int focus_notify(srs_client_t *c, srs_voice_focus_t focus);
80 static int command_notify(srs_client_t *c, int idx, int ntoken, char **tokens,
81 uint32_t *start, uint32_t *end,
82 srs_audiobuf_t *audio);
83 static int voice_notify(srs_client_t *c, srs_voice_event_t *event);
85 static int reply_status(client_t *c, uint32_t reqno, int status,
87 #define reply_register reply_status
88 #define reply_unregister reply_status
89 #define reply_focus reply_status
90 static int reply_render(client_t *c, uint32_t reqno, uint32_t id);
91 static int reply_voiceqry(client_t *c, uint32_t reqno,
92 srs_voice_actor_t *actors, int nactor);
94 static client_t *create_client(server_t *s, mrp_transport_t *lt)
99 c = mrp_allocz(sizeof(*c));
102 mrp_list_init(&c->hook);
105 c->id = s->next_id++;
106 c->t = mrp_transport_accept(lt, c, MRP_TRANSPORT_REUSEADDR);
109 mrp_list_append(&s->clients, &c->hook);
117 t = mrp_transport_accept(lt, NULL, MRP_TRANSPORT_REUSEADDR);
118 mrp_transport_destroy(t);
125 static void destroy_client(client_t *c)
127 mrp_list_delete(&c->hook);
129 mrp_transport_destroy(c->t);
130 client_destroy(c->c);
136 static void register_client(client_t *c, srs_req_register_t *req)
138 static srs_client_ops_t ops = {
139 .notify_focus = focus_notify,
140 .notify_command = command_notify,
141 .notify_render = voice_notify,
144 srs_context_t *srs = c->s->self->srs;
145 char *name = req->name;
146 char *appcls = req->appclass;
147 char **cmds = req->commands;
148 int ncmd = req->ncommand;
151 snprintf(id, sizeof(id), "native-client-%d", c->id);
153 mrp_debug("received register request from native client #%d", c->id);
155 c->c = client_create(srs, SRS_CLIENT_TYPE_EXTERNAL, name, appcls,
156 cmds, ncmd, id, &ops, c);
159 reply_register(c, req->reqno, SRS_STATUS_OK, "OK");
161 reply_register(c, req->reqno, SRS_STATUS_FAILED, "failed");
167 static void unregister_client(client_t *c, srs_req_unregister_t *req)
169 mrp_debug("received unregister request from native client #%d", c->id);
171 reply_unregister(c, req->reqno, SRS_STATUS_OK, "OK");
176 static void request_focus(client_t *c, srs_req_focus_t *req)
178 mrp_debug("received focus request from native client #%d", c->id);
180 if (client_request_focus(c->c, req->focus))
181 reply_focus(c, req->reqno, SRS_STATUS_OK, "OK");
183 reply_focus(c, req->reqno, SRS_STATUS_FAILED, "failed");
187 static void request_voice(client_t *c, srs_req_voice_t *req)
189 const char *msg = req->msg;
190 const char *voice = req->voice;
191 double rate = req->rate;
192 double pitch = req->pitch;
193 int timeout = req->timeout;
194 int events = req->events;
197 mrp_debug("received voice render request from native client #%d", c->id);
199 reqid = client_render_voice(c->c, msg, voice, rate, pitch, timeout, events);
201 if (reqid != SRS_VOICE_INVALID)
202 reply_render(c, req->reqno, reqid);
204 reply_status(c, req->reqno, SRS_STATUS_FAILED, "failed");
208 static void cancel_voice(client_t *c, srs_ccl_voice_t *req)
210 mrp_debug("received voice cancel request from native client #%d", c->id);
212 client_cancel_voice(c->c, req->id);
213 reply_status(c, req->reqno, SRS_STATUS_OK, "OK");
217 static void query_voices(client_t *c, srs_req_voiceqry_t *req)
219 srs_voice_actor_t *actors;
222 mrp_debug("received voice query request from native client #%d", c->id);
224 nactor = client_query_voices(c->c, req->lang, &actors);
225 reply_voiceqry(c, req->reqno, actors, nactor);
226 client_free_queried_voices(actors);
230 static int reply_status(client_t *c, uint32_t reqno, int status,
233 srs_rpl_status_t rpl;
235 mrp_debug("replying <%d, %s> to request #%d from native client #%d",
236 status, msg, reqno, c->id);
238 rpl.type = SRS_REPLY_STATUS;
241 rpl.msg = (char *)msg;
243 return send_message(c->t, (srs_msg_t *)&rpl);
247 static int reply_render(client_t *c, uint32_t reqno, uint32_t id)
251 mrp_debug("replying <#%u> to request #%d from native client #%d", id,
254 rpl.type = SRS_REPLY_RENDERVOICE;
258 return send_message(c->t, (srs_msg_t *)&rpl);
262 static int focus_notify(srs_client_t *client, srs_voice_focus_t focus)
264 client_t *c = (client_t *)client->user_data;
267 mrp_debug("relaying focus event to native client #%d", c->id);
269 evt.type = SRS_EVENT_FOCUS;
272 return send_message(c->t, (srs_msg_t *)&evt);
276 static int command_notify(srs_client_t *client, int idx,
277 int ntoken, char **tokens, uint32_t *start,
278 uint32_t *end, srs_audiobuf_t *audio)
280 client_t *c = (client_t *)client->user_data;
281 srs_evt_command_t evt;
287 mrp_debug("relaying command event to native client #%d", c->id);
289 evt.type = SRS_EVENT_COMMAND;
294 return send_message(c->t, (srs_msg_t *)&evt);
298 static int voice_notify(srs_client_t *client, srs_voice_event_t *event)
300 client_t *c = (client_t *)client->user_data;
303 mrp_debug("relaying voice event to native client #%d", c->id);
305 evt.type = SRS_EVENT_VOICE;
306 evt.event = event->type;
309 if (event->type == SRS_VOICE_EVENT_PROGRESS) {
310 evt.pcnt = event->data.progress.pcnt;
311 evt.msec = event->data.progress.msec;
318 return send_message(c->t, (srs_msg_t *)&evt);
322 static int reply_voiceqry(client_t *c, uint32_t reqno,
323 srs_voice_actor_t *actors, int nactor)
325 srs_rpl_voiceqry_t rpl;
327 mrp_debug("replying to request #%u from native client #%d", reqno, c->id);
332 rpl.type = SRS_REPLY_QUERYVOICES;
337 return send_message(c->t, (srs_msg_t *)&rpl);
341 static inline void dump_message(void *data, uint32_t type_id)
345 if (mrp_print_native(buf, sizeof(buf), data, type_id) > 0)
346 mrp_debug("got message of type 0x%x: %s", type_id, buf);
350 static void connection_evt(mrp_transport_t *lt, void *user_data)
352 server_t *s = (server_t *)user_data;
355 c = create_client(s, lt);
358 mrp_log_info("Accepted new native client connection.");
360 mrp_log_error("Failed to accept new native client connection.");
364 static void closed_evt(mrp_transport_t *t, int error, void *user_data)
366 client_t *c = (client_t *)user_data;
371 mrp_log_error("Native client connection closed with error %d (%s).",
372 error, strerror(error));
374 mrp_log_info("Native client connection closed.");
380 static void recv_evt(mrp_transport_t *t, void *data, uint32_t type_id,
383 client_t *c = (client_t *)user_data;
384 srs_msg_t *req = (srs_msg_t *)data;
388 dump_message(data, type_id);
391 case SRS_REQUEST_REGISTER:
392 register_client(c, &req->reg_req);
395 case SRS_REQUEST_UNREGISTER:
396 unregister_client(c, &req->bye_req);
399 case SRS_REQUEST_FOCUS:
400 request_focus(c, &req->focus_req);
403 case SRS_REQUEST_RENDERVOICE:
404 request_voice(c, &req->voice_req);
407 case SRS_REQUEST_CANCELVOICE:
408 cancel_voice(c, &req->voice_ccl);
411 case SRS_REQUEST_QUERYVOICES:
412 query_voices(c, &req->voice_qry);
422 static int transport_setup(server_t *s)
424 static mrp_transport_evt_t evt = {
425 { .recvnative = recv_evt },
426 { .recvnativefrom = NULL },
427 .connection = connection_evt,
428 .closed = closed_evt,
431 srs_context_t *srs = s->self->srs;
434 const char *type, *opt, *val;
435 int flags, state, sock;
438 alen = mrp_transport_resolve(NULL, s->address, &addr, sizeof(addr), &type);
441 mrp_log_error("Failed to resolve transport address '%s'.",
446 flags = MRP_TRANSPORT_REUSEADDR | MRP_TRANSPORT_NONBLOCK | \
447 MRP_TRANSPORT_MODE_NATIVE;
450 s->lt = mrp_transport_create(srs->ml, type, &evt, s, flags);
452 state = MRP_TRANSPORT_LISTENED;
454 s->lt = mrp_transport_create_from(srs->ml, type, &sock, &evt,
459 mrp_log_error("Failed to create transport for native clients.");
463 if ((typemap = register_message_types()) == NULL) {
464 mrp_log_error("Failed to register native messages.");
468 if (!mrp_transport_setopt(s->lt, "type-map", typemap)) {
469 mrp_log_error("Failed to set transport type map.");
474 if (mrp_transport_bind(s->lt, &addr, alen) &&
475 mrp_transport_listen(s->lt, 0)) {
476 mrp_log_info("Listening on transport '%s'...", s->address);
481 mrp_log_error("Failed to bind/listen transport.");
484 mrp_log_info("Using passed in socket fd %d...", s->sock);
491 mrp_transport_destroy(s->lt);
499 static void transport_cleanup(server_t *s)
501 mrp_transport_destroy(s->lt);
506 static int create_native(srs_plugin_t *plugin)
510 mrp_debug("creating native client interface plugin");
512 if ((s = mrp_allocz(sizeof(*s))) == NULL)
515 mrp_list_init(&s->clients);
518 plugin->plugin_data = s;
529 static int config_native(srs_plugin_t *plugin, srs_cfg_t *cfg)
531 server_t *s = (server_t *)plugin->plugin_data;
533 mrp_debug("configure native client interface plugin");
535 s->address = srs_config_get_string(cfg, CONFIG_ADDRESS, DEFAULT_ADDRESS);
536 s->sock = srs_config_get_int32(cfg, CONFIG_SOCKET , DEFAULT_SOCKET);
539 mrp_log_info("Using native client transport: '%s'.", s->address);
541 mrp_log_info("Using native client socket: %d.", s->sock);
547 static int start_native(srs_plugin_t *plugin)
549 server_t *s = (server_t *)plugin->plugin_data;
553 return transport_setup(s);
557 static void stop_native(srs_plugin_t *plugin)
563 static void destroy_native(srs_plugin_t *plugin)
565 server_t *s = (server_t *)plugin->plugin_data;
567 transport_cleanup(s);
572 SRS_DECLARE_PLUGIN(PLUGIN_NAME, PLUGIN_DESCR, PLUGIN_AUTHORS, PLUGIN_VERSION,
573 create_native, config_native, start_native, stop_native,