[asterisk-commits] oej: branch oej/teapot-1.8 r412030 - in /team/oej/teapot-1.8: ./ channels/ ch...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Wed Apr 9 04:50:26 CDT 2014
Author: oej
Date: Wed Apr 9 04:50:14 2014
New Revision: 412030
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=412030
Log:
Integrating silence suppression into teapot for testing
This code is from the branch roibos-cng-support-1.8
Added:
team/oej/teapot-1.8/README.roibos-cng.txt (with props)
team/oej/teapot-1.8/include/asterisk/silencedetection.h (with props)
team/oej/teapot-1.8/main/silencedetection.c (with props)
Modified:
team/oej/teapot-1.8/channels/chan_iax2.c
team/oej/teapot-1.8/channels/chan_sip.c
team/oej/teapot-1.8/channels/sip/include/sip.h
team/oej/teapot-1.8/configs/sip.conf.sample
team/oej/teapot-1.8/funcs/func_frame_trace.c
team/oej/teapot-1.8/include/asterisk/channel.h
team/oej/teapot-1.8/include/asterisk/frame.h
team/oej/teapot-1.8/main/audiohook.c
team/oej/teapot-1.8/main/channel.c
team/oej/teapot-1.8/main/features.c
team/oej/teapot-1.8/main/frame.c
team/oej/teapot-1.8/patches/roibos-cng-support-1.8.diff
team/oej/teapot-1.8/res/res_noise.c
team/oej/teapot-1.8/res/res_rtp_asterisk.c
Added: team/oej/teapot-1.8/README.roibos-cng.txt
URL: http://svnview.digium.com/svn/asterisk/team/oej/teapot-1.8/README.roibos-cng.txt?view=auto&rev=412030
==============================================================================
--- team/oej/teapot-1.8/README.roibos-cng.txt (added)
+++ team/oej/teapot-1.8/README.roibos-cng.txt Wed Apr 9 04:50:14 2014
@@ -1,0 +1,165 @@
+Edvina AB
+Olle E. Johansson
+
+
+Started: 2012-09-18
+Updated: 2014-04-07
+
+
+
+
+
+Silence Suppression and Comfort Noise support in Asterisk 1.8
+=============================================================
+
+Comfort Noise in SIP/RTP is
+- negotiated in the SDP as a codec
+- starts activated by a silence in the media stream
+- the sender stops sending media, sends a single CNG RTP packet that indicates
+ a noise level
+- the receiver activated a Comfort Noise Generator in the call until media
+ reappears from the sender
+
+A requirement for using this is that it is included as a codec with payload
+13 (or dynamic) in the SDP
+
+Asterisk Architecture
+=====================
+In a bridged call, where one end is SIP with CNG enabled, the RTP system
+will get an incoming CNG frame with a noise level. This will be sent
+over the bridge to the bridged channel.
+
+If that channel is SIP with CNG enabled for the call, the RTP system
+will send out a CNG frame. This is to enable forwarding a CNG frame
+across to another SIP device which now gets the responsibility to play out
+the noise.
+
+It that channel is a type that doesn't support CNG or SIP with CNG
+disabled, then Asterisk needs to generate noise in the bridged
+channel - not the SIP channel that received the CNG frame.
+
+Current state:
+==============
+
+* RTP Channel
+-------------
+
+- Asterisk RTP (res_rtp_asterisk.c) will read CNG packets and produce a warning.
+ These will be forwarded to the core.
+- CNG packets will be sent only as RTP keepalives
+
+* SIP Channel
+-------------
+- The SIP channel will negotiate any CNG support if offered and
+ offer CNG if configured. SIP.conf setting:
+ ;comfort-noise=yes ; Enable Comfort Noise generation on RTP streams
+ ; ; Available per device too
+
+* Core
+------
+- If a generator is active and CNG is received, Asterisk moves to timer based
+ generation of outbound packets
+- Comfort noise generator added to core
+- Comfort noise generator will be used when CNG frame is received, until the RTP
+ channel signals that CNG will end.
+
+Detecting Silence
+=================
+The current silence detector in Asterisk only supports signed linear audio.
+This means that for a g.729 call we have to transcode to signed linear, listen
+for audio and in some cases, but not all, transcode back.
+
+Later we have to
+- Add silence detection to the codec modules so they can signal silence
+ in an incoming stream to the core
+
+Debugging
+=========
+Place a call between one phone that supports CN (I've used a SNOM 820) and a
+phone that lacks support for it (or has it disabled for testing).
+- Turn on RTP debug and SIP debug.
+- Set core debug to 3.
+You will now see that Asterisk receives a CN RTP packet, and will activate
+the noise generator on the other channel. This happens many times during
+the call.
+
+Todo :: comfort noise support
+-----------------------------
+ - Check how this affects RTP bridge and queue bridge
+ - Add CN support in SDP for outbound calls
+
+Done:
+ - Support in core bridge
+ - For inbound streams, generate noise in calls (both inbound and outbound calls)
+ - Added res_noise.c from cmantunes from https://issues.asterisk.org/jira/browse/ASTERISK-5263
+ This includes a noise generator
+ - Add SIP negotiation in SDP - done
+ - Support CN codec on incoming INVITEs - done
+ - Silence detection and suppression added for SIP calls
+
+References
+----------
+
+- RFC 3389 http://tools.ietf.org/html/rfc3389
+- Appendix II to Recommendation G.711 (02/2000) - A comfort noise
+ payload definition for ITU-T G.711 use in packet-based
+ multimedia communication systems.
+
+
+Terms
+-----
+- DTX Discontinues Transmission capability
+- VAD Voice Activity Detection
+- CN Comfort Noise , http://en.wikipedia.org/wiki/Comfort_noise
+- CNG Comfort Noise Generator
+- Silence Suppression: http://en.wikipedia.org/wiki/Silence_suppression
+
+RTP Framing (RFC 3389 section 4)
+--------------------------------
+The RTP header for the comfort noise packet SHOULD be constructed as
+ if the comfort noise were an independent codec. Thus, the RTP
+ timestamp designates the beginning of the comfort noise period.
+
+At the beginning of
+ an inactive voice segment (silence period), a CN packet is
+ transmitted in the same RTP stream and indicated by the CN payload
+ type. The CN packet update rate is left implementation specific. For
+ example, the CN packet may be sent periodically or only when there is
+ a significant change in the background noise characteristics. The
+ CNG algorithm at the receiver uses the information in the CN payload
+ to update its noise generation model and then produce an appropriate
+ amount of comfort noise.
+
+Noise Level (RFC 3389 Section 3.1)
+----------------------------------
+The magnitude of the noise level is packed into the least significant
+ bits of the noise-level byte with the most significant bit unused and
+ always set to 0 as shown below in Figure 1. The least significant
+ bit of the noise level magnitude is packed into the least significant
+ bit of the byte.
+
+ The noise level is expressed in -dBov, with values from 0 to 127
+ representing 0 to -127 dBov. dBov is the level relative to the
+ overload of the system. (Note: Representation relative to the
+ overload point of a system is particularly useful for digital
+ implementations, since one does not need to know the relative
+ calibration of the analog circuitry.) For example, in the case of a
+ u-law system, the reference would be a square wave with values +/-
+ 8031, and this square wave represents 0dBov. This translates into
+ 6.18dBm0.
+
+-----------------
+Various notes:
+=============
+From file:
+
+The logic for determining if a native bridge can be performed or not lives in ast_channel_bridge in channel.c - there is an if statement with many conditions that have to be met before doing it. You can extend that and add another which is "if the CN support on channel A is the same as channel B then allow native bridge"
+
+Question: If I run RTP bridge (not the p2p or remote) can we still operate
+on timer? If not, I have to disable RTP bridging totally. If we rely on incoming
+packets (which will not happen) to send out, CN will not work.
+
+Yes, you can still operate on timer. The RTP bridge still has all the normal bridging logic in it. That's how music on hold and such works.
+
+Brian West in ASTERISK-140 2004-04-24:
+"This is the one thing that keeps asterisk out of the big boy toy box.... lets get it going boys."
Propchange: team/oej/teapot-1.8/README.roibos-cng.txt
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: team/oej/teapot-1.8/README.roibos-cng.txt
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: team/oej/teapot-1.8/README.roibos-cng.txt
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: team/oej/teapot-1.8/channels/chan_iax2.c
URL: http://svnview.digium.com/svn/asterisk/team/oej/teapot-1.8/channels/chan_iax2.c?view=diff&rev=412030&r1=412029&r2=412030
==============================================================================
--- team/oej/teapot-1.8/channels/chan_iax2.c (original)
+++ team/oej/teapot-1.8/channels/chan_iax2.c Wed Apr 9 04:50:14 2014
@@ -1329,6 +1329,7 @@
case AST_CONTROL_T38_PARAMETERS:
case AST_CONTROL_AOC:
case AST_CONTROL_INCOMPLETE:
+ case AST_CONTROL_CNG_END:
is_allowed = -1;
break;
Modified: team/oej/teapot-1.8/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/team/oej/teapot-1.8/channels/chan_sip.c?view=diff&rev=412030&r1=412029&r2=412030
==============================================================================
--- team/oej/teapot-1.8/channels/chan_sip.c (original)
+++ team/oej/teapot-1.8/channels/chan_sip.c Wed Apr 9 04:50:14 2014
@@ -274,6 +274,7 @@
#include "sip/include/sip2cause.h"
#include "asterisk/ccss.h"
#include "asterisk/xml.h"
+#include "asterisk/silencedetection.h"
#include "sip/include/dialog.h"
#include "sip/include/dialplan_functions.h"
#include "sip/include/rtcp.h"
@@ -1332,6 +1333,7 @@
static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int oldsdp, int add_audio, int add_t38);
static void do_setnat(struct sip_pvt *p);
static void stop_media_flows(struct sip_pvt *p);
+static int activate_silence_detection(struct sip_pvt *dialog);
/*--- Authentication stuff */
static int reply_digest(struct sip_pvt *p, struct sip_request *req, char *header, int sipmethod, char *digest, int digest_len);
@@ -4742,6 +4744,10 @@
ast_set2_flag(&p->flags[1], *(unsigned int *) data, SIP_PAGE2_USE_SRTP);
res = 0;
break;
+ case AST_OPTION_CNG_SUPPORT:
+ /* Check if the current dialog has agreed on Comfort Noise support */
+ res = (p->noncodeccapability & AST_RTP_CN);
+ break;
default:
break;
}
@@ -6843,6 +6849,8 @@
res = transmit_response_with_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL, oldsdp, TRUE);
ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
start_rtcp_events(p, sched);
+ /* If we've agreed on CN for this channel, try activating silence detection and suppression on it */
+ activate_silence_detection(p);
}
sip_pvt_unlock(p);
return res;
@@ -6854,7 +6862,20 @@
struct sip_pvt *p = ast->tech_pvt;
int res = 0;
+ if (frame == &ast_null_frame || frame->frametype == AST_FRAME_NULL) {
+ /* We do not send null frames. Sorry */
+ return 1;
+ }
+
switch (frame->frametype) {
+ case AST_FRAME_CNG:
+ /* We get this frame if silence suppression is active. */
+ if (p->rtp) {
+ ast_rtp_instance_sendcng(p->rtp, 64);
+ }
+ res = 0;
+ break;
+
case AST_FRAME_VOICE:
if (!(frame->subclass.codec & ast->nativeformats)) {
char s1[512], s2[512], s3[512];
@@ -7407,6 +7428,39 @@
return res;
}
+
+/*! \brief Activates a DSP to detect silence, and suppress silent frames
+ and send CNG (comfort noise generation) requests at start of silence instead */
+static int activate_silence_detection(struct sip_pvt *dialog)
+{
+ ast_debug(3, "SILDET: Checking if we need silence detection on %s\n", dialog->callid);
+
+ if (! (dialog->jointnoncodeccapability & AST_RTP_CN)) {
+ ast_debug(4, "SILDET: Channel does not support Comfort Noise on %s\n", dialog->callid);
+ /* If this is a re-invite that turns CN off, deactivate it. */
+ if (dialog->owner) {
+ ast_sildet_deactivate(dialog->owner);
+ }
+ return FALSE;
+ }
+
+
+ /* Check if we really want silence suppression */
+ if (!dialog || !dialog->rtp || !dialog->owner || !ast_test_flag(&dialog->flags[2], SIP_PAGE3_SILENCE_DETECTION)) {
+ ast_debug(3, "SILDET: Channel does not need silence suppression on %s\n", dialog->callid);
+ return FALSE;
+ }
+
+ if(ast_sildet_activate(dialog->owner, dialog->silencelevel, dialog->silenceframes)) {
+
+ /* We now have a call where we have a DSP. The rest of the magic is happening somewhere else in chan_sip. */
+ ast_debug(3, "SILDET: Activated silence suppression on call %s\n", dialog->callid);
+ } else {
+ ast_debug(3, "SILDET: Failed to activate silence detection on call %s\n", dialog->callid);
+ }
+ return TRUE;
+}
+
/*!
* \brief Initiate a call in the SIP channel
*
@@ -7625,6 +7679,7 @@
manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate",
"Channel: %s\r\nUniqueid: %s\r\nChanneltype: %s\r\nSIPcallid: %s\r\nSIPfullcontact: %s\r\n",
tmp->name, tmp->uniqueid, "SIP", i->callid, i->fullcontact);
+
return tmp;
}
@@ -7922,7 +7977,6 @@
ast_frfree(fr);
fr = &ast_null_frame;
}
-
sip_pvt_unlock(p);
return fr;
@@ -8189,6 +8243,8 @@
ast_string_field_set(p, mohinterpret, default_mohinterpret);
ast_string_field_set(p, mohsuggest, default_mohsuggest);
p->capability = sip_cfg.capability;
+ p->silencelevel = sip_cfg.silencelevel;
+ p->silenceframes = sip_cfg.silenceframes;
p->allowtransfer = sip_cfg.allowtransfer;
if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) ||
(ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) {
@@ -9686,7 +9742,7 @@
struct ast_str *s2 = ast_str_alloca(SIPBUFSIZE);
struct ast_str *s3 = ast_str_alloca(SIPBUFSIZE);
- ast_verbose("Non-codec capabilities (dtmf): us - %s, peer - %s, combined - %s\n",
+ ast_verbose("Non-codec capabilities (dtmf, cn): us - %s, peer - %s, combined - %s\n",
ast_rtp_lookup_mime_multiple2(s1, p->noncodeccapability, 0, 0),
ast_rtp_lookup_mime_multiple2(s2, peernoncodeccapability, 0, 0),
ast_rtp_lookup_mime_multiple2(s3, newnoncodeccapability, 0, 0));
@@ -18373,7 +18429,6 @@
ast_cli(fd, " PRACK support: %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[2], SIP_PAGE3_PRACK)));
ast_cli(fd, " Video Support: %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_VIDEOSUPPORT) || ast_test_flag(&peer->flags[1], SIP_PAGE2_VIDEOSUPPORT_ALWAYS)));
ast_cli(fd, " Text Support : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_TEXTSUPPORT)));
- ast_cli(fd, " Comfort Noise: %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOW_CN)));
ast_cli(fd, " Ign SDP ver : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_IGNORESDPVERSION)));
ast_cli(fd, " Trust RPID : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[0], SIP_TRUSTRPID)));
ast_cli(fd, " Send RPID : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[0], SIP_SENDRPID)));
@@ -18416,7 +18471,8 @@
ast_cli(fd, " Codec Order : (");
print_codec_to_cli(fd, &peer->prefs);
ast_cli(fd, ")\n");
-
+ ast_cli(fd, " Comfort Noise: %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOW_CN)));
+ ast_cli(fd, " Silence Supp.: %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[2], SIP_PAGE3_SILENCE_DETECTION)));
ast_cli(fd, " Auto-Framing : %s\n", AST_CLI_YESNO(peer->autoframing));
ast_cli(fd, " Status : ");
peer_status(peer, status, sizeof(status));
@@ -18494,6 +18550,8 @@
astman_append(s, "SIP-Sess-Min: %d\r\n", peer->stimer.st_min_se);
astman_append(s, "SIP-RTP-Engine: %s\r\n", peer->engine);
astman_append(s, "SIP-Encryption: %s\r\n", ast_test_flag(&peer->flags[1], SIP_PAGE2_USE_SRTP) ? "Y" : "N");
+ astman_append(s, "SIP-SilenceSupp: %s\r\n", ast_test_flag(&peer->flags[2], SIP_PAGE3_SILENCE_DETECTION) ? "Y" : "N");
+ astman_append(s, "SIP-ComfortNoise: %s\r\n", (ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOW_CN)?"Y":"N"));
/* - is enumerated */
astman_append(s, "SIP-DTMFmode: %s\r\n", dtmfmode2str(ast_test_flag(&peer->flags[0], SIP_DTMF)));
@@ -19114,6 +19172,10 @@
ast_cli(a->fd, " DTMF: %s\n", dtmfmode2str(ast_test_flag(&global_flags[0], SIP_DTMF)));
ast_cli(a->fd, " Qualify: %d\n", default_qualify);
ast_cli(a->fd, " Use ClientCode: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_USECLIENTCODE)));
+ ast_cli(a->fd, " Comfort Noise: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOW_CN)));
+ ast_cli(a->fd, " Silence suppression: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[2], SIP_PAGE3_SILENCE_DETECTION)));
+ ast_cli(a->fd, " Silence threshold: %d\n", sip_cfg.silencelevel);
+ ast_cli(a->fd, " Silence frames: %d\n", sip_cfg.silenceframes);
ast_cli(a->fd, " Progress inband: %s\n", (ast_test_flag(&global_flags[0], SIP_PROG_INBAND) == SIP_PROG_INBAND_NEVER) ? "Never" : (AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_PROG_INBAND) != SIP_PROG_INBAND_NO)));
ast_cli(a->fd, " Language: %s\n", default_language);
ast_cli(a->fd, " MOH Interpret: %s\n", default_mohinterpret);
@@ -19476,6 +19538,7 @@
ast_cli(a->fd, " Non-Codec Capability (DTMF): %d\n", cur->noncodeccapability);
ast_cli(a->fd, " Their Codec Capability: %s\n", ast_getformatname_multiple(formatbuf, sizeof(formatbuf), cur->peercapability));
ast_cli(a->fd, " Joint Codec Capability: %s\n", ast_getformatname_multiple(formatbuf, sizeof(formatbuf), cur->jointcapability));
+ ast_cli(a->fd, " Non-Codec Capability (DTMF, CN): %d\n", cur->noncodeccapability);
ast_cli(a->fd, " Format: %s\n", ast_getformatname_multiple(formatbuf, sizeof(formatbuf), cur->owner ? cur->owner->nativeformats : 0) );
ast_cli(a->fd, " T.38 support %s\n", AST_CLI_YESNO(cur->udptl != NULL));
ast_cli(a->fd, " Video support %s\n", AST_CLI_YESNO(cur->vrtp != NULL));
@@ -19513,6 +19576,9 @@
ast_cli(a->fd, " Promiscuous Redir: %s\n", AST_CLI_YESNO(ast_test_flag(&cur->flags[0], SIP_PROMISCREDIR)));
ast_cli(a->fd, " Route: %s\n", cur->route ? cur->route->hop : "N/A");
ast_cli(a->fd, " DTMF Mode: %s\n", dtmfmode2str(ast_test_flag(&cur->flags[0], SIP_DTMF)));
+ ast_cli(a->fd, " Silence Detection: %s\n", AST_CLI_YESNO(ast_test_flag(&cur->flags[2], SIP_PAGE3_SILENCE_DETECTION)));
+ ast_cli(a->fd, " Silence level: %d\n", cur->silencelevel);
+ ast_cli(a->fd, " Silence frames: %d\n", cur->silenceframes);
ast_cli(a->fd, " SIP Options: ");
if (cur->sipoptions) {
int x;
@@ -21156,6 +21222,8 @@
ast_string_field_set(p, theirtag_early, p->tag);
}
ast_rtp_instance_activate(p->rtp);
+ /* If we've agreed on CN for this channel, try activating silence detection and suppression on it */
+ activate_silence_detection(p);
} else {
/* Alcatel PBXs are known to send 183s with no SDP after sending
* a 100 Trying response. We're just going to treat this sort of thing
@@ -21186,6 +21254,8 @@
}
}
ast_rtp_instance_activate(p->rtp);
+ /* If we've agreed on CN for this channel, try activating silence detection and suppression on it */
+ activate_silence_detection(p);
} else if (!reinvite) {
struct ast_sockaddr remote_address = {{0,}};
@@ -24151,6 +24221,7 @@
c_state = AST_STATE_UP;
}
+
switch(c_state) {
case AST_STATE_DOWN:
ast_debug(2, "%s: New call is still down.... Trying... \n", c->name);
@@ -26474,6 +26545,7 @@
ast_debug(3, "Requesting SSRC change in %s\n", p->callid);
ast_queue_control(p->owner, AST_CONTROL_SRCCHANGE);
}
+ activate_silence_detection(p);
}
check_pendings(p);
} else if (p->glareinvite == seqno) {
@@ -28140,6 +28212,9 @@
} else if (!strcasecmp(v->name, "comfort-noise")) {
ast_set_flag(&mask[1], SIP_PAGE2_ALLOW_CN);
ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_ALLOW_CN);
+ } else if (!strcasecmp(v->name, "silencesuppression")) {
+ ast_set_flag(&mask[2], SIP_PAGE3_SILENCE_DETECTION);
+ ast_set2_flag(&flags[2], ast_true(v->value), SIP_PAGE3_SILENCE_DETECTION);
} else
res = 0;
@@ -29369,6 +29444,8 @@
ast_copy_string(default_callerid, DEFAULT_CALLERID, sizeof(default_callerid));
ast_copy_string(default_mwi_from, DEFAULT_MWI_FROM, sizeof(default_mwi_from));
sip_cfg.compactheaders = DEFAULT_COMPACTHEADERS;
+ sip_cfg.silencelevel = DEFAULT_SILENCELEVEL;
+ sip_cfg.silenceframes = DEFAULT_SILENCEFRAMES;
global_reg_timeout = DEFAULT_REGISTRATION_TIMEOUT;
global_regattempts_max = 0;
global_reg_retry_403 = 0;
@@ -29935,6 +30012,18 @@
}
} else if (!strcasecmp(v->name, "use_q850_reason")) {
ast_set2_flag(&global_flags[1], ast_true(v->value), SIP_PAGE2_Q850_REASON);
+ } else if (!strcasecmp(v->name, "silencelevel")) {
+ if (sscanf(v->value, "%30d", &sip_cfg.silencelevel) != 1
+ || sip_cfg.silencelevel < 1 ) {
+ ast_log(LOG_WARNING, "'%s' is not a valid silencelevel value at line %d. Using default.\n", v->value, v->lineno);
+ sip_cfg.silencelevel = DEFAULT_SILENCELEVEL;
+ }
+ } else if (!strcasecmp(v->name, "silenceframes")) {
+ if (sscanf(v->value, "%30d", &sip_cfg.silenceframes) != 1
+ || sip_cfg.silenceframes < 0 || sip_cfg.silenceframes > 150) {
+ ast_log(LOG_WARNING, "'%s' is not a valid silencelevel value at line %d. Using default.\n", v->value, v->lineno);
+ sip_cfg.silenceframes = DEFAULT_SILENCEFRAMES;
+ }
} else if (!strcasecmp(v->name, "maxforwards")) {
if (sscanf(v->value, "%30d", &sip_cfg.default_max_forwards) != 1
|| sip_cfg.default_max_forwards < 1 || 255 < sip_cfg.default_max_forwards) {
Modified: team/oej/teapot-1.8/channels/sip/include/sip.h
URL: http://svnview.digium.com/svn/asterisk/team/oej/teapot-1.8/channels/sip/include/sip.h?view=diff&rev=412030&r1=412029&r2=412030
==============================================================================
--- team/oej/teapot-1.8/channels/sip/include/sip.h (original)
+++ team/oej/teapot-1.8/channels/sip/include/sip.h Wed Apr 9 04:50:14 2014
@@ -61,6 +61,8 @@
#define DEFAULT_AUTHLIMIT 100
#define DEFAULT_AUTHTIMEOUT 30
+#define DEFAULT_SILENCELEVEL 100
+#define DEFAULT_SILENCEFRAMES 10 /* Number of frames of silence to let through before we start suppressing it */
/* guard limit must be larger than guard secs */
/* guard min must be < 1000, and should be >= 250 */
@@ -369,8 +371,8 @@
#define SIP_PAGE3_INVITE_WAIT_FOR_PRACK (1 << 4) /*!< D: Wait for PRACK response before sending 200 OK */
#define SIP_PAGE3_ANSWER_WAIT_FOR_PRACK (1 << 5) /*!< D: Send ANSWER when PRACK is received */
- #define SIP_PAGE3_FLAGS_TO_COPY \
- (SIP_PAGE3_SNOM_AOC | SIP_PAGE3_PRACK | SIP_PAGE3_DIRECT_MEDIA_OUTGOING)
+#define SIP_PAGE3_FLAGS_TO_COPY \
+ (SIP_PAGE3_SNOM_AOC | SIP_PAGE3_PRACK | SIP_PAGE3_DIRECT_MEDIA_OUTGOING | SIP_PAGE3_SILENCE_DETECTION)
/*@}*/
@@ -729,6 +731,8 @@
format_t capability; /*!< Supported codecs */
int tcp_enabled;
int default_max_forwards; /*!< Default max forwards (SIP Anti-loop) */
+ unsigned int silencelevel; /*!< Default silence treshold for silence detection */
+ unsigned int silenceframes; /*!< Default silence period - how many frames to wait before suppressing silence */
};
/*! \brief The SIP socket definition */
@@ -1110,6 +1114,10 @@
uint32_t dialogver; /*!< SUBSCRIBE: Version for subscription dialog-info */
struct ast_dsp *dsp; /*!< Inband DTMF or Fax CNG tone Detection dsp */
+ unsigned int silencelevel; /*!< Silence treshold */
+ unsigned int silenceframes; /*!< How many frames to wait for silence before activating silence
+ support and sending CNG */
+ unsigned int silencecounter; /*!< Frame Counter used for silence detection. */
struct sip_peer *relatedpeer; /*!< If this dialog is related to a peer, which one
Used in peerpoke, mwi subscriptions */
Modified: team/oej/teapot-1.8/configs/sip.conf.sample
URL: http://svnview.digium.com/svn/asterisk/team/oej/teapot-1.8/configs/sip.conf.sample?view=diff&rev=412030&r1=412029&r2=412030
==============================================================================
--- team/oej/teapot-1.8/configs/sip.conf.sample (original)
+++ team/oej/teapot-1.8/configs/sip.conf.sample Wed Apr 9 04:50:14 2014
@@ -562,6 +562,34 @@
; when we're on hold (must be > rtptimeout)
;rtpkeepalive=<secs> ; Send keepalives in the RTP stream to keep NAT open
; (default is off - zero)
+
+;--------------------------- SIP Silence detection/suppression for RTP ------------------------------------
+; The silence detection assigns a software DSP to each channel and converts all audio into
+; signed linear in order to be able to detect silence. This will require a lot of CPU
+; per channel including transcoding.
+;
+; You can decide to enable comfort-noise and disable silencesuppression. In this case, Asterisk
+; will negotitate comfort noise and accept it coming in, but since there is no DSP on the channel
+; Asterisk will never send any CN packet and suppress audio.
+;
+; Enabling silencesuppression and comfort noise will save a lot of bandwidth in your calls. There
+; will only be RTP flows when someone is speaking.
+;
+;silencesuppression = YES ; Enable silence suppression - by default turned off.
+; ; settable per device too
+; ; You want to enable comfort noise too. Default is off.
+;silencelevel = 100 ; Silence detection noise level - below this is considered silent.
+ ; Default = 100
+;silenceperiod = 10 ; How many frames of silence should we get before we supress
+ ; audio. Consider packetization. A normal ALAW stream has 20 ms audio
+ ; per RTP packet. A value of 2 means we will start sending CNG at the third silent
+ ; packet, after 40 ms of silence. Default is 10 frames.
+;comfort-noise=yes ; Enable Comfort Noise generation on RTP streams.
+; ; Default is off
+; ; Available per device too
+; ; Generating comfort noise is a burden to your CPU
+; ; This should not be enabled on low-end devices.
+; ; You should not enable this unless you have a timer.
;--------------------------- SIP Session-Timers (RFC 4028)------------------------------------
; SIP Session-Timers provide an end-to-end keep-alive mechanism for active SIP sessions.
Modified: team/oej/teapot-1.8/funcs/func_frame_trace.c
URL: http://svnview.digium.com/svn/asterisk/team/oej/teapot-1.8/funcs/func_frame_trace.c?view=diff&rev=412030&r1=412029&r2=412030
==============================================================================
--- team/oej/teapot-1.8/funcs/func_frame_trace.c (original)
+++ team/oej/teapot-1.8/funcs/func_frame_trace.c Wed Apr 9 04:50:14 2014
@@ -329,6 +329,9 @@
case AST_CONTROL_CNG_END:
ast_verbose("SubClass: CNG_END");
break;
+ case AST_CONTROL_CNG_END:
+ ast_verbose("SubClass: CNG_END");
+ break;
}
if (frame->subclass.integer == -1) {
Modified: team/oej/teapot-1.8/include/asterisk/channel.h
URL: http://svnview.digium.com/svn/asterisk/team/oej/teapot-1.8/include/asterisk/channel.h?view=diff&rev=412030&r1=412029&r2=412030
==============================================================================
--- team/oej/teapot-1.8/include/asterisk/channel.h (original)
+++ team/oej/teapot-1.8/include/asterisk/channel.h Wed Apr 9 04:50:14 2014
@@ -2406,6 +2406,42 @@
void ast_channel_stop_noise_generator(struct ast_channel *chan, struct ast_noise_generator *state);
/*!
+ * \brief An opaque 'object' structure use by noise generators on channels.
+ */
+struct ast_noise_generator;
+
+/*!
+ * \brief Starts a noise generator on the given channel.
+ * \param chan The channel to generate silence on
+ * \param level The noise level in negative (dBOV)
+ * \return An ast_noise_generator pointer, or NULL if an error occurs
+ *
+ * \details
+ * This function will cause SLINEAR noise to be generated on the supplied
+ * channel until it is disabled; if the channel cannot be put into SLINEAR
+ * mode then the function will fail.
+ *
+ * \note
+ * The pointer returned by this function must be preserved and passed to
+ * ast_channel_stop_noise_generator when you wish to stop the noise
+ * generation.
+ */
+struct ast_noise_generator *ast_channel_start_noise_generator(struct ast_channel *chan, const float level);
+
+/*!
+ * \brief Stops a previously-started noise generator on the given channel.
+ * \param chan The channel to operate on
+ * \param state The ast_noise_generator pointer return by a previous call to
+ * ast_channel_start_noise_generator.
+ * \return nothing
+ *
+ * \details
+ * This function will stop the operating noise generator and return the channel
+ * to its previous write format.
+ */
+void ast_channel_stop_noise_generator(struct ast_channel *chan, struct ast_noise_generator *state);
+
+/*!
* \brief Check if the channel can run in internal timing mode.
* \param chan The channel to check
* \return boolean
Modified: team/oej/teapot-1.8/include/asterisk/frame.h
URL: http://svnview.digium.com/svn/asterisk/team/oej/teapot-1.8/include/asterisk/frame.h?view=diff&rev=412030&r1=412029&r2=412030
==============================================================================
--- team/oej/teapot-1.8/include/asterisk/frame.h (original)
+++ team/oej/teapot-1.8/include/asterisk/frame.h Wed Apr 9 04:50:14 2014
@@ -531,6 +531,10 @@
if not enabled a noise generator will kick in */
#define AST_OPTION_CNG_SUPPORT 20
+/*! Support of CNG transmission,
+ if not enabled a noise generator will kick in */
+#define AST_OPTION_CNG_SUPPORT 20
+
struct oprmode {
struct ast_channel *peer;
int mode;
Added: team/oej/teapot-1.8/include/asterisk/silencedetection.h
URL: http://svnview.digium.com/svn/asterisk/team/oej/teapot-1.8/include/asterisk/silencedetection.h?view=auto&rev=412030
==============================================================================
--- team/oej/teapot-1.8/include/asterisk/silencedetection.h (added)
+++ team/oej/teapot-1.8/include/asterisk/silencedetection.h Wed Apr 9 04:50:14 2014
@@ -1,0 +1,49 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2014, Olle E. Johansson
+ *
+ * Olle E. Johansson <oej at edvina.net>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * \brief Audiohook for silnce detection
+ */
+
+#ifndef _ASTERISK_SILENCEDETECTION_H
+#define _ASTERISK_SILENCEDETECTION_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*! \brief Activation of silence detection
+ \param chan The channel
+ \param silencelevel Audio treshold for silence
+ \param silenceframes Number of frames before we react
+
+ \note That this function assumes the channel is set to read signed linear audio
+
+*/
+int ast_sildet_activate(struct ast_channel *chan, unsigned int silencelevel, unsigned int silenceframes);
+
+/*! \brief Deactivation of silence detection
+ \param chan The channel
+*/
+int ast_sildet_deactivate(struct ast_channel *chan);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_SILENCEDETECTION_H */
Propchange: team/oej/teapot-1.8/include/asterisk/silencedetection.h
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: team/oej/teapot-1.8/include/asterisk/silencedetection.h
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: team/oej/teapot-1.8/include/asterisk/silencedetection.h
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: team/oej/teapot-1.8/main/audiohook.c
URL: http://svnview.digium.com/svn/asterisk/team/oej/teapot-1.8/main/audiohook.c?view=diff&rev=412030&r1=412029&r2=412030
==============================================================================
--- team/oej/teapot-1.8/main/audiohook.c (original)
+++ team/oej/teapot-1.8/main/audiohook.c Wed Apr 9 04:50:14 2014
@@ -626,6 +626,7 @@
struct ast_frame *start_frame = frame, *middle_frame = frame, *end_frame = frame;
struct ast_audiohook *audiohook = NULL;
int samples = frame->samples;
+ int needsdrop = 0;
/* ---Part_1. translate start_frame to SLINEAR if necessary. */
/* If the frame coming in is not signed linear we have to send it through the in_translate path */
@@ -705,15 +706,25 @@
* be taken here to exit early. */
}
ast_audiohook_unlock(audiohook);
+ if (middle_frame->frametype == AST_FRAME_NULL) {
+ /* This frame is going nowhere after this */
+ needsdrop = 1;
+ }
}
AST_LIST_TRAVERSE_SAFE_END;
end_frame = middle_frame;
}
/* ---Part_3: Decide what to do with the end_frame (whether to transcode or not) */
+ if (needsdrop) {
+ /* The frame needs to go away badly */
+ ast_frfree(middle_frame);
+ return &ast_null_frame;
+ }
+
if (middle_frame == end_frame) {
/* Middle frame was modified and became the end frame... let's see if we need to transcode */
- if (end_frame->subclass.codec != start_frame->subclass.codec) {
+ if (end_frame->frametype == AST_FRAME_VOICE && end_frame->subclass.codec != start_frame->subclass.codec) {
if (out_translate->format != start_frame->subclass.codec) {
if (out_translate->trans_pvt)
ast_translator_free_path(out_translate->trans_pvt);
Modified: team/oej/teapot-1.8/main/channel.c
URL: http://svnview.digium.com/svn/asterisk/team/oej/teapot-1.8/main/channel.c?view=diff&rev=412030&r1=412029&r2=412030
==============================================================================
--- team/oej/teapot-1.8/main/channel.c (original)
+++ team/oej/teapot-1.8/main/channel.c Wed Apr 9 04:50:14 2014
@@ -4412,6 +4412,7 @@
case AST_CONTROL_SRCUPDATE:
case AST_CONTROL_SRCCHANGE:
case AST_CONTROL_RADIO_KEY:
+ case AST_CONTROL_CNG_END:
case AST_CONTROL_RADIO_UNKEY:
case AST_CONTROL_OPTION:
case AST_CONTROL_WINK:
@@ -7344,6 +7345,12 @@
ast_debug(1, "*** Bridge got CNG END frame \n");
ast_channel_stop_noise_generator(other, NULL);
break;
+ case AST_CONTROL_CNG_END:
+ /* If we are playing out CNG noise on the bridged channel, stop it now.
+ otherwise, ignore this frame. */
+ ast_debug(1, "*** Bridge got CNG END frame \n");
+ ast_channel_stop_noise_generator(other, NULL);
+ break;
case AST_CONTROL_HOLD:
case AST_CONTROL_UNHOLD:
case AST_CONTROL_VIDUPDATE:
@@ -7364,6 +7371,21 @@
}
if (bridge_exit)
break;
+ }
+ if (f->frametype == AST_FRAME_CNG) {
+ /* We got a CNG frame
+ Check if the bridged channel has active CNG
+ */
+ int cngsupport = 0;
+ int len = sizeof(cngsupport);
+ ast_channel_queryoption(other, AST_OPTION_CNG_SUPPORT, &cngsupport, &len, 0);
+ if (cngsupport) {
+ ast_debug(1, "*** Bridge got CNG frame. Forwarding it \n");
+ ast_write(other, f);
+ } else {
+ ast_debug(1, "*** Bridge got CNG frame. Playing out noise. (CNG not supported by other channel) Level: - %d\n", f->subclass.integer );
+ ast_channel_start_noise_generator(other, (float) -f->subclass.integer);
+ }
}
if (f->frametype == AST_FRAME_CNG) {
/* We got a CNG frame
@@ -8389,6 +8411,305 @@
}
ast_free(state);
}
+
+
+#ifdef HAVE_EXP10L
+#define FUNC_EXP10 exp10l
+#elif (defined(HAVE_EXPL) && defined(HAVE_LOGL))
+#define FUNC_EXP10(x) expl((x) * logl(10.0))
+#elif (defined(HAVE_EXP) && defined(HAVE_LOG))
+#define FUNC_EXP10(x) (long double)exp((x) * log(10.0))
+#endif
+
+/*! \brief Noise generator default frame */
+static struct ast_frame noiseframedefaults = {
+ .frametype = AST_FRAME_VOICE,
+ .subclass.codec = AST_FORMAT_SLINEAR,
+ .offset = AST_FRIENDLY_OFFSET,
+ .mallocd = 0,
+ .data.ptr = NULL,
+ .datalen = 0,
+ .samples = 0,
+ .src = "noisegenerator",
+ .delivery.tv_sec = 0,
+ .delivery.tv_usec = 0
+};
+
+struct ast_noise_generator {
+ int old_write_format;
+ float level;
+};
+
+
+#ifndef LOW_MEMORY
+/*
+ * We pregenerate 64k of white noise samples that will be used instead of
+ * generating the samples continously and wasting CPU cycles. The buffer
+ * below stores these pregenerated samples.
+ */
+static float pregeneratedsamples[65536L];
+#endif
+
+/*
+ * We need a nice, not too expensive, gaussian random number generator.
+ * It generates two random numbers at a time, which is great.
+ * From http://www.taygeta.com/random/gaussian.html
+ */
+static void box_muller_rng(float stddev, float *rn1, float *rn2) {
+ const float twicerandmaxinv = 2.0 / RAND_MAX;
+ float x1, x2, w;
+
+ do {
+ x1 = random() * twicerandmaxinv - 1.0;
+ x2 = random() * twicerandmaxinv - 1.0;
+ w = x1 * x1 + x2 * x2;
+ } while (w >= 1.0);
+
+ w = stddev * sqrt((-2.0 * logf(w)) / w);
+ *rn1 = x1 * w;
+ *rn2 = x2 * w;
+}
+
+static void *noise_generator_alloc(struct ast_channel *chan, void *params) {
+ struct ast_noise_generator *state = params;
+ float level = state->level; /* level is noise level in dBov */
+ float *pnoisestddev; /* pointer to calculated noise standard dev */
+ const float maxsigma = 32767.0 / 3.0;
+
+ /*
+ * When level is zero (full power, by definition) standard deviation
+ * (sigma) is calculated so that 3 * sigma equals max sample value
+ * before overload. For signed linear, which is what we use, this
+ * value is 32767. The max value of sigma will therefore be
+ * 32767.0 / 3.0. This guarantees that roughly 99.7% of the samples
+ * generated will be between -32767 and +32767. The rest, 0.3%,
+ * will be clipped to comform to the channel limits, i.e., +/-32767.
+ *
+ */
+ pnoisestddev = malloc(sizeof (float));
+ if(pnoisestddev) {
+ *pnoisestddev = maxsigma * FUNC_EXP10(level / 20.0);
+ }
+
+ return (void *) pnoisestddev;
+}
+
+static void noise_generator_release(struct ast_channel *chan, void *data) {
+ free((float *)data);
+}
+
+/*! \brief Generator of White Noise at a certain level.
+
+Current level is defined in the generator data structure as noiselevel (float) in dBov's
+
+
+ Level is a non-positive number. For example, WhiteNoise(0.0) generates
+ white noise at full power, while WhiteNoise(-3.0) generates white noise at
+ half full power. Every -3dBov's reduces white noise power in half. Full
+ power in this case is defined as noise that overloads the channel roughly 0.3%
+ of the time. Note that values below -69 dBov's start to give out silence
+ frequently, resulting in intermittent noise, i.e, alternating periods of
+ silence and noise.
+
+This code orginally contributed to Asterisk by cmantunes in issue ASTERISK-5263
+as part of res_noise.c
+*/
+static int noise_generate(struct ast_channel *chan, void *data, int len, int samples) {
+#ifdef LOW_MEMORY
+ float randomnumber[2];
+ float sampleamplitude;
+ int j;
+#else
+ uint16_t start;
+#endif
+ float noisestddev = *(float *)data;
+ struct ast_frame f;
+ int16_t *buf, *pbuf;
+ int i;
+
+#ifdef LOW_MEMORY
+ /* We need samples to be an even number */
+ if (samples & 0x1) {
+ ast_log(LOG_WARNING, "Samples (%d) needs to be an even number\n", samples);
+ return -1;
+ }
+#endif
+
+ /* Allocate enough space for samples.
[... 2159 lines stripped ...]
More information about the asterisk-commits
mailing list