[asterisk-commits] oej: branch oej/roibos-cng-support-1.8 r411923 - in /team/oej/roibos-cng-supp...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Apr 8 09:14:14 CDT 2014


Author: oej
Date: Tue Apr  8 09:14:08 2014
New Revision: 411923

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=411923
Log:
Updating before the big merge!

Stay tuned folks, this is exciting stuff. Soon time for champagne and popcorn. At least one of them.

Added:
    team/oej/roibos-cng-support-1.8/patches/
    team/oej/roibos-cng-support-1.8/patches/roibos-cng-support-1.8.diff   (with props)
    team/oej/roibos-cng-support-1.8/patches/silence-detection-games-1.8.diff   (with props)
Modified:
    team/oej/roibos-cng-support-1.8/README.roibos-cng.txt

Modified: team/oej/roibos-cng-support-1.8/README.roibos-cng.txt
URL: http://svnview.digium.com/svn/asterisk/team/oej/roibos-cng-support-1.8/README.roibos-cng.txt?view=diff&rev=411923&r1=411922&r2=411923
==============================================================================
--- team/oej/roibos-cng-support-1.8/README.roibos-cng.txt (original)
+++ team/oej/roibos-cng-support-1.8/README.roibos-cng.txt Tue Apr  8 09:14:08 2014
@@ -3,14 +3,14 @@
 
 
 Started: 2012-09-18
-Updated: 2013-12-05
+Updated: 2014-04-07
 
 
 
 
 
-Comfort Noise support in Asterisk 1.8
-=====================================
+Silence Suppression and Comfort Noise support in Asterisk 1.8
+=============================================================
 
 Comfort Noise in SIP/RTP is 
 - negotiated in the SDP as a codec
@@ -67,15 +67,11 @@
 =================
 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 then transcode back. Not a good efficient solution. I will implement
-this as a showcase in the first step.
+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
-- Add silence detection as a framehook procedure. This way we only have to
-  convert the media once and have a "fork" listen for silence. We keep
-  the media in the existing format over the bridge.
 
 Debugging
 =========
@@ -91,12 +87,6 @@
 -----------------------------
   - Check how this affects RTP bridge and queue bridge
   - Add CN support in SDP for outbound calls
-  - As step 2, add silence detection to calls
-  	- Measure noise level
-  	- Start sending CNG
-  	- Listen for talk detection
-  	- Stop sending CNG, send media
-  - Ask Matt J and File about frame hooks and how this solution should look
 
 Done:
   - Support in core bridge
@@ -105,6 +95,7 @@
     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
 ----------

Added: team/oej/roibos-cng-support-1.8/patches/roibos-cng-support-1.8.diff
URL: http://svnview.digium.com/svn/asterisk/team/oej/roibos-cng-support-1.8/patches/roibos-cng-support-1.8.diff?view=auto&rev=411923
==============================================================================
--- team/oej/roibos-cng-support-1.8/patches/roibos-cng-support-1.8.diff (added)
+++ team/oej/roibos-cng-support-1.8/patches/roibos-cng-support-1.8.diff Tue Apr  8 09:14:08 2014
@@ -1,0 +1,1155 @@
+Index: channels/chan_sip.c
+===================================================================
+--- channels/chan_sip.c	(.../branches/1.8)	(revision 411922)
++++ channels/chan_sip.c	(.../team/oej/roibos-cng-support-1.8)	(revision 411922)
+@@ -4768,6 +4768,10 @@
+ 		 * implied else case here
+ 		 */
+ 		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;
+ 	}
+@@ -5762,6 +5766,10 @@
+ 		dialog->noncodeccapability |= AST_RTP_DTMF;
+ 	else
+ 		dialog->noncodeccapability &= ~AST_RTP_DTMF;
++
++	if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_ALLOW_CN)) {
++		dialog->noncodeccapability |= AST_RTP_CN;
++	}
+ 	dialog->directmediaha = ast_duplicate_ha_list(peer->directmediaha);
+ 	if (peer->call_limit)
+ 		ast_set_flag(&dialog->flags[0], SIP_CALL_LIMIT);
+@@ -9733,7 +9741,7 @@
+ 		   they are acceptable */
+ 		p->jointcapability = newjointcapability;                /* Our joint codec profile for this call */
+ 		p->peercapability = newpeercapability;                  /* The other side's capability in latest offer */
+-		p->jointnoncodeccapability = newnoncodeccapability;     /* DTMF capabilities */
++		p->jointnoncodeccapability = newnoncodeccapability;     /* CN and DTMF capabilities */
+ 
+ 		/* respond with single most preferred joint codec, limiting the other side's choice */
+ 		if (ast_test_flag(&p->flags[1], SIP_PAGE2_PREFERRED_CODEC)) {
+@@ -11521,6 +11529,7 @@
+ 		fmt = ast_codec_pref_getsize(pref, codec);
+ 	} else /* I don't see how you couldn't have p->rtp, but good to check for and error out if not there like earlier code */
+ 		return;
++
+ 	ast_str_append(m_buf, 0, " %d", rtp_code);
+ 	ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code,
+ 		       ast_rtp_lookup_mime_subtype2(1, codec,
+@@ -11640,7 +11649,7 @@
+ 	}
+ }
+ 
+-/*! \brief Add RFC 2833 DTMF offer to SDP */
++/*! \brief Add CN and RFC 2833 DTMF offer to SDP */
+ static void add_noncodec_to_sdp(const struct sip_pvt *p, int format,
+ 				struct ast_str **m_buf, struct ast_str **a_buf,
+ 				int debug)
+@@ -12006,10 +12015,12 @@
+ 				add_tcodec_to_sdp(p, x, &m_text, &a_text, debug, &min_text_packet_size);
+ 		}
+ 
+-		/* Now add DTMF RFC2833 telephony-event as a codec */
++		/* Now add Comfort Noise and DTMF RFC2833 telephony-event as a codec */
+ 		for (x = 1LL; x <= AST_RTP_MAX; x <<= 1) {
+-			if (!(p->jointnoncodeccapability & x))
++			if (!(p->jointnoncodeccapability & x)) {
++				ast_debug(1, "NOT Adding non-codec 0x%lx (%s) to SDP\n", (int64_t)x, ast_rtp_lookup_mime_subtype2(0, x, 0));
+ 				continue;
++			}
+ 
+ 			add_noncodec_to_sdp(p, x, &m_audio, &a_audio, debug);
+ 		}
+@@ -16806,6 +16817,9 @@
+ 			p->noncodeccapability |= AST_RTP_DTMF;
+ 		else
+ 			p->noncodeccapability &= ~AST_RTP_DTMF;
++		if (ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOW_CN)) {
++			p->noncodeccapability |= AST_RTP_CN;
++		}
+ 		p->jointnoncodeccapability = p->noncodeccapability;
+ 		p->rtptimeout = peer->rtptimeout;
+ 		p->rtpholdtimeout = peer->rtpholdtimeout;
+@@ -18268,6 +18282,7 @@
+ 		ast_cli(fd, "  User=Phone   : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[0], SIP_USEREQPHONE)));
+ 		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)));
+@@ -18380,6 +18395,7 @@
+ 		astman_append(s, "SIP-T.38Support: %s\r\n", (ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT)?"Y":"N"));
+ 		astman_append(s, "SIP-T.38EC: %s\r\n", faxec2str(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT)));
+ 		astman_append(s, "SIP-T.38MaxDtgrm: %d\r\n", peer->t38_maxdatagram);
++		astman_append(s, "SIP-ComfortNoise: %s\r\n", (ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOW_CN)?"Y":"N"));
+ 		astman_append(s, "SIP-Sess-Timers: %s\r\n", stmode2str(peer->stimer.st_mode_oper));
+ 		astman_append(s, "SIP-Sess-Refresh: %s\r\n", strefresherparam2str(peer->stimer.st_ref));
+ 		astman_append(s, "SIP-Sess-Expires: %d\r\n", peer->stimer.st_max_se);
+@@ -18833,6 +18849,7 @@
+ 				"Disabled");
+ 	ast_cli(a->fd, "  Videosupport:           %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_VIDEOSUPPORT)));
+ 	ast_cli(a->fd, "  Textsupport:            %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_TEXTSUPPORT)));
++	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, "  Ignore SDP sess. ver.:  %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_IGNORESDPVERSION)));
+ 	ast_cli(a->fd, "  AutoCreate Peer:        %s\n", AST_CLI_YESNO(sip_cfg.autocreatepeer));
+ 	ast_cli(a->fd, "  Match Auth Username:    %s\n", AST_CLI_YESNO(global_match_auth_username));
+@@ -19356,6 +19373,7 @@
+ 			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));
++			ast_cli(a->fd, "  Comfort Noise support   %s\n", AST_CLI_YESNO(ast_test_flag(&cur->flags[1],SIP_PAGE2_ALLOW_CN)));
+ 			ast_cli(a->fd, "  MaxCallBR:              %d kbps\n", cur->maxcallbitrate);
+ 			ast_cli(a->fd, "  Theoretical Address:    %s\n", ast_sockaddr_stringify(&cur->sa));
+ 			ast_cli(a->fd, "  Received Address:       %s\n", ast_sockaddr_stringify(&cur->recv));
+@@ -27790,6 +27808,9 @@
+ 	} else if (!strcasecmp(v->name, "buggymwi")) {
+ 		ast_set_flag(&mask[1], SIP_PAGE2_BUGGY_MWI);
+ 		ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_BUGGY_MWI);
++	} 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
+ 		res = 0;
+ 
+Index: channels/sip/include/sip.h
+===================================================================
+--- channels/sip/include/sip.h	(.../branches/1.8)	(revision 411922)
++++ channels/sip/include/sip.h	(.../team/oej/roibos-cng-support-1.8)	(revision 411922)
+@@ -347,6 +347,7 @@
+ #define SIP_PAGE2_VIDEOSUPPORT_ALWAYS       (1 << 27)   /*!< DP: Always set up video, even if endpoints don't support it */
+ #define SIP_PAGE2_HAVEPEERCONTEXT           (1 << 28)   /*< Are we associated with a configured peer context? */
+ #define SIP_PAGE2_USE_SRTP                  (1 << 29)   /*!< DP: Whether we should offer (only)  SRTP */
++#define SIP_PAGE2_ALLOW_CN                  (1 << 30)   /*!< DP: If we allow Comfort Noise generation */
+ 
+ #define SIP_PAGE2_FLAGS_TO_COPY \
+ 	(SIP_PAGE2_ALLOWSUBSCRIBE | SIP_PAGE2_ALLOWOVERLAP | SIP_PAGE2_IGNORESDPVERSION | \
+@@ -354,7 +355,8 @@
+ 	SIP_PAGE2_BUGGY_MWI | SIP_PAGE2_TEXTSUPPORT | SIP_PAGE2_FAX_DETECT | \
+ 	SIP_PAGE2_UDPTL_DESTINATION | SIP_PAGE2_VIDEOSUPPORT_ALWAYS | SIP_PAGE2_PREFERRED_CODEC | \
+ 	SIP_PAGE2_RPID_IMMEDIATE | SIP_PAGE2_RPID_UPDATE | SIP_PAGE2_SYMMETRICRTP |\
+-	SIP_PAGE2_Q850_REASON | SIP_PAGE2_HAVEPEERCONTEXT | SIP_PAGE2_USE_SRTP)
++	SIP_PAGE2_Q850_REASON | SIP_PAGE2_HAVEPEERCONTEXT | SIP_PAGE2_USE_SRTP |\
++	SIP_PAGE2_ALLOW_CN )
+ 
+ 
+ #define SIP_PAGE3_SNOM_AOC               (1 << 0)  /*!< DPG: Allow snom aoc messages */
+Index: funcs/func_frame_trace.c
+===================================================================
+--- funcs/func_frame_trace.c	(.../branches/1.8)	(revision 411922)
++++ funcs/func_frame_trace.c	(.../team/oej/roibos-cng-support-1.8)	(revision 411922)
+@@ -326,6 +326,9 @@
+ 		case AST_CONTROL_UPDATE_RTP_PEER:
+ 			ast_verbose("SubClass: UPDATE_RTP_PEER\n");
+ 			break;
++		case AST_CONTROL_CNG_END:
++			ast_verbose("SubClass: CNG_END");
++			break;
+ 		}
+ 		
+ 		if (frame->subclass.integer == -1) {
+Index: include/asterisk/channel.h
+===================================================================
+--- include/asterisk/channel.h	(.../branches/1.8)	(revision 411922)
++++ include/asterisk/channel.h	(.../team/oej/roibos-cng-support-1.8)	(revision 411922)
+@@ -2370,6 +2370,42 @@
+ void ast_channel_stop_silence_generator(struct ast_channel *chan, struct ast_silence_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
+Index: include/asterisk/frame.h
+===================================================================
+--- include/asterisk/frame.h	(.../branches/1.8)	(revision 411922)
++++ include/asterisk/frame.h	(.../team/oej/roibos-cng-support-1.8)	(revision 411922)
+@@ -301,6 +301,8 @@
+ #define AST_FORMAT_SPEEX16    (1ULL << 33)
+ /*! Raw mu-law data (G.711) */
+ #define AST_FORMAT_TESTLAW    (1ULL << 47)
++/*! Comfort noise. Not a frame type per se, but needs to be added as a codec */
++#define AST_FORMAT_CN    (1ULL << 48)
+ /*! Reserved bit - do not use */
+ #define AST_FORMAT_RESERVED   (1ULL << 63)
+ 
+@@ -362,6 +364,7 @@
+ 	 *
+ 	 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
+ 	 */
++	AST_CONTROL_CNG_END = 32,	/*!< Disable CNG playout in bridge */
+ };
+ 
+ enum ast_frame_read_action {
+@@ -524,6 +527,10 @@
+ #define AST_OPTION_SECURE_SIGNALING        18
+ #define AST_OPTION_SECURE_MEDIA            19
+ 
++/*! 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;
+Index: main/channel.c
+===================================================================
+--- main/channel.c	(.../branches/1.8)	(revision 411922)
++++ main/channel.c	(.../team/oej/roibos-cng-support-1.8)	(revision 411922)
+@@ -4425,6 +4425,7 @@
+ 	case AST_CONTROL_AOC:
+ 	case AST_CONTROL_END_OF_Q:
+ 	case AST_CONTROL_UPDATE_RTP_PEER:
++	case AST_CONTROL_CNG_END:
+ 		break;
+ 
+ 	case AST_CONTROL_INCOMPLETE:
+@@ -4594,6 +4595,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:
+@@ -7330,6 +7332,12 @@
+ 					ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen);
+ 				}
+ 				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:
+@@ -7351,6 +7359,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_VOICE) ||
+ 		    (f->frametype == AST_FRAME_DTMF_BEGIN) ||
+ 		    (f->frametype == AST_FRAME_DTMF) ||
+@@ -8358,6 +8381,305 @@
+ }
+ 
+ 
++#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.
++	 * Remember that slin uses signed dword samples */
++	len = samples * sizeof (int16_t);
++	if(!(buf = ast_alloca(len))) {
++		ast_log(LOG_WARNING, "Unable to allocate buffer to generate %d samples\n", samples);
++		return -1;
++	}
++
++	/* Setup frame */
++	memcpy(&f, &noiseframedefaults, sizeof (f));
++	f.data.ptr = buf;
++	f.datalen = len;
++	f.samples = samples;
++
++	/* Let's put together our frame "data" */
++	pbuf = buf;
++
++#ifdef LOW_MEMORY
++	/* We need to generate samples every time we are called */
++	for (i = 0; i < samples; i += 2) {
++		box_muller_rng(noisestddev, &randomnumber[0], &randomnumber[1]);
++		for (j = 0; j < 2; j++) {
++			sampleamplitude = randomnumber[j];
++			if (sampleamplitude > 32767.0)
++				sampleamplitude = 32767.0;
++			else if (sampleamplitude < -32767.0)
++				sampleamplitude = -32767.0;
++			*(pbuf++) = (int16_t)sampleamplitude;
++		}
++	}
++#else
++	/*
++	 * We are going to use pregenerated samples. But we start at
++	 * different points on the pregenerated samples buffer every time
++	 * to create a little bit more randomness
++	 *
++	 */
++	start = (uint16_t) (65536.0 * random() / RAND_MAX);
++	for (i = 0; i < samples; i++) {
++		*(pbuf++) = (int16_t)(noisestddev * pregeneratedsamples[start++]);
++	}
++#endif
++
++	/* Send it out */
++	if (ast_write(chan, &f) < 0) {
++		ast_log(LOG_WARNING, "Failed to write frame to channel '%s'\n", chan->name);
++		return -1;
++	}
++	return 0;
++}
++
++static struct ast_generator noise_generator = 
++{
++	alloc: noise_generator_alloc,
++	release: noise_generator_release,
++	generate: noise_generate,
++} ;
++
++static void *cng_channel_params_copy(void *data)
++{
++	const struct ast_noise_generator *src = data;
++	struct ast_noise_generator *dest = ast_calloc(1, sizeof(struct ast_noise_generator));
++	if (!dest) {
++		return NULL;
++	}
++	dest->level = src->level;
++	dest->old_write_format = src->old_write_format;
++	return dest;
++}
++
++static void cng_channel_params_destroy(void *data)
++{
++	struct ast_noise_generator *ng = data;
++	ast_free(ng);
++}
++
++static const struct ast_datastore_info cng_channel_datastore_info = {
++	.type = "Comfort Noise Generator",
++	.duplicate = cng_channel_params_copy,
++	.destroy = cng_channel_params_destroy,
++};
++
++static int ast_channel_cng_params_init(struct ast_channel *chan, int level, int old_write_format)
++{
++	struct ast_noise_generator *new_cng;
++	struct ast_datastore *cng_datastore;
++
++	/* If we already have a datastore, reuse it */
++	if ((cng_datastore = ast_channel_datastore_find(chan, &cng_channel_datastore_info, NULL))) {
++		new_cng = cng_datastore->data;
++	} else {
++		/* Create new datastore */
++		new_cng = ast_calloc(1, sizeof(struct ast_noise_generator));
++		if (!new_cng) {
++			return -1;
++		}
++
++		if (!(cng_datastore = ast_datastore_alloc(&cng_channel_datastore_info, NULL))) {
++			cng_channel_params_destroy(new_cng);
++			return -1;
++		}
++		cng_datastore->data = new_cng;
++		ast_channel_datastore_add(chan, cng_datastore);
++	}
++	new_cng->level = level;
++	new_cng->old_write_format = old_write_format;
++
++	return 0;
++}
++
++struct ast_noise_generator *ast_channel_start_noise_generator(struct ast_channel *chan, const float level)
++{
++	struct ast_noise_generator  *state;
++	int i;
++
++#ifndef LOW_MEMORY
++	/* This should only be done once per asterisk instance */
++	if (pregeneratedsamples[0] == 0.0) {
++		for (i = 0; i < sizeof (pregeneratedsamples) / sizeof (pregeneratedsamples[0]); i += 2) {
++			box_muller_rng(1.0, &pregeneratedsamples[i], &pregeneratedsamples[i + 1]);
++		}
++	}
++#endif
++
++	if (level > 0) {
++		ast_log(LOG_ERROR, "Noise generator: Invalid argument -  non-positive floating-point argument for noise level in dBov's required \n");
++		return NULL;
++	}
++
++	if (!(state = ast_calloc(1, sizeof(*state)))) {
++		return NULL;
++	}
++
++	state->old_write_format = chan->writeformat;
++	state->level = level;
++
++	if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
++		ast_log(LOG_ERROR, "Could not set write format to SLINEAR\n");
++		ast_free(state);
++		return NULL;
++	}
++
++	/* Store noise generation data in a channel datastore */
++	ast_channel_cng_params_init(chan, level, chan->writeformat);
++
++	ast_activate_generator(chan, &noise_generator, state);
++
++	ast_debug(1, "Started noise generator on '%s'\n", chan->name);
++
++	return state;
++}
++
++void ast_channel_stop_noise_generator(struct ast_channel *chan, struct ast_noise_generator *state)
++{
++	struct ast_datastore *cng_datastore;
++	struct ast_noise_generator *cngstate = state;
++
++	if (!cngstate) {
++		if (!(cng_datastore = ast_channel_datastore_find(chan, &cng_channel_datastore_info, NULL))) {
++			return;
++		}
++		cngstate = cng_datastore->data;
++	}
++
++	/* We will leave the allocated channel datastore in memory for reuse */
++	ast_deactivate_generator(chan);
++
++	ast_debug(1, "Stopped silence generator on '%s'\n", chan->name);
++
++	if (ast_set_write_format(chan, cngstate->old_write_format) < 0)
++		ast_log(LOG_ERROR, "Could not return write format to its original state\n");
++
++	ast_free(state);
++}
++
++
++
+ /*! \ brief Convert channel reloadreason (ENUM) to text string for manager event */
+ const char *channelreloadreason2txt(enum channelreloadreason reason)
+ {
+Index: main/features.c
+===================================================================
+--- main/features.c	(.../branches/1.8)	(revision 411922)
++++ main/features.c	(.../team/oej/roibos-cng-support-1.8)	(revision 411922)
+@@ -4232,6 +4232,12 @@
+ 				}
+ 				ast_indicate_data(other, f->subclass.integer, f->data.ptr, f->datalen);
+ 				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(2, "*** Bridge got CNG END frame \n");
++				ast_channel_stop_noise_generator(other, NULL);
++				break;
+ 			case AST_CONTROL_AOC:
+ 			case AST_CONTROL_HOLD:
+ 			case AST_CONTROL_UNHOLD:
+@@ -4351,6 +4357,21 @@
+ 					ast_debug(1, "Set feature timer to %ld ms\n", config->feature_timer);
+ 				}
+ 			}
++		} else 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) \n");
++				ast_moh_start(other, NULL, NULL);
++			}
++			
+ 		}
+ 		if (f)
+ 			ast_frfree(f);
+Index: main/frame.c
+===================================================================
+--- main/frame.c	(.../branches/1.8)	(revision 411922)
++++ main/frame.c	(.../team/oej/roibos-cng-support-1.8)	(revision 411922)
+@@ -126,6 +126,7 @@
+ 	{ AST_FORMAT_SIREN14, "siren14", 32000, "ITU G.722.1 Annex C, (Siren14, licensed from Polycom)", 120, 20, 80, 20, 20 },	/*!< Binary commercial distribution */
+ 	{ AST_FORMAT_TESTLAW, "testlaw", 8000, "G.711 test-law", 80, 10, 150, 10, 20 },                        /*!< codec_ulaw.c */
+ 	{ AST_FORMAT_G719, "g719", 48000, "ITU G.719", 160, 20, 80, 20, 20 },
++	{ AST_FORMAT_CN, "cn", 8000, "Comfort Noise"},
+ };
+ 
+ struct ast_frame ast_null_frame = { AST_FRAME_NULL, };
+Index: main/rtp_engine.c
+===================================================================
+--- main/rtp_engine.c	(.../branches/1.8)	(revision 411922)
++++ main/rtp_engine.c	(.../team/oej/roibos-cng-support-1.8)	(revision 411922)
+@@ -162,7 +162,7 @@
+ 	[16] = {1, AST_FORMAT_ADPCM}, /* 11.025 kHz */
+ 	[17] = {1, AST_FORMAT_ADPCM}, /* 22.050 kHz */
+ 	[18] = {1, AST_FORMAT_G729A},
+-	[19] = {0, AST_RTP_CN},         /* Also used for CN */
++	[19] = {1, AST_FORMAT_CN},         /* Also used for CN */
+ 	[26] = {1, AST_FORMAT_JPEG},
+ 	[31] = {1, AST_FORMAT_H261},
+ 	[34] = {1, AST_FORMAT_H263},
+Index: configs/sip.conf.sample
+===================================================================
+--- configs/sip.conf.sample	(.../branches/1.8)	(revision 411922)
++++ configs/sip.conf.sample	(.../team/oej/roibos-cng-support-1.8)	(revision 411922)
+@@ -300,6 +300,12 @@
+ ;allow=ulaw                     ; Allow codecs in order of preference
+ ;allow=ilbc                     ; see https://wiki.asterisk.org/wiki/display/AST/RTP+Packetization
+ 				; for framing options
++;comfort-noise=yes		; Enable Comfort Noise generation on RTP streams
++;				; 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 internal
++;				; timing support enabled in asterisk.conf
+ ;autoframing=yes		; Set packetization based on the remote endpoint's (ptime)
+ 				; preferences. Defaults to no.
+ ;
+Index: res/res_noise.c
+===================================================================
+--- res/res_noise.c	(.../branches/1.8)	(revision 0)
++++ res/res_noise.c	(.../team/oej/roibos-cng-support-1.8)	(revision 411922)
+@@ -0,0 +1,164 @@
++/*
++ * Asterisk -- An open source telephony toolkit.
++ *
++ * Copyright (C) 1999 - 2005, Digium, Inc.
++ *
++ * Contributed by Carlos Antunes <cmantunes at gmail.com>
++ *
++ * 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 Just generate white noise 
++ * 
++ */
++
++/*** MODULEINFO
++	<support_level>random</support_level>
++ ***/
++
++
++#include "asterisk.h"
++
++ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
++
++#include "asterisk/lock.h"
++#include "asterisk/file.h"
++#include "asterisk/logger.h"
++#include "asterisk/channel.h"
++#include "asterisk/pbx.h"
++#include "asterisk/module.h"
++#include "asterisk/app.h"
++
++#include <math.h>
++
++static char *app = "WhiteNoise";
++
++
++/*** DOCUMENTATION
++	<application name="WhiteNoise" language="en_US">
++		<synopsis>
++			Generates white noise
++		</synopsis>
++		<syntax>
++		<parameter name="args">
++			<argument name="timeout" required="true" />
++			<argument name="level" required="false" />
++		</parameter>
++		</syntax>
++		<description>
++			<para>Generates white noise at 'level' dBov's for 'timeout' seconds or indefinitely if timeout
++			is absent or is zero.</para>
++			<para>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.</para>
++
++		</description>
++	</application>
++***/
++
++static int noise_exec(struct ast_channel *chan, const char *data) {
++
++	struct ast_module_user *u;
++	char *excessdata;
++	float level = 0;
++	float timeout = 0;
++	char *s;
++	int res;
++	struct ast_noise_generator *gendata;
++
++        AST_DECLARE_APP_ARGS(args,
++                AST_APP_ARG(timeout);
++                AST_APP_ARG(level);
++        );
++
++	/* Verify we potentially have arguments and get local copy */
++        if (!data) {
++                ast_log(LOG_WARNING, "WhiteNoise usage following: WhiteNoise([timeout[, level]])\n");
++                return -1;
++        }
++	
++	/* Separate arguments */	
++        s = ast_strdupa(data);
++        AST_STANDARD_APP_ARGS(args, s);
++
++	if (args.timeout) {	
++		/* Extract second argument, if available, and validate
++		 * timeout is non-negative. Zero timeout means no timeout */
++		args.timeout = ast_trim_blanks(args.timeout);
++		timeout = strtof(args.timeout, &excessdata);
++		if ((excessdata && *excessdata) || timeout < 0) {
++			ast_log(LOG_WARNING, "Invalid argument 'timeout': WhiteNoise requires non-negative floating-point argument for timeout in seconds\n");				
++			return -1;
++		}
++
++		/* Convert timeout to milliseconds
++		 * and ensure minimum of 20ms      */
++		timeout = roundf(timeout * 1000.0);
++		if (timeout > 0 && timeout < 20) {
++			timeout = 20;
++		}
++	} 
++
++	if (args.level) {
++		/* Extract first argument and ensure we have
++		 * a valid noise level argument value        */
++		args.level = ast_trim_blanks(args.level);
++		level = strtof(args.level, &excessdata);
++		if ((excessdata && *excessdata) || level > 0) {
++			ast_log(LOG_ERROR, "Invalid argument 'level': WhiteNoise requires non-positive floating-point argument for noise level in dBov's\n");
++			return -1;
++		}
++	} 
++
++	ast_debug(1, "Setting up white noise generator with level %.1fdBov's and %.0fms %stimeout\n", level, timeout, timeout == 0 ? "(no) " : "");
++
++	u = ast_module_user_add(chan);
++	if (chan->_state != AST_STATE_UP) {
++		ast_answer(chan);
++	}
++	gendata = ast_channel_start_noise_generator(chan, level);
++	if (data == NULL)	{
++		ast_log(LOG_WARNING, "Failed to activate white noise generator on '%s'\n",chan->name);
++		res = -1;
++	} else {
++		/* Just do the noise... */
++		res = -1;
++		if (timeout > 0) {
++			res = ast_safe_sleep(chan, timeout);
++		} else  {
++			while(!ast_safe_sleep(chan, 10000));
++		}
++		ast_channel_stop_noise_generator(chan, gendata);
++	}
++	ast_module_user_remove(u);
++	return res;
++}
++
++static int unload_module(void) {
++	ast_module_user_hangup_all();
++	
++	return ast_unregister_application(app);
++}
++
++static int load_module(void) {
++	return ast_register_application_xml(app, noise_exec);
++}
++
++AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "White Noise Generator Application",
++                .load = load_module,
++                .unload = unload_module,
++               );
+
+Property changes on: res/res_noise.c
+___________________________________________________________________
+Added: svn:eol-style
+## -0,0 +1 ##
++native
+\ No newline at end of property
+Added: svn:mime-type
+## -0,0 +1 ##
++text/plain
+\ No newline at end of property
+Added: svn:keywords
+## -0,0 +1 ##
++Author Date Id Revision
+\ No newline at end of property
+Index: res/res_rtp_asterisk.c
+===================================================================
+--- res/res_rtp_asterisk.c	(.../branches/1.8)	(revision 411922)
++++ res/res_rtp_asterisk.c	(.../team/oej/roibos-cng-support-1.8)	(revision 411922)
+@@ -114,6 +114,7 @@
+ #define FLAG_NAT_INACTIVE_NOWARN        (1 << 1)
+ #define FLAG_NEED_MARKER_BIT            (1 << 3)
+ #define FLAG_DTMF_COMPENSATE            (1 << 4)
++#define FLAG_CN_ACTIVE			(1 << 5)
+ 
+ /*! \brief RTP session description */
+ struct ast_rtp {
+@@ -1753,17 +1754,8 @@
+ 	/* Convert comfort noise into audio with various codecs.  Unfortunately this doesn't
+ 	   totally help us out becuase we don't have an engine to keep it going and we are not
+ 	   guaranteed to have it every 20ms or anything */
+-	if (rtpdebug)
++	if (rtpdebug) {
+ 		ast_debug(0, "- RTP 3389 Comfort noise event: Level %" PRId64 " (len = %d)\n", rtp->lastrxformat, len);
+-
+-	if (ast_test_flag(rtp, FLAG_3389_WARNING)) {
+-		struct ast_sockaddr remote_address = { {0,} };
+-
+-		ast_rtp_instance_get_remote_address(instance, &remote_address);
+-
+-		ast_log(LOG_NOTICE, "Comfort noise support incomplete in Asterisk (RFC 3389). Please turn off on client if possible. Client address: %s\n",
+-			ast_sockaddr_stringify(&remote_address));
+-		ast_set_flag(rtp, FLAG_3389_WARNING);
+ 	}
+ 
+ 	/* Must have at least one byte */
+@@ -1780,10 +1772,22 @@
+ 		rtp->f.datalen = 0;
+ 	}
+ 	rtp->f.frametype = AST_FRAME_CNG;
++		/* The noise level is expressed in -dBov with values 0 to 127, representing 0 to -127 dBov
++		   It's in bits 1-7 in the payload. Bit 0 is always 0.
++		*/
+ 	rtp->f.subclass.integer = data[0] & 0x7f;
+ 	rtp->f.samples = 0;
+ 	rtp->f.delivery.tv_usec = rtp->f.delivery.tv_sec = 0;
+ 
++	if(!ast_test_flag(rtp, FLAG_CN_ACTIVE)) {
++		ast_set_flag(rtp, FLAG_CN_ACTIVE);
++		ast_debug(2, "ACTIVATING Comfort Noise on channel Level - %d\n", rtp->f.subclass.integer);
++		/* Start the generator on the other end. */
++	
++	} else {
++		/* Check if the level is the same. If not, reactivate. */
++	}
++
+ 	return &rtp->f;
+ }
+ 
+@@ -2368,6 +2372,15 @@
+ 	/* If the payload is not actually an Asterisk one but a special one pass it off to the respective handler */
+ 	if (!payload.asterisk_format) {
+ 		struct ast_frame *f = NULL;
++		if (payload.code != AST_RTP_CN && ast_test_flag(rtp, FLAG_CN_ACTIVE)) {
++			/* Insert a control frame to indicate that we need to shut down Comfort Noise generators, if active */
++			struct ast_frame cngoff = { AST_FRAME_CONTROL, { AST_CONTROL_CNG_END, } };
++			ast_debug(2, "DEACTIVATING Comfort Noise \n");
++			ast_clear_flag(rtp, FLAG_CN_ACTIVE);
++			f = ast_frdup(&cngoff);
++			AST_LIST_INSERT_TAIL(&frames, f, frame_list);
++			f = NULL;
++		}
+ 		if (payload.code == AST_RTP_DTMF) {
+ 			/* process_dtmf_rfc2833 may need to return multiple frames. We do this
+ 			 * by passing the pointer to the frame list to it so that the method
+@@ -2395,6 +2408,15 @@
+ 		}
+ 		return &ast_null_frame;
+ 	}
++	if (ast_test_flag(rtp, FLAG_CN_ACTIVE)) {
++		struct ast_frame *f = NULL;
++		struct ast_frame cngoff = { AST_FRAME_CONTROL, { AST_CONTROL_CNG_END, } };
++		ast_debug(2, "DEACTIVATING Comfort Noise \n");
++		ast_clear_flag(rtp, FLAG_CN_ACTIVE);
++		f = ast_frdup(&cngoff);
++		AST_LIST_INSERT_TAIL(&frames, f, frame_list);
++		f = NULL;
++	}
+ 
+ 	rtp->lastrxformat = rtp->f.subclass.codec = payload.code;
+ 	rtp->f.frametype = (rtp->f.subclass.codec & AST_FORMAT_AUDIO_MASK) ? AST_FRAME_VOICE : (rtp->f.subclass.codec & AST_FORMAT_VIDEO_MASK) ? AST_FRAME_VIDEO : AST_FRAME_TEXT;
+Index: README.roibos-cng.txt
+===================================================================
+--- README.roibos-cng.txt	(.../branches/1.8)	(revision 0)
++++ README.roibos-cng.txt	(.../team/oej/roibos-cng-support-1.8)	(revision 411922)
+@@ -0,0 +1,174 @@
++Edvina AB
++Olle E. Johansson
++
++
++Started: 2012-09-18
++Updated: 2013-12-05
++
++
++
++
++
++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
++=====================

[... 998 lines stripped ...]



More information about the asterisk-commits mailing list