diff options
author | pcadach <pcadach@f38db490-d61c-443f-a65b-d21fe96a405b> | 2006-09-28 10:41:38 +0000 |
---|---|---|
committer | pcadach <pcadach@f38db490-d61c-443f-a65b-d21fe96a405b> | 2006-09-28 10:41:38 +0000 |
commit | 4ac9afef329d9525cda5dd82a8af1e4af0b054bf (patch) | |
tree | 549d5d4dee7804215437b180a6be243074532c9b | |
parent | 732c0b576358d2e9fa5544274a73b23bdccf6476 (diff) |
Handle HOLD/RETRIEVE notifications
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@43845 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r-- | CHANGES | 2 | ||||
-rw-r--r-- | channels/chan_h323.c | 61 | ||||
-rw-r--r-- | channels/h323/ast_h323.cxx | 103 | ||||
-rw-r--r-- | channels/h323/ast_h323.h | 23 | ||||
-rw-r--r-- | channels/h323/chan_h323.h | 12 | ||||
-rw-r--r-- | configs/h323.conf.sample | 15 |
6 files changed, 208 insertions, 8 deletions
@@ -15,3 +15,5 @@ Changes since Asterisk 1.4-beta was branched: * Ability to use libcap to set high ToS bits when non-root on Linux. If configure is unable to find libcap then you can use --with-cap to specify the path. + * H323 remote hold notification support added (by NOTIFY message + and/or H.450 supplementary service) diff --git a/channels/chan_h323.c b/channels/chan_h323.c index 0fad86ffa..836093f44 100644 --- a/channels/chan_h323.c +++ b/channels/chan_h323.c @@ -117,6 +117,7 @@ rfc2833_cb on_set_rfc2833_payload; hangup_cb on_hangup; setcapabilities_cb on_setcapabilities; setpeercapabilities_cb on_setpeercapabilities; +onhold_cb on_hold; /* global debug flag */ int h323debug; @@ -866,7 +867,7 @@ static int oh323_indicate(struct ast_channel *c, int condition, const void *data ast_mutex_unlock(&pvt->lock); if (h323debug) - ast_log(LOG_DEBUG, "OH323: Indicating %d on %s\n", condition, token); + ast_log(LOG_DEBUG, "OH323: Indicating %d on %s (%s)\n", condition, token, c->name); switch(condition) { case AST_CONTROL_RINGING: @@ -910,11 +911,14 @@ static int oh323_indicate(struct ast_channel *c, int condition, const void *data free(token); return -1; case AST_CONTROL_HOLD: + h323_hold_call(token, 1); + /* We should start MOH only if remote party isn't provide audio for us */ ast_moh_start(c, data, NULL); if (token) free(token); return 0; case AST_CONTROL_UNHOLD: + h323_hold_call(token, 0); ast_moh_stop(c); if (token) free(token); @@ -1338,6 +1342,17 @@ static int update_common_options(struct ast_variable *v, struct call_options *op options->tunnelOptions |= H323_TUNNEL_QSIG; else ast_log(LOG_WARNING, "Invalid value %s for %s at line %d\n", v->value, v->name, v->lineno); + } else if (!strcasecmp(v->name, "hold")) { + if (!strcasecmp(v->value, "none")) + options->holdHandling = ~0; + else if (!strcasecmp(v->value, "notify")) + options->holdHandling |= H323_HOLD_NOTIFY; + else if (!strcasecmp(v->value, "q931only")) + options->holdHandling |= H323_HOLD_NOTIFY | H323_HOLD_Q931ONLY; + else if (!strcasecmp(v->value, "h450")) + options->holdHandling |= H323_HOLD_H450; + else + ast_log(LOG_WARNING, "Invalid value %s for %s at line %d\n", v->value, v->name, v->lineno); } else return 1; @@ -1364,6 +1379,7 @@ static struct oh323_user *build_user(char *name, struct ast_variable *v, struct user->ha = (struct ast_ha *)NULL; memcpy(&user->options, &global_options, sizeof(user->options)); user->options.dtmfmode = 0; + user->options.holdHandling = 0; /* Set default context */ strncpy(user->context, default_context, sizeof(user->context) - 1); if (user && !found) @@ -1410,6 +1426,10 @@ static struct oh323_user *build_user(char *name, struct ast_variable *v, struct } if (!user->options.dtmfmode) user->options.dtmfmode = global_options.dtmfmode; + if (user->options.holdHandling == ~0) + user->options.holdHandling = 0; + else if (!user->options.holdHandling) + user->options.holdHandling = global_options.holdHandling; ASTOBJ_UNMARK(user); ast_free_ha(oldha); return user; @@ -1472,6 +1492,7 @@ static struct oh323_peer *build_peer(const char *name, struct ast_variable *v, s peer->ha = NULL; memcpy(&peer->options, &global_options, sizeof(peer->options)); peer->options.dtmfmode = 0; + peer->options.holdHandling = 0; peer->addr.sin_port = htons(h323_signalling_port); peer->addr.sin_family = AF_INET; if (!found && name) @@ -1511,6 +1532,10 @@ static struct oh323_peer *build_peer(const char *name, struct ast_variable *v, s } if (!peer->options.dtmfmode) peer->options.dtmfmode = global_options.dtmfmode; + if (peer->options.holdHandling == ~0) + peer->options.holdHandling = 0; + else if (!peer->options.holdHandling) + peer->options.holdHandling = global_options.holdHandling; ASTOBJ_UNMARK(peer); ast_free_ha(oldha); return peer; @@ -2450,6 +2475,32 @@ static void set_local_capabilities(unsigned call_reference, const char *token) ast_log(LOG_DEBUG, "Capabilities for connection %s is set\n", token); } +static void remote_hold(unsigned call_reference, const char *token, int is_hold) +{ + struct oh323_pvt *pvt; + + if (h323debug) + ast_log(LOG_DEBUG, "Setting %shold status for connection %s\n", (is_hold ? "" : "un"), token); + + pvt = find_call_locked(call_reference, token); + if (!pvt) + return; + if (pvt->owner && !ast_channel_trylock(pvt->owner)) { + if (is_hold) + ast_queue_control(pvt->owner, AST_CONTROL_HOLD); + else + ast_queue_control(pvt->owner, AST_CONTROL_UNHOLD); + ast_channel_unlock(pvt->owner); + } + else { + if (is_hold) + pvt->newcontrol = AST_CONTROL_HOLD; + else + pvt->newcontrol = AST_CONTROL_UNHOLD; + } + ast_mutex_unlock(&pvt->lock); +} + static void *do_monitor(void *data) { int res; @@ -2767,6 +2818,7 @@ static int reload_config(int is_reload) global_options.dtmfcodec[0] = H323_DTMF_RFC2833_PT; global_options.dtmfcodec[1] = H323_DTMF_CISCO_PT; global_options.dtmfmode = 0; + global_options.holdHandling = 0; global_options.capability = GLOBAL_CAPABILITY; global_options.bridge = 1; /* Do native bridging by default */ strncpy(default_context, "default", sizeof(default_context) - 1); @@ -2861,6 +2913,10 @@ static int reload_config(int is_reload) } if (!global_options.dtmfmode) global_options.dtmfmode = H323_DTMF_RFC2833; + if (global_options.holdHandling == ~0) + global_options.holdHandling = 0; + else if (!global_options.holdHandling) + global_options.holdHandling = H323_HOLD_H450; for (cat = ast_category_browse(cfg, NULL); cat; cat = ast_category_browse(cfg, cat)) { if (strcasecmp(cat, "general")) { @@ -3175,7 +3231,8 @@ static enum ast_module_load_result load_module(void) set_dtmf_payload, hangup_connection, set_local_capabilities, - set_peer_capabilities); + set_peer_capabilities, + remote_hold); /* start the h.323 listener */ if (h323_start_listener(h323_signalling_port, bindaddr)) { ast_log(LOG_ERROR, "Unable to create H323 listener.\n"); diff --git a/channels/h323/ast_h323.cxx b/channels/h323/ast_h323.cxx index be22ca64a..826be727b 100644 --- a/channels/h323/ast_h323.cxx +++ b/channels/h323/ast_h323.cxx @@ -40,6 +40,12 @@ #include <h323neg.h> #include <mediafmt.h> #include <lid.h> +#ifdef H323_H450 +#include "h4501.h" +#include "h4504.h" +#include "h45011.h" +#include "h450pdu.h" +#endif #ifdef __cplusplus extern "C" { @@ -528,10 +534,20 @@ MyH323Connection::MyH323Connection(MyH323EndPoint & ep, unsigned callReference, unsigned options) : H323Connection(ep, callReference, options) { +#ifdef H323_H450 + /* Dispatcher will free out all registered handlers */ + if (h450dispatcher) + delete h450dispatcher; + h450dispatcher = new H450xDispatcher(*this); + h4502handler = new H4502Handler(*this, *h450dispatcher); + h4504handler = new MyH4504Handler(*this, *h450dispatcher); + h4506handler = new H4506Handler(*this, *h450dispatcher); + h45011handler = new H45011Handler(*this, *h450dispatcher); +#endif cause = -1; sessionId = 0; bridging = FALSE; - progressSetup = progressAlert = 0; + holdHandling = progressSetup = progressAlert = 0; dtmfMode = 0; dtmfCodec[0] = dtmfCodec[1] = (RTP_DataFrame::PayloadTypes)0; redirect_reason = -1; @@ -664,6 +680,7 @@ void MyH323Connection::SetCallOptions(void *o, BOOL isIncoming) progressSetup = opts->progress_setup; progressAlert = opts->progress_alert; + holdHandling = opts->holdHandling; dtmfCodec[0] = (RTP_DataFrame::PayloadTypes)opts->dtmfcodec[0]; dtmfCodec[1] = (RTP_DataFrame::PayloadTypes)opts->dtmfcodec[1]; dtmfMode = opts->dtmfmode; @@ -1641,6 +1658,48 @@ BOOL MyH323Connection::StartControlChannel(const H225_TransportAddress & h245Add return TRUE; } +#ifdef H323_H450 +void MyH323Connection::OnReceivedLocalCallHold(int linkedId) +{ + if (on_hold) + on_hold(GetCallReference(), (const char *)GetCallToken(), 1); +} + +void MyH323Connection::OnReceivedLocalCallRetrieve(int linkedId) +{ + if (on_hold) + on_hold(GetCallReference(), (const char *)GetCallToken(), 0); +} +#endif + +void MyH323Connection::MyHoldCall(BOOL isHold) +{ + if (((holdHandling & H323_HOLD_NOTIFY) != 0) || ((holdHandling & H323_HOLD_Q931ONLY) != 0)) { + PBYTEArray x ((const BYTE *)(isHold ? "\xF9" : "\xFA"), 1); + H323SignalPDU signal; + signal.BuildNotify(*this); + signal.GetQ931().SetIE((Q931::InformationElementCodes)39 /* Q931::NotifyIE */, x); + if (h323debug) + cout << "Sending " << (isHold ? "HOLD" : "RETRIEVE") << " notification: " << signal << endl; + if ((holdHandling & H323_HOLD_Q931ONLY) != 0) { + PBYTEArray rawData; + signal.GetQ931().RemoveIE(Q931::UserUserIE); + signal.GetQ931().Encode(rawData); + signallingChannel->WritePDU(rawData); + } else + WriteSignalPDU(signal); + } +#ifdef H323_H450 + if ((holdHandling & H323_HOLD_H450) != 0) { + if (isHold) + h4504handler->HoldCall(TRUE); + else if (IsLocalHold()) + h4504handler->RetrieveCall(); + } +#endif +} + + /* MyH323_ExternalRTPChannel */ MyH323_ExternalRTPChannel::MyH323_ExternalRTPChannel(MyH323Connection & connection, const H323Capability & capability, @@ -1722,6 +1781,32 @@ BOOL MyH323_ExternalRTPChannel::OnReceivedAckPDU(const H245_H2250LogicalChannelA return FALSE; } +#ifdef H323_H450 +MyH4504Handler::MyH4504Handler(MyH323Connection &_conn, H450xDispatcher &_disp) + :H4504Handler(_conn, _disp) +{ + conn = &_conn; +} + +void MyH4504Handler::OnReceivedLocalCallHold(int linkedId) +{ + if (conn) { + conn->Lock(); + conn->OnReceivedLocalCallHold(linkedId); + conn->Unlock(); + } +} + +void MyH4504Handler::OnReceivedLocalCallRetrieve(int linkedId) +{ + if (conn) { + conn->Lock(); + conn->OnReceivedLocalCallRetrieve(linkedId); + conn->Unlock(); + } +} +#endif + /** IMPLEMENTATION OF C FUNCTIONS */ @@ -1780,7 +1865,8 @@ void h323_callback_register(setup_incoming_cb ifunc, rfc2833_cb dtmffunc, hangup_cb hangupfunc, setcapabilities_cb capabilityfunc, - setpeercapabilities_cb peercapabilityfunc) + setpeercapabilities_cb peercapabilityfunc, + onhold_cb holdfunc) { on_incoming_call = ifunc; on_outgoing_call = sfunc; @@ -1796,6 +1882,7 @@ void h323_callback_register(setup_incoming_cb ifunc, on_hangup = hangupfunc; on_setcapabilities = capabilityfunc; on_setpeercapabilities = peercapabilityfunc; + on_hold = holdfunc; } /** @@ -2097,6 +2184,18 @@ void h323_native_bridge(const char *token, const char *them, char *capability) } +int h323_hold_call(const char *token, int is_hold) +{ + MyH323Connection *conn = (MyH323Connection *)endPoint->FindConnectionWithLock(token); + if (!conn) { + cout << "ERROR: No connection found, this is bad" << endl; + return -1; + } + conn->MyHoldCall((BOOL)is_hold); + conn->Unlock(); + return 0; +} + #undef cout #undef endl void h323_end_process(void) diff --git a/channels/h323/ast_h323.h b/channels/h323/ast_h323.h index 7199b0f34..f933217b2 100644 --- a/channels/h323/ast_h323.h +++ b/channels/h323/ast_h323.h @@ -93,6 +93,11 @@ public: virtual BOOL HandleSignalPDU(H323SignalPDU &pdu); BOOL EmbedTunneledInfo(H323SignalPDU &pdu); #endif +#ifdef H323_H450 + virtual void OnReceivedLocalCallHold(int linkedId); + virtual void OnReceivedLocalCallRetrieve(int linkedId); +#endif + void MyHoldCall(BOOL localHold); PString sourceAliases; PString destAliases; @@ -108,6 +113,7 @@ public: int tunnelOptions; #endif + unsigned holdHandling; unsigned progressSetup; unsigned progressAlert; int cause; @@ -156,6 +162,23 @@ public: void Main(); }; +#ifdef H323_H450 +#include <h450pdu.h> + +class MyH4504Handler : public H4504Handler +{ + PCLASSINFO(MyH4504Handler, H4504Handler); + +public: + MyH4504Handler(MyH323Connection &_conn, H450xDispatcher &_disp); + virtual void OnReceivedLocalCallHold(int linkedId); + virtual void OnReceivedLocalCallRetrieve(int linkedId); + +private: + MyH323Connection *conn; +}; +#endif + #include "compat_h323.h" #endif /* !defined AST_H323_H */ diff --git a/channels/h323/chan_h323.h b/channels/h323/chan_h323.h index 13296ed1a..c8bd6a3d9 100644 --- a/channels/h323/chan_h323.h +++ b/channels/h323/chan_h323.h @@ -39,6 +39,10 @@ #define H323_TUNNEL_CISCO (1 << 0) #define H323_TUNNEL_QSIG (1 << 1) +#define H323_HOLD_NOTIFY (1 << 0) +#define H323_HOLD_Q931ONLY (1 << 1) +#define H323_HOLD_H450 (1 << 2) + /** call_option struct holds various bits * of information for each call */ typedef struct call_options { @@ -58,6 +62,7 @@ typedef struct call_options { int bridge; int nat; int tunnelOptions; + int holdHandling; struct ast_codec_pref prefs; } call_options_t; @@ -184,6 +189,9 @@ extern setcapabilities_cb on_setcapabilities; typedef void (*setpeercapabilities_cb)(unsigned, const char *, int, struct ast_codec_pref *); extern setpeercapabilities_cb on_setpeercapabilities; +typedef void (*onhold_cb)(unsigned, const char *, int); +extern onhold_cb on_hold; + /* debug flag */ extern int h323debug; @@ -224,7 +232,8 @@ extern "C" { rfc2833_cb, hangup_cb, setcapabilities_cb, - setpeercapabilities_cb); + setpeercapabilities_cb, + onhold_cb); int h323_set_capabilities(const char *, int, int, struct ast_codec_pref *, int); int h323_set_alias(struct oh323_alias *); int h323_set_gk(int, char *, char *); @@ -249,6 +258,7 @@ extern "C" { int h323_answering_call(const char *token, int); int h323_soft_hangup(const char *data); int h323_show_codec(int fd, int argc, char *argv[]); + int h323_hold_call(const char *token, int); #ifdef __cplusplus } diff --git a/configs/h323.conf.sample b/configs/h323.conf.sample index ef8ff3004..e27f1d5de 100644 --- a/configs/h323.conf.sample +++ b/configs/h323.conf.sample @@ -104,9 +104,18 @@ port = 1720 ; example, for Cisco CallManager when Q.SIG tunneling is enabled for a ; gateway where Asterisk lives. ; The option can be used multiple times, one option per line. -;tunneling=none ; Totally disable tunneling (default) -;tunneling=cisco ; Enable Cisco-specific tunneling -;tunneling=qsig ; Enable tunneling via Q.SIG messages +;tunneling=none ; Totally disable tunneling (default) +;tunneling=cisco ; Enable Cisco-specific tunneling +;tunneling=qsig ; Enable tunneling via Q.SIG messages +; +; Specify how to pass hold notification to remote party. Default is to +; use H.450.4 supplementary service message. +;hold=none ; Do not pass hold/retrieve notifications +;hold=notify ; Use H.225 NOTIFY message +;hold=q931only ; Use stripped H.225 NOTIFY message (Q.931 part +; ; only, usable for Cisco CallManager) +;hold=h450 ; Pass notification as H.450.4 supplementary +; ; service ; ;------------------------------ JITTER BUFFER CONFIGURATION -------------------------- ; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of a |