]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
FS-6535 --resolve mod_rayo: allow multiple grammars (including dtmf) when using unimrcp
authorChris Rienzo <chris@rienzo.com>
Fri, 13 Jun 2014 02:55:26 +0000 (22:55 -0400)
committerChris Rienzo <chris@rienzo.com>
Fri, 13 Jun 2014 02:56:34 +0000 (22:56 -0400)
src/include/switch_ivr.h
src/mod/event_handlers/mod_rayo/rayo_input_component.c
src/switch_ivr_async.c

index 01c82f4f311d7824814a53e7831bc06134eef6b9..f8e18b3d92cb5f55229588552515f3984d51972a 100644 (file)
@@ -238,7 +238,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_resume_detect_speech(switch_core_sess
   \param name the grammar name
   \return SWITCH_STATUS_SUCCESS if all is well
 */
-SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_load_grammar(switch_core_session_t *session, char *grammar, char *name);
+SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_load_grammar(switch_core_session_t *session, const char *grammar, const char *name);
 
 /*!
   \brief Unload a grammar on a background speech detection handle
index a9412ee2b62ead7104f964b13e14164ea0939ba1..b4549b454f2d0353f9bc8d08e4d6f29426490b22 100644 (file)
@@ -413,14 +413,141 @@ static int validate_call_input(iks *input, const char **error)
        return 1;
 }
 
+static char *setup_grammars_pocketsphinx(struct input_component *component, switch_core_session_t *session, iks *input, const struct xmpp_error **stanza_error, const char **error_detail)
+{
+       const char *jsgf_path;
+       switch_stream_handle_t grammar = { 0 };
+       SWITCH_STANDARD_STREAM(grammar);
+
+       /* transform SRGS grammar to JSGF */
+       if (!(component->grammar = srgs_parse(globals.parser, iks_find_cdata(input, "grammar")))) {
+               *stanza_error = STANZA_ERROR_BAD_REQUEST;
+               *error_detail = "Failed to parse grammar body";
+               return NULL;
+       }
+
+       jsgf_path = srgs_grammar_to_jsgf_file(component->grammar, SWITCH_GLOBAL_dirs.grammar_dir, "gram");
+       if (!jsgf_path) {
+               *stanza_error = STANZA_ERROR_BAD_REQUEST;
+               *error_detail = "Grammar conversion to JSGF error";
+               return NULL;
+       }
+
+       /* build pocketsphinx grammar string */
+       grammar.write_function(&grammar,
+               "{start-input-timers=%s,no-input-timeout=%d,speech-timeout=%d,confidence-threshold=%d}%s",
+               component->start_timers ? "true" : "false",
+               component->initial_timeout,
+               component->max_silence,
+               (int)ceil(component->min_confidence * 100.0),
+               jsgf_path);
+
+       return (char *)grammar.data;
+}
+
+static char *setup_grammars_unimrcp(struct input_component *component, switch_core_session_t *session, iks *input, const struct xmpp_error **stanza_error, const char **error_detail)
+{
+       iks *grammar_tag;
+       switch_stream_handle_t grammar_uri_list = { 0 };
+       SWITCH_STANDARD_STREAM(grammar_uri_list);
+
+       /* unlock handler mutex, otherwise deadlock will happen when switch_ivr_detect_speech_init adds a new media bug */
+       switch_mutex_unlock(component->handler->mutex);
+       if (switch_ivr_detect_speech_init(session, component->recognizer, "", NULL) != SWITCH_STATUS_SUCCESS) {
+               switch_mutex_lock(component->handler->mutex);
+               *stanza_error = STANZA_ERROR_INTERNAL_SERVER_ERROR;
+               *error_detail = "Failed to initialize recognizer";
+               return NULL;
+       }
+       switch_mutex_lock(component->handler->mutex);
+
+       /* load unimrcp grammars and return uri-list */
+       grammar_uri_list.write_function(&grammar_uri_list, "{start-recognize=true,start-input-timers=%s,confidence-threshold=%f,sensitivity-level=%f",
+                                                       component->start_timers ? "true" : "false",
+                                                       component->min_confidence,
+                                                       component->sensitivity);
+       if (component->initial_timeout > 0) {
+               grammar_uri_list.write_function(&grammar_uri_list, ",no-input-timeout=%d",
+                       component->initial_timeout);
+       }
+
+       if (component->max_silence > 0) {
+               grammar_uri_list.write_function(&grammar_uri_list, ",speech-complete-timeout=%d,speech-incomplete-timeout=%d",
+                       component->max_silence,
+                       component->max_silence);
+       }
+
+       if (!zstr(component->language)) {
+               grammar_uri_list.write_function(&grammar_uri_list, ",speech-language=%s", component->language);
+       }
+
+       if (!strcmp(iks_find_attrib_soft(input, "mode"), "any") || !strcmp(iks_find_attrib_soft(input, "mode"), "dtmf")) {
+               /* set dtmf params */
+               if (component->inter_digit_timeout > 0) {
+                       grammar_uri_list.write_function(&grammar_uri_list, ",dtmf-interdigit-timeout=%d", component->inter_digit_timeout);
+               }
+               if (component->term_digit) {
+                       grammar_uri_list.write_function(&grammar_uri_list, ",dtmf-term-char=%c", component->term_digit);
+               }
+       }
+       grammar_uri_list.write_function(&grammar_uri_list, "}");
+
+       for (grammar_tag = iks_find(input, "grammar"); grammar_tag; grammar_tag = iks_next_tag(grammar_tag)) {
+               const char *grammar_name;
+               iks *grammar_cdata;
+               const char *grammar;
+
+               /* is this a grammar? */
+               if (strcmp("grammar", iks_name(grammar_tag))) {
+                       continue;
+               }
+
+               /* get the srgs contained in this grammar */
+               if (!(grammar_cdata = iks_child(grammar_tag)) || iks_type(grammar_cdata) != IKS_CDATA) {
+                       *stanza_error = STANZA_ERROR_BAD_REQUEST;
+                       *error_detail = "Missing grammar";
+                       switch_safe_free(grammar_uri_list.data);
+                       return NULL;
+               }
+
+               /* load the grammar */
+               grammar = switch_core_sprintf(RAYO_POOL(component), "{start-recognize=false}inline:%s", iks_cdata(grammar_cdata));
+               grammar_name = switch_core_sprintf(RAYO_POOL(component), "grammar-%d", rayo_actor_seq_next(RAYO_ACTOR(component)));
+               /* unlock handler mutex, otherwise deadlock will happen if switch_ivr_detect_speech_load_grammar removes the media bug */
+               switch_mutex_unlock(component->handler->mutex);
+               if (switch_ivr_detect_speech_load_grammar(session, grammar, grammar_name) != SWITCH_STATUS_SUCCESS) {
+                       switch_mutex_lock(component->handler->mutex);
+                       *stanza_error = STANZA_ERROR_INTERNAL_SERVER_ERROR;
+                       *error_detail = "Failed to load grammar";
+                       switch_safe_free(grammar_uri_list.data);
+                       return NULL;
+               }
+               switch_mutex_lock(component->handler->mutex);
+
+               /* add grammar to uri-list */
+               grammar_uri_list.write_function(&grammar_uri_list, "session:%s\r\n", grammar_name);
+       }
+
+       return (char *)grammar_uri_list.data;
+}
+
+static char *setup_grammars_unknown(struct input_component *component, switch_core_session_t *session, iks *input, const struct xmpp_error **stanza_error, const char **error_detail)
+{
+       switch_stream_handle_t grammar = { 0 };
+       SWITCH_STANDARD_STREAM(grammar);
+       grammar.write_function(&grammar, "%s", iks_find_cdata(input, "grammar"));
+       return (char *)grammar.data;
+}
+
 /**
  * Start call input on voice resource
  */
-static iks *start_call_voice_input(struct input_component *component, switch_core_session_t *session, iks *input, iks *iq, const char *output_file, int barge_in)
+static iks *start_call_voice_input(struct input_component *component, switch_core_session_t *session, iks *input, iks *iq, int barge_in)
 {
        struct input_handler *handler = component->handler;
-       switch_stream_handle_t grammar = { 0 };
-       SWITCH_STANDARD_STREAM(grammar);
+       char *grammar = NULL;
+       const struct xmpp_error *stanza_error = NULL;
+       const char *error_detail = NULL;
 
        if (component->speech_mode && handler->voice_component) {
                /* don't allow multi voice input */
@@ -441,72 +568,23 @@ static iks *start_call_voice_input(struct input_component *component, switch_cor
                RAYO_UNLOCK(component);
                RAYO_DESTROY(component);
                return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Must use the same recognizer for the entire call");
+       } else if (zstr(handler->last_recognizer)) {
+               handler->last_recognizer = switch_core_session_strdup(session, component->recognizer);
        }
-       handler->last_recognizer = switch_core_session_strdup(session, component->recognizer);
 
        if (!strcmp(component->recognizer, "pocketsphinx")) {
-               const char *jsgf_path;
-
-               /* transform SRGS grammar to JSGF */
-               if (!(component->grammar = srgs_parse(globals.parser, iks_find_cdata(input, "grammar")))) {
-                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Failed to parse grammar body\n");
-                       handler->voice_component = NULL;
-                       RAYO_UNLOCK(component);
-                       RAYO_DESTROY(component);
-                       return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Failed to parse grammar body");
-               }
-               jsgf_path = srgs_grammar_to_jsgf_file(component->grammar, SWITCH_GLOBAL_dirs.grammar_dir, "gram");
-               if (!jsgf_path) {
-                       handler->voice_component = NULL;
-                       RAYO_UNLOCK(component);
-                       RAYO_DESTROY(component);
-                       return iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "Grammar conversion to JSGF error");
-               }
-
-               /* build pocketsphinx grammar string */
-               grammar.write_function(&grammar,
-                       "{start-input-timers=%s,no-input-timeout=%d,speech-timeout=%d,confidence-threshold=%d}%s",
-                       component->start_timers ? "true" : "false",
-                       component->initial_timeout,
-                       component->max_silence,
-                       (int)ceil(component->min_confidence * 100.0),
-                       jsgf_path);
-       } else if (!strncmp(component->recognizer, "unimrcp", strlen("unimrcp"))) {
-               /* send inline grammar to unimrcp */
-               grammar.write_function(&grammar, "{start-input-timers=%s,confidence-threshold=%f,sensitivity-level=%f",
-                                                               component->start_timers ? "true" : "false",
-                                                               component->min_confidence,
-                                                               component->sensitivity);
-
-               if (component->initial_timeout > 0) {
-                       grammar.write_function(&grammar, ",no-input-timeout=%d",
-                               component->initial_timeout);
-               }
-
-               if (component->max_silence > 0) {
-                       grammar.write_function(&grammar, ",speech-complete-timeout=%d,speech-incomplete-timeout=%d",
-                               component->max_silence,
-                               component->max_silence);
-               }
-
-               if (!zstr(component->language)) {
-                       grammar.write_function(&grammar, ",speech-language=%s", component->language);
-               }
-
-               if (!strcmp(iks_find_attrib_soft(input, "mode"), "any")) {
-                       /* set dtmf params */
-                       if (component->inter_digit_timeout > 0) {
-                               grammar.write_function(&grammar, ",dtmf-interdigit-timeout=%d", component->inter_digit_timeout);
-                       }
-                       if (component->term_digit) {
-                               grammar.write_function(&grammar, ",dtmf-term-char=%c", component->term_digit);
-                       }
-               }
-
-               grammar.write_function(&grammar, "}inline:%s", iks_find_cdata(input, "grammar"));
+               grammar = setup_grammars_pocketsphinx(component, session, input, &stanza_error, &error_detail);
+       } else if (!strcmp(component->recognizer, "unimrcp")) {
+               grammar = setup_grammars_unimrcp(component, session, input, &stanza_error, &error_detail);
        } else {
-               /* passthrough to unknown ASR module */
-               grammar.write_function(&grammar, "%s", iks_find_cdata(input, "grammar"));
+               grammar = setup_grammars_unknown(component, session, input, &stanza_error, &error_detail);
+       }
+
+       if (!grammar) {
+               handler->voice_component = NULL;
+               RAYO_UNLOCK(component);
+               RAYO_DESTROY(component);
+               return iks_new_error_detailed(iq, stanza_error, error_detail);
        }
 
        /* acknowledge command */
@@ -514,15 +592,15 @@ static iks *start_call_voice_input(struct input_component *component, switch_cor
 
        /* start speech detection */
        switch_channel_set_variable(switch_core_session_get_channel(session), "fire_asr_events", "true");
-       switch_mutex_unlock(handler->mutex); /* unlock handler mutex, otherwise deadlock will happen when switch_ivr_detect_speech adds a new media bug */
-       if (switch_ivr_detect_speech(session, component->recognizer, grammar.data, "mod_rayo_grammar", "", NULL) != SWITCH_STATUS_SUCCESS) {
+       /* unlock handler mutex, otherwise deadlock will happen if switch_ivr_detect_speech adds a media bug */
+       switch_mutex_unlock(handler->mutex);
+       if (switch_ivr_detect_speech(session, component->recognizer, grammar, "mod_rayo_grammar", "", NULL) != SWITCH_STATUS_SUCCESS) {
                switch_mutex_lock(handler->mutex);
                handler->voice_component = NULL;
                rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_ERROR);
-       } else {
-               switch_mutex_lock(handler->mutex);
        }
-       switch_safe_free(grammar.data);
+       switch_mutex_lock(handler->mutex);
+       switch_safe_free(grammar);
 
        return NULL;
 }
@@ -530,7 +608,7 @@ static iks *start_call_voice_input(struct input_component *component, switch_cor
 /**
  * Start call input on DTMF resource
  */
-static iks *start_call_dtmf_input(struct input_component *component, switch_core_session_t *session, iks *input, iks *iq, const char *output_file, int barge_in)
+static iks *start_call_dtmf_input(struct input_component *component, switch_core_session_t *session, iks *input, iks *iq, int barge_in)
 {
        /* parse the grammar */
        if (!(component->grammar = srgs_parse(globals.parser, iks_find_cdata(input, "grammar")))) {
@@ -558,7 +636,7 @@ static iks *start_call_dtmf_input(struct input_component *component, switch_core
  * @param input the input request
  * @param iq the original input/prompt request
  */
-static iks *start_call_input(struct input_component *component, switch_core_session_t *session, iks *input, iks *iq, const char *output_file, int barge_in)
+static iks *start_call_input(struct input_component *component, switch_core_session_t *session, iks *input, iks *iq, int barge_in)
 {
        iks *result = NULL;
 
@@ -610,9 +688,9 @@ static iks *start_call_input(struct input_component *component, switch_core_sess
        component->speech_mode = strcmp(iks_find_attrib_soft(input, "mode"), "dtmf");
 
        if (component->speech_mode) {
-               result = start_call_voice_input(component, session, input, iq, output_file, barge_in);
+               result = start_call_voice_input(component, session, input, iq, barge_in);
        } else {
-               result = start_call_dtmf_input(component, session, input, iq, output_file, barge_in);
+               result = start_call_dtmf_input(component, session, input, iq, barge_in);
        }
 
        switch_mutex_unlock(handler->mutex);
@@ -671,7 +749,7 @@ static iks *start_call_input_component(struct rayo_actor *call, struct rayo_mess
                switch_core_destroy_memory_pool(&pool);
                return iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "Failed to create input entity");
        }
-       return start_call_input(input_component, session, input, iq, NULL, 0);
+       return start_call_input(input_component, session, input, iq, 0);
 }
 
 /**
index 95b167af27eae8b7dcae75f3bed26aefe91f69a1..ea034e8e508aebf283b96a9a98a32e00f28c1220 100644 (file)
@@ -4042,7 +4042,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_resume_detect_speech(switch_core_sess
        return SWITCH_STATUS_FALSE;
 }
 
-SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_load_grammar(switch_core_session_t *session, char *grammar, char *name)
+SWITCH_DECLARE(switch_status_t) switch_ivr_detect_speech_load_grammar(switch_core_session_t *session, const char *grammar, const char *name)
 {
        switch_channel_t *channel = switch_core_session_get_channel(session);
        struct speech_thread_handle *sth = switch_channel_get_private(channel, SWITCH_SPEECH_KEY);