diff options
-rw-r--r-- | apps/app_stasis.c | 40 | ||||
-rw-r--r-- | include/asterisk/cli.h | 16 | ||||
-rw-r--r-- | include/asterisk/strings.h | 12 | ||||
-rw-r--r-- | main/channel.c | 92 | ||||
-rw-r--r-- | main/manager_channels.c | 92 |
5 files changed, 206 insertions, 46 deletions
diff --git a/apps/app_stasis.c b/apps/app_stasis.c index 40269239d..62b35b49e 100644 --- a/apps/app_stasis.c +++ b/apps/app_stasis.c @@ -358,6 +358,43 @@ static int send_end_msg(struct app *app, struct ast_channel *chan) return 0; } +static void dtmf_handler(struct app *app, struct ast_channel_blob *obj) +{ + RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref); + RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref); + const char *direction; + + /* To simplify events, we'll only generate on receive */ + direction = ast_json_string_get( + ast_json_object_get(obj->blob, "direction")); + + if (strcmp("Received", direction) != 0) { + return; + } + + extra = ast_json_pack( + "{s: o}", + "digit", ast_json_ref(ast_json_object_get(obj->blob, "digit"))); + if (!extra) { + return; + } + + msg = app_event_create("dtmf-received", obj->snapshot, extra); + if (!msg) { + return; + } + + app_send(app, msg); +} + +static void blob_handler(struct app *app, struct ast_channel_blob *blob) +{ + /* To simplify events, we'll only generate on DTMF end */ + if (strcmp(ast_channel_blob_json_type(blob), "dtmf_end") == 0) { + dtmf_handler(app, blob); + } +} + static void sub_handler(void *data, struct stasis_subscription *sub, struct stasis_topic *topic, struct stasis_message *message) @@ -373,6 +410,9 @@ static void sub_handler(void *data, struct stasis_subscription *sub, return; } app_send(app, msg); + } else if (ast_channel_blob_type() == stasis_message_type(message)) { + struct ast_channel_blob *blob = stasis_message_data(message); + blob_handler(app, blob); } if (stasis_subscription_final_message(sub, message)) { ao2_cleanup(data); diff --git a/include/asterisk/cli.h b/include/asterisk/cli.h index 21a03834d..458ebc8aa 100644 --- a/include/asterisk/cli.h +++ b/include/asterisk/cli.h @@ -28,6 +28,7 @@ extern "C" { #endif #include "asterisk/linkedlists.h" +#include "asterisk/strings.h" void ast_cli(int fd, const char *fmt, ...) __attribute__((format(printf, 2, 3))); @@ -57,12 +58,17 @@ void ast_cli(int fd, const char *fmt, ...) */ #define ESS(x) ((x) == 1 ? "" : "s") -/*! \brief return Yes or No depending on the argument. - * This is used in many places in CLI command, having a function to generate - * this helps maintaining a consistent output (and possibly emitting the - * output in other languages, at some point). +/*! + * \brief Return Yes or No depending on the argument. + * + * Note that this should probably still be used for CLI commands instead of + * AST_YESNO(), in the off chance we someday want to translate the CLI. + * + * \param x Boolean value + * \return "Yes" if x is true (non-zero) + * \return "No" if x is false (zero) */ -#define AST_CLI_YESNO(x) (x) ? "Yes" : "No" +#define AST_CLI_YESNO(x) AST_YESNO(x) /*! \brief return On or Off depending on the argument. * This is used in many places in CLI command, having a function to generate diff --git a/include/asterisk/strings.h b/include/asterisk/strings.h index afbda7816..d16e9f7bd 100644 --- a/include/asterisk/strings.h +++ b/include/asterisk/strings.h @@ -83,6 +83,18 @@ static force_inline int attribute_pure ast_strlen_zero(const char *s) #define S_COR(a, b, c) ({typeof(&((b)[0])) __x = (b); (a) && !ast_strlen_zero(__x) ? (__x) : (c);}) /*! + * \brief return Yes or No depending on the argument. + * + * Note that this macro is used my AMI, where a literal "Yes" and "No" are + * expected, and translations would cause problems. + * + * \param x Boolean value + * \return "Yes" if x is true (non-zero) + * \return "No" if x is false (zero) + */ +#define AST_YESNO(x) ((x) ? "Yes" : "No") + +/*! \brief Gets a pointer to the first non-whitespace character in a string. \param str the input string \return a pointer to the first non-whitespace character diff --git a/main/channel.c b/main/channel.c index a9bcf4481..415af35e2 100644 --- a/main/channel.c +++ b/main/channel.c @@ -3714,42 +3714,56 @@ int ast_waitfordigit_full(struct ast_channel *c, int timeout_ms, int audiofd, in return 0; /* Time is up */ } -static void send_dtmf_event(struct ast_channel *chan, const char *direction, const char digit, const char *begin, const char *end) +enum DtmfDirection { + DTMF_RECEIVED, + DTMF_SENT +}; + +static const char *dtmf_direction_to_string(enum DtmfDirection direction) { - /*** DOCUMENTATION - <managerEventInstance> - <synopsis>Raised when a DTMF digit has started or ended on a channel.</synopsis> - <syntax> - <parameter name="Direction"> - <enumlist> - <enum name="Received"/> - <enum name="Sent"/> - </enumlist> - </parameter> - <parameter name="Begin"> - <enumlist> - <enum name="Yes"/> - <enum name="No"/> - </enumlist> - </parameter> - <parameter name="End"> - <enumlist> - <enum name="Yes"/> - <enum name="No"/> - </enumlist> - </parameter> - </syntax> - </managerEventInstance> - ***/ - ast_manager_event(chan, EVENT_FLAG_DTMF, - "DTMF", - "Channel: %s\r\n" - "Uniqueid: %s\r\n" - "Digit: %c\r\n" - "Direction: %s\r\n" - "Begin: %s\r\n" - "End: %s\r\n", - ast_channel_name(chan), ast_channel_uniqueid(chan), digit, direction, begin, end); + switch (direction) { + case DTMF_RECEIVED: + return "Received"; + case DTMF_SENT: + return "Sent"; + } + + return "?"; +} + +static void send_dtmf_begin_event(struct ast_channel *chan, + enum DtmfDirection direction, const char digit) +{ + RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref); + char digit_str[] = { digit, '\0' }; + + blob = ast_json_pack("{ s: s, s: s, s: s }", + "type", "dtmf_begin", + "digit", digit_str, + "direction", dtmf_direction_to_string(direction)); + if (!blob) { + return; + } + + publish_channel_blob(chan, blob); +} + +static void send_dtmf_end_event(struct ast_channel *chan, + enum DtmfDirection direction, const char digit, long duration_ms) +{ + RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref); + char digit_str[] = { digit, '\0' }; + + blob = ast_json_pack("{ s: s, s: s, s: s, s: i }", + "type", "dtmf_end", + "digit", digit_str, + "direction", dtmf_direction_to_string(direction), + "duration_ms", duration_ms); + if (!blob) { + return; + } + + publish_channel_blob(chan, blob); } static void ast_read_generator_actions(struct ast_channel *chan, struct ast_frame *f) @@ -4104,7 +4118,7 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio) } break; case AST_FRAME_DTMF_END: - send_dtmf_event(chan, "Received", f->subclass.integer, "No", "Yes"); + send_dtmf_end_event(chan, DTMF_RECEIVED, f->subclass.integer, f->len); ast_log(LOG_DTMF, "DTMF end '%c' received on %s, duration %ld ms\n", f->subclass.integer, ast_channel_name(chan), f->len); /* Queue it up if DTMF is deferred, or if DTMF emulation is forced. */ if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_DEFER_DTMF) || ast_test_flag(ast_channel_flags(chan), AST_FLAG_EMULATE_DTMF)) { @@ -4190,7 +4204,7 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio) } break; case AST_FRAME_DTMF_BEGIN: - send_dtmf_event(chan, "Received", f->subclass.integer, "Yes", "No"); + send_dtmf_begin_event(chan, DTMF_RECEIVED, f->subclass.integer); ast_log(LOG_DTMF, "DTMF begin '%c' received on %s\n", f->subclass.integer, ast_channel_name(chan)); if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_DEFER_DTMF | AST_FLAG_END_DTMF_ONLY | AST_FLAG_EMULATE_DTMF) || (!ast_tvzero(*ast_channel_dtmf_tv(chan)) && @@ -5042,7 +5056,7 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr) if (old_frame != fr) f = fr; } - send_dtmf_event(chan, "Sent", fr->subclass.integer, "Yes", "No"); + send_dtmf_begin_event(chan, DTMF_SENT, fr->subclass.integer); ast_clear_flag(ast_channel_flags(chan), AST_FLAG_BLOCKING); ast_channel_unlock(chan); res = ast_senddigit_begin(chan, fr->subclass.integer); @@ -5058,7 +5072,7 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr) ast_frfree(new_frame); } } - send_dtmf_event(chan, "Sent", fr->subclass.integer, "No", "Yes"); + send_dtmf_end_event(chan, DTMF_SENT, fr->subclass.integer, fr->len); ast_clear_flag(ast_channel_flags(chan), AST_FLAG_BLOCKING); ast_channel_unlock(chan); res = ast_senddigit_end(chan, fr->subclass.integer, fr->len); diff --git a/main/manager_channels.c b/main/manager_channels.c index ce306e4bb..03cf71fd8 100644 --- a/main/manager_channels.c +++ b/main/manager_channels.c @@ -275,8 +275,8 @@ struct ast_str *ast_manager_build_channel_state_string_suffix( struct ast_var_t *var; AST_LIST_TRAVERSE(snapshot->manager_vars, var, entries) { ast_str_append(&out, 0, "ChanVariable%s: %s=%s\r\n", - suffix, - var->name, var->value); + suffix, + var->name, var->value); } } @@ -657,6 +657,90 @@ static void channel_hangup_request(struct ast_channel_blob *obj) ast_str_buffer(extra)); } +static void channel_dtmf_begin(struct ast_channel_blob *obj) +{ + RAII_VAR(struct ast_str *, channel_event_string, NULL, ast_free); + const char *digit = + ast_json_string_get(ast_json_object_get(obj->blob, "digit")); + const char *direction = + ast_json_string_get(ast_json_object_get(obj->blob, "direction")); + + channel_event_string = manager_build_channel_state_string(obj->snapshot); + + if (!channel_event_string) { + return; + } + + /*** DOCUMENTATION + <managerEventInstance> + <synopsis>Raised when a DTMF digit has started on a channel.</synopsis> + <syntax> + <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" /> + <parameter name="Digit"> + <para>DTMF digit received or transmitted (0-9, A-E, # or *</para> + </parameter> + <parameter name="Direction"> + <enumlist> + <enum name="Received"/> + <enum name="Sent"/> + </enumlist> + </parameter> + </syntax> + </managerEventInstance> + ***/ + manager_event(EVENT_FLAG_DTMF, "DTMFBegin", + "%s" + "Digit: %s\r\n" + "Direction: %s\r\n", + ast_str_buffer(channel_event_string), + digit, direction); +} + +static void channel_dtmf_end(struct ast_channel_blob *obj) +{ + RAII_VAR(struct ast_str *, channel_event_string, NULL, ast_free); + const char *digit = + ast_json_string_get(ast_json_object_get(obj->blob, "digit")); + const char *direction = + ast_json_string_get(ast_json_object_get(obj->blob, "direction")); + long duration_ms = + ast_json_integer_get(ast_json_object_get(obj->blob, "duration_ms")); + + channel_event_string = manager_build_channel_state_string(obj->snapshot); + + if (!channel_event_string) { + return; + } + + /*** DOCUMENTATION + <managerEventInstance> + <synopsis>Raised when a DTMF digit has ended on a channel.</synopsis> + <syntax> + <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" /> + <parameter name="Digit"> + <para>DTMF digit received or transmitted (0-9, A-E, # or *</para> + </parameter> + <parameter name="DurationMs"> + <para>Duration (in milliseconds) DTMF was sent/received</para> + </parameter> + <parameter name="Direction"> + <enumlist> + <enum name="Received"/> + <enum name="Sent"/> + </enumlist> + </parameter> + </syntax> + </managerEventInstance> + ***/ + manager_event(EVENT_FLAG_DTMF, "DTMFEnd", + "%s" + "Digit: %s\r\n" + "DurationMs: %ld\r\n" + "Direction: %s\r\n", + ast_str_buffer(channel_event_string), + digit, duration_ms, direction); +} + /*! * \brief Callback processing messages on the channel topic. */ @@ -672,6 +756,10 @@ static void channel_blob_cb(void *data, struct stasis_subscription *sub, channel_userevent(obj); } else if (strcmp("hangup_request", ast_channel_blob_json_type(obj)) == 0) { channel_hangup_request(obj); + } else if (strcmp("dtmf_begin", ast_channel_blob_json_type(obj)) == 0) { + channel_dtmf_begin(obj); + } else if (strcmp("dtmf_end", ast_channel_blob_json_type(obj)) == 0) { + channel_dtmf_end(obj); } } |