[svn-commits] oej: branch oej/roibus-comfort-noise-trunk r377007 - in /team/oej/roibus-comf...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Mon Dec 3 04:32:42 CST 2012


Author: oej
Date: Mon Dec  3 04:32:25 2012
New Revision: 377007

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=377007
Log:
Adaption of comfort noise for trunk

Modified:
    team/oej/roibus-comfort-noise-trunk/README.roibos-cng.txt
    team/oej/roibus-comfort-noise-trunk/channels/chan_sip.c
    team/oej/roibus-comfort-noise-trunk/channels/sip/include/sip.h
    team/oej/roibus-comfort-noise-trunk/configs/sip.conf.sample
    team/oej/roibus-comfort-noise-trunk/include/asterisk/channel.h
    team/oej/roibus-comfort-noise-trunk/include/asterisk/format.h
    team/oej/roibus-comfort-noise-trunk/include/asterisk/frame.h
    team/oej/roibus-comfort-noise-trunk/main/channel.c
    team/oej/roibus-comfort-noise-trunk/main/features.c
    team/oej/roibus-comfort-noise-trunk/main/format.c
    team/oej/roibus-comfort-noise-trunk/main/rtp_engine.c
    team/oej/roibus-comfort-noise-trunk/res/res_rtp_asterisk.c

Modified: team/oej/roibus-comfort-noise-trunk/README.roibos-cng.txt
URL: http://svnview.digium.com/svn/asterisk/team/oej/roibus-comfort-noise-trunk/README.roibos-cng.txt?view=diff&rev=377007&r1=377006&r2=377007
==============================================================================
--- team/oej/roibus-comfort-noise-trunk/README.roibos-cng.txt (original)
+++ team/oej/roibus-comfort-noise-trunk/README.roibos-cng.txt Mon Dec  3 04:32:25 2012
@@ -155,3 +155,160 @@
 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.
 
 
+Edvina AB
+Olle E. Johansson
+
+
+Started: 2012-09-18
+Updated: 2012-11-19
+
+
+
+
+
+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.
+
+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
+  - As step 2, add silence detection to calls
+  	- Measure noise level
+  	- Start sending CNG
+  	- Listen for talk detection
+  	- Stop sending CNG, send media
+
+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
+
+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 
+- CNG Comfort Noise Generator
+
+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.
+
+

Modified: team/oej/roibus-comfort-noise-trunk/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/team/oej/roibus-comfort-noise-trunk/channels/chan_sip.c?view=diff&rev=377007&r1=377006&r2=377007
==============================================================================
--- team/oej/roibus-comfort-noise-trunk/channels/chan_sip.c (original)
+++ team/oej/roibus-comfort-noise-trunk/channels/chan_sip.c Mon Dec  3 04:32:25 2012
@@ -92,7 +92,7 @@
  * the sip_hangup() function
  */
 
-/*! \li \ref chan_sip.c uses configuration files \ref sip.conf and \ref sip_notify.conf
+/*! \li \ref chan_sip.c uses configuration files \ref sip.conf, \ref sip2cause.conf and \ref sip_notify.conf
  * \addtogroup configuration_file
  */
 
@@ -102,6 +102,10 @@
 
 /*! \page sip_notify.conf sip_notify.conf
  * \verbinclude sip_notify.conf.sample
+ */
+
+/*! \page sip2cause.conf sip2cause.conf
+ * \verbinclude sip2cause.conf.sample
  */
 
 /*!
@@ -4973,6 +4977,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;
 	}
@@ -6103,6 +6111,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->directmediaacl = ast_duplicate_acl_list(peer->directmediaacl);
 
@@ -10518,7 +10530,7 @@
 		   they are acceptable */
 		ast_format_cap_copy(p->jointcaps, newjointcapability);                /* Our joint codec profile for this call */
 		ast_format_cap_copy(p->peercaps, 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)) {
@@ -12649,6 +12661,7 @@
 		fmt = ast_codec_pref_getsize(pref, format);
 	} 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, mime, rate);
 
@@ -12772,7 +12785,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)
@@ -13205,10 +13218,12 @@
 		}
 		ast_format_cap_iter_end(tmpcap);
 
-		/* 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(3, "NOT Adding non-codec 0x%x (%s) to SDP\n", x, ast_rtp_lookup_mime_subtype2(0, x, 0)); */
 				continue;
+			}
 
 			add_noncodec_to_sdp(p, x, &m_audio, &a_audio, debug);
 		}
@@ -18127,6 +18142,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;
@@ -19850,6 +19868,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)));
@@ -19978,6 +19997,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);
@@ -20437,6 +20457,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", autocreatepeer2str(sip_cfg.autocreatepeer));
 	ast_cli(a->fd, "  Match Auth Username:    %s\n", AST_CLI_YESNO(global_match_auth_username));
@@ -20969,6 +20990,7 @@
 			ast_cli(a->fd, "  Format:                 %s\n", cur->owner ? ast_getformatname_multiple(formatbuf, sizeof(formatbuf), ast_channel_nativeformats(cur->owner)) : "(nothing)" );
 			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));
@@ -29926,6 +29948,8 @@
 	} 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_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_ALLOW_CN);
 	} else
 		res = 0;
 

Modified: team/oej/roibus-comfort-noise-trunk/channels/sip/include/sip.h
URL: http://svnview.digium.com/svn/asterisk/team/oej/roibus-comfort-noise-trunk/channels/sip/include/sip.h?view=diff&rev=377007&r1=377006&r2=377007
==============================================================================
--- team/oej/roibus-comfort-noise-trunk/channels/sip/include/sip.h (original)
+++ team/oej/roibus-comfort-noise-trunk/channels/sip/include/sip.h Mon Dec  3 04:32:25 2012
@@ -356,6 +356,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 | \
@@ -363,7 +364,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 */

Modified: team/oej/roibus-comfort-noise-trunk/configs/sip.conf.sample
URL: http://svnview.digium.com/svn/asterisk/team/oej/roibus-comfort-noise-trunk/configs/sip.conf.sample?view=diff&rev=377007&r1=377006&r2=377007
==============================================================================
--- team/oej/roibus-comfort-noise-trunk/configs/sip.conf.sample (original)
+++ team/oej/roibus-comfort-noise-trunk/configs/sip.conf.sample Mon Dec  3 04:32:25 2012
@@ -310,6 +310,13 @@
 ;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
 ;
 ; This option specifies a preference for which music on hold class this channel
 ; should listen to when put on hold if the music class has not been set on the

Modified: team/oej/roibus-comfort-noise-trunk/include/asterisk/channel.h
URL: http://svnview.digium.com/svn/asterisk/team/oej/roibus-comfort-noise-trunk/include/asterisk/channel.h?view=diff&rev=377007&r1=377006&r2=377007
==============================================================================
--- team/oej/roibus-comfort-noise-trunk/include/asterisk/channel.h (original)
+++ team/oej/roibus-comfort-noise-trunk/include/asterisk/channel.h Mon Dec  3 04:32:25 2012
@@ -2391,6 +2391,42 @@
  * to its previous write format.
  */
 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.

Modified: team/oej/roibus-comfort-noise-trunk/include/asterisk/format.h
URL: http://svnview.digium.com/svn/asterisk/team/oej/roibus-comfort-noise-trunk/include/asterisk/format.h?view=diff&rev=377007&r1=377006&r2=377007
==============================================================================
--- team/oej/roibus-comfort-noise-trunk/include/asterisk/format.h (original)
+++ team/oej/roibus-comfort-noise-trunk/include/asterisk/format.h Mon Dec  3 04:32:25 2012
@@ -101,6 +101,8 @@
 	AST_FORMAT_SLINEAR192       = 27 + AST_FORMAT_TYPE_AUDIO,
 	AST_FORMAT_SPEEX32          = 28 + AST_FORMAT_TYPE_AUDIO,
 	AST_FORMAT_CELT             = 29 + AST_FORMAT_TYPE_AUDIO,
+	/*! Comfort noise. Not a frame type per se, but needs to be added as a codec */
+	AST_FORMAT_CN	    	    = 30 + AST_FORMAT_TYPE_AUDIO,
 
 	/*! H.261 Video */
 	AST_FORMAT_H261             = 1 + AST_FORMAT_TYPE_VIDEO,

Modified: team/oej/roibus-comfort-noise-trunk/include/asterisk/frame.h
URL: http://svnview.digium.com/svn/asterisk/team/oej/roibus-comfort-noise-trunk/include/asterisk/frame.h?view=diff&rev=377007&r1=377006&r2=377007
==============================================================================
--- team/oej/roibus-comfort-noise-trunk/include/asterisk/frame.h (original)
+++ team/oej/roibus-comfort-noise-trunk/include/asterisk/frame.h Mon Dec  3 04:32:25 2012
@@ -267,6 +267,7 @@
 	AST_CONTROL_MCID = 31,			/*!< Indicate that the caller is being malicious. */
 	AST_CONTROL_UPDATE_RTP_PEER = 32, /*!< Interrupt the bridge and have it update the peer */
 	AST_CONTROL_PVT_CAUSE_CODE = 33, /*!< Contains an update to the protocol-specific cause-code stored for branching dials */
+	AST_CONTROL_CNG_END = 34,       /*!< Disable CNG playout in bridge */
 };
 
 enum ast_frame_read_action {
@@ -431,6 +432,10 @@
  * Option data is an integer value of 0 or 1 */
 #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;

Modified: team/oej/roibus-comfort-noise-trunk/main/channel.c
URL: http://svnview.digium.com/svn/asterisk/team/oej/roibus-comfort-noise-trunk/main/channel.c?view=diff&rev=377007&r1=377006&r2=377007
==============================================================================
--- team/oej/roibus-comfort-noise-trunk/main/channel.c (original)
+++ team/oej/roibus-comfort-noise-trunk/main/channel.c Mon Dec  3 04:32:25 2012
@@ -4448,6 +4448,7 @@
 	case AST_CONTROL_MCID:
 	case AST_CONTROL_UPDATE_RTP_PEER:
 	case AST_CONTROL_PVT_CAUSE_CODE:
+	case AST_CONTROL_CNG_END:
 		break;
 
 	case AST_CONTROL_INCOMPLETE:
@@ -4635,6 +4636,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:
@@ -7630,6 +7632,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:
@@ -7650,6 +7658,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) ||
@@ -8840,6 +8863,306 @@
 	}
 	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.format.id = 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", ast_channel_name(chan));
+		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;
+	}
+
+	ast_format_copy(&state->old_write_format, ast_channel_writeformat(chan));
+	state->level = level;
+
+	if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
+		ast_log(LOG_ERROR, "Could not set write format to SLINEAR in channel %s\n", ast_channel_name(chan));
+		ast_free(state);
+		return NULL;
+	}
+
+	/* Store noise generation data in a channel datastore */
+	ast_channel_cng_params_init(chan, level, ast_channel_writeformat(chan));
+
+	ast_activate_generator(chan, &noise_generator, state);
+
+	ast_debug(1, "Started noise generator on '%s'\n", ast_channel_name(chan));
+
+	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", ast_channel_name(chan));
+
+	if (ast_set_write_format(chan, &cngstate->old_write_format) < 0)
+		ast_log(LOG_ERROR, "Could not return write format to its original state on channel %s\n", ast_channel_name(chan));
+
+	ast_free(state);
+}
+
 
 
 /*! \ brief Convert channel reloadreason (ENUM) to text string for manager event */

Modified: team/oej/roibus-comfort-noise-trunk/main/features.c
URL: http://svnview.digium.com/svn/asterisk/team/oej/roibus-comfort-noise-trunk/main/features.c?view=diff&rev=377007&r1=377006&r2=377007
==============================================================================
--- team/oej/roibus-comfort-noise-trunk/main/features.c (original)
+++ team/oej/roibus-comfort-noise-trunk/main/features.c Mon Dec  3 04:32:25 2012
@@ -4588,6 +4588,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(3, "*** Bridge got CNG END frame \n");
+				ast_channel_stop_noise_generator(other, NULL);
+ 				break;
 			case AST_CONTROL_PVT_CAUSE_CODE:
 			case AST_CONTROL_AOC:
 			case AST_CONTROL_HOLD:
@@ -4709,6 +4715,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);

Modified: team/oej/roibus-comfort-noise-trunk/main/format.c
URL: http://svnview.digium.com/svn/asterisk/team/oej/roibus-comfort-noise-trunk/main/format.c?view=diff&rev=377007&r1=377006&r2=377007
==============================================================================
--- team/oej/roibus-comfort-noise-trunk/main/format.c (original)
+++ team/oej/roibus-comfort-noise-trunk/main/format.c Mon Dec  3 04:32:25 2012
@@ -1048,6 +1048,7 @@
 	format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_ILBC, 0), "ilbc", 8000, "iLBC", 50, 30, 30, 30, 30, 0, 0);                 /*!< codec_ilbc.c */ /* inc=30ms - workaround */
 	format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_G726_AAL2, 0), "g726aal2", 8000, "G.726 AAL2", 40, 10, 300, 10, 20, 0, 0); /*!< codec_g726.c */
 	format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_G722, 0), "g722", 16000, "G722", 80, 10, 150, 10, 20, 0, 0);               /*!< codec_g722.c */
+	format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_CN, 0), "Comfort Noise", 8000, "CN", 0, 0, 0, 0, 20, 0, 0);
 	format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR16, 0), "slin16", 16000, "16 bit Signed Linear PCM (16kHz)", 320, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (16kHz) */
 	format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_JPEG, 0), "jpeg", 0, "JPEG image", 0, 0, 0, 0 ,0 ,0 ,0);          /*!< See format_jpeg.c */
 	format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_PNG, 0), "png", 0, "PNG image", 0, 0, 0, 0 ,0 ,0 ,0);             /*!< PNG Image format */

Modified: team/oej/roibus-comfort-noise-trunk/main/rtp_engine.c
URL: http://svnview.digium.com/svn/asterisk/team/oej/roibus-comfort-noise-trunk/main/rtp_engine.c?view=diff&rev=377007&r1=377006&r2=377007
==============================================================================
--- team/oej/roibus-comfort-noise-trunk/main/rtp_engine.c (original)
+++ team/oej/roibus-comfort-noise-trunk/main/rtp_engine.c Mon Dec  3 04:32:25 2012
@@ -2265,7 +2265,7 @@
 	set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_G726_AAL2, 0), 0, "audio", "AAL2-G726-32", 8000);
 	set_next_mime_type(NULL, AST_RTP_DTMF, "audio", "telephone-event", 8000);
 	set_next_mime_type(NULL, AST_RTP_CISCO_DTMF, "audio", "cisco-telephone-event", 8000);
-	set_next_mime_type(NULL, AST_RTP_CN, "audio", "CN", 8000);
+	set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_CN, 0), AST_FORMAT_CN, "audio", "CN", 8000);
 	set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_JPEG, 0), 0, "video", "JPEG", 90000);
 	set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_PNG, 0), 0, "video", "PNG", 90000);
 	set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_H261, 0), 0, "video", "H261", 90000);

Modified: team/oej/roibus-comfort-noise-trunk/res/res_rtp_asterisk.c
URL: http://svnview.digium.com/svn/asterisk/team/oej/roibus-comfort-noise-trunk/res/res_rtp_asterisk.c?view=diff&rev=377007&r1=377006&r2=377007
==============================================================================
--- team/oej/roibus-comfort-noise-trunk/res/res_rtp_asterisk.c (original)
+++ team/oej/roibus-comfort-noise-trunk/res/res_rtp_asterisk.c Mon Dec  3 04:32:25 2012
@@ -158,7 +158,7 @@
 /*! \brief Notification that the ICE/TURN worker thread should stop */
 static int worker_terminate;
 
-#define FLAG_3389_WARNING               (1 << 0)
+#define FLAG_CN_ACTIVE			(1 << 0)
 #define FLAG_NAT_ACTIVE                 (3 << 1)
 #define FLAG_NAT_INACTIVE               (0 << 1)
 #define FLAG_NAT_INACTIVE_NOWARN        (1 << 1)
@@ -3010,17 +3010,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 %d (len = %d)\n", (int) rtp->lastrxformat.id, 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 */
@@ -3037,9 +3028,21 @@
 		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(0, "###### 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;
 }
@@ -3650,6 +3653,15 @@
 	if (!rtp->themssrc) {
 		rtp->themssrc = ntohl(rtpheader[2]); /* Record their SSRC to put in future RR */
 	}
+	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(0, "####### 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 (rtp_debug_test_addr(&addr)) {

[... 19 lines stripped ...]



More information about the svn-commits mailing list