b4192358c319321065c4d2e96c1d4a71dc18e15c
[AGL/meta-agl-demo.git] / recipes-demo-hmi / navigation / navigation / 0001-add-4A-playback-support.patch
1 gpsnavi: Add AGL 4A playback support
2
3 To properly support 4A on AGL, pull in the binding, add some code
4 to open / close the "navigation" role, and use a simple gstreamer
5 pipeline to play the generated files.  The existing script templates
6 have had their playback commands removed, but are retained for
7 driving the voice file generation.
8
9 Signed-off-by: Scott Murray <scott.murray@konsulko.com>
10
11 diff --git a/agl/config.xml b/agl/config.xml
12 index 9d4c0ca..960f652 100755
13 --- a/agl/config.xml
14 +++ b/agl/config.xml
15 @@ -7,11 +7,13 @@
16    <author>AISIN AW</author>
17    <feature name="urn:AGL:widget:required-permission">
18      <param name="urn:AGL:permission::public:no-htdocs" value="required" />
19 +    <param name="urn:AGL:permission:audio:public:audiostream" value="required" />
20      <param name="http://tizen.org/privilege/internal/dbus" value="required" />
21    </feature>
22    <feature name="urn:AGL:widget:required-api">
23      <param name="homescreen" value="ws" />
24      <param name="windowmanager" value="ws" />
25 +    <param name="ahl-4a" value="ws" />
26    </feature>
27    <license>GPL</license>
28    <feature name="urn:AGL:widget:file-properties">
29 diff --git a/configure.ac b/configure.ac
30 index 33348c3..6b7b391 100755
31 --- a/configure.ac
32 +++ b/configure.ac
33 @@ -26,11 +26,13 @@ PKG_CHECK_MODULES([GLIB], [glib-2.0 gthread-2.0])
34  PKG_CHECK_MODULES([FREETYPE2], [freetype2])
35  PKG_CHECK_MODULES([WAYLAND], [wayland-client wayland-egl egl])
36  PKG_CHECK_MODULES([GL], [glesv2])
37 +PKG_CHECK_MODULES([GSTREAMER], [gstreamer-1.0])
38  PKG_CHECK_MODULES([ZLIB], [zlib])
39  PKG_CHECK_MODULES([SQLITE3], [sqlite3])
40  PKG_CHECK_MODULES([EXPAT], [expat])
41  PKG_CHECK_MODULES([OPENSSL], [openssl])
42  PKG_CHECK_MODULES([DBUSCXX], [dbus-c++-1])
43 +PKG_CHECK_MODULES([LIBAFBWSC], [libafbwsc])
44  PKG_CHECK_MODULES([WINDOWMANAGER], [libwindowmanager])
45  PKG_CHECK_MODULES([HOMESCREEN], [libhomescreen])
46  
47 diff --git a/flite_agl.in b/flite_agl.in
48 index 28b512c..d11b043 100644
49 --- a/flite_agl.in
50 +++ b/flite_agl.in
51 @@ -1,6 +1,3 @@
52  #!/bin/sh
53  TMP=/tmp/navi.wav
54  echo "$1" | flite_hts_engine -m  @datadir@/Voice/us/cmu_us_arctic_slt.htsvoice -o $TMP
55 -paplay --property='media.role=Navi' $TMP
56 -rm -f $TMP
57 -
58 diff --git a/jtalk_agl.in b/jtalk_agl.in
59 index 76900f4..857c824 100644
60 --- a/jtalk_agl.in
61 +++ b/jtalk_agl.in
62 @@ -1,6 +1,3 @@
63  #!/bin/sh
64  TMP=/tmp/navi.wav
65  echo "$1" | open_jtalk -ow $TMP -m @exec_prefix@/share/Voice/mei/mei_normal.htsvoice -x @exec_prefix@/share/dic/
66 -paplay --property='media.role=Navi' $TMP
67 -rm -f $TMP
68 -
69 diff --git a/src/Makefile.am b/src/Makefile.am
70 index affb9a5..6d0fa55 100755
71 --- a/src/Makefile.am
72 +++ b/src/Makefile.am
73 @@ -26,6 +26,7 @@ sms/sms-core/SMCoreDM/RG/RG_GuideNear.c \
74  sms/sms-core/SMCoreDM/RG/RG_ShareData.c \
75  sms/sms-core/SMCoreDM/RG/RG_GuideVoiceBuild_en.c \
76  sms/sms-core/SMCoreDM/RG/RG_GuideVoice.c \
77 +sms/sms-core/SMCoreDM/RG/RG_GuideVoice4A.cpp \
78  sms/sms-core/SMCoreDM/RG/RG_GuideApi.c \
79  sms/sms-core/SMCoreDM/SCRTThread.c \
80  sms/sms-core/SMCoreDAL/SMDALAreaCls.c \
81 @@ -537,6 +538,8 @@ libnavicore_la_CFLAGS = -fPIC \
82          @FREETYPE2_CFLAGS@ \
83          @WAYLAND_CFLAGS@ \
84          @GL_CFLAGS@ \
85 +        @GSTREAMER_CFLAGS@ \
86 +        @LIBAFBWSC_CFLAGS@ \
87          @ZLIB_CFLAGS@ \
88          @SQLITE3_CFLAGS@ \
89          @EXPAT_CFLAGS@ \
90 @@ -547,6 +550,8 @@ libnavicore_la_CXXFLAGS = -fPIC \
91          @FREETYPE2_CFLAGS@ \
92          @WAYLAND_CFLAGS@ \
93          @GL_CFLAGS@ \
94 +        @GSTREAMER_CFLAGS@ \
95 +        @LIBAFBWSC_CFLAGS@ \
96          @ZLIB_CFLAGS@ \
97          @SQLITE3_CFLAGS@ \
98          @EXPAT_CFLAGS@ \
99 @@ -555,6 +560,8 @@ libnavicore_la_CXXFLAGS = -fPIC \
100  libnavicore_la_LIBADD = \
101          @OPENSSL_LIBS@ \
102          @GL_LIBS@ \
103 +        @GSTREAMER_LIBS@ \
104 +        @LIBAFBWSC_LIBS@ \
105          @ZLIB_LIBS@ \
106          @SQLITE3_LIBS@ \
107          @EXPAT_LIBS@
108 diff --git a/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice.c b/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice.c
109 index 3828d5c..36e6775 100755
110 --- a/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice.c
111 +++ b/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice.c
112 @@ -16,6 +16,8 @@
113  \r
114  #include "sms-core/SMCoreDM/SMCoreDMInternal.h"\r
115  \r
116 +extern int play_voice(const char* voice_gen_cmd);\r
117 +\r
118  typedef struct _tts_text_tbl \r
119  {\r
120         INT32   code;\r
121 @@ -205,9 +207,9 @@ E_SC_RESULT RG_CTL_CreateVoiceText(RT_NAME_t *in, INT32 language)
122         }\r
123         else\r
124         {\r
125 -               strncat(tts_voice, "\" & ", (TTSMAX - len - 1));\r
126 +               strncat(tts_voice, "\"", (TTSMAX - len - 1));\r
127                 \r
128 -               system(tts_voice);\r
129 +               play_voice(tts_voice);\r
130         }\r
131         \r
132         return (e_SC_RESULT_SUCCESS);\r
133 diff --git a/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice4A.cpp b/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice4A.cpp
134 new file mode 100644
135 index 0000000..6b59c0e
136 --- /dev/null
137 +++ b/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice4A.cpp
138 @@ -0,0 +1,223 @@
139 +/*
140 + * Copyright (C) 2018 Konsulko Group
141 + * Author: Scott Murray <scott.murray@konsulko.com>
142 + *
143 + * This program is licensed under GPL version 2 license.
144 + * See the LICENSE file distributed with this source file.
145 + */
146 +
147 +#include <string>
148 +#include <iostream>
149 +#include <cstring>
150 +#include <unistd.h>
151 +#include <sys/types.h>
152 +#include <sys/stat.h>
153 +#include <pthread.h>
154 +#include <gst/gst.h>
155 +#include <json-c/json.h>
156 +
157 +extern "C"
158 +{
159 +#include <afb/afb-wsj1.h>
160 +#include <afb/afb-ws-client.h>
161 +#include <systemd/sd-event.h>
162 +}
163 +
164 +#define NAVI_TMPFILE   "/tmp/navi.wav"
165 +
166 +static struct afb_wsj1* ws;
167 +static struct afb_wsj1_itf itf;
168 +sd_event* loop;
169 +
170 +// port and token from src/glview/glview_wayland.cpp
171 +extern long g_port;
172 +extern std::string g_token;
173 +
174 +static int set_role_state(bool state);
175 +
176 +void play_voice_file(const char *output)
177 +{
178 +       if(!output)
179 +               return;
180 +
181 +        // Initialize GStreamer
182 +        gst_init(NULL, NULL);
183 +
184 +       std::string pipeline_str = "filesrc location=";
185 +       pipeline_str += NAVI_TMPFILE;
186 +       pipeline_str += " ! wavparse ! audioconvert ! audioresample ! alsasink device=";
187 +       pipeline_str += output;
188 +       GstElement *pipeline = gst_parse_launch(pipeline_str.c_str(), NULL);
189 +       if(!pipeline) {
190 +               std::cerr << "gstreamer pipeline construction failed!" << std::endl;
191 +               return;
192 +       }
193 +
194 +       // Start pipeline
195 +       gst_element_set_state(pipeline, GST_STATE_PLAYING);
196 +       std::cerr << "Playing guidance" << std::endl;
197 +
198 +       // Wait until error or EOS
199 +       GstBus *bus = gst_element_get_bus(pipeline);
200 +       GstMessage *msg = gst_bus_timed_pop_filtered(bus,
201 +                                                    GST_CLOCK_TIME_NONE,
202 +                                                    (GstMessageType) (GST_MESSAGE_ERROR | GST_MESSAGE_EOS));
203 +
204 +       // Free resources
205 +       if(msg != NULL)
206 +               gst_message_unref(msg);
207 +       gst_object_unref(bus);
208 +       gst_element_set_state(pipeline, GST_STATE_NULL);
209 +       gst_object_unref(pipeline);
210 +
211 +       // Remove temporary file
212 +       unlink(NAVI_TMPFILE);
213 +
214 +       return;
215 +}
216 +
217 +static void on_hangup(void *closure, struct afb_wsj1 *wsj)
218 +{
219 +}
220 +
221 +static void on_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg)
222 +{
223 +}
224 +
225 +static void on_event(void* closure, const char* event, struct afb_wsj1_msg *msg)
226 +{
227 +}
228 +
229 +static void on_reply(void *closure, struct afb_wsj1_msg *msg)
230 +{
231 +       bool state = (bool) closure;
232 +
233 +       if(!state) {
234 +               // Role is closed, return
235 +               return;
236 +       }
237 +
238 +       // We opened the role, play the file
239 +       struct json_object* reply = afb_wsj1_msg_object_j(msg);
240 +       if(reply) {
241 +               struct json_object* response;
242 +               int rc = json_object_object_get_ex(reply, "response", &response);
243 +               if(rc) {
244 +                       struct json_object* val;
245 +                       rc = json_object_object_get_ex(response, "device_uri", &val);
246 +                       if (rc && json_object_get_string_len(val)) {
247 +                               const char* jres_pcm = json_object_get_string(val);
248 +                               play_voice_file(jres_pcm);
249 +                       }
250 +               }
251 +       }
252 +
253 +       // Give up role now that we're done
254 +       set_role_state(false);
255 +}
256 +
257 +static void *event_loop_run(void *args)
258 +{
259 +       struct sd_event* loop = (struct sd_event*)(args);
260 +
261 +       for(;;)
262 +               sd_event_run(loop, 30000000);
263 +}
264 +
265 +static int start_event_loop(void)
266 +{
267 +       if(ws && loop) {
268 +               pthread_t thread_id;
269 +               if(pthread_create(&thread_id, NULL, event_loop_run, loop) != 0) {
270 +                       return -1;
271 +               } else {
272 +                       return thread_id;
273 +               }
274 +        } else {
275 +               return -1;
276 +       }
277 +}
278 +
279 +static int init_ws(int port, std::string &token)
280 +{
281 +       loop = NULL;
282 +       std::string uri;
283 +
284 +       if(sd_event_default(&loop) < 0) {
285 +               std::cerr << __FUNCTION__ << ": Failed to create event loop" << std::endl;
286 +               goto error;
287 +       }
288 +
289 +       // Initialize interface for websocket
290 +       itf.on_hangup = on_hangup;
291 +       itf.on_call = on_call;
292 +       itf.on_event = on_event;
293 +
294 +       uri = "ws://localhost:" + std::to_string(port) + "/api?token=" + token;
295 +       std::cerr << "Using URI: " << uri << std::endl;
296 +       ws = afb_ws_client_connect_wsj1(loop, uri.c_str(), &itf, NULL);
297 +       if(ws == NULL) {
298 +               std::cerr << __FUNCTION__ << ": Failed to create websocket connection" << std::endl;
299 +               goto error;
300 +       }
301 +       start_event_loop();
302 +       return 0;
303 +error:
304 +       if(loop) {
305 +               sd_event_unref(loop);
306 +       }
307 +       return -1;
308 +}
309 +
310 +static int set_role_state(bool state)
311 +{
312 +       int rc;
313 +       json_object *jsonData = json_object_new_object();
314 +
315 +       json_object_object_add(jsonData, "action", json_object_new_string(state ? "open" : "close"));
316 +       rc = afb_wsj1_call_j(ws, "ahl-4a", "navigation", jsonData, on_reply, (void*) state);
317 +       if (rc < 0) {
318 +               std::cerr << __FUNCTION__ <<  ": Failed to call ahl-4a/navigation!" << std::endl;
319 +       }
320 +       return rc;
321 +}
322 +
323 +pthread_mutex_t ws_mutex = PTHREAD_MUTEX_INITIALIZER;
324 +
325 +static void *play_voice_handler(void *data)
326 +{
327 +       int rc;
328 +       char *voice_gen_cmd = (char*) data;
329 +       if(!voice_gen_cmd)
330 +               return NULL;
331 +
332 +       pthread_mutex_lock(&ws_mutex);
333 +       if(!ws) {
334 +               rc = init_ws(g_port, g_token);
335 +               pthread_mutex_unlock(&ws_mutex);
336 +               if(rc < 0)
337 +                       return NULL;
338 +       }
339 +       pthread_mutex_unlock(&ws_mutex);
340 +
341 +       // Generate guidance voice file
342 +       rc = system(voice_gen_cmd);
343 +       free(voice_gen_cmd);
344 +
345 +       // Try to get role and play file
346 +       set_role_state(true);
347 +
348 +       return NULL;
349 +}
350 +
351 +extern "C" int play_voice(const char* voice_gen_cmd)
352 +{
353 +       pthread_t handler_thread;
354 +       char *tmp;
355 +
356 +       if(!voice_gen_cmd)
357 +               return -1;
358 +
359 +       tmp = strdup(voice_gen_cmd);
360 +       return pthread_create(&handler_thread, NULL, play_voice_handler, (void*) tmp);
361 +}