[asterisk-commits] file: trunk r103799 - in /trunk: channels/ include/asterisk/ main/
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Mon Feb 18 17:47:02 CST 2008
Author: file
Date: Mon Feb 18 17:47:01 2008
New Revision: 103799
URL: http://svn.digium.com/view/asterisk?view=rev&rev=103799
Log:
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)
Modified:
trunk/channels/chan_sip.c
trunk/include/asterisk/channel.h
trunk/include/asterisk/frame.h
trunk/main/channel.c
trunk/main/frame.c
Modified: trunk/channels/chan_sip.c
URL: http://svn.digium.com/view/asterisk/trunk/channels/chan_sip.c?view=diff&rev=103799&r1=103798&r2=103799
==============================================================================
--- trunk/channels/chan_sip.c (original)
+++ trunk/channels/chan_sip.c Mon Feb 18 17:47:01 2008
@@ -1689,6 +1689,7 @@
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 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 @@
.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,
};
@@ -3080,6 +3083,53 @@
res = (reliable) ?
__sip_reliable_xmit(p, seqno, 0, req->data, req->len, (reliable == XMIT_CRITICAL), req->method) :
__sip_xmit(p, req->data, req->len);
+ 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;
}
@@ -3771,6 +3821,37 @@
ast_debug(1, "Setting NAT on TRTP to %s\n", mode);
ast_rtp_setnat(p->trtp, 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 */
@@ -4741,8 +4822,7 @@
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);
@@ -5026,6 +5106,26 @@
/* ast_rtcp_send_h261fur(p->vrtp); */
} 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;
@@ -5459,9 +5559,8 @@
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,23 +6726,20 @@
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;
- }
-
- ast_debug(3, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
+ change_t38_state(p, T38_DISABLED);
+ }
/* Now gather all of the codecs that we are asked for: */
ast_rtp_get_current_formats(newaudiortp, &peercapability, &peernoncodeccapability);
@@ -14438,23 +14534,19 @@
} 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 @@
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 @@
/* 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 @@
} 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 @@
} 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 @@
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 @@
} 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 @@
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);
Modified: trunk/include/asterisk/channel.h
URL: http://svn.digium.com/view/asterisk/trunk/include/asterisk/channel.h?view=diff&rev=103799&r1=103798&r2=103799
==============================================================================
--- trunk/include/asterisk/channel.h (original)
+++ trunk/include/asterisk/channel.h Mon Feb 18 17:47:01 2008
@@ -387,6 +387,17 @@
AST_STATE_PRERING, /*!< Channel has detected an incoming call and is waiting for ring */
AST_STATE_MUTE = (1 << 16), /*!< Do not transmit voice data */
+};
+
+/*!
+ * \brief Possible T38 states on channels
+ */
+enum ast_t38_state {
+ T38_STATE_UNAVAILABLE, /*!< T38 is unavailable on this channel or disabled by configuration */
+ T38_STATE_UNKNOWN, /*!< The channel supports T38 but the current status is unknown */
+ T38_STATE_NEGOTIATING, /*!< T38 is being negotiated */
+ T38_STATE_REJECTED, /*!< Remote side has rejected our offer */
+ T38_STATE_NEGOTIATED, /*!< T38 established */
};
/*! \brief Main Channel structure associated with a channel.
@@ -1300,10 +1311,10 @@
/*! Checks the value of an option */
/*!
- * Query the value of an option, optionally blocking until a reply is received
+ * Query the value of an option
* Works similarly to setoption except only reads the options.
*/
-struct ast_frame *ast_channel_queryoption(struct ast_channel *channel, int option, void *data, int *datalen, int block);
+int ast_channel_queryoption(struct ast_channel *channel, int option, void *data, int *datalen, int block);
/*! Checks for HTML support on a channel */
/*! Returns 0 if channel does not support HTML or non-zero if it does */
@@ -1557,6 +1568,18 @@
#endif
}
+/*! \brief Retrieves the current T38 state of a channel */
+static inline enum ast_t38_state ast_channel_get_t38_state(struct ast_channel *chan)
+{
+ enum ast_t38_state state = T38_STATE_UNAVAILABLE;
+ int datalen = sizeof(state);
+
+ ast_channel_queryoption(chan, AST_OPTION_T38_STATE, &state, &datalen, 0);
+
+ return state;
+}
+
+
#ifdef DO_CRASH
#define CRASH do { fprintf(stderr, "!! Forcing immediate crash a-la abort !!\n"); *((int *)0) = 0; } while(0)
#else
Modified: trunk/include/asterisk/frame.h
URL: http://svn.digium.com/view/asterisk/trunk/include/asterisk/frame.h?view=diff&rev=103799&r1=103798&r2=103799
==============================================================================
--- trunk/include/asterisk/frame.h (original)
+++ trunk/include/asterisk/frame.h Mon Feb 18 17:47:01 2008
@@ -292,6 +292,15 @@
AST_CONTROL_HOLD = 16, /*!< Indicate call is placed on hold */
AST_CONTROL_UNHOLD = 17, /*!< Indicate call is left from hold */
AST_CONTROL_VIDUPDATE = 18, /*!< Indicate video frame update */
+ AST_CONTROL_T38 = 19 /*!< T38 state change request/notification */
+};
+
+enum ast_control_t38 {
+ AST_T38_REQUEST_NEGOTIATE = 1, /*!< Request T38 on a channel (voice to fax) */
+ AST_T38_REQUEST_TERMINATE, /*!< Terminate T38 on a channel (fax to voice) */
+ AST_T38_NEGOTIATED, /*!< T38 negotiated (fax mode) */
+ AST_T38_TERMINATED, /*!< T38 terminated (back to voice) */
+ AST_T38_REFUSED /*!< T38 refused for some reason (usually rejected by remote end) */
};
#define AST_SMOOTHER_FLAG_G729 (1 << 0)
@@ -339,6 +348,12 @@
/*! Explicitly enable or disable echo cancelation for the given channel */
#define AST_OPTION_ECHOCAN 8
+
+/* !
+ * Read-only. Allows query current status of T38 on the channel.
+ * data: ast_t38state
+ */
+#define AST_OPTION_T38_STATE 10
struct oprmode {
struct ast_channel *peer;
Modified: trunk/main/channel.c
URL: http://svn.digium.com/view/asterisk/trunk/main/channel.c?view=diff&rev=103799&r1=103798&r2=103799
==============================================================================
--- trunk/main/channel.c (original)
+++ trunk/main/channel.c Mon Feb 18 17:47:01 2008
@@ -4481,23 +4481,28 @@
/*! \brief Sets an option on a channel */
int ast_channel_setoption(struct ast_channel *chan, int option, void *data, int datalen, int block)
{
- int res;
-
- if (chan->tech->setoption) {
- res = chan->tech->setoption(chan, option, data, datalen);
- if (res < 0)
- return res;
- } else {
+ if (!chan->tech->setoption) {
errno = ENOSYS;
return -1;
}
- if (block) {
- /* XXX Implement blocking -- just wait for our option frame reply, discarding
- intermediate packets. XXX */
+
+ if (block)
ast_log(LOG_ERROR, "XXX Blocking not implemented yet XXX\n");
+
+ return chan->tech->setoption(chan, option, data, datalen);
+}
+
+int ast_channel_queryoption(struct ast_channel *chan, int option, void *data, int *datalen, int block)
+{
+ if (!chan->tech->queryoption) {
+ errno = ENOSYS;
return -1;
}
- return 0;
+
+ if (block)
+ ast_log(LOG_ERROR, "XXX Blocking not implemented yet XXX\n");
+
+ return chan->tech->queryoption(chan, option, data, datalen);
}
struct tonepair_def {
Modified: trunk/main/frame.c
URL: http://svn.digium.com/view/asterisk/trunk/main/frame.c?view=diff&rev=103799&r1=103798&r2=103799
==============================================================================
--- trunk/main/frame.c (original)
+++ trunk/main/frame.c Mon Feb 18 17:47:01 2008
@@ -710,6 +710,7 @@
char cn[60];
char cp[40];
char cmn[40];
+ const char *message = "Unknown";
if (!name)
name = noname;
@@ -785,6 +786,24 @@
break;
case AST_CONTROL_UNHOLD:
strcpy(subclass, "Unhold");
+ break;
+ case AST_CONTROL_T38:
+ if (f->datalen != sizeof(enum ast_control_t38)) {
+ message = "Invalid";
+ } else {
+ enum ast_control_t38 state = *((enum ast_control_t38 *) f->data);
+ if (state == AST_T38_REQUEST_NEGOTIATE)
+ message = "Negotiation Requested";
+ else if (state == AST_T38_REQUEST_TERMINATE)
+ message = "Negotiation Request Terminated";
+ else if (state == AST_T38_NEGOTIATED)
+ message = "Negotiated";
+ else if (state == AST_T38_TERMINATED)
+ message = "Terminated";
+ else if (state == AST_T38_REFUSED)
+ message = "Refused";
+ }
+ snprintf(subclass, sizeof(subclass), "T38/%s", message);
break;
case -1:
strcpy(subclass, "Stop generators");
More information about the asterisk-commits
mailing list