diff options
-rw-r--r-- | recipes-demo-hmi/navigation/navigation/0001-add-4A-playback-support.patch | 361 | ||||
-rwxr-xr-x | recipes-demo-hmi/navigation/navigation_git.bb | 3 |
2 files changed, 363 insertions, 1 deletions
diff --git a/recipes-demo-hmi/navigation/navigation/0001-add-4A-playback-support.patch b/recipes-demo-hmi/navigation/navigation/0001-add-4A-playback-support.patch new file mode 100644 index 000000000..b4192358c --- /dev/null +++ b/recipes-demo-hmi/navigation/navigation/0001-add-4A-playback-support.patch @@ -0,0 +1,361 @@ +gpsnavi: Add AGL 4A playback support + +To properly support 4A on AGL, pull in the binding, add some code +to open / close the "navigation" role, and use a simple gstreamer +pipeline to play the generated files. The existing script templates +have had their playback commands removed, but are retained for +driving the voice file generation. + +Signed-off-by: Scott Murray <scott.murray@konsulko.com> + +diff --git a/agl/config.xml b/agl/config.xml +index 9d4c0ca..960f652 100755 +--- a/agl/config.xml ++++ b/agl/config.xml +@@ -7,11 +7,13 @@ + <author>AISIN AW</author> + <feature name="urn:AGL:widget:required-permission"> + <param name="urn:AGL:permission::public:no-htdocs" value="required" /> ++ <param name="urn:AGL:permission:audio:public:audiostream" value="required" /> + <param name="http://tizen.org/privilege/internal/dbus" value="required" /> + </feature> + <feature name="urn:AGL:widget:required-api"> + <param name="homescreen" value="ws" /> + <param name="windowmanager" value="ws" /> ++ <param name="ahl-4a" value="ws" /> + </feature> + <license>GPL</license> + <feature name="urn:AGL:widget:file-properties"> +diff --git a/configure.ac b/configure.ac +index 33348c3..6b7b391 100755 +--- a/configure.ac ++++ b/configure.ac +@@ -26,11 +26,13 @@ PKG_CHECK_MODULES([GLIB], [glib-2.0 gthread-2.0]) + PKG_CHECK_MODULES([FREETYPE2], [freetype2]) + PKG_CHECK_MODULES([WAYLAND], [wayland-client wayland-egl egl]) + PKG_CHECK_MODULES([GL], [glesv2]) ++PKG_CHECK_MODULES([GSTREAMER], [gstreamer-1.0]) + PKG_CHECK_MODULES([ZLIB], [zlib]) + PKG_CHECK_MODULES([SQLITE3], [sqlite3]) + PKG_CHECK_MODULES([EXPAT], [expat]) + PKG_CHECK_MODULES([OPENSSL], [openssl]) + PKG_CHECK_MODULES([DBUSCXX], [dbus-c++-1]) ++PKG_CHECK_MODULES([LIBAFBWSC], [libafbwsc]) + PKG_CHECK_MODULES([WINDOWMANAGER], [libwindowmanager]) + PKG_CHECK_MODULES([HOMESCREEN], [libhomescreen]) + +diff --git a/flite_agl.in b/flite_agl.in +index 28b512c..d11b043 100644 +--- a/flite_agl.in ++++ b/flite_agl.in +@@ -1,6 +1,3 @@ + #!/bin/sh + TMP=/tmp/navi.wav + echo "$1" | flite_hts_engine -m @datadir@/Voice/us/cmu_us_arctic_slt.htsvoice -o $TMP +-paplay --property='media.role=Navi' $TMP +-rm -f $TMP +- +diff --git a/jtalk_agl.in b/jtalk_agl.in +index 76900f4..857c824 100644 +--- a/jtalk_agl.in ++++ b/jtalk_agl.in +@@ -1,6 +1,3 @@ + #!/bin/sh + TMP=/tmp/navi.wav + echo "$1" | open_jtalk -ow $TMP -m @exec_prefix@/share/Voice/mei/mei_normal.htsvoice -x @exec_prefix@/share/dic/ +-paplay --property='media.role=Navi' $TMP +-rm -f $TMP +- +diff --git a/src/Makefile.am b/src/Makefile.am +index affb9a5..6d0fa55 100755 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -26,6 +26,7 @@ sms/sms-core/SMCoreDM/RG/RG_GuideNear.c \ + sms/sms-core/SMCoreDM/RG/RG_ShareData.c \ + sms/sms-core/SMCoreDM/RG/RG_GuideVoiceBuild_en.c \ + sms/sms-core/SMCoreDM/RG/RG_GuideVoice.c \ ++sms/sms-core/SMCoreDM/RG/RG_GuideVoice4A.cpp \ + sms/sms-core/SMCoreDM/RG/RG_GuideApi.c \ + sms/sms-core/SMCoreDM/SCRTThread.c \ + sms/sms-core/SMCoreDAL/SMDALAreaCls.c \ +@@ -537,6 +538,8 @@ libnavicore_la_CFLAGS = -fPIC \ + @FREETYPE2_CFLAGS@ \ + @WAYLAND_CFLAGS@ \ + @GL_CFLAGS@ \ ++ @GSTREAMER_CFLAGS@ \ ++ @LIBAFBWSC_CFLAGS@ \ + @ZLIB_CFLAGS@ \ + @SQLITE3_CFLAGS@ \ + @EXPAT_CFLAGS@ \ +@@ -547,6 +550,8 @@ libnavicore_la_CXXFLAGS = -fPIC \ + @FREETYPE2_CFLAGS@ \ + @WAYLAND_CFLAGS@ \ + @GL_CFLAGS@ \ ++ @GSTREAMER_CFLAGS@ \ ++ @LIBAFBWSC_CFLAGS@ \ + @ZLIB_CFLAGS@ \ + @SQLITE3_CFLAGS@ \ + @EXPAT_CFLAGS@ \ +@@ -555,6 +560,8 @@ libnavicore_la_CXXFLAGS = -fPIC \ + libnavicore_la_LIBADD = \ + @OPENSSL_LIBS@ \ + @GL_LIBS@ \ ++ @GSTREAMER_LIBS@ \ ++ @LIBAFBWSC_LIBS@ \ + @ZLIB_LIBS@ \ + @SQLITE3_LIBS@ \ + @EXPAT_LIBS@ +diff --git a/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice.c b/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice.c +index 3828d5c..36e6775 100755 +--- a/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice.c ++++ b/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice.c +@@ -16,6 +16,8 @@ +
+ #include "sms-core/SMCoreDM/SMCoreDMInternal.h"
+
++extern int play_voice(const char* voice_gen_cmd);
++
+ typedef struct _tts_text_tbl
+ {
+ INT32 code;
+@@ -205,9 +207,9 @@ E_SC_RESULT RG_CTL_CreateVoiceText(RT_NAME_t *in, INT32 language) + }
+ else
+ {
+- strncat(tts_voice, "\" & ", (TTSMAX - len - 1));
++ strncat(tts_voice, "\"", (TTSMAX - len - 1));
+
+- system(tts_voice);
++ play_voice(tts_voice);
+ }
+
+ return (e_SC_RESULT_SUCCESS);
+diff --git a/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice4A.cpp b/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice4A.cpp +new file mode 100644 +index 0000000..6b59c0e +--- /dev/null ++++ b/src/sms/sms-core/SMCoreDM/RG/RG_GuideVoice4A.cpp +@@ -0,0 +1,223 @@ ++/* ++ * Copyright (C) 2018 Konsulko Group ++ * Author: Scott Murray <scott.murray@konsulko.com> ++ * ++ * This program is licensed under GPL version 2 license. ++ * See the LICENSE file distributed with this source file. ++ */ ++ ++#include <string> ++#include <iostream> ++#include <cstring> ++#include <unistd.h> ++#include <sys/types.h> ++#include <sys/stat.h> ++#include <pthread.h> ++#include <gst/gst.h> ++#include <json-c/json.h> ++ ++extern "C" ++{ ++#include <afb/afb-wsj1.h> ++#include <afb/afb-ws-client.h> ++#include <systemd/sd-event.h> ++} ++ ++#define NAVI_TMPFILE "/tmp/navi.wav" ++ ++static struct afb_wsj1* ws; ++static struct afb_wsj1_itf itf; ++sd_event* loop; ++ ++// port and token from src/glview/glview_wayland.cpp ++extern long g_port; ++extern std::string g_token; ++ ++static int set_role_state(bool state); ++ ++void play_voice_file(const char *output) ++{ ++ if(!output) ++ return; ++ ++ // Initialize GStreamer ++ gst_init(NULL, NULL); ++ ++ std::string pipeline_str = "filesrc location="; ++ pipeline_str += NAVI_TMPFILE; ++ pipeline_str += " ! wavparse ! audioconvert ! audioresample ! alsasink device="; ++ pipeline_str += output; ++ GstElement *pipeline = gst_parse_launch(pipeline_str.c_str(), NULL); ++ if(!pipeline) { ++ std::cerr << "gstreamer pipeline construction failed!" << std::endl; ++ return; ++ } ++ ++ // Start pipeline ++ gst_element_set_state(pipeline, GST_STATE_PLAYING); ++ std::cerr << "Playing guidance" << std::endl; ++ ++ // Wait until error or EOS ++ GstBus *bus = gst_element_get_bus(pipeline); ++ GstMessage *msg = gst_bus_timed_pop_filtered(bus, ++ GST_CLOCK_TIME_NONE, ++ (GstMessageType) (GST_MESSAGE_ERROR | GST_MESSAGE_EOS)); ++ ++ // Free resources ++ if(msg != NULL) ++ gst_message_unref(msg); ++ gst_object_unref(bus); ++ gst_element_set_state(pipeline, GST_STATE_NULL); ++ gst_object_unref(pipeline); ++ ++ // Remove temporary file ++ unlink(NAVI_TMPFILE); ++ ++ return; ++} ++ ++static void on_hangup(void *closure, struct afb_wsj1 *wsj) ++{ ++} ++ ++static void on_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg) ++{ ++} ++ ++static void on_event(void* closure, const char* event, struct afb_wsj1_msg *msg) ++{ ++} ++ ++static void on_reply(void *closure, struct afb_wsj1_msg *msg) ++{ ++ bool state = (bool) closure; ++ ++ if(!state) { ++ // Role is closed, return ++ return; ++ } ++ ++ // We opened the role, play the file ++ struct json_object* reply = afb_wsj1_msg_object_j(msg); ++ if(reply) { ++ struct json_object* response; ++ int rc = json_object_object_get_ex(reply, "response", &response); ++ if(rc) { ++ struct json_object* val; ++ rc = json_object_object_get_ex(response, "device_uri", &val); ++ if (rc && json_object_get_string_len(val)) { ++ const char* jres_pcm = json_object_get_string(val); ++ play_voice_file(jres_pcm); ++ } ++ } ++ } ++ ++ // Give up role now that we're done ++ set_role_state(false); ++} ++ ++static void *event_loop_run(void *args) ++{ ++ struct sd_event* loop = (struct sd_event*)(args); ++ ++ for(;;) ++ sd_event_run(loop, 30000000); ++} ++ ++static int start_event_loop(void) ++{ ++ if(ws && loop) { ++ pthread_t thread_id; ++ if(pthread_create(&thread_id, NULL, event_loop_run, loop) != 0) { ++ return -1; ++ } else { ++ return thread_id; ++ } ++ } else { ++ return -1; ++ } ++} ++ ++static int init_ws(int port, std::string &token) ++{ ++ loop = NULL; ++ std::string uri; ++ ++ if(sd_event_default(&loop) < 0) { ++ std::cerr << __FUNCTION__ << ": Failed to create event loop" << std::endl; ++ goto error; ++ } ++ ++ // Initialize interface for websocket ++ itf.on_hangup = on_hangup; ++ itf.on_call = on_call; ++ itf.on_event = on_event; ++ ++ uri = "ws://localhost:" + std::to_string(port) + "/api?token=" + token; ++ std::cerr << "Using URI: " << uri << std::endl; ++ ws = afb_ws_client_connect_wsj1(loop, uri.c_str(), &itf, NULL); ++ if(ws == NULL) { ++ std::cerr << __FUNCTION__ << ": Failed to create websocket connection" << std::endl; ++ goto error; ++ } ++ start_event_loop(); ++ return 0; ++error: ++ if(loop) { ++ sd_event_unref(loop); ++ } ++ return -1; ++} ++ ++static int set_role_state(bool state) ++{ ++ int rc; ++ json_object *jsonData = json_object_new_object(); ++ ++ json_object_object_add(jsonData, "action", json_object_new_string(state ? "open" : "close")); ++ rc = afb_wsj1_call_j(ws, "ahl-4a", "navigation", jsonData, on_reply, (void*) state); ++ if (rc < 0) { ++ std::cerr << __FUNCTION__ << ": Failed to call ahl-4a/navigation!" << std::endl; ++ } ++ return rc; ++} ++ ++pthread_mutex_t ws_mutex = PTHREAD_MUTEX_INITIALIZER; ++ ++static void *play_voice_handler(void *data) ++{ ++ int rc; ++ char *voice_gen_cmd = (char*) data; ++ if(!voice_gen_cmd) ++ return NULL; ++ ++ pthread_mutex_lock(&ws_mutex); ++ if(!ws) { ++ rc = init_ws(g_port, g_token); ++ pthread_mutex_unlock(&ws_mutex); ++ if(rc < 0) ++ return NULL; ++ } ++ pthread_mutex_unlock(&ws_mutex); ++ ++ // Generate guidance voice file ++ rc = system(voice_gen_cmd); ++ free(voice_gen_cmd); ++ ++ // Try to get role and play file ++ set_role_state(true); ++ ++ return NULL; ++} ++ ++extern "C" int play_voice(const char* voice_gen_cmd) ++{ ++ pthread_t handler_thread; ++ char *tmp; ++ ++ if(!voice_gen_cmd) ++ return -1; ++ ++ tmp = strdup(voice_gen_cmd); ++ return pthread_create(&handler_thread, NULL, play_voice_handler, (void*) tmp); ++} diff --git a/recipes-demo-hmi/navigation/navigation_git.bb b/recipes-demo-hmi/navigation/navigation_git.bb index f35a27551..8a42eb68c 100755 --- a/recipes-demo-hmi/navigation/navigation_git.bb +++ b/recipes-demo-hmi/navigation/navigation_git.bb @@ -9,7 +9,7 @@ LIC_FILES_CHKSUM="file://LICENSE;md5=3595e9c703a847d990664d2b396a9df0 \ DEPENDS = " \ glib-2.0 freetype sqlite3 wayland zlib expat openssl virtual/libgles2 virtual/libgl virtual/egl \ - wayland libdbus-c++ af-main af-binder libwindowmanager libhomescreen \ + wayland libdbus-c++ af-main af-binder libwindowmanager libhomescreen gstreamer1.0 \ " RDEPENDS_${PN} = " flite openjtalk glib-2.0 freetype sqlite3 wayland zlib expat openssl \ @@ -20,6 +20,7 @@ RDEPENDS_${PN} += " agl-service-navigation " SRCREV="66b2f6f062e0f141cdeecd449a2d7267078906d3" SRC_URI="git://github.com/AGLExport/gpsnavi.git;branch=agl \ + file://0001-add-4A-playback-support.patch \ file://download_mapdata_jp.sh \ file://download_mapdata_uk.sh \ file://org.agl.naviapi.conf \ |