Add sound manager initial source code
[staging/soundmanager.git] / libsoundmanager / libsoundmanager.cpp
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/libsoundmanager.hpp>
24
25 #define ELOG(args,...) _ELOG(__FUNCTION__,__LINE__,args,##__VA_ARGS__)
26 #define DLOG(args,...) _DLOG(__FUNCTION__,__LINE__,args,##__VA_ARGS__)
27
28 using namespace std;
29
30 static void _DLOG(const char* func, const int line, const char* log, ...);
31 static void _ELOG(const char* func, const int line, const char* log, ...);
32 static bool has_verb(const string& verb);
33 static const char API[] = "soundmanager";
34
35 static void _on_hangup_static(void *closure, struct afb_wsj1 *wsj)
36 {
37         static_cast<LibSoundmanager*>(closure)->on_hangup(NULL,wsj);
38 }
39
40 static void _on_call_static(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg)
41 {
42         /* LibSoundmanager is not called from other process */
43 }
44
45 static void _on_event_static(void* closure, const char* event, struct afb_wsj1_msg *msg)
46 {
47         static_cast<LibSoundmanager*>(closure)->on_event(NULL,event,msg);
48 }
49
50 static void _on_reply_static(void *closure, struct afb_wsj1_msg *msg)
51 {
52         static_cast<LibSoundmanager*>(closure)->on_reply(NULL,msg);             
53 }
54
55
56 /**
57  * This function is constructor
58  * 
59  * #### Parameters
60  * - port  [in] : This argument should be specified to the port number to be used for websocket
61  * - token [in] : This argument should be specified to the token to be used for websocket
62  *
63  * #### Rreturn
64  * Nothing
65  *
66  * #### Note
67  * Use this constructor
68  *
69  */
70 LibSoundmanager::LibSoundmanager(const int port, const string& token)
71 {
72         int ret;
73         if(port > 0 && token.size() > 0)
74         {
75                 mport = port;
76                 mtoken = token;
77         }
78         else
79         {
80                 ELOG("port and token should be > 0, Initial port and token uses.");
81         }
82
83         ret = initialize_websocket();
84         if(ret != 0 )
85         {
86                 ELOG("Failed to initialize websocket");
87         }
88         else{
89                 DLOG("Initialized");
90         }
91 }
92
93 LibSoundmanager::~LibSoundmanager()
94 {
95         if(mploop)
96         {
97                 sd_event_unref(mploop);
98         }
99         if(sp_websock != NULL)
100         {
101                 free(sp_websock);
102         }
103 }
104
105 /**
106  * This function register callback function for reply/event message from sound manager
107  * 
108  * #### Parameters
109  * - event_cb [in] : This argument should be specified to the callback for subscribed event
110  * - reply_cb [in] : This argument should be specified to the reply callback for call function
111  *
112  * #### Rreturn
113  * - Returns 0 on success or -1 in case of error.
114  *
115  * #### Note
116  * Event callback is invoked by sound manager for event you subscribed.
117  * If you would like to get event, please call subscribe function before/after this function
118  */
119 void LibSoundmanager::register_callback(
120         void (*event_cb)(const std::string& event, struct json_object* event_contents), 
121         void (*reply_cb)(struct json_object* reply_contents),
122         void (*hangup_cb)(void))
123 {
124         onEvent = event_cb;
125         onReply = reply_cb;
126         onHangup = hangup_cb;
127 }
128
129 int LibSoundmanager::initialize_websocket()
130 {
131         mploop = NULL;
132         onEvent = nullptr;
133         onReply = nullptr;
134         int ret = sd_event_default(&mploop);
135         if(ret < 0)
136         {
137                 ELOG("Failed to create event loop");
138                 goto END;
139         }
140         /* Initialize interface from websocket */
141         
142         minterface.on_hangup = _on_hangup_static;
143         minterface.on_call = _on_call_static; /* Is this necessary? */
144         minterface.on_event = _on_event_static;
145         muri += "ws://localhost:" + to_string(mport) + "/api?token=" + mtoken; /*To be modified*/
146         sp_websock = afb_ws_client_connect_wsj1(mploop, muri.c_str(), &minterface, this);
147         if(sp_websock == NULL)
148         {
149                 ELOG("Failed to create websocket connection");
150                 goto END;
151         }
152
153         /* creates the evsrc */
154         //ret = sd_event_add_io(mploop,&mevent_src, sp_websock->fd, EPOLLIN, event_callback, NULL);
155         
156         return 0;
157 END:
158         if(mploop)
159         {
160                 sd_event_unref(mploop);
161         }
162         return -1;
163 }
164
165 static void *event_loop_run(void *args)
166 {
167         struct sd_event* loop = (struct sd_event*)(args);
168         DLOG("start eventloop");
169         for(;;)
170                 sd_event_run(loop, 30000000);
171 }
172
173 /**
174  * This function start receiving the reply/event message from sound manager
175  * 
176  * #### Parameters
177  *      Nothing
178  * #### Rreturn
179  * - Returns thread_id on success or -1 in case of error.
180  *
181  * #### Note
182  *
183  */
184 int LibSoundmanager::run_eventloop()
185 {
186         if(mploop && sp_websock)
187         {
188                 pthread_t thread_id;
189             int ret = pthread_create(&thread_id, NULL, event_loop_run, mploop);
190                 if(ret != 0)
191                 {
192                         ELOG("Cannot run eventloop due to error:%d", errno);
193                         return -1;
194                 }
195                 else
196                         return thread_id;
197         }
198         else
199         {
200                 ELOG("Connecting is not established yet");
201                 return -1;
202         }
203 }
204
205 /**
206  * This function calls the API of Audio Manager via WebSocket
207  * 
208  * #### Parameters
209  * - verb [in] : This argument should be specified to the API name (e.g. "connect")
210  * - arg  [in] : This argument should be specified to the argument of API. And this argument expects JSON object
211  *      
212  * #### Rreturn
213  * - Returns 0 on success or -1 in case of error.
214  *
215  * #### Note
216  * To call Audio Manager's APIs, the application should set its function name, arguments to JSON format.
217  *
218  */
219 int LibSoundmanager::call(const string& verb, struct json_object* arg)
220 {
221         int ret;
222         if(!sp_websock)
223         {
224                 return -1;
225         }
226         if (!has_verb(verb))
227         {
228                 ELOG("verb doesn't exit");
229                 return -1;
230         }
231         ret = afb_wsj1_call_j(sp_websock, API, verb.c_str(), arg, _on_reply_static, this);
232         if (ret < 0) {
233                 ELOG("Failed to call verb:%s",verb.c_str());
234         }
235         return ret;
236 }
237
238 /**
239  * This function calls the API of Audio Manager via WebSocket
240  * This function is overload function of "call"
241  * 
242  * #### Parameters
243  * - verb [in] : This argument should be specified to the API name (e.g. "connect")
244  * - arg  [in] : This argument should be specified to the argument of API. And this argument expects JSON object
245  *      
246  * #### Rreturn
247  * - Returns 0 on success or -1 in case of error.
248  *
249  * #### Note
250  * To call Audio Manager's APIs, the application should set its function name, arguments to JSON format.
251  * 
252  */
253 int LibSoundmanager::call(const char* verb, struct json_object* arg)
254 {
255         int ret;
256         if(!sp_websock)
257         {
258                 return -1;
259         }
260         if (!has_verb(string(verb)))
261         {
262                 ELOG("verb doesn't exit");
263                 return -1;
264         }
265         ret = afb_wsj1_call_j(sp_websock, API, verb, arg, _on_reply_static, this);
266         if (ret < 0) {
267                 ELOG("Failed to call verb:%s",verb);
268         }
269         return ret;
270 }
271
272 /**
273  * Register callback function for each event
274  * 
275  * #### Parameters
276  * - event_name [in] : This argument should be specified to the event name
277  *      
278  * #### Rreturn
279  * - Returns 0 on success or -1 in case of error.
280  *
281  * #### Note
282  * This function enables to get an event to your callback function. 
283  * Regarding the list of event name, please refer to CommandSender API and RountingSender API.
284  *
285  */
286 int LibSoundmanager::subscribe(const string& event_name)
287 {
288         if(!sp_websock)
289         {
290                 return -1;
291         }
292         struct json_object* j_obj = json_object_new_object();
293         json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str()));
294
295         int ret = afb_wsj1_call_j(sp_websock, API, "subscribe", j_obj, _on_reply_static, this);
296         if (ret < 0) {
297                 ELOG("Failed to call verb:%s",__FUNCTION__);
298         }
299         return ret;
300 }
301
302 /**
303  * Unregister callback function for each event
304  * 
305  * #### Parameters
306  * - event_name [in] : This argument should be specified to the event name
307  *      
308  * #### Rreturn
309  * - Returns 0 on success or -1 in case of error.
310  *
311  * #### Note
312  * This function disables to get an event to your callback function. 
313  *
314  */
315 int LibSoundmanager::unsubscribe(const string& event_name)
316 {
317         if(!sp_websock)
318         {
319                 return -1;
320         }
321         struct json_object* j_obj = json_object_new_object();
322         json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str()));
323
324         int ret = afb_wsj1_call_j(sp_websock, API, "unsubscribe", j_obj, _on_reply_static, this);
325         if (ret < 0) {
326                 ELOG("Failed to call verb:%s",__FUNCTION__);
327         }
328         return ret;
329 }
330
331 am_Error_e LibSoundmanager::connect(const am_sourceID_t sourceID, const am_sinkID_t sinkID, am_mainConnectionID_t& mainConnectionID)
332 {
333         /*int ret;
334         char *key;
335         rc = asprintf(&key, "%d:%s/%s", ++num, api, "connect"); 
336         ret = afb_wsj1_call_s(wsj1, api, verb, object, on_reply, key);
337         if(ret < 0)
338         {
339                 fprintf(stderr, "calling %s/%s(%s) failed: %m\n", api, verb, object);
340                 
341         }*/
342         /* open the json scripts */
343         // get mainconnedction ID */
344         //mainConnectionID = xx; 
345         return E_OK;    
346 }
347
348 am_Error_e LibSoundmanager::disconnect(const am_mainConnectionID_t mainConnectionID)
349 {
350         return E_OK;
351 }
352
353 /*const struct afb_wsj1* LibSoundmanager::get_websocket_handler()
354 {
355         if(sp_websock)
356         {
357                 return sp_websock;
358         }
359         return nullptr;
360 }
361
362 const struct sd_event* LibSoundmanager::get_sd_event()
363 {
364         if(mploop)
365         {
366                 return mploop;
367         }
368         return nullptr;
369 }*/
370
371 /************* Callback Function *************/
372
373 void LibSoundmanager::on_hangup(void *closure, struct afb_wsj1 *wsj)
374 {
375         DLOG("%s called", __FUNCTION__);
376         if(onHangup != nullptr)
377         {
378                 onHangup();
379         }
380 }
381
382 void LibSoundmanager::on_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg)
383 {
384 }
385
386 /*
387 * event is like "soundmanager/newMainConnection"
388 * msg is like {"event":"soundmanager\/newMainConnection","data":{"mainConnectionID":3,"sourceID":101,"sinkID":100,"delay":0,"connectionState":4},"jtype":"afb-event"})}
389 *               ^key^   ^^^^^^^^^^^^ value ^^^^^^^^^^^^
390 * so you can get 
391         event name : struct json_object obj = json_object_object_get(msg,"event")
392 */ 
393 void LibSoundmanager::on_event(void *closure, const char *event, struct afb_wsj1_msg *msg)
394 {
395         cout << "ON-EVENT:" << event << "(" << afb_wsj1_msg_object_s(msg) << ")" << endl;       
396         if(onEvent != nullptr)
397         {
398                 const string ev(event);
399                 struct json_object* ev_contents = afb_wsj1_msg_object_j(msg);
400                 onEvent(ev, ev_contents);
401         }
402 }
403
404 void LibSoundmanager::on_reply(void *closure, struct afb_wsj1_msg *msg)
405 {
406         cout << "ON-REPLY:" <<  "(" << afb_wsj1_msg_object_s(msg) << ")" << endl;
407         if(onReply != nullptr)
408         {
409                 struct json_object* reply = afb_wsj1_msg_object_j(msg);
410                 onReply(reply);
411         }
412 }
413
414 /* Internal Function in libsoundmanager */
415
416 static void _ELOG(const char* func, const int line, const char* log, ...)
417 {
418         char *message;
419         va_list args;
420         va_start(args, log);
421         if (log == NULL || vasprintf(&message, log, args) < 0)
422                 message = NULL;
423         cout << "[ERROR]" << func << "(" << line << "):" << message << endl; 
424         va_end(args);
425         free(message);
426 }
427
428 static void _DLOG(const char* func, const int line, const char* log, ...)
429 {
430         char *message;
431         va_list args;
432         va_start(args, log);
433         if (log == NULL || vasprintf(&message, log, args) < 0)
434                 message = NULL;
435         cout << "[DEBUG]" << func << "(" << line << "):" << message << endl; 
436         va_end(args);
437         free(message);
438 }
439
440 static bool has_verb(const string& verb)
441 {
442         DLOG("verb is %s", verb.c_str());
443         if(find(api_list.begin(), api_list.end(), verb) != api_list.end())
444                 return true;
445         else
446                 return false;
447 }