From 4356f162344892a205460c10c13eb13d1384de2a Mon Sep 17 00:00:00 2001 From: Naveen Albert Date: Thu, 24 Aug 2023 14:07:06 +0000 Subject: [PATCH] sig_analog: Add Call Waiting Deluxe support. Adds support for Call Waiting Deluxe options to enhance the current call waiting feature. As part of this change, a mechanism is also added that allows a channel driver to queue an audio file for Dial() to play, which is necessary for the announcement function. ASTERISK-30373 #close Resolves: #271 --- apps/app_dial.c | 9 ++ channels/chan_dahdi.c | 24 +++ channels/chan_dahdi.h | 4 + channels/chan_iax2.c | 1 + channels/sig_analog.c | 216 +++++++++++++++++++++++++ channels/sig_analog.h | 9 ++ configs/samples/chan_dahdi.conf.sample | 25 +++ funcs/func_frame_trace.c | 3 + include/asterisk/frame.h | 1 + main/channel.c | 2 + 10 files changed, 294 insertions(+) diff --git a/apps/app_dial.c b/apps/app_dial.c index 5c6982a292a..17d55d55034 100644 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -1686,6 +1686,15 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, case AST_CONTROL_PVT_CAUSE_CODE: ast_indicate_data(in, AST_CONTROL_PVT_CAUSE_CODE, f->data.ptr, f->datalen); break; + case AST_CONTROL_PLAYBACK_BEGIN: + if (!f->data.ptr) { + ast_log(LOG_WARNING, "Got playback begin directive without filename on %s\n", ast_channel_name(c)); + } else { + const char *filename = f->data.ptr; + ast_verb(3, "Playing audio file %s on %s\n", filename, ast_channel_name(in)); + ast_streamfile(in, filename, ast_channel_language(in)); + } + break; case -1: if (single && !caller_entertained) { ast_verb(3, "%s stopped sounds\n", ast_channel_name(c)); diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index 3b3f12474c0..f06ebf78396 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -9191,6 +9191,26 @@ static int dahdi_write(struct ast_channel *ast, struct ast_frame *frame) return -1; } + if (p->sig == SIG_FXOLS || p->sig == SIG_FXOKS || p->sig == SIG_FXOGS) { + struct analog_pvt *analog_p = p->sig_pvt; + if (analog_p->callwaitdeluxepending) { + unsigned int mssinceflash = ast_tvdiff_ms(ast_tvnow(), analog_p->flashtime); + if (mssinceflash >= 1000) { + /* Timer expired: the user hasn't yet selected an option. Take the default action and get on with it. */ + /* Note: If in the future Advanced Call Waiting Deluxe (*76) is supported, then as part of the + * dialing code, we'll need to automatically invoke the preselected behavior about 2-3 seconds after + * the call waiting begins (this allows for the SAS, CAS, and CWCID spill to be sent first). + */ + analog_p->callwaitdeluxepending = 0; + analog_callwaiting_deluxe(analog_p, 0); + } + ast_mutex_unlock(&p->lock); + /* The user shouldn't hear anything after hook flashing, until a decision is made, by the user or when the timer expires. */ + ast_debug(5, "Dropping frame since Call Waiting Deluxe pending on %s\n", ast_channel_name(ast)); + return 0; + } + } + if (p->dialing) { ast_mutex_unlock(&p->lock); ast_debug(5, "Dropping frame since I'm still dialing on %s...\n", @@ -12947,6 +12967,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf, tmp->usedistinctiveringdetection = usedistinctiveringdetection; tmp->callwaitingcallerid = conf->chan.callwaitingcallerid; + tmp->callwaitingdeluxe = conf->chan.callwaitingdeluxe; /* Not used in DAHDI pvt, only analog pvt */ tmp->threewaycalling = conf->chan.threewaycalling; tmp->threewaysilenthold = conf->chan.threewaysilenthold; tmp->calledsubscriberheld = conf->chan.calledsubscriberheld; /* Not used in chan_dahdi.c, just analog pvt, but must exist on the DAHDI pvt anyways */ @@ -13285,6 +13306,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf, break; } analog_p->callwaitingcallerid = conf->chan.callwaitingcallerid; + analog_p->callwaitingdeluxe = conf->chan.callwaitingdeluxe; analog_p->ringt = conf->chan.ringt; analog_p->ringt_base = ringt_base; analog_p->onhooktime = time(NULL); @@ -18413,6 +18435,8 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct confp->chan.callwaiting = ast_true(v->value); } else if (!strcasecmp(v->name, "callwaitingcallerid")) { confp->chan.callwaitingcallerid = ast_true(v->value); + } else if (!strcasecmp(v->name, "callwaitingdeluxe")) { + confp->chan.callwaitingdeluxe = ast_true(v->value); } else if (!strcasecmp(v->name, "context")) { ast_copy_string(confp->chan.context, v->value, sizeof(confp->chan.context)); } else if (!strcasecmp(v->name, "language")) { diff --git a/channels/chan_dahdi.h b/channels/chan_dahdi.h index edf28e1ebae..108b8604670 100644 --- a/channels/chan_dahdi.h +++ b/channels/chan_dahdi.h @@ -229,6 +229,10 @@ struct dahdi_pvt { * \note Set from the "callwaitingcallerid" value read in from chan_dahdi.conf */ unsigned int callwaitingcallerid:1; + /*! + * \brief TRUE if Call Waiting Deluxe options should be available + */ + unsigned int callwaitingdeluxe:1; /*! * \brief TRUE if support for call forwarding enabled. * Dial *72 to enable call forwarding. diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index 3f8e5c1d328..c0cf5572a59 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -1447,6 +1447,7 @@ static int iax2_is_control_frame_allowed(int subtype) case AST_CONTROL_STREAM_RESTART: case AST_CONTROL_STREAM_REVERSE: case AST_CONTROL_STREAM_FORWARD: + case AST_CONTROL_PLAYBACK_BEGIN: /* Only supported by app_dial currently */ /* None of these playback stream control frames should go across the link. */ case AST_CONTROL_RECORD_CANCEL: case AST_CONTROL_RECORD_STOP: diff --git a/channels/sig_analog.c b/channels/sig_analog.c index 92fc47eda6c..1c3e9d6dd52 100644 --- a/channels/sig_analog.c +++ b/channels/sig_analog.c @@ -1590,6 +1590,161 @@ static int analog_handles_digit(struct ast_frame *f) } } +enum callwaiting_deluxe_option { + CWD_CONFERENCE = '3', + CWD_HOLD = '6', + CWD_DROP = '7', + CWD_ANNOUNCEMENT = '8', + CWD_FORWARD = '9', +}; + +static const char *callwaiting_deluxe_optname(int option) +{ + switch (option) { + case CWD_CONFERENCE: + return "CONFERENCE"; + case CWD_HOLD: + return "HOLD"; + case CWD_DROP: + return "DROP"; + case CWD_ANNOUNCEMENT: + return "ANNOUNCEMENT"; + case CWD_FORWARD: + return "FORWARD"; + default: + return "DEFAULT"; + } +} + +int analog_callwaiting_deluxe(struct analog_pvt *p, int option) +{ + const char *announce_var; + char announcement[PATH_MAX]; + + ast_debug(1, "Handling Call Waiting on channel %d with option %c: treatment %s\n", p->channel, option, callwaiting_deluxe_optname(option)); + + if (!p->subs[ANALOG_SUB_CALLWAIT].owner) { + /* This can happen if the caller hook flashes and the call waiting hangs up before the CWD timer expires (1 second) */ + ast_debug(1, "Call waiting call disappeared before it could be handled?\n"); + return -1; + } + + analog_lock_sub_owner(p, ANALOG_SUB_CALLWAIT); + if (!p->subs[ANALOG_SUB_CALLWAIT].owner) { + ast_log(LOG_WARNING, "Whoa, the call-waiting call disappeared.\n"); + return -1; + } + + /* Note that when p->callwaitdeluxepending, dahdi_write will drop incoming frames to the channel, + * since the user shouldn't hear anything after flashing until either a DTMF has been received + * or it's been a second and the decision is made automatically. */ + + switch (option) { + case CWD_CONFERENCE: + /* We should never have a call waiting if we have a 3-way anyways, but check just in case, + * there better be no existing SUB_THREEWAY since we're going to make one (and then swap the call wait to it) */ + if (p->subs[ANALOG_SUB_THREEWAY].owner) { + ast_channel_unlock(p->subs[ANALOG_SUB_CALLWAIT].owner); + ast_log(LOG_ERROR, "Already have a 3-way call on channel %d, can't conference!\n", p->channel); + return -1; + } + + /* To conference the incoming call, swap it from SUB_CALLWAIT to SUB_THREEWAY, + * and then the existing 3-way logic will ensure that flashing again will drop the call waiting */ + analog_alloc_sub(p, ANALOG_SUB_THREEWAY); + analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_CALLWAIT); + analog_unalloc_sub(p, ANALOG_SUB_CALLWAIT); + + ast_verb(3, "Building conference call with %s and %s\n", ast_channel_name(p->subs[ANALOG_SUB_THREEWAY].owner), ast_channel_name(p->subs[ANALOG_SUB_REAL].owner)); + analog_set_inthreeway(p, ANALOG_SUB_THREEWAY, 1); + analog_set_inthreeway(p, ANALOG_SUB_REAL, 1); + + if (ast_channel_state(p->subs[ANALOG_SUB_THREEWAY].owner) == AST_STATE_RINGING) { + ast_setstate(p->subs[ANALOG_SUB_THREEWAY].owner, AST_STATE_UP); + ast_queue_control(p->subs[ANALOG_SUB_THREEWAY].owner, AST_CONTROL_ANSWER); + /* Stop the ringing on the call wait channel (yeah, apparently this is how it's done) */ + ast_queue_hold(p->subs[ANALOG_SUB_THREEWAY].owner, p->mohsuggest); + ast_queue_unhold(p->subs[ANALOG_SUB_THREEWAY].owner); + } + analog_stop_callwait(p); + + ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner); /* Unlock what was originally SUB_CALLWAIT */ + break; + case CWD_HOLD: /* The CI-7112 Visual Director sends "HOLD" for "Play hold message" rather than "ANNOUNCEMENT". For default behavior, nothing is actually sent. */ + case CWD_ANNOUNCEMENT: + /* We can't just call ast_streamfile here, this thread isn't responsible for media on the call waiting channel. + * Indicate to the dialing channel in app_dial that it needs to play media. + * + * This is a lot easier than other ways of trying to send early media to the channel + * (such as every call from the core to dahdi_read, sending the channel one frame of the audio file, etc.) + */ + + /* There's not a particularly good stock audio prompt to use here. The Pat Fleet library has some better + * ones but we want one that is also in the default Allison Smith library. "One moment please" works okay. + * Check if a variable containing the prompt to use was specified on the call waiting channel, and + * fall back to a reasonable default if not. */ + + /* The SUB_CALLWAIT channel is already locked here, no need to lock and unlock to get the variable. */ + announce_var = pbx_builtin_getvar_helper(p->subs[ANALOG_SUB_CALLWAIT].owner, "CALLWAITDELUXEANNOUNCEMENT"); + ast_copy_string(announcement, S_OR(announce_var, "one-moment-please"), sizeof(announcement)); + ast_debug(2, "Call Waiting Deluxe announcement for %s: %s\n", ast_channel_name(p->subs[ANALOG_SUB_CALLWAIT].owner), announcement); + ast_channel_unlock(p->subs[ANALOG_SUB_CALLWAIT].owner); + /* Tell app_dial what file to play. */ + ast_queue_control_data(p->subs[ANALOG_SUB_CALLWAIT].owner, AST_CONTROL_PLAYBACK_BEGIN, announcement, strlen(announcement) + 1); + /* Unlike all the other options, the call waiting is still active with this option, + * so we don't call analog_stop_callwait(p) + * The call waiting will continue to be here, and at some later point the user can flash again and choose a finalizing option + * (or even queue the announcement again... and again... and again...) + */ + break; + case CWD_FORWARD: + /* Go away, call waiting, call again some other day... */ + analog_stop_callwait(p); + /* Can't use p->call_forward exten because that's for *72 forwarding, and sig_analog doesn't + * have a Busy/Don't Answer call forwarding exten internally, so let the dialplan deal with it. + * by sending the call to the 'f' extension. + */ + ast_channel_call_forward_set(p->subs[ANALOG_SUB_CALLWAIT].owner, "f"); + ast_channel_unlock(p->subs[ANALOG_SUB_CALLWAIT].owner); + /* app_dial already has a verbose message for forwarding, so we don't really need one here also since that does the job */ + break; + case CWD_DROP: + /* Fall through: logic is identical to hold, except we drop the original call right after we swap. */ + default: + /* Swap to call-wait, same as with the non-deluxe call waiting handling. */ + analog_swap_subs(p, ANALOG_SUB_REAL, ANALOG_SUB_CALLWAIT); + analog_play_tone(p, ANALOG_SUB_REAL, -1); + analog_set_new_owner(p, p->subs[ANALOG_SUB_REAL].owner); + ast_debug(1, "Making %s the new owner\n", ast_channel_name(p->owner)); + if (ast_channel_state(p->subs[ANALOG_SUB_REAL].owner) == AST_STATE_RINGING) { + ast_setstate(p->subs[ANALOG_SUB_REAL].owner, AST_STATE_UP); + ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_ANSWER); + } + analog_stop_callwait(p); + + if (option == CWD_DROP) { + /* Disconnect the previous call (the original call is now the SUB_CALLWAIT since we swapped above) */ + ast_queue_hangup(p->subs[ANALOG_SUB_CALLWAIT].owner); + ast_verb(3, "Dropping original call and swapping to call waiting on %s\n", ast_channel_name(p->subs[ANALOG_SUB_REAL].owner)); + } else { + /* Start music on hold if appropriate */ + if (!p->subs[ANALOG_SUB_CALLWAIT].inthreeway) { + ast_queue_hold(p->subs[ANALOG_SUB_CALLWAIT].owner, p->mohsuggest); + } + ast_verb(3, "Holding original call and swapping to call waiting on %s\n", ast_channel_name(p->subs[ANALOG_SUB_REAL].owner)); + } + + /* Stop ringing on the incoming call */ + ast_queue_hold(p->subs[ANALOG_SUB_REAL].owner, p->mohsuggest); + ast_queue_unhold(p->subs[ANALOG_SUB_REAL].owner); + + /* Unlock the call-waiting call that we swapped to real-call. */ + ast_channel_unlock(p->subs[ANALOG_SUB_REAL].owner); + } + analog_update_conf(p); + return 0; +} + void analog_handle_dtmf(struct analog_pvt *p, struct ast_channel *ast, enum analog_sub idx, struct ast_frame **dest) { struct ast_frame *f = *dest; @@ -1627,6 +1782,50 @@ void analog_handle_dtmf(struct analog_pvt *p, struct ast_channel *ast, enum anal p->subs[idx].f.frametype = AST_FRAME_NULL; p->subs[idx].f.subclass.integer = 0; *dest = &p->subs[idx].f; + } else if (p->callwaitdeluxepending) { + if (f->frametype == AST_FRAME_DTMF_END) { + unsigned int mssinceflash = ast_tvdiff_ms(ast_tvnow(), p->flashtime); + p->callwaitdeluxepending = 0; + + /* This is the case where a user explicitly took action (made a decision) + * for Call Waiting Deluxe. + * Because we already handled the hook flash, if the user doesn't do + * anything within a second, then we still need to eventually take + * the default action (swap) for the call waiting. + * + * dahdi_write will also drop audio if callwaitdeluxepending is set HIGH, + * and also check if flashtime hits 1000, in which case it will set the flag LOW and then take the + * default action, e.g. analog_callwaiting_deluxe(p, 0); + */ + + /* Slightly less than 1000, so there's no chance of a race condition + * between do_monitor when it sees flashtime hitting 1000 and us. */ + if (mssinceflash > 990) { + /* This was more than a second ago, clear the flag and process normally. */ + /* Because another thread has to monitor channels with pending CWDs, + * in theory, we shouldn't need to check this here. */ + ast_debug(1, "It's been %u ms since the last flash, this is not a Call Waiting Deluxe DTMF\n", mssinceflash); + analog_cb_handle_dtmf(p, ast, idx, dest); + return; + } + /* Okay, actually do something now. */ + switch (f->subclass.integer) { + case CWD_CONFERENCE: + case CWD_HOLD: + case CWD_DROP: + case CWD_ANNOUNCEMENT: + case CWD_FORWARD: + ast_debug(1, "Got some DTMF, but it's for Call Waiting Deluxe: %c\n", f->subclass.integer); + analog_callwaiting_deluxe(p, f->subclass.integer); + break; + default: + ast_log(LOG_WARNING, "Invalid Call Waiting Deluxe option (%c), using default\n", f->subclass.integer); + analog_callwaiting_deluxe(p, 0); + } + } + p->subs[idx].f.frametype = AST_FRAME_NULL; + p->subs[idx].f.subclass.integer = 0; + *dest = &p->subs[idx].f; } else { analog_cb_handle_dtmf(p, ast, idx, dest); } @@ -2967,6 +3166,7 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_ break; } } + p->callwaitdeluxepending = 0; ast_queue_control_data(ast, AST_CONTROL_PVT_CAUSE_CODE, cause_code, data_size); ast_channel_hangupcause_hash_set(ast, cause_code, data_size); switch (p->sig) { @@ -3282,6 +3482,7 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_ } /* Remember last time we got a flash-hook */ gettimeofday(&p->flashtime, NULL); + p->callwaitdeluxepending = 0; switch (mysig) { case ANALOG_SIG_FXOLS: case ANALOG_SIG_FXOGS: @@ -3310,6 +3511,20 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_ goto winkflashdone; } + /* If line has Call Waiting Deluxe, see what the user wants to do. + * Only do this if this is an as yet unanswered call waiting, not an existing, answered SUB_CALLWAIT. */ + if (ast_channel_state(p->subs[ANALOG_SUB_CALLWAIT].owner) == AST_STATE_RINGING) { + if (p->callwaitingdeluxe) { + /* This thread cannot block, so just set the flag that we need + * to wait for a Call Waiting Deluxe option (or let it time out), + * and then we're done for now. */ + ast_channel_unlock(p->subs[ANALOG_SUB_CALLWAIT].owner); + p->callwaitdeluxepending = 1; + ast_debug(1, "Deferring call waiting manipulation, waiting for Call Waiting Deluxe option from user\n"); + goto winkflashdone; + } + } + /* Swap to call-wait */ analog_swap_subs(p, ANALOG_SUB_REAL, ANALOG_SUB_CALLWAIT); analog_play_tone(p, ANALOG_SUB_REAL, -1); @@ -3844,6 +4059,7 @@ void *analog_handle_init_event(struct analog_pvt *i, int event) res = analog_off_hook(i); i->fxsoffhookstate = 1; i->cshactive = 0; + i->callwaitdeluxepending = 0; if (res && (errno == EBUSY)) { break; } diff --git a/channels/sig_analog.h b/channels/sig_analog.h index 81043f39a58..8674b3b0680 100644 --- a/channels/sig_analog.h +++ b/channels/sig_analog.h @@ -331,6 +331,7 @@ struct analog_pvt { /* XXX: All variables after this are internal */ unsigned int callwaiting:1; /*!< TRUE if call waiting is enabled. (Active option) */ + unsigned int callwaitingdeluxe:1; /*!< TRUE if Call Waiting Deluxe options are available */ unsigned int cshactive:1; /*!< TRUE if FXS channel is currently held by an incoming call */ unsigned int dialednone:1; unsigned int dialing:1; /*!< TRUE if in the process of dialing digits or sending something */ @@ -346,6 +347,12 @@ struct analog_pvt { * gives a positive reply. */ unsigned int callwaitcas:1; + + /*! + * \brief TRUE if a Call Waiting Deluxe action is currently pending. + */ + unsigned int callwaitdeluxepending:1; + unsigned int call_qualifier:1; /*!< Call qualifier delivery */ char callwait_num[AST_MAX_EXTENSION]; @@ -397,6 +404,8 @@ void *analog_handle_init_event(struct analog_pvt *i, int event); int analog_config_complete(struct analog_pvt *p); +int analog_callwaiting_deluxe(struct analog_pvt *p, int option); + void analog_handle_dtmf(struct analog_pvt *p, struct ast_channel *ast, enum analog_sub index, struct ast_frame **dest); enum analog_cid_start analog_str_to_cidstart(const char *value); diff --git a/configs/samples/chan_dahdi.conf.sample b/configs/samples/chan_dahdi.conf.sample index c9f5aa4fd07..c8ae7cb6cc3 100644 --- a/configs/samples/chan_dahdi.conf.sample +++ b/configs/samples/chan_dahdi.conf.sample @@ -685,6 +685,31 @@ usecallerid=yes ; callwaiting=yes ; +; Whether or not to allow for Call Waiting Deluxe options to be used. +; The Call Waiting Deluxe options are: +; 3: Conference - conference the call waiting call with the existing call +; 7: Drop - drop current call and switch to new (same as hanging up and answering) +; 6/8: Announcement - play announcement to call waiting caller telling them to hold +; 9: Forward - forward the call waiting call to a preconfigured extension +; Doing nothing after 1 second triggers default behavior of holding the current call +; and switching to the new one. +; +; Some CPE (Caller ID units, screenphones, etc.) have dedicated buttons for utilizing +; the Call Waiting Deluxe features, but these can also be used from any phone manually +; by simply hook flashing as usual and then quickly dialing the appropriate DTMF digit. +; If no digit is received within 1 second, the default action is assumed, +; as if Call Waiting Deluxe was not used. +; +; The following extensions are also available in the dialplan to utilize this functionality: +; - The CALLWAITDELUXEANNOUNCEMENT variable can be set on the incoming channel to control the +; announcement prompt played to the call waiting caller. Default is "one-moment-please". +; - If the forward option is used, the call waiting channel will be forwarded to the 'f' extension +; in the channel's configured context. You can then use any dialplan mechanism to route the call. +; +; Default is 'no'. +; +;callwaitingdeluxe=yes +; ; Configure the number of outstanding call waiting calls for internal ISDN ; endpoints before bouncing the calls as busy. This option is equivalent to ; the callwaiting option for analog ports. diff --git a/funcs/func_frame_trace.c b/funcs/func_frame_trace.c index 108f17aa869..3cc45684abf 100644 --- a/funcs/func_frame_trace.c +++ b/funcs/func_frame_trace.c @@ -379,6 +379,9 @@ static void print_frame(struct ast_frame *frame) case AST_CONTROL_RECORD_MUTE: ast_verbose("SubClass: RECORD_MUTE\n"); break; + case AST_CONTROL_PLAYBACK_BEGIN: + ast_verbose("SubClass: PLAYBACK_BEGIN\n"); + break; } if (frame->subclass.integer == -1) { diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h index 6c6fafd1cf2..a643322b651 100644 --- a/include/asterisk/frame.h +++ b/include/asterisk/frame.h @@ -335,6 +335,7 @@ enum ast_control_frame_type { AST_CONTROL_STREAM_RESTART = 1002, /*!< Indicate to a channel in playback to restart the stream */ AST_CONTROL_STREAM_REVERSE = 1003, /*!< Indicate to a channel in playback to rewind */ AST_CONTROL_STREAM_FORWARD = 1004, /*!< Indicate to a channel in playback to fast forward */ + AST_CONTROL_PLAYBACK_BEGIN = 1005, /*!< Indicate to a dialing interface that playback of an audio file should begin on the dialing channel. Currently only supported by app_dial. */ /* Control frames to manipulate recording on a channel. */ AST_CONTROL_RECORD_CANCEL = 1100, /*!< Indicated to a channel in record to stop recording and discard the file */ AST_CONTROL_RECORD_STOP = 1101, /*!< Indicated to a channel in record to stop recording */ diff --git a/main/channel.c b/main/channel.c index 844cea8a670..ab66d52f3f9 100644 --- a/main/channel.c +++ b/main/channel.c @@ -4317,6 +4317,7 @@ static int attribute_const is_visible_indication(enum ast_control_frame_type con case AST_CONTROL_RECORD_STOP: case AST_CONTROL_RECORD_SUSPEND: case AST_CONTROL_RECORD_MUTE: + case AST_CONTROL_PLAYBACK_BEGIN: break; case AST_CONTROL_INCOMPLETE: @@ -4618,6 +4619,7 @@ static int indicate_data_internal(struct ast_channel *chan, int _condition, cons case AST_CONTROL_RECORD_STOP: case AST_CONTROL_RECORD_SUSPEND: case AST_CONTROL_RECORD_MUTE: + case AST_CONTROL_PLAYBACK_BEGIN: /* Nothing left to do for these. */ res = 0; break;