There can be one and only one thread handling a channel's media at a time.
Otherwise, we don't know which thread is going to handle the media frames.
ASTERISK-27625
Change-Id: I4d6a2fe7386ea447ee199003bf8ad681cb30454e
void ast_unreplace_sigchld(void);
/*!
- \brief Send DTMF to a channel
-
- \param chan The channel that will receive the DTMF frames
- \param peer (optional) Peer channel that will be autoserviced while the
- primary channel is receiving DTMF
- \param digits This is a string of characters representing the DTMF digits
- to be sent to the channel. Valid characters are
- "0123456789*#abcdABCD". Note: You can pass arguments 'f' or
- 'F', if you want to Flash the channel (if supported by the
- channel), or 'w' to add a 500 millisecond pause to the DTMF
- sequence.
- \param between This is the number of milliseconds to wait in between each
- DTMF digit. If zero milliseconds is specified, then the
- default value of 100 will be used.
- \param duration This is the duration that each DTMF digit should have.
-*/
+ * \brief Send a string of DTMF digits to a channel
+ *
+ * \param chan The channel that will receive the DTMF frames
+ * \param peer (optional) Peer channel that will be autoserviced while the
+ * primary channel is receiving DTMF
+ * \param digits This is a string of characters representing the DTMF digits
+ * to be sent to the channel. Valid characters are
+ * "0123456789*#abcdABCD". Note: You can pass arguments 'f' or
+ * 'F', if you want to Flash the channel (if supported by the
+ * channel), or 'w' to add a 500 millisecond pause to the DTMF
+ * sequence.
+ * \param between This is the number of milliseconds to wait in between each
+ * DTMF digit. If zero milliseconds is specified, then the
+ * default value of 100 will be used.
+ * \param duration This is the duration that each DTMF digit should have.
+ *
+ * \pre This must only be called by the channel's media handler thread.
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure or a channel hung up.
+ */
int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const char *digits, int between, unsigned int duration);
+/*!
+ * \brief Send a string of DTMF digits to a channel from an external thread.
+ *
+ * \param chan The channel that will receive the DTMF frames
+ * \param digits This is a string of characters representing the DTMF digits
+ * to be sent to the channel. Valid characters are
+ * "0123456789*#abcdABCD". Note: You can pass arguments 'f' or
+ * 'F', if you want to Flash the channel (if supported by the
+ * channel), or 'w' to add a 500 millisecond pause to the DTMF
+ * sequence.
+ * \param between This is the number of milliseconds to wait in between each
+ * DTMF digit. If zero milliseconds is specified, then the
+ * default value of 100 will be used.
+ * \param duration This is the duration that each DTMF digit should have.
+ *
+ * \pre This must only be called by threads that are not the channel's
+ * media handler thread.
+ *
+ * \return Nothing
+ */
+void ast_dtmf_stream_external(struct ast_channel *chan, const char *digits, int between, unsigned int duration);
+
/*! \brief Stream a filename (or file descriptor) as a generator. */
int ast_linear_stream(struct ast_channel *chan, const char *filename, int fd, int allowoverride);
}
#endif
-int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const char *digits, int between, unsigned int duration)
+static int external_sleep(struct ast_channel *chan, int ms)
+{
+ usleep(ms * 1000);
+ return 0;
+}
+
+static int dtmf_stream(struct ast_channel *chan, const char *digits, int between, unsigned int duration, int is_external)
{
const char *ptr;
int res;
struct ast_silence_generator *silgen = NULL;
+ int (*my_sleep)(struct ast_channel *chan, int ms);
+ int (*my_senddigit)(struct ast_channel *chan, char digit, unsigned int duration);
- if (!between) {
- between = 100;
+ if (is_external) {
+ my_sleep = external_sleep;
+ my_senddigit = ast_senddigit_external;
+ } else {
+ my_sleep = ast_safe_sleep;
+ my_senddigit = ast_senddigit;
}
- if (peer && ast_autoservice_start(peer)) {
- return -1;
+ if (!between) {
+ between = 100;
}
/* Need a quiet time before sending digits. */
if (ast_opt_transmit_silence) {
silgen = ast_channel_start_silence_generator(chan);
}
- res = ast_safe_sleep(chan, 100);
+ res = my_sleep(chan, 100);
if (res) {
goto dtmf_stream_cleanup;
}
for (ptr = digits; *ptr; ptr++) {
if (*ptr == 'w') {
/* 'w' -- wait half a second */
- if ((res = ast_safe_sleep(chan, 500))) {
+ res = my_sleep(chan, 500);
+ if (res) {
break;
}
} else if (*ptr == 'W') {
/* 'W' -- wait a second */
- if ((res = ast_safe_sleep(chan, 1000))) {
+ res = my_sleep(chan, 1000);
+ if (res) {
break;
}
} else if (strchr("0123456789*#abcdfABCDF", *ptr)) {
ast_indicate(chan, AST_CONTROL_FLASH);
} else {
/* Character represents valid DTMF */
- ast_senddigit(chan, *ptr, duration);
+ my_senddigit(chan, *ptr, duration);
}
/* pause between digits */
- if ((res = ast_safe_sleep(chan, between))) {
+ res = my_sleep(chan, between);
+ if (res) {
break;
}
} else {
if (silgen) {
ast_channel_stop_silence_generator(chan, silgen);
}
+
+ return res;
+}
+
+int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const char *digits, int between, unsigned int duration)
+{
+ int res;
+
+ if (peer && ast_autoservice_start(peer)) {
+ return -1;
+ }
+ res = dtmf_stream(chan, digits, between, duration, 0);
if (peer && ast_autoservice_stop(peer)) {
res = -1;
}
return res;
}
+void ast_dtmf_stream_external(struct ast_channel *chan, const char *digits, int between, unsigned int duration)
+{
+ dtmf_stream(chan, digits, between, duration, 1);
+}
+
struct linear_state {
int fd;
int autoclose;
char dtmf[];
};
-static int app_control_dtmf(struct stasis_app_control *control,
- struct ast_channel *chan, void *data)
+static void dtmf_in_bridge(struct ast_channel *chan, struct stasis_app_control_dtmf_data *dtmf_data)
{
- struct stasis_app_control_dtmf_data *dtmf_data = data;
+ if (dtmf_data->before) {
+ usleep(dtmf_data->before * 1000);
+ }
- if (ast_channel_state(chan) != AST_STATE_UP) {
- ast_indicate(chan, AST_CONTROL_PROGRESS);
+ ast_dtmf_stream_external(chan, dtmf_data->dtmf, dtmf_data->between, dtmf_data->duration);
+
+ if (dtmf_data->after) {
+ usleep(dtmf_data->after * 1000);
}
+}
+static void dtmf_no_bridge(struct ast_channel *chan, struct stasis_app_control_dtmf_data *dtmf_data)
+{
if (dtmf_data->before) {
ast_safe_sleep(chan, dtmf_data->before);
}
if (dtmf_data->after) {
ast_safe_sleep(chan, dtmf_data->after);
}
+}
+
+static int app_control_dtmf(struct stasis_app_control *control,
+ struct ast_channel *chan, void *data)
+{
+ struct stasis_app_control_dtmf_data *dtmf_data = data;
+
+ if (ast_channel_state(chan) != AST_STATE_UP) {
+ ast_indicate(chan, AST_CONTROL_PROGRESS);
+ }
+
+ if (stasis_app_get_bridge(control)) {
+ dtmf_in_bridge(chan, dtmf_data);
+ } else {
+ dtmf_no_bridge(chan, dtmf_data);
+ }
return 0;
}