Skip to content
Snippets Groups Projects
Commit 726ee873 authored by Matt Jordan's avatar Matt Jordan
Browse files

chan_pjsip: Handle T.38 faxes with direct media bridges

When a channel is in a direct media bridge, a re-INVITE may arrive that forces
Asterisk to re-negotiate the media to a T.38 fax. When this occurs, the bridge
must change its technology to a simple bridge, and re-INVITE the media back
to Asterisk.

Generally, this logic mostly already exists in Asterisk. However, prior to this
patch, there were a few bugs:
(1) The T.38 framehook currently prevents a channel capable of T.38 faxes from
    ever entering into a direct media bridge. This applies even when the only
    media being passed over the channel is audio. This patch fixes this bug
    by having the framehook specify that it defers caring about any frame type.
    This allows the channels to enter into a direct media bridge, which will
    be broken when a re-INVITE is received.
(2) When a re-INVITE is received, nothing instructed the bridging layer to
    re-inspect the allowed bridging technology. This now occurs when either
    a re-INVITE is received from a peer, or when a response is received from
    the far end (that is, when the T.38 state changes to either
    T38_PEER_REINVITE or T38_LOCAL_REINVITE).
(3) chan_pjsip needs to do a small amount of work to prevent a direct media
    bridge from being chosen when a T.38 session is in progress. When a T.38
    session supplement has a t38 datastore - which is added when we detect
    we should start thinking about T.38 on a channel - we now refuse a native
    RTP bridge.
(4) When a BYE request is received, we don't terminate the T.38 session. If
    the other side of a T.38 fax survives the hangup (due to the 'g' flag
    in Dial, for example), we don't currently re-INVITE the media on the
    other channel back to audio. This patch now has res_pjsip_t38 intercept
    BYE requests and inform the far side that the T.38 session is terminated.
    This naturally causes the correct re-INVITEs to be sent.

ASTERISK-25582

Change-Id: Iabd6aa578e633d16e6b9f342091264e4324a79eb
parent 04874067
Branches
Tags
No related merge requests found
...@@ -162,11 +162,18 @@ static enum ast_rtp_glue_result chan_pjsip_get_rtp_peer(struct ast_channel *chan ...@@ -162,11 +162,18 @@ static enum ast_rtp_glue_result chan_pjsip_get_rtp_peer(struct ast_channel *chan
struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(chan); struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(chan);
struct chan_pjsip_pvt *pvt; struct chan_pjsip_pvt *pvt;
struct ast_sip_endpoint *endpoint; struct ast_sip_endpoint *endpoint;
struct ast_datastore *datastore;
if (!channel || !channel->session || !(pvt = channel->pvt) || !pvt->media[SIP_MEDIA_AUDIO]->rtp) { if (!channel || !channel->session || !(pvt = channel->pvt) || !pvt->media[SIP_MEDIA_AUDIO]->rtp) {
return AST_RTP_GLUE_RESULT_FORBID; return AST_RTP_GLUE_RESULT_FORBID;
} }
datastore = ast_sip_session_get_datastore(channel->session, "t38");
if (datastore) {
ao2_ref(datastore, -1);
return AST_RTP_GLUE_RESULT_FORBID;
}
endpoint = channel->session->endpoint; endpoint = channel->session->endpoint;
*instance = pvt->media[SIP_MEDIA_AUDIO]->rtp; *instance = pvt->media[SIP_MEDIA_AUDIO]->rtp;
......
...@@ -161,6 +161,9 @@ static void t38_change_state(struct ast_sip_session *session, struct ast_sip_ses ...@@ -161,6 +161,9 @@ static void t38_change_state(struct ast_sip_session *session, struct ast_sip_ses
parameters.max_ifp = ast_udptl_get_far_max_ifp(session_media->udptl); parameters.max_ifp = ast_udptl_get_far_max_ifp(session_media->udptl);
parameters.request_response = AST_T38_REQUEST_NEGOTIATE; parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
ast_udptl_set_tag(session_media->udptl, "%s", ast_channel_name(session->channel)); ast_udptl_set_tag(session_media->udptl, "%s", ast_channel_name(session->channel));
/* Inform the bridge the channel is in that it needs to be reconfigured */
ast_channel_set_unbridged(session->channel, 1);
break; break;
case T38_ENABLED: case T38_ENABLED:
parameters = state->their_parms; parameters = state->their_parms;
...@@ -177,7 +180,8 @@ static void t38_change_state(struct ast_sip_session *session, struct ast_sip_ses ...@@ -177,7 +180,8 @@ static void t38_change_state(struct ast_sip_session *session, struct ast_sip_ses
} }
break; break;
case T38_LOCAL_REINVITE: case T38_LOCAL_REINVITE:
/* wait until we get a peer response before responding to local reinvite */ /* Inform the bridge the channel is in that it needs to be reconfigured */
ast_channel_set_unbridged(session->channel, 1);
break; break;
case T38_MAX_ENUM: case T38_MAX_ENUM:
/* Well, that shouldn't happen */ /* Well, that shouldn't happen */
...@@ -463,6 +467,11 @@ static void t38_masq(void *data, int framehook_id, ...@@ -463,6 +467,11 @@ static void t38_masq(void *data, int framehook_id,
ast_framehook_detach(new_chan, framehook_id); ast_framehook_detach(new_chan, framehook_id);
} }
static int t38_consume(void *data, enum ast_frame_type type)
{
return 0;
}
static const struct ast_datastore_info t38_framehook_datastore = { static const struct ast_datastore_info t38_framehook_datastore = {
.type = "T38 framehook", .type = "T38 framehook",
}; };
...@@ -475,6 +484,7 @@ static void t38_attach_framehook(struct ast_sip_session *session) ...@@ -475,6 +484,7 @@ static void t38_attach_framehook(struct ast_sip_session *session)
static struct ast_framehook_interface hook = { static struct ast_framehook_interface hook = {
.version = AST_FRAMEHOOK_INTERFACE_VERSION, .version = AST_FRAMEHOOK_INTERFACE_VERSION,
.event_cb = t38_framehook, .event_cb = t38_framehook,
.consume_cb = t38_consume,
.chan_fixup_cb = t38_masq, .chan_fixup_cb = t38_masq,
.chan_breakdown_cb = t38_masq, .chan_breakdown_cb = t38_masq,
}; };
...@@ -560,6 +570,41 @@ static struct ast_sip_session_supplement t38_supplement = { ...@@ -560,6 +570,41 @@ static struct ast_sip_session_supplement t38_supplement = {
.outgoing_request = t38_outgoing_invite_request, .outgoing_request = t38_outgoing_invite_request,
}; };
static int t38_incoming_bye_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
{
struct ast_datastore *datastore;
struct ast_sip_session_media *session_media;
if (!session->channel) {
return 0;
}
datastore = ast_sip_session_get_datastore(session, "t38");
if (!datastore) {
return 0;
}
session_media = ao2_find(session->media, "image", OBJ_KEY);
if (!session_media) {
ao2_ref(datastore, -1);
return 0;
}
t38_change_state(session, session_media, datastore->data, T38_REJECTED);
ao2_ref(datastore, -1);
ao2_ref(session_media, -1);
return 0;
}
/*! \brief Supplement for handling a remote termination of T.38 state */
static struct ast_sip_session_supplement t38_bye_supplement = {
.method = "BYE",
.priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL + 1,
.incoming_request = t38_incoming_bye_request,
};
/*! \brief Parse a T.38 image stream and store the attribute information */ /*! \brief Parse a T.38 image stream and store the attribute information */
static void t38_interpret_sdp(struct t38_state *state, struct ast_sip_session *session, struct ast_sip_session_media *session_media, static void t38_interpret_sdp(struct t38_state *state, struct ast_sip_session *session, struct ast_sip_session_media *session_media,
const struct pjmedia_sdp_media *stream) const struct pjmedia_sdp_media *stream)
...@@ -889,6 +934,7 @@ static int unload_module(void) ...@@ -889,6 +934,7 @@ static int unload_module(void)
{ {
ast_sip_session_unregister_sdp_handler(&image_sdp_handler, "image"); ast_sip_session_unregister_sdp_handler(&image_sdp_handler, "image");
ast_sip_session_unregister_supplement(&t38_supplement); ast_sip_session_unregister_supplement(&t38_supplement);
ast_sip_session_unregister_supplement(&t38_bye_supplement);
return 0; return 0;
} }
...@@ -915,6 +961,11 @@ static int load_module(void) ...@@ -915,6 +961,11 @@ static int load_module(void)
goto end; goto end;
} }
if (ast_sip_session_register_supplement(&t38_bye_supplement)) {
ast_log(LOG_ERROR, "Unable to register T.38 BYE session supplement\n");
goto end;
}
if (ast_sip_session_register_sdp_handler(&image_sdp_handler, "image")) { if (ast_sip_session_register_sdp_handler(&image_sdp_handler, "image")) {
ast_log(LOG_ERROR, "Unable to register SDP handler for image stream type\n"); ast_log(LOG_ERROR, "Unable to register SDP handler for image stream type\n");
goto end; goto end;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment