Sound Manager
libsoundmanager.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017 TOYOTA MOTOR CORPORATION
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <stdarg.h>
18 #include <sys/socket.h>
19 #include <iostream>
20 #include <algorithm>
21 #include <thread>
22 #include <errno.h>
23 #include <libsoundmanager.hpp>
24 
25 #define ELOG(args,...) _ELOG(__FUNCTION__,__LINE__,args,##__VA_ARGS__)
26 #ifdef DEBUGMODE
27  #define DLOG(args,...) _DLOG(__FUNCTION__,__LINE__,args,##__VA_ARGS__)
28 #else
29  #define DLOG(args,...)
30 #endif
31 static void _DLOG(const char* func, const int line, const char* log, ...);
32 static void _ELOG(const char* func, const int line, const char* log, ...);
33 
34 using namespace std;
35 
36 static bool has_verb(const std::string& verb);
37 static const char API[] = "soundmanager";
38 static int eventIndent(const string& event);
39 
40 static const std::vector<std::string> api_list{
41  std::string("connect"),
42  std::string("disconnect"),
43  std::string("setVolume"),
44  std::string("volumeStep"),
45  std::string("setSinkMuteState"),
46  std::string("getListMainConnections"),
47  std::string("ackConnect"),
48  std::string("ackDisconnect"),
49  std::string("ackSetSourceState"),
50  std::string("registerSource"),
51  std::string("deregisterSource"),
52  std::string("subscribe"),
53  std::string("unsubscribe")
54 };
55 
56 static const std::vector<std::string> event_list{
57  std::string("asyncSetSourceState"),
58  std::string("newMainConnection"),
59  std::string("volumeChanged"),
60  std::string("removedMainConnection"),
61  std::string("sinkMuteStateChanged"),
62  std::string("mainConnectionStateChanged"),
63  std::string("setRoutingReady"),
64  std::string("setRoutingRundown"),
65  std::string("asyncConnect")
66 };
67 
68 static void _on_hangup_static(void *closure, struct afb_wsj1 *wsj)
69 {
70  static_cast<LibSoundmanager*>(closure)->on_hangup(NULL,wsj);
71 }
72 
73 static void _on_call_static(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg)
74 {
75  /* LibSoundmanager is not called from other process */
76 }
77 
78 static void _on_event_static(void* closure, const char* event, struct afb_wsj1_msg *msg)
79 {
80  static_cast<LibSoundmanager*>(closure)->on_event(NULL,event,msg);
81 }
82 
83 static void _on_reply_static(void *closure, struct afb_wsj1_msg *msg)
84 {
85  static_cast<LibSoundmanager*>(closure)->on_reply(NULL,msg);
86 }
87 
88 static void *event_loop_run(void *args)
89 {
90  struct sd_event* loop = (struct sd_event*)(args);
91  DLOG("start eventloop");
92  for(;;)
93  sd_event_run(loop, 30000000);
94 }
95 
97 {
98 }
99 
101 {
102  if(mploop)
103  {
104  sd_event_unref(mploop);
105  }
106  if(sp_websock != NULL)
107  {
108  afb_wsj1_unref(sp_websock);
109  }
110 }
111 
112 
126 int LibSoundmanager::init(int port, const string& token)
127 {
128  int ret;
129  if(port > 0 && token.size() > 0)
130  {
131  mport = port;
132  mtoken = token;
133  }
134  else
135  {
136  ELOG("port and token should be > 0, Initial port and token uses.");
137  return -1;
138  }
139 
140  ret = initialize_websocket();
141  if(ret != 0 )
142  {
143  ELOG("Failed to initialize websocket");
144  return -1;
145  }
146  ret = init_event();
147  if(ret != 0 )
148  {
149  ELOG("Failed to initialize websocket");
150  return -1;
151  }
152  if(ret == -1){
153  ELOG("Failed to create thread");
154  return -1;
155  }
156  return 0;
157 }
158 
159 int LibSoundmanager::initialize_websocket()
160 {
161  mploop = NULL;
162  onEvent = nullptr;
163  onReply = nullptr;
164  int ret = sd_event_default(&mploop);
165  if(ret < 0)
166  {
167  ELOG("Failed to create event loop");
168  goto END;
169  }
170  /* Initialize interface from websocket */
171  {
172  minterface.on_hangup = _on_hangup_static;
173  minterface.on_call = _on_call_static;
174  minterface.on_event = _on_event_static;
175  string muri = "ws://localhost:" + to_string(mport) + "/api?token=" + mtoken;
176  sp_websock = afb_ws_client_connect_wsj1(mploop, muri.c_str(), &minterface, this);
177  }
178  if(sp_websock == NULL)
179  {
180  ELOG("Failed to create websocket connection");
181  goto END;
182  }
183 
184  return 0;
185 END:
186  if(mploop)
187  {
188  sd_event_unref(mploop);
189  }
190  return -1;
191 }
192 
193 int LibSoundmanager::init_event(){
194  /* subscribe most important event for sound right */
195  return subscribe(string("asyncSetSourceState"));
196 }
197 
213  void (*event_cb)(const string& event, struct json_object* event_contents),
214  void (*reply_cb)(struct json_object* reply_contents),
215  void (*hangup_cb)(void))
216 {
217  onEvent = event_cb;
218  onReply = reply_cb;
219  onHangup = hangup_cb;
220 }
221 
237  void (*reply_cb)(struct json_object* reply_contents),
238  void (*hangup_cb)(void))
239 {
240  onReply = reply_cb;
241  onHangup = hangup_cb;
242 }
243 
259 int LibSoundmanager::registerSource(const string& sourceName)
260 {
261  if(!sp_websock)
262  {
263  return -1;
264  }
265  struct json_object* j_obj = json_object_new_object();
266  struct json_object* jsn = json_object_new_string(sourceName.c_str());
267  json_object_object_add(j_obj, "appname", jsn);
268  return this->call(__FUNCTION__, j_obj);
269 }
270 
287 int LibSoundmanager::connect(int sourceID, int sinkID)
288 {
289  if(!sp_websock)
290  {
291  return -1;
292  }
293  struct json_object* j_obj = json_object_new_object();
294  struct json_object* jsource = json_object_new_int(sourceID);
295  struct json_object* jsink = json_object_new_int(sinkID);
296  json_object_object_add(j_obj, "sourceID", jsource);
297  json_object_object_add(j_obj, "sinkID", jsink);
298  return this->call(__FUNCTION__, j_obj);
299 }
300 
319 int LibSoundmanager::connect(int sourceID, const string& sinkName)
320 {
321  if(!sp_websock)
322  {
323  return -1;
324  }
325  struct json_object* j_obj = json_object_new_object();
326  struct json_object* jsource = json_object_new_int(sourceID);
327  //struct json_object* jsink = json_object_new_int(1);
328  struct json_object* jsink = json_object_new_string(sinkName.c_str());
329  json_object_object_add(j_obj, "sourceID", jsource);
330  json_object_object_add(j_obj, "sinkID", jsink);
331  return this->call(__FUNCTION__, j_obj);
332 }
333 
347 int LibSoundmanager::disconnect(int connectionID)
348 {
349  if(!sp_websock)
350  {
351  return -1;
352  }
353  struct json_object* j_obj = json_object_new_object();
354  struct json_object* jconnection = json_object_new_int(connectionID);
355  json_object_object_add(j_obj, "mainConnectionID", jconnection);
356  return this->call(__FUNCTION__, j_obj);
357 }
358 
374 int LibSoundmanager::ackSetSourceState(int handle, int error)
375 {
376  if(!sp_websock)
377  {
378  return -1;
379  }
380  struct json_object* j_obj = json_object_new_object();
381  struct json_object* jhandle = json_object_new_int(handle);
382  struct json_object* jerrno = json_object_new_int(error);
383  json_object_object_add(j_obj, "handle", jhandle);
384  json_object_object_add(j_obj, "error", jerrno);
385  return this->call(__FUNCTION__, j_obj);
386 }
387 
402 int LibSoundmanager::call(const string& verb, struct json_object* arg)
403 {
404  int ret;
405  if(!sp_websock)
406  {
407  return -1;
408  }
409  if (!has_verb(verb))
410  {
411  ELOG("verb doesn't exit");
412  return -1;
413  }
414  ret = afb_wsj1_call_j(sp_websock, API, verb.c_str(), arg, _on_reply_static, this);
415  if (ret < 0) {
416  ELOG("Failed to call verb:%s",verb.c_str());
417  }
418  return ret;
419 }
420 
436 int LibSoundmanager::call(const char* verb, struct json_object* arg)
437 {
438  int ret;
439  if(!sp_websock)
440  {
441  return -1;
442  }
443  if (!has_verb(string(verb)))
444  {
445  ELOG("verb doesn't exit");
446  return -1;
447  }
448  ret = afb_wsj1_call_j(sp_websock, API, verb, arg, _on_reply_static, this);
449  if (ret < 0) {
450  ELOG("Failed to call verb:%s",verb);
451  }
452  return ret;
453 }
454 
469 int LibSoundmanager::subscribe(const string& event_name)
470 {
471  if(!sp_websock)
472  {
473  return -1;
474  }
475  struct json_object* j_obj = json_object_new_object();
476  json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str()));
477 
478  int ret = afb_wsj1_call_j(sp_websock, API, "subscribe", j_obj, _on_reply_static, this);
479  if (ret < 0) {
480  ELOG("Failed to call verb:%s",__FUNCTION__);
481  }
482  return ret;
483 }
484 
498 int LibSoundmanager::unsubscribe(const string& event_name)
499 {
500  if(!sp_websock)
501  {
502  return -1;
503  }
504  struct json_object* j_obj = json_object_new_object();
505  json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str()));
506 
507  int ret = afb_wsj1_call_j(sp_websock, API, "unsubscribe", j_obj, _on_reply_static, this);
508  if (ret < 0) {
509  ELOG("Failed to call verb:%s",__FUNCTION__);
510  }
511  return ret;
512 }
513 
529 {
530  if (et > 1 && et < NumItems) {
531  this->handlers[et] = std::move(f);
532  }
533 }
534 
535 
536 /************* Callback Function *************/
537 
538 void LibSoundmanager::on_hangup(void *closure, struct afb_wsj1 *wsj)
539 {
540  DLOG("%s called", __FUNCTION__);
541  if(onHangup != nullptr)
542  {
543  onHangup();
544  }
545 }
546 
547 void LibSoundmanager::on_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg)
548 {
549 }
550 
551 /*
552 * event is like "soundmanager/newMainConnection"
553 * msg is like {"event":"soundmanager\/newMainConnection","data":{"mainConnectionID":3,"sourceID":101,"sinkID":100,"delay":0,"connectionState":4},"jtype":"afb-event"})}
554 * ^key^ ^^^^^^^^^^^^ value ^^^^^^^^^^^^
555 * so you can get
556  event name : struct json_object obj = json_object_object_get(msg,"event")
557 */
558 void LibSoundmanager::on_event(void *closure, const char *event, struct afb_wsj1_msg *msg)
559 {
560  /* check event is for us */
561  string ev = string(event);
562  if (ev.find(API) == string::npos) {
563  /* It's not us */
564  return;
565  }
566  struct json_object* ev_contents = afb_wsj1_msg_object_j(msg);
567  if(onEvent != nullptr)
568  {
569  onEvent(ev, ev_contents);
570  }
571  else{}
572 
573  dispatch_event(ev, ev_contents);
574 
575  json_object_put(ev_contents);
576 }
577 
578 void LibSoundmanager::on_reply(void *closure, struct afb_wsj1_msg *msg)
579 {
580  struct json_object* reply = afb_wsj1_msg_object_j(msg);
581  /*struct json_object *json_data = json_object_object_get(reply, "response");
582  struct json_object *jverb = json_object_object_get(json_data, "verb");
583  const char* cverb = json_object_get_string(jverb);
584  DLOG("cverb is %s",cverb);
585  string verb = string(cverb);
586  DLOG("verb is %s",verb.c_str());
587 
588  if(verb == "registerSource"){
589  struct json_object *jsourceID = json_object_object_get(json_data, "sourceID");
590  int sourceID = json_object_get_int(jsourceID);
591  msourceIDs.push_back(sourceID);
592  DLOG("my sourceID is created: %d", sourceID);
593  }*/
594  if(onReply != nullptr)
595  {
596  onReply(reply);
597  }
598  json_object_put(reply);
599 }
600 
601 int LibSoundmanager::dispatch_event(const string &event , json_object* event_contents){
602  //dipatch event
603  EventType_SM x;
604 
605  if(event.find(event_list[0].c_str())){
606  x = Event_AsyncSetSourceState;
607  }
608  else{
609  return -1;
610  }
611  auto i = this->handlers.find(x);
612  if(i != handlers.end()){
613  i->second(event_contents);
614  return 0;
615  }
616  return -1;
617 }
618 
619 /* Internal Function in libsoundmanager */
620 
621 static void _ELOG(const char* func, const int line, const char* log, ...)
622 {
623  char *message;
624  va_list args;
625  va_start(args, log);
626  if (log == NULL || vasprintf(&message, log, args) < 0)
627  message = NULL;
628  cout << "[ERROR: soundmanager]" << func << "(" << line << "):" << message << endl;
629  va_end(args);
630  free(message);
631 }
632 
633 static void _DLOG(const char* func, const int line, const char* log, ...)
634 {
635  char *message;
636  va_list args;
637  va_start(args, log);
638  if (log == NULL || vasprintf(&message, log, args) < 0)
639  message = NULL;
640  cout << "[DEBUG: soundmanager]" << func << "(" << line << "):" << message << endl;
641  va_end(args);
642  free(message);
643 }
644 
645 static bool has_verb(const string& verb)
646 {
647  if(find(api_list.begin(), api_list.end(), verb) != api_list.end())
648  return true;
649  else
650  return false;
651 }
void register_callback(void(*event_cb)(const std::string &event, struct json_object *event_contents), void(*reply_cb)(struct json_object *reply_contents), void(*hangup_cb)(void)=nullptr)
void set_event_handler(enum EventType_SM et, handler_fun f)
int init(int port, const std::string &token)
int unsubscribe(const std::string &event_name)
int disconnect(int connectionID)
int call(const std::string &verb, struct json_object *arg)
int connect(int sourceID, int sinkID)
std::function< void(struct json_object *)> handler_fun
void on_hangup(void *closure, struct afb_wsj1 *wsj)
int subscribe(const std::string &event_name)
int ackSetSourceState(int handle, int error)
void on_reply(void *closure, struct afb_wsj1_msg *msg)
void on_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg)
void on_event(void *closure, const char *event, struct afb_wsj1_msg *msg)
int registerSource(const std::string &sourceName)
#define ELOG(args,...)
#define DLOG(args,...)