diff options
author | Joshua Colp <jcolp@digium.com> | 2008-02-18 23:47:01 +0000 |
---|---|---|
committer | Joshua Colp <jcolp@digium.com> | 2008-02-18 23:47:01 +0000 |
commit | e54da94808472d0a35b3bd51f07e2f102210b95e (patch) | |
tree | b5f0bd047d53611a89ec0394a3c4efec497c9365 /channels/chan_sip.c | |
parent | 6c3a7a3e516dee84fde9fe8dce2d827a50079574 (diff) |
Add a non-invasive API for application level manipulation of T38 on a channel. This uses control frames (so they can even pass across IAX2) to negotiate T38 and provided a way of getting the current status of T38 using queryoption. This should by no means cause any issues and if it does I will take responsibility for it.
(closes issue #11873)
Reported by: dimas
Patches:
v4-t38-api.patch uploaded by dimas (license 88)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@103799 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'channels/chan_sip.c')
-rw-r--r-- | channels/chan_sip.c | 152 |
1 files changed, 119 insertions, 33 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 43ed8648d..54e233aa8 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -1689,6 +1689,7 @@ static int sip_transfer(struct ast_channel *ast, const char *dest); static int sip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); static int sip_senddigit_begin(struct ast_channel *ast, char digit); static int sip_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration); +static int sip_queryoption(struct ast_channel *chan, int option, void *data, int *datalen); static const char *sip_get_callid(struct ast_channel *chan); static int handle_request_do(struct sip_request *req, struct sockaddr_in *sin); @@ -2009,6 +2010,7 @@ static int sip_handle_t38_reinvite(struct ast_channel *chan, struct sip_pvt *pvt static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans); static struct ast_udptl *sip_get_udptl_peer(struct ast_channel *chan); static int sip_set_udptl_peer(struct ast_channel *chan, struct ast_udptl *udptl); +static void change_t38_state(struct sip_pvt *p, int state); /*------ Session-Timers functions --------- */ static void proc_422_rsp(struct sip_pvt *p, struct sip_request *rsp); @@ -2050,6 +2052,7 @@ static const struct ast_channel_tech sip_tech = { .early_bridge = ast_rtp_early_bridge, .send_text = sip_sendtext, /* called with chan locked */ .func_channel_read = acf_channel_read, + .queryoption = sip_queryoption, .get_pvt_uniqueid = sip_get_callid, }; @@ -3083,6 +3086,53 @@ static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittyp return res; } +/*! \brief Query an option on a SIP dialog */ +static int sip_queryoption(struct ast_channel *chan, int option, void *data, int *datalen) +{ + int res = -1; + enum ast_t38_state state = T38_STATE_UNAVAILABLE; + struct sip_pvt *p = (struct sip_pvt *) chan->tech_pvt; + + switch (option) { + case AST_OPTION_T38_STATE: + /* Make sure we got an ast_t38_state enum passed in */ + if (*datalen != sizeof(enum ast_t38_state)) { + ast_log(LOG_ERROR, "Invalid datalen for AST_OPTION_T38_STATE option. Expected %d, got %d\n", (int)sizeof(enum ast_t38_state), *datalen); + return -1; + } + + sip_pvt_lock(p); + + /* Now if T38 support is enabled we need to look and see what the current state is to get what we want to report back */ + if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT)) { + switch (p->t38.state) { + case T38_LOCAL_DIRECT: + case T38_LOCAL_REINVITE: + case T38_PEER_DIRECT: + case T38_PEER_REINVITE: + state = T38_STATE_NEGOTIATING; + break; + case T38_ENABLED: + state = T38_STATE_NEGOTIATED; + break; + default: + state = T38_STATE_UNKNOWN; + } + } + + sip_pvt_unlock(p); + + *((enum ast_t38_state *) data) = state; + res = 0; + + break; + default: + break; + } + + return res; +} + /*! \brief Locate closing quote in a string, skipping escaped quotes. * optionally with a limit on the search. * start must be past the first quote. @@ -3773,6 +3823,37 @@ static void do_setnat(struct sip_pvt *p, int natflags) } } +/*! \brief Change the T38 state on a SIP dialog */ +static void change_t38_state(struct sip_pvt *p, int state) +{ + int old = p->t38.state; + struct ast_channel *chan = p->owner; + enum ast_control_t38 message = 0; + + /* Don't bother changing if we are already in the state wanted */ + if (old == state) + return; + + p->t38.state = state; + ast_debug(2, "T38 state changed to %d on channel %s\n", p->t38.state, chan ? chan->name : "<none>"); + + /* If no channel was provided we can't send off a control frame */ + if (!chan) + return; + + /* Given the state requested and old state determine what control frame we want to queue up */ + if (state == T38_ENABLED) + message = AST_T38_NEGOTIATED; + else if (state == T38_DISABLED && old == T38_ENABLED) + message = AST_T38_TERMINATED; + else if (state == T38_DISABLED && old == T38_LOCAL_REINVITE) + message = AST_T38_REFUSED; + + /* Woot we got a message, create a control frame and send it on! */ + if (message) + ast_queue_control_data(chan, AST_CONTROL_T38, &message, sizeof(message)); +} + /*! \brief Set the global T38 capabilities on a SIP dialog structure */ static void set_t38_capabilities(struct sip_pvt *p) { @@ -4741,8 +4822,7 @@ static int sip_answer(struct ast_channel *ast) ast_setstate(ast, AST_STATE_UP); ast_debug(1, "SIP answering channel: %s\n", ast->name); if (p->t38.state == T38_PEER_DIRECT) { - p->t38.state = T38_ENABLED; - ast_debug(2,"T38State change to %d on channel %s\n", p->t38.state, ast->name); + change_t38_state(p, T38_ENABLED); res = transmit_response_with_t38_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL); } else res = transmit_response_with_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL, FALSE); @@ -5027,6 +5107,26 @@ static int sip_indicate(struct ast_channel *ast, int condition, const void *data } else res = -1; break; + case AST_CONTROL_T38: /* T38 control frame */ + if (datalen != sizeof(enum ast_control_t38)) { + ast_log(LOG_ERROR, "Invalid datalen for AST_CONTROL_T38. Expected %d, got %d\n", (int)sizeof(enum ast_control_t38), (int)datalen); + } else { + switch (*((enum ast_control_t38 *) data)) { + case AST_T38_REQUEST_NEGOTIATE: /* Request T38 */ + if (p->t38.state != T38_ENABLED) { + change_t38_state(p, T38_LOCAL_REINVITE); + transmit_reinvite_with_sdp(p, TRUE, FALSE); + } + break; + case AST_T38_REQUEST_TERMINATE: /* Shutdown T38 */ + if (p->t38.state == T38_ENABLED) + transmit_reinvite_with_sdp(p, FALSE, FALSE); + break; + default: + break; + } + } + break; case -1: res = -1; break; @@ -5459,9 +5559,8 @@ static struct ast_frame *sip_read(struct ast_channel *ast) if (!ast_test_flag(&p->flags[0], SIP_GOTREFER)) { if (!p->pendinginvite) { ast_debug(3, "Sending reinvite on SIP (%s) for T.38 negotiation.\n",ast->name); - p->t38.state = T38_LOCAL_REINVITE; + change_t38_state(p, T38_LOCAL_REINVITE); transmit_reinvite_with_sdp(p, TRUE, FALSE); - ast_debug(2, "T38 state changed to %d on channel %s\n", p->t38.state, ast->name); } } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) { ast_debug(3, "Deferring reinvite on SIP (%s) - it will be re-negotiated for T.38\n", ast->name); @@ -6627,24 +6726,21 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action p->t38.peercapability, p->t38.jointcapability); - /* Remote party offers T38, we need to update state */ if (t38action == SDP_T38_ACCEPT) { if (p->t38.state == T38_LOCAL_DIRECT || p->t38.state == T38_LOCAL_REINVITE) - p->t38.state = T38_ENABLED; + change_t38_state(p, T38_ENABLED); } else if (t38action == SDP_T38_INITIATE) { if (p->owner && p->lastinvite) { - p->t38.state = T38_PEER_REINVITE; /* T38 Offered in re-invite from remote party */ + change_t38_state(p, T38_PEER_REINVITE); /* T38 Offered in re-invite from remote party */ } else { - p->t38.state = T38_PEER_DIRECT; /* T38 Offered directly from peer in first invite */ + change_t38_state(p, T38_PEER_DIRECT); /* T38 Offered directly from peer in first invite */ } } } else { - p->t38.state = T38_DISABLED; + change_t38_state(p, T38_DISABLED); } - ast_debug(3, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>"); - /* Now gather all of the codecs that we are asked for: */ ast_rtp_get_current_formats(newaudiortp, &peercapability, &peernoncodeccapability); ast_rtp_get_current_formats(newvideortp, &vpeercapability, &vpeernoncodeccapability); @@ -14438,23 +14534,19 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru } else { ast_debug(2, "Strange... The other side of the bridge does not have a udptl struct\n"); sip_pvt_lock(bridgepvt); - bridgepvt->t38.state = T38_DISABLED; + change_t38_state(bridgepvt, T38_DISABLED); sip_pvt_unlock(bridgepvt); - ast_debug(1,"T38 state changed to %d on channel %s\n", bridgepvt->t38.state, bridgepeer->tech->type); - p->t38.state = T38_DISABLED; - ast_debug(2,"T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>"); + change_t38_state(p, T38_DISABLED); } } else { /* Other side is not a SIP channel */ ast_debug(2, "Strange... The other side of the bridge is not a SIP channel\n"); - p->t38.state = T38_DISABLED; - ast_debug(2,"T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>"); + change_t38_state(p, T38_DISABLED); } } if ((p->t38.state == T38_LOCAL_REINVITE) || (p->t38.state == T38_LOCAL_DIRECT)) { /* If there was T38 reinvite and we are supposed to answer with 200 OK than this should set us to T38 negotiated mode */ - p->t38.state = T38_ENABLED; - ast_debug(1, "T38 changed state to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>"); + change_t38_state(p, T38_ENABLED); } if (!req->ignore && p->owner) { @@ -14591,7 +14683,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru terribly wrong since we don't renegotiate codecs, only IP/port . */ - p->t38.state = T38_DISABLED; + change_t38_state(p, T38_DISABLED); /* Try to reset RTP timers */ ast_rtp_set_rtptimers_onhold(p->rtp); ast_log(LOG_ERROR, "Got error on T.38 re-invite. Bad configuration. Peer needs to have T.38 disabled.\n"); @@ -14607,11 +14699,11 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru /* We tried to send T.38 out in an initial INVITE and the remote side rejected it, right now we can't fall back to audio so totally abort. */ - p->t38.state = T38_DISABLED; /* Try to reset RTP timers */ ast_rtp_set_rtptimers_onhold(p->rtp); ast_log(LOG_ERROR, "Got error on T.38 initial invite. Bailing out.\n"); + change_t38_state(p, T38_DISABLED); /* The dialog is now terminated */ if (p->owner && !req->ignore) ast_queue_control(p->owner, AST_CONTROL_CONGESTION); @@ -16497,9 +16589,8 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int } else { /* Something is wrong with peers udptl struct */ ast_log(LOG_WARNING, "Strange... The other side of the bridge don't have udptl struct\n"); sip_pvt_lock(bridgepvt); - bridgepvt->t38.state = T38_DISABLED; + change_t38_state(bridgepvt, T38_DISABLED); sip_pvt_unlock(bridgepvt); - ast_debug(2,"T38 state changed to %d on channel %s\n", bridgepvt->t38.state, bridgepeer->name); if (req->ignore) transmit_response(p, "488 Not acceptable here", req); else @@ -16509,8 +16600,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int } else { /* The other side is already setup for T.38 most likely so we need to acknowledge this too */ transmit_response_with_t38_sdp(p, "200 OK", req, XMIT_CRITICAL); - p->t38.state = T38_ENABLED; - ast_debug(1, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>"); + change_t38_state(p, T38_ENABLED); } } else { /* Other side is not a SIP channel */ @@ -16518,8 +16608,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int transmit_response(p, "488 Not acceptable here", req); else transmit_response_reliable(p, "488 Not acceptable here", req); - p->t38.state = T38_DISABLED; - ast_debug(2,"T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>"); + change_t38_state(p, T38_DISABLED); if (!p->lastinvite) /* Only destroy if this is *not* a re-invite */ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); @@ -16527,8 +16616,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int } else { /* we are not bridged in a call */ transmit_response_with_t38_sdp(p, "200 OK", req, XMIT_CRITICAL); - p->t38.state = T38_ENABLED; - ast_debug(1,"T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>"); + change_t38_state(p, T38_ENABLED); } } else if (p->t38.state == T38_DISABLED) { /* Channel doesn't have T38 offered or enabled */ int sendok = TRUE; @@ -20711,10 +20799,8 @@ static int sip_handle_t38_reinvite(struct ast_channel *chan, struct sip_pvt *pvt ast_debug(3, "Responding 200 OK on SIP '%s' - It's UDPTL soon redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(p->udptlredirip.sin_addr), ntohs(p->udptlredirip.sin_port)); else ast_debug(3, "Responding 200 OK on SIP '%s' - It's UDPTL soon redirected to us (IP %s)\n", p->callid, ast_inet_ntoa(p->ourip.sin_addr)); - pvt->t38.state = T38_ENABLED; - p->t38.state = T38_ENABLED; - ast_debug(2, "T38 changed state to %d on channel %s\n", pvt->t38.state, pvt->owner ? pvt->owner->name : "<none>"); - ast_debug(2, "T38 changed state to %d on channel %s\n", p->t38.state, chan ? chan->name : "<none>"); + change_t38_state(pvt, T38_ENABLED); + change_t38_state(p, T38_ENABLED); transmit_response_with_t38_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL); p->lastrtprx = p->lastrtptx = time(NULL); sip_pvt_unlock(p); |