Add sound manager initial source code
[staging/soundmanager.git] / libsoundmanager / libsoundmanager.cpp
diff --git a/libsoundmanager/libsoundmanager.cpp b/libsoundmanager/libsoundmanager.cpp
new file mode 100644 (file)
index 0000000..9f77723
--- /dev/null
@@ -0,0 +1,447 @@
+/*
+ * Copyright (c) 2017 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdarg.h>
+#include <sys/socket.h>
+#include <iostream>
+#include <algorithm>
+#include <thread>
+#include <errno.h>
+#include <libsoundmanager/libsoundmanager.hpp>
+
+#define ELOG(args,...) _ELOG(__FUNCTION__,__LINE__,args,##__VA_ARGS__)
+#define DLOG(args,...) _DLOG(__FUNCTION__,__LINE__,args,##__VA_ARGS__)
+
+using namespace std;
+
+static void _DLOG(const char* func, const int line, const char* log, ...);
+static void _ELOG(const char* func, const int line, const char* log, ...);
+static bool has_verb(const string& verb);
+static const char API[] = "soundmanager";
+
+static void _on_hangup_static(void *closure, struct afb_wsj1 *wsj)
+{
+       static_cast<LibSoundmanager*>(closure)->on_hangup(NULL,wsj);
+}
+
+static void _on_call_static(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg)
+{
+       /* LibSoundmanager is not called from other process */
+}
+
+static void _on_event_static(void* closure, const char* event, struct afb_wsj1_msg *msg)
+{
+       static_cast<LibSoundmanager*>(closure)->on_event(NULL,event,msg);
+}
+
+static void _on_reply_static(void *closure, struct afb_wsj1_msg *msg)
+{
+       static_cast<LibSoundmanager*>(closure)->on_reply(NULL,msg);             
+}
+
+
+/**
+ * This function is constructor
+ * 
+ * #### Parameters
+ * - port  [in] : This argument should be specified to the port number to be used for websocket
+ * - token [in] : This argument should be specified to the token to be used for websocket
+ *
+ * #### Rreturn
+ * Nothing
+ *
+ * #### Note
+ * Use this constructor
+ *
+ */
+LibSoundmanager::LibSoundmanager(const int port, const string& token)
+{
+       int ret;
+       if(port > 0 && token.size() > 0)
+       {
+               mport = port;
+               mtoken = token;
+       }
+       else
+       {
+               ELOG("port and token should be > 0, Initial port and token uses.");
+       }
+
+       ret = initialize_websocket();
+       if(ret != 0 )
+       {
+               ELOG("Failed to initialize websocket");
+       }
+       else{
+               DLOG("Initialized");
+       }
+}
+
+LibSoundmanager::~LibSoundmanager()
+{
+       if(mploop)
+       {
+               sd_event_unref(mploop);
+       }
+       if(sp_websock != NULL)
+       {
+               free(sp_websock);
+       }
+}
+
+/**
+ * This function register callback function for reply/event message from sound manager
+ * 
+ * #### Parameters
+ * - event_cb [in] : This argument should be specified to the callback for subscribed event
+ * - reply_cb [in] : This argument should be specified to the reply callback for call function
+ *
+ * #### Rreturn
+ * - Returns 0 on success or -1 in case of error.
+ *
+ * #### Note
+ * Event callback is invoked by sound manager for event you subscribed.
+ * If you would like to get event, please call subscribe function before/after this function
+ */
+void LibSoundmanager::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))
+{
+       onEvent = event_cb;
+       onReply = reply_cb;
+       onHangup = hangup_cb;
+}
+
+int LibSoundmanager::initialize_websocket()
+{
+       mploop = NULL;
+       onEvent = nullptr;
+       onReply = nullptr;
+       int ret = sd_event_default(&mploop);
+       if(ret < 0)
+       {
+               ELOG("Failed to create event loop");
+               goto END;
+       }
+       /* Initialize interface from websocket */
+       
+       minterface.on_hangup = _on_hangup_static;
+       minterface.on_call = _on_call_static; /* Is this necessary? */
+       minterface.on_event = _on_event_static;
+       muri += "ws://localhost:" + to_string(mport) + "/api?token=" + mtoken; /*To be modified*/
+       sp_websock = afb_ws_client_connect_wsj1(mploop, muri.c_str(), &minterface, this);
+       if(sp_websock == NULL)
+       {
+               ELOG("Failed to create websocket connection");
+               goto END;
+       }
+
+       /* creates the evsrc */
+       //ret = sd_event_add_io(mploop,&mevent_src, sp_websock->fd, EPOLLIN, event_callback, NULL);
+       
+       return 0;
+END:
+       if(mploop)
+       {
+               sd_event_unref(mploop);
+       }
+       return -1;
+}
+
+static void *event_loop_run(void *args)
+{
+       struct sd_event* loop = (struct sd_event*)(args);
+       DLOG("start eventloop");
+       for(;;)
+               sd_event_run(loop, 30000000);
+}
+
+/**
+ * This function start receiving the reply/event message from sound manager
+ * 
+ * #### Parameters
+ *     Nothing
+ * #### Rreturn
+ * - Returns thread_id on success or -1 in case of error.
+ *
+ * #### Note
+ *
+ */
+int LibSoundmanager::run_eventloop()
+{
+       if(mploop && sp_websock)
+       {
+               pthread_t thread_id;
+           int ret = pthread_create(&thread_id, NULL, event_loop_run, mploop);
+               if(ret != 0)
+               {
+                       ELOG("Cannot run eventloop due to error:%d", errno);
+                       return -1;
+               }
+               else
+                       return thread_id;
+       }
+       else
+       {
+               ELOG("Connecting is not established yet");
+               return -1;
+       }
+}
+
+/**
+ * This function calls the API of Audio Manager via WebSocket
+ * 
+ * #### Parameters
+ * - verb [in] : This argument should be specified to the API name (e.g. "connect")
+ * - arg  [in] : This argument should be specified to the argument of API. And this argument expects JSON object
+ *     
+ * #### Rreturn
+ * - Returns 0 on success or -1 in case of error.
+ *
+ * #### Note
+ * To call Audio Manager's APIs, the application should set its function name, arguments to JSON format.
+ *
+ */
+int LibSoundmanager::call(const string& verb, struct json_object* arg)
+{
+       int ret;
+       if(!sp_websock)
+       {
+               return -1;
+       }
+       if (!has_verb(verb))
+       {
+               ELOG("verb doesn't exit");
+               return -1;
+       }
+       ret = afb_wsj1_call_j(sp_websock, API, verb.c_str(), arg, _on_reply_static, this);
+       if (ret < 0) {
+               ELOG("Failed to call verb:%s",verb.c_str());
+       }
+       return ret;
+}
+
+/**
+ * This function calls the API of Audio Manager via WebSocket
+ * This function is overload function of "call"
+ * 
+ * #### Parameters
+ * - verb [in] : This argument should be specified to the API name (e.g. "connect")
+ * - arg  [in] : This argument should be specified to the argument of API. And this argument expects JSON object
+ *     
+ * #### Rreturn
+ * - Returns 0 on success or -1 in case of error.
+ *
+ * #### Note
+ * To call Audio Manager's APIs, the application should set its function name, arguments to JSON format.
+ * 
+ */
+int LibSoundmanager::call(const char* verb, struct json_object* arg)
+{
+       int ret;
+       if(!sp_websock)
+       {
+               return -1;
+       }
+       if (!has_verb(string(verb)))
+       {
+               ELOG("verb doesn't exit");
+               return -1;
+       }
+       ret = afb_wsj1_call_j(sp_websock, API, verb, arg, _on_reply_static, this);
+       if (ret < 0) {
+               ELOG("Failed to call verb:%s",verb);
+       }
+       return ret;
+}
+
+/**
+ * Register callback function for each event
+ * 
+ * #### Parameters
+ * - event_name [in] : This argument should be specified to the event name
+ *     
+ * #### Rreturn
+ * - Returns 0 on success or -1 in case of error.
+ *
+ * #### Note
+ * This function enables to get an event to your callback function. 
+ * Regarding the list of event name, please refer to CommandSender API and RountingSender API.
+ *
+ */
+int LibSoundmanager::subscribe(const string& event_name)
+{
+       if(!sp_websock)
+       {
+               return -1;
+       }
+       struct json_object* j_obj = json_object_new_object();
+       json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str()));
+
+       int ret = afb_wsj1_call_j(sp_websock, API, "subscribe", j_obj, _on_reply_static, this);
+       if (ret < 0) {
+               ELOG("Failed to call verb:%s",__FUNCTION__);
+       }
+       return ret;
+}
+
+/**
+ * Unregister callback function for each event
+ * 
+ * #### Parameters
+ * - event_name [in] : This argument should be specified to the event name
+ *     
+ * #### Rreturn
+ * - Returns 0 on success or -1 in case of error.
+ *
+ * #### Note
+ * This function disables to get an event to your callback function. 
+ *
+ */
+int LibSoundmanager::unsubscribe(const string& event_name)
+{
+       if(!sp_websock)
+       {
+               return -1;
+       }
+       struct json_object* j_obj = json_object_new_object();
+       json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str()));
+
+       int ret = afb_wsj1_call_j(sp_websock, API, "unsubscribe", j_obj, _on_reply_static, this);
+       if (ret < 0) {
+               ELOG("Failed to call verb:%s",__FUNCTION__);
+       }
+       return ret;
+}
+
+am_Error_e LibSoundmanager::connect(const am_sourceID_t sourceID, const am_sinkID_t sinkID, am_mainConnectionID_t& mainConnectionID)
+{
+       /*int ret;
+       char *key;
+       rc = asprintf(&key, "%d:%s/%s", ++num, api, "connect"); 
+       ret = afb_wsj1_call_s(wsj1, api, verb, object, on_reply, key);
+       if(ret < 0)
+       {
+               fprintf(stderr, "calling %s/%s(%s) failed: %m\n", api, verb, object);
+               
+       }*/
+       /* open the json scripts */
+       // get mainconnedction ID */
+       //mainConnectionID = xx; 
+       return E_OK;    
+}
+
+am_Error_e LibSoundmanager::disconnect(const am_mainConnectionID_t mainConnectionID)
+{
+       return E_OK;
+}
+
+/*const struct afb_wsj1* LibSoundmanager::get_websocket_handler()
+{
+       if(sp_websock)
+       {
+               return sp_websock;
+       }
+       return nullptr;
+}
+
+const struct sd_event* LibSoundmanager::get_sd_event()
+{
+       if(mploop)
+       {
+               return mploop;
+       }
+       return nullptr;
+}*/
+
+/************* Callback Function *************/
+
+void LibSoundmanager::on_hangup(void *closure, struct afb_wsj1 *wsj)
+{
+       DLOG("%s called", __FUNCTION__);
+       if(onHangup != nullptr)
+       {
+               onHangup();
+       }
+}
+
+void LibSoundmanager::on_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg)
+{
+}
+
+/*
+* event is like "soundmanager/newMainConnection"
+* msg is like {"event":"soundmanager\/newMainConnection","data":{"mainConnectionID":3,"sourceID":101,"sinkID":100,"delay":0,"connectionState":4},"jtype":"afb-event"})}
+*               ^key^   ^^^^^^^^^^^^ value ^^^^^^^^^^^^
+* so you can get 
+       event name : struct json_object obj = json_object_object_get(msg,"event")
+*/ 
+void LibSoundmanager::on_event(void *closure, const char *event, struct afb_wsj1_msg *msg)
+{
+       cout << "ON-EVENT:" << event << "(" << afb_wsj1_msg_object_s(msg) << ")" << endl;       
+       if(onEvent != nullptr)
+       {
+               const string ev(event);
+               struct json_object* ev_contents = afb_wsj1_msg_object_j(msg);
+               onEvent(ev, ev_contents);
+       }
+}
+
+void LibSoundmanager::on_reply(void *closure, struct afb_wsj1_msg *msg)
+{
+       cout << "ON-REPLY:" <<  "(" << afb_wsj1_msg_object_s(msg) << ")" << endl;
+       if(onReply != nullptr)
+       {
+               struct json_object* reply = afb_wsj1_msg_object_j(msg);
+               onReply(reply);
+       }
+}
+
+/* Internal Function in libsoundmanager */
+
+static void _ELOG(const char* func, const int line, const char* log, ...)
+{
+       char *message;
+       va_list args;
+       va_start(args, log);
+       if (log == NULL || vasprintf(&message, log, args) < 0)
+               message = NULL;
+       cout << "[ERROR]" << func << "(" << line << "):" << message << endl; 
+       va_end(args);
+       free(message);
+}
+
+static void _DLOG(const char* func, const int line, const char* log, ...)
+{
+       char *message;
+       va_list args;
+       va_start(args, log);
+       if (log == NULL || vasprintf(&message, log, args) < 0)
+               message = NULL;
+       cout << "[DEBUG]" << func << "(" << line << "):" << message << endl; 
+       va_end(args);
+       free(message);
+}
+
+static bool has_verb(const string& verb)
+{
+       DLOG("verb is %s", verb.c_str());
+       if(find(api_list.begin(), api_list.end(), verb) != api_list.end())
+               return true;
+       else
+               return false;
+}