diff --git a/include/asterisk/bridge_channel.h b/include/asterisk/bridge_channel.h index 66f9be6e5fabd4353a3a5ddd99efdd0902bb1b61..797be4ebcb1ee13fb59aa3e2851773a0b75374a5 100644 --- a/include/asterisk/bridge_channel.h +++ b/include/asterisk/bridge_channel.h @@ -162,6 +162,8 @@ struct ast_bridge_channel { struct timeval dtmf_tv; /*! Digit currently sending into the bridge. (zero if not sending) */ char dtmf_digit; + /*! Non-zero if a T.38 session terminate is owed to the bridge. */ + char t38_terminate; } owed; /*! DTMF hook sequence state */ struct { diff --git a/main/bridge_channel.c b/main/bridge_channel.c index 3f141452e500810c308fa79da43749d106bc374b..8172660335692563aa7ee3d3ee6fedfb7b5beed3 100644 --- a/main/bridge_channel.c +++ b/main/bridge_channel.c @@ -637,6 +637,8 @@ void ast_bridge_channel_kick(struct ast_bridge_channel *bridge_channel, int caus */ static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) { + const struct ast_control_t38_parameters *t38_parameters; + ast_assert(frame->frametype != AST_FRAME_BRIDGE_ACTION_SYNC); ast_bridge_channel_lock_bridge(bridge_channel); @@ -663,6 +665,27 @@ static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel, * We explicitly will not remember HOLD/UNHOLD frames because * things like attended transfers will handle them. */ + switch (frame->subclass.integer) { + case AST_CONTROL_T38_PARAMETERS: + t38_parameters = frame->data.ptr; + switch (t38_parameters->request_response) { + case AST_T38_REQUEST_NEGOTIATE: + case AST_T38_NEGOTIATED: + bridge_channel->owed.t38_terminate = 1; + break; + case AST_T38_REQUEST_TERMINATE: + case AST_T38_TERMINATED: + case AST_T38_REFUSED: + bridge_channel->owed.t38_terminate = 0; + break; + default: + break; + } + break; + default: + break; + } + break; default: break; } @@ -689,6 +712,7 @@ static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel, static void bridge_channel_cancel_owed_events(struct ast_bridge_channel *bridge_channel) { bridge_channel->owed.dtmf_digit = '\0'; + bridge_channel->owed.t38_terminate = 0; } void bridge_channel_settle_owed_events(struct ast_bridge *orig_bridge, struct ast_bridge_channel *bridge_channel) @@ -710,6 +734,23 @@ void bridge_channel_settle_owed_events(struct ast_bridge *orig_bridge, struct as bridge_channel->owed.dtmf_digit = '\0'; orig_bridge->technology->write(orig_bridge, NULL, &frame); } + if (bridge_channel->owed.t38_terminate) { + struct ast_control_t38_parameters t38_parameters = { + .request_response = AST_T38_TERMINATED, + }; + struct ast_frame frame = { + .frametype = AST_FRAME_CONTROL, + .subclass.integer = AST_CONTROL_T38_PARAMETERS, + .data.ptr = &t38_parameters, + .datalen = sizeof(t38_parameters), + .src = "Bridge channel owed T.38 terminate", + }; + + ast_debug(1, "T.38 terminate simulated to bridge %s because %s left.\n", + orig_bridge->uniqueid, ast_channel_name(bridge_channel->chan)); + bridge_channel->owed.t38_terminate = 0; + orig_bridge->technology->write(orig_bridge, NULL, &frame); + } } /*! @@ -2736,6 +2777,18 @@ int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel, ast_channel_sending_dtmf_tv(bridge_channel->chan), "bridge end"); } + /* Complete any T.38 session before exiting the bridge. */ + if (ast_channel_is_t38_active(bridge_channel->chan)) { + struct ast_control_t38_parameters t38_parameters = { + .request_response = AST_T38_TERMINATED, + }; + + ast_debug(1, "Channel %s simulating T.38 terminate for bridge end.\n", + ast_channel_name(bridge_channel->chan)); + ast_indicate_data(bridge_channel->chan, AST_CONTROL_T38_PARAMETERS, + &t38_parameters, sizeof(t38_parameters)); + } + /* Indicate a source change since this channel is leaving the bridge system. */ ast_indicate(bridge_channel->chan, AST_CONTROL_SRCCHANGE); diff --git a/main/channel.c b/main/channel.c index f9addb01625f88fb5afd5ed52e60197417799b3c..852de1aef8f358d41984bee849c7044ca8604dc0 100644 --- a/main/channel.c +++ b/main/channel.c @@ -4479,6 +4479,7 @@ static int indicate_data_internal(struct ast_channel *chan, int _condition, cons * in switch statements. */ enum ast_control_frame_type condition = _condition; struct ast_tone_zone_sound *ts = NULL; + const struct ast_control_t38_parameters *t38_parameters; int res; switch (condition) { @@ -4498,6 +4499,22 @@ static int indicate_data_internal(struct ast_channel *chan, int _condition, cons case AST_CONTROL_UNHOLD: ast_channel_hold_state_set(chan, _condition); break; + case AST_CONTROL_T38_PARAMETERS: + t38_parameters = data; + switch (t38_parameters->request_response) { + case AST_T38_REQUEST_NEGOTIATE: + case AST_T38_NEGOTIATED: + ast_channel_set_is_t38_active_nolock(chan, 1); + break; + case AST_T38_REQUEST_TERMINATE: + case AST_T38_TERMINATED: + case AST_T38_REFUSED: + ast_channel_set_is_t38_active_nolock(chan, 0); + break; + default: + break; + } + break; default: break; }