[svn-commits] kpfleming: trunk r349249 - in /trunk: ./ include/asterisk/ main/ res/

SVN commits to the Digium repositories svn-commits at lists.digium.com
Wed Dec 28 12:59:19 CST 2011


Author: kpfleming
Date: Wed Dec 28 12:59:16 2011
New Revision: 349249

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=349249
Log:
Improve T.38 gateway V.21 preamble detection.

This commit removes the V.21 preamble detection code previously added to the
generic DSP implementation in Asterisk, and instead enhances the res_fax module
to be able to utilize V.21 preamble detection functionality made available by
FAX technology modules. This commit also adds such support to res_fax_spandsp,
which uses the Spandsp modem tone detection code to do the V.21 preamble
detection.

There should be no functional change here, other than much more reliable V.21
preamble detection (and thus T.38 gateway initiation).
........

Merged revisions 349248 from http://svn.asterisk.org/svn/asterisk/branches/10

Modified:
    trunk/   (props changed)
    trunk/include/asterisk/dsp.h
    trunk/include/asterisk/res_fax.h
    trunk/main/dsp.c
    trunk/res/res_fax.c
    trunk/res/res_fax_spandsp.c

Propchange: trunk/
------------------------------------------------------------------------------
Binary property 'branch-10-merged' - no diff available.

Modified: trunk/include/asterisk/dsp.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/dsp.h?view=diff&rev=349249&r1=349248&r2=349249
==============================================================================
--- trunk/include/asterisk/dsp.h (original)
+++ trunk/include/asterisk/dsp.h Wed Dec 28 12:59:16 2011
@@ -45,9 +45,8 @@
 
 #define DSP_FAXMODE_DETECT_CNG		(1 << 0)
 #define DSP_FAXMODE_DETECT_CED		(1 << 1)
-#define DSP_FAXMODE_DETECT_V21		(1 << 2)
-#define DSP_FAXMODE_DETECT_SQUELCH	(1 << 3)
-#define DSP_FAXMODE_DETECT_ALL	(DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED | DSP_FAXMODE_DETECT_V21)
+#define DSP_FAXMODE_DETECT_SQUELCH	(1 << 2)
+#define DSP_FAXMODE_DETECT_ALL	(DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED)
 
 #define DSP_TONE_STATE_SILENCE  0
 #define DSP_TONE_STATE_RINGING  1

Modified: trunk/include/asterisk/res_fax.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/res_fax.h?view=diff&rev=349249&r1=349248&r2=349249
==============================================================================
--- trunk/include/asterisk/res_fax.h (original)
+++ trunk/include/asterisk/res_fax.h Wed Dec 28 12:59:16 2011
@@ -44,6 +44,8 @@
 	AST_FAX_TECH_MULTI_DOC = (1 << 4),
 	/*! T.38 - T.30 Gateway */
 	AST_FAX_TECH_GATEWAY = (1 << 5),
+	/*! V21 detection is supported */
+	AST_FAX_TECH_V21_DETECT = (1 << 6),
 };
 
 /*! \brief fax modem capabilities */
@@ -160,6 +162,8 @@
 			uint32_t send_cng:1;
 			/*! send a T.38 reinvite */
 			uint32_t request_t38:1;
+			/*! a V.21 preamble was detected */
+			uint32_t v21_detected:1;
 		};
 	} option;
 	/*! override the minimum transmission rate with a channel variable */

Modified: trunk/main/dsp.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/dsp.c?view=diff&rev=349249&r1=349248&r2=349249
==============================================================================
--- trunk/main/dsp.c (original)
+++ trunk/main/dsp.c Wed Dec 28 12:59:16 2011
@@ -243,20 +243,6 @@
 	int last_hit;		/* Indicates if the last processed block was a hit */
 
 } tone_detect_state_t;
-
-typedef struct
-{
-	int block_size;
-	goertzel_state_t tone;
-	float energy;		/* Accumulated energy of the current block */
-	int samples_pending;	/* Samples remain to complete the current block */
-
-	float threshold;	/* Energy of the tone relative to energy from all other signals to consider a hit */
-
-	int hit_count;
-	int miss_count;
-
-} v21_detect_state_t;
 
 typedef struct
 {
@@ -405,7 +391,6 @@
 	digit_detect_state_t digit_state;
 	tone_detect_state_t cng_tone_state;
 	tone_detect_state_t ced_tone_state;
-	v21_detect_state_t v21_state;
 };
 
 static void mute_fragment(struct ast_dsp *dsp, fragment_t *fragment)
@@ -479,56 +464,10 @@
 	ast_debug(1, "Setup tone %d Hz, %d ms, block_size=%d, hits_required=%d\n", freq, duration, s->block_size, s->hits_required);
 }
 
-static void ast_v21_detect_init(v21_detect_state_t *s, unsigned int sample_rate)
-{
-	float x;
-	int periods_in_block;
-
-	/* If we want to remove tone, it is important to have block size not
-	   to exceed frame size. Otherwise by the moment tone is detected it is too late
-	   to squelch it from previous frames. Block size is 20ms at the given sample rate.*/
-	s->block_size = (20 * sample_rate) / 1000;
-
-	periods_in_block = s->block_size * 1850 / sample_rate;
-
-	/* Make sure we will have at least 5 periods at target frequency for analisys.
-	   This may make block larger than expected packet and will make squelching impossible
-	   but at least we will be detecting the tone */
-	if (periods_in_block < 5) {
-		periods_in_block = 5;
-	}
-
-	/* Now calculate final block size. It will contain integer number of periods */
-	s->block_size = periods_in_block * sample_rate / 1850;
-
-	goertzel_init(&s->tone, 1850.0, s->block_size, sample_rate);
-
-	s->samples_pending = s->block_size;
-	s->hit_count = 0;
-	s->miss_count = 0;
-	s->energy = 0.0;
-
-	/* We want tone energy to be amp decibels above the rest of the signal (the noise).
-	   According to Parseval's theorem the energy computed in time domain equals to energy
-	   computed in frequency domain. So subtracting energy in the frequency domain (Goertzel result)
-	   from the energy in the time domain we will get energy of the remaining signal (without the tone
-	   we are detecting). We will be checking that
-		10*log(Ew / (Et - Ew)) > amp
-	   Calculate threshold so that we will be actually checking
-		Ew > Et * threshold
-	*/
-
-	x = pow(10.0, 16 / 10.0);
-	s->threshold = x / (x + 1);
-
-	ast_debug(1, "Setup v21 detector, block_size=%d\n", s->block_size);
-}
-
 static void ast_fax_detect_init(struct ast_dsp *s)
 {
 	ast_tone_detect_init(&s->cng_tone_state, FAX_TONE_CNG_FREQ, FAX_TONE_CNG_DURATION, FAX_TONE_CNG_DB, s->sample_rate);
 	ast_tone_detect_init(&s->ced_tone_state, FAX_TONE_CED_FREQ, FAX_TONE_CED_DURATION, FAX_TONE_CED_DB, s->sample_rate);
-	ast_v21_detect_init(&s->v21_state, s->sample_rate);
 	if (s->faxmode & DSP_FAXMODE_DETECT_SQUELCH) {
 		s->cng_tone_state.squelch = 1;
 		s->ced_tone_state.squelch = 1;
@@ -578,89 +517,6 @@
 	} else {
 		ast_dtmf_detect_init(&s->td.dtmf, sample_rate);
 	}
-}
-
-/*! \brief Detect a v21 preamble.
- * This code is derived from the tone_detect code and detects a pattern of 1850
- * Hz tone found in a v21 preamble.
- */
-static int v21_detect(struct ast_dsp *dsp, v21_detect_state_t *s, int16_t *amp, int samples)
-{
-	float tone_energy;
-	int i;
-	int hit = 0;
-	int limit;
-	int res = 0;
-	int16_t *ptr;
-	int start, end;
-
-	for (start = 0;  start < samples;  start = end) {
-		/* Process in blocks. */
-		limit = samples - start;
-		if (limit > s->samples_pending) {
-			limit = s->samples_pending;
-		}
-		end = start + limit;
-
-		for (i = limit, ptr = amp ; i > 0; i--, ptr++) {
-			/* signed 32 bit int should be enough to suqare any possible signed 16 bit value */
-			s->energy += (int32_t) *ptr * (int32_t) *ptr;
-
-			goertzel_sample(&s->tone, *ptr);
-		}
-
-		s->samples_pending -= limit;
-
-		if (s->samples_pending) {
-			/* Finished incomplete (last) block */
-			break;
-		}
-
-		tone_energy = goertzel_result(&s->tone);
-
-		/* Scale to make comparable */
-		tone_energy *= 2.0;
-		s->energy *= s->block_size;
-
-		ast_debug(10, "v21 1850 Ew=%.2E, Et=%.2E, s/n=%10.2f\n", tone_energy, s->energy, tone_energy / (s->energy - tone_energy));
-
-		hit = 0;
-		if (tone_energy > s->energy * s->threshold) {
-			ast_debug(10, "Hit! count=%d; miss_count=%d\n", s->hit_count, s->miss_count);
-			hit = 1;
-		}
-
-		if (hit) {
-			if (s->miss_count == 3 || (s->hit_count == 1 && s->miss_count == 2)) {
-				s->hit_count++;
-			} else {
-				s->hit_count = 1;
-			}
-
-			s->miss_count = 0;
-		} else {
-			s->miss_count++;
-			if (s->miss_count > 3) {
-				s->hit_count = 0;
-			}
-		}
-
-		if (s->hit_count == 4) {
-			ast_debug(1, "v21 preamble detected\n");
-			res = 1;
-		}
-
-		/* Reinitialise the detector for the next block */
-		goertzel_reset(&s->tone);
-
-		/* Advance to the next block */
-		s->energy = 0.0;
-		s->samples_pending = s->block_size;
-
-		amp += limit;
-	}
-
-	return res;
 }
 
 static int tone_detect(struct ast_dsp *dsp, tone_detect_state_t *s, int16_t *amp, int samples)
@@ -1599,10 +1455,6 @@
 		if ((dsp->faxmode & DSP_FAXMODE_DETECT_CED) && tone_detect(dsp, &dsp->ced_tone_state, shortdata, len)) {
 			fax_digit = 'e';
 		}
-
-		if ((dsp->faxmode & DSP_FAXMODE_DETECT_V21) && v21_detect(dsp, &dsp->v21_state, shortdata, len)) {
-			fax_digit = 'g';
-		}
 	}
 
 	if (dsp->features & (DSP_FEATURE_DIGIT_DETECT | DSP_FEATURE_BUSY_DETECT)) {

Modified: trunk/res/res_fax.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_fax.c?view=diff&rev=349249&r1=349248&r2=349249
==============================================================================
--- trunk/res/res_fax.c (original)
+++ trunk/res/res_fax.c Wed Dec 28 12:59:16 2011
@@ -249,13 +249,12 @@
 struct fax_gateway {
 	/*! \brief FAX Session */
 	struct ast_fax_session *s;
+	struct ast_fax_session *peer_v21_session;
+	struct ast_fax_session *chan_v21_session;
 	/*! \brief reserved fax session token */
 	struct ast_fax_tech_token *token;
 	/*! \brief the start of our timeout counter */
 	struct timeval timeout_start;
-	/*! \brief DSP Processor */
-	struct ast_dsp *chan_dsp;
-	struct ast_dsp *peer_dsp;
 	/*! \brief framehook used in gateway mode */
 	int framehook;
 	/*! \brief bridged */
@@ -336,7 +335,7 @@
 	enum ast_fax_modems modems;
 	uint32_t statusevents:1;
 	uint32_t ecm:1;
-	unsigned int minrate;	
+	unsigned int minrate;
 	unsigned int maxrate;
 } general_options;
 
@@ -371,7 +370,7 @@
 };
 
 static void debug_check_frame_for_silence(struct ast_fax_session *s, unsigned int c2s, struct ast_frame *frame)
-{	
+{
 	struct debug_info_history *history = c2s ? &s->debug_info->c2s : &s->debug_info->s2c;
 	int dspsilence;
 	unsigned int last_consec_frames, last_consec_ms;
@@ -403,7 +402,7 @@
 	history->consec_ms += (frame->samples / 8);
 }
 
-static void destroy_callback(void *data) 
+static void destroy_callback(void *data)
 {
 	if (data) {
 		ao2_ref(data, -1);
@@ -421,9 +420,9 @@
 	struct ast_fax_session_details *details;
 	struct ast_datastore *datastore;
 
-	ast_channel_lock(chan);	
+	ast_channel_lock(chan);
 	if (!(datastore = ast_channel_datastore_find(chan, &fax_datastore, NULL))) {
-		ast_channel_unlock(chan);	
+		ast_channel_unlock(chan);
 		return NULL;
 	}
 	if (!(details = datastore->data)) {
@@ -431,8 +430,8 @@
 		ast_channel_unlock(chan);
 		return NULL;
 	}
-	ao2_ref(details, 1);	
-	ast_channel_unlock(chan);	
+	ao2_ref(details, 1);
+	ast_channel_unlock(chan);
 
 	return details;
 }
@@ -442,11 +441,11 @@
 {
 	struct ast_fax_session_details *d = details;
 	struct ast_fax_document *doc;
-	
+
 	while ((doc = AST_LIST_REMOVE_HEAD(&d->documents, next))) {
 		ast_free(doc);
 	}
-	ast_string_field_free_memory(d);	
+	ast_string_field_free_memory(d);
 }
 
 /*! \brief create a FAX session details structure */
@@ -457,7 +456,7 @@
 	if (!(d = ao2_alloc(sizeof(*d), destroy_session_details))) {
 		return NULL;
 	}
-	
+
 	if (ast_string_field_init(d, 512)) {
 		ao2_ref(d, -1);
 		return NULL;
@@ -512,7 +511,7 @@
 }
 
 /*! \brief returns a reference counted details structure from the channel's fax datastore.  If the datastore
- * does not exist it will be created */	
+ * does not exist it will be created */
 static struct ast_fax_session_details *find_or_create_details(struct ast_channel *chan)
 {
 	struct ast_fax_session_details *details;
@@ -556,7 +555,7 @@
 }
 
 static int update_modem_bits(enum ast_fax_modems *bits, const char *value)
-{		
+{
 	char *m[5], *tok, *v = (char *)value;
 	int i = 0, j;
 
@@ -636,7 +635,13 @@
 		ast_build_string(&buf, &size, "GATEWAY");
 		first = 0;
 	}
-
+	if (caps & AST_FAX_TECH_V21_DETECT) {
+		if (!first) {
+			ast_build_string(&buf, &size, ",");
+		}
+		ast_build_string(&buf, &size, "V21");
+		first = 0;
+	}
 
 	return out;
 }
@@ -748,7 +753,7 @@
 		ast_module_unref(ast_module_info->self);
 		ast_free(fax);
 		ast_verb(4, "Unregistered FAX module type '%s'\n", tech->type);
-		break;	
+		break;
 	}
 	AST_RWLIST_TRAVERSE_SAFE_END;
 	AST_RWLIST_UNLOCK(&faxmodules);
@@ -856,7 +861,7 @@
 		}
 		ao2_ref(s->details, -1);
 	}
-	
+
 	if (s->debug_info) {
 		ast_dsp_free(s->debug_info->dsp);
 		ast_free(s->debug_info);
@@ -996,7 +1001,7 @@
 			return NULL;
 		}
 		ast_dsp_set_threshold(s->debug_info->dsp, 128);
-	}	
+	}
 
 	if (!(s->channame = ast_strdup(chan->name))) {
 		fax_session_release(s, token);
@@ -1311,7 +1316,7 @@
 	}
 
 	ast_channel_lock(chan);
-	/* update session details */	
+	/* update session details */
 	if (ast_strlen_zero(details->headerinfo) && (tempvar = pbx_builtin_getvar_helper(chan, "LOCALHEADERINFO"))) {
 		ast_string_field_set(details, headerinfo, tempvar);
 	}
@@ -1409,7 +1414,7 @@
 			    (frame->datalen == sizeof(t38_parameters))) {
 				unsigned int was_t38 = t38negotiated;
 				struct ast_control_t38_parameters *parameters = frame->data.ptr;
-				
+
 				switch (parameters->request_response) {
 				case AST_T38_REQUEST_NEGOTIATE:
 					/* the other end has requested a switch to T.38, so reply that we are willing, if we can
@@ -1435,15 +1440,15 @@
 						ast_smoother_free(fax->smoother);
 						fax->smoother = NULL;
 					}
-					
+
 					report_fax_status(chan, details, "T.38 Negotiated");
-					
+
 					ast_verb(3, "Channel '%s' switched to T.38 FAX session '%d'.\n", chan->name, fax->id);
 				}
 			} else if ((frame->frametype == expected_frametype) &&
 				   (!memcmp(&frame->subclass, &expected_framesubclass, sizeof(frame->subclass)))) {
 				struct ast_frame *f;
-				
+
 				if (fax->smoother) {
 					/* push the frame into a smoother */
 					if (ast_smoother_feed(fax->smoother, frame) < 0) {
@@ -1805,7 +1810,7 @@
 		ao2_ref(details, -1);
 		return -1;
 	}
-	
+
 	ast_atomic_fetchadd_int(&faxregistry.fax_rx_attempts, 1);
 
 	pbx_builtin_setvar_helper(chan, "FAXERROR", "Channel Problems");
@@ -1909,7 +1914,7 @@
 
 	get_manager_event_info(chan, &info);
 	manager_event(EVENT_FLAG_CALL,
-		      "ReceiveFAX", 
+		      "ReceiveFAX",
 		      "Channel: %s\r\n"
 		      "Context: %s\r\n"
 		      "Exten: %s\r\n"
@@ -2265,7 +2270,7 @@
 		ao2_ref(details, -1);
 		return -1;
 	}
-	
+
 	/* check for unsupported FAX application options */
 	if (ast_test_flag(&opts, OPT_CALLERMODE) || ast_test_flag(&opts, OPT_CALLEDMODE)) {
 		ast_string_field_set(details, error, "INVALID_ARGUMENTS");
@@ -2407,7 +2412,7 @@
 	ast_channel_lock(chan);
 	get_manager_event_info(chan, &info);
 	manager_event(EVENT_FLAG_CALL,
-		      "SendFAX", 
+		      "SendFAX",
 		      "Channel: %s\r\n"
 		      "Context: %s\r\n"
 		      "Exten: %s\r\n"
@@ -2439,20 +2444,34 @@
 	return (!channel_alive) ? -1 : 0;
 }
 
+/*! \brief destroy the v21 detection parts of a fax gateway session */
+static void destroy_v21_sessions(struct fax_gateway *gateway)
+{
+	if (gateway->chan_v21_session) {
+		ao2_lock(faxregistry.container);
+		ao2_unlink(faxregistry.container, gateway->chan_v21_session);
+		ao2_unlock(faxregistry.container);
+
+		ao2_ref(gateway->chan_v21_session, -1);
+		gateway->chan_v21_session = NULL;
+	}
+
+	if (gateway->peer_v21_session) {
+		ao2_lock(faxregistry.container);
+		ao2_unlink(faxregistry.container, gateway->peer_v21_session);
+		ao2_unlock(faxregistry.container);
+
+		ao2_ref(gateway->peer_v21_session, -1);
+		gateway->peer_v21_session = NULL;
+	}
+}
+
 /*! \brief destroy a FAX gateway session structure */
 static void destroy_gateway(void *data)
 {
 	struct fax_gateway *gateway = data;
 
-	if (gateway->chan_dsp) {
-		ast_dsp_free(gateway->chan_dsp);
-		gateway->chan_dsp = NULL;
-	}
-
-	if (gateway->peer_dsp) {
-		ast_dsp_free(gateway->peer_dsp);
-		gateway->peer_dsp = NULL;
-	}
+	destroy_v21_sessions(gateway);
 
 	if (gateway->s) {
 		fax_session_release(gateway->s, gateway->token);
@@ -2468,35 +2487,38 @@
 }
 
 /*! \brief Create a new fax gateway object.
+ * \param chan the channel the gateway object will be attached to
  * \param details the fax session details
  * \return NULL or a fax gateway object
  */
-static struct fax_gateway *fax_gateway_new(struct ast_fax_session_details *details)
+static struct fax_gateway *fax_gateway_new(struct ast_channel *chan, struct ast_fax_session_details *details)
 {
 	struct fax_gateway *gateway = ao2_alloc(sizeof(*gateway), destroy_gateway);
+	struct ast_fax_session_details *v21_details;
 	if (!gateway) {
 		return NULL;
 	}
 
-	gateway->chan_dsp = ast_dsp_new();
-	if (!gateway->chan_dsp) {
+	if (!(v21_details = session_details_new())) {
 		ao2_ref(gateway, -1);
 		return NULL;
 	}
 
-	gateway->peer_dsp = ast_dsp_new();
-	if (!gateway->peer_dsp) {
+	v21_details->caps = AST_FAX_TECH_V21_DETECT;
+	if (!(gateway->chan_v21_session = fax_session_new(v21_details, chan, NULL, NULL))) {
+		ao2_ref(v21_details, -1);
 		ao2_ref(gateway, -1);
 		return NULL;
 	}
 
+	if (!(gateway->peer_v21_session = fax_session_new(v21_details, chan, NULL, NULL))) {
+		ao2_ref(v21_details, -1);
+		ao2_ref(gateway, -1);
+		return NULL;
+	}
+	ao2_ref(v21_details, -1);
+
 	gateway->framehook = -1;
-
-	ast_dsp_set_features(gateway->chan_dsp, DSP_FEATURE_FAX_DETECT);
-	ast_dsp_set_faxmode(gateway->chan_dsp, DSP_FAXMODE_DETECT_V21);
-
-	ast_dsp_set_features(gateway->peer_dsp, DSP_FEATURE_FAX_DETECT);
-	ast_dsp_set_faxmode(gateway->peer_dsp, DSP_FAXMODE_DETECT_V21);
 
 	details->caps = AST_FAX_TECH_GATEWAY;
 	if (details->gateway_timeout && !(gateway->s = fax_session_reserve(details, &gateway->token))) {
@@ -2591,24 +2613,20 @@
 
 static struct ast_frame *fax_gateway_detect_v21(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *active, struct ast_frame *f)
 {
-	struct ast_frame *dfr = ast_frdup(f);
-	struct ast_dsp *active_dsp = (active == chan) ? gateway->chan_dsp : gateway->peer_dsp;
 	struct ast_channel *other = (active == chan) ? peer : chan;
+	struct ast_fax_session *active_v21_session = (active == chan) ? gateway->chan_v21_session : gateway->peer_v21_session;
+
+	if (!active_v21_session || gateway->detected_v21) {
+		return f;
+	}
+
+	if (active_v21_session->tech->write(active_v21_session, f) == 0 &&
+	    active_v21_session->details->option.v21_detected) {
+		gateway->detected_v21 = 1;
+	}
 
 	if (gateway->detected_v21) {
-		return f;
-	}
-
-	if (!dfr) {
-		return f;
-	}
-
-	if (!(dfr = ast_dsp_process(active, active_dsp, dfr))) {
-		return f;
-	}
-
-	if (dfr->frametype == AST_FRAME_DTMF && dfr->subclass.integer == 'g') {
-		gateway->detected_v21 = 1;
+		destroy_v21_sessions(gateway);
 		if (ast_channel_get_t38_state(other) == T38_STATE_UNKNOWN) {
 			ast_debug(1, "detected v21 preamble from %s\n", active->name);
 			return fax_gateway_request_t38(gateway, chan, f);
@@ -2617,7 +2635,6 @@
 		}
 	}
 
-	ast_frfree(dfr);
 	return f;
 }
 
@@ -3120,7 +3137,7 @@
 	set_channel_variables(chan, details);
 
 	/* set up the frame hook*/
-	gateway = fax_gateway_new(details);
+	gateway = fax_gateway_new(chan, details);
 	if (!gateway) {
 		ast_string_field_set(details, result, "FAILED");
 		ast_string_field_set(details, resultstr, "error initializing gateway session");
@@ -3468,7 +3485,7 @@
 	switch (cmd) {
 	case CLI_INIT:
 		e->command = "fax set debug {on|off}";
-		e->usage = 
+		e->usage =
 			"Usage: fax set debug { on | off }\n"
 			"       Enable/Disable FAX debugging on new FAX sessions.  The basic FAX debugging will result in\n"
 			"       additional events sent to manager sessions with 'call' class permissions.  When\n"
@@ -3499,11 +3516,11 @@
 {
 	struct fax_module *fax;
 	unsigned int num_modules = 0;
-	
+
 	switch (cmd) {
 	case CLI_INIT:
 		e->command = "fax show capabilities";
-		e->usage = 
+		e->usage =
 			"Usage: fax show capabilities\n"
 			"       Shows the capabilities of the registered FAX technology modules\n";
 		return NULL;
@@ -3594,12 +3611,12 @@
 
 	return CLI_SUCCESS;
 }
-	
+
 /*! \brief display fax stats */
 static char *cli_fax_show_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	struct fax_module *fax;
-	
+
 	switch (cmd) {
 	case CLI_INIT:
 		e->command = "fax show stats";
@@ -3628,6 +3645,36 @@
 	return CLI_SUCCESS;
 }
 
+static const char *cli_session_type(struct ast_fax_session *s)
+{
+	if (s->details->caps & AST_FAX_TECH_AUDIO) {
+		return "G.711";
+	}
+	if (s->details->caps & AST_FAX_TECH_T38) {
+		return "T.38";
+	}
+
+	return "none";
+}
+
+static const char *cli_session_operation(struct ast_fax_session *s)
+{
+	if (s->details->caps & AST_FAX_TECH_GATEWAY) {
+		return "gateway";
+	}
+	if (s->details->caps & AST_FAX_TECH_SEND) {
+		return "send";
+	}
+	if (s->details->caps & AST_FAX_TECH_RECEIVE) {
+		return "receive";
+	}
+	if (s->details->caps & AST_FAX_TECH_V21_DETECT) {
+		return "V.21";
+	}
+
+	return "none";
+}
+
 /*! \brief display fax sessions */
 static char *cli_fax_show_sessions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
@@ -3658,10 +3705,8 @@
 
 		ast_cli(a->fd, "%-20.20s %-10.10s %-10d %-5.5s %-10.10s %-15.15s %-30s\n",
 			s->channame, s->tech->type, s->id,
-			(s->details->caps & AST_FAX_TECH_AUDIO) ? "G.711" : "T.38",
-			(s->details->caps & AST_FAX_TECH_GATEWAY)
-				? "gateway"
-				: (s->details->caps & AST_FAX_TECH_SEND) ? "send" : "receive",
+			cli_session_type(s),
+			cli_session_operation(s),
 			ast_fax_state_to_str(s->state), S_OR(filenames, ""));
 
 		ast_free(filenames);
@@ -3693,9 +3738,9 @@
 	struct ast_flags config_flags = { 0 };
 	char modems[128] = "";
 
-	/* set defaults */	
+	/* set defaults */
 	general_options.minrate = RES_FAX_MINRATE;
-	general_options.maxrate = RES_FAX_MAXRATE;	
+	general_options.maxrate = RES_FAX_MAXRATE;
 	general_options.statusevents = RES_FAX_STATUSEVENTS;
 	general_options.modems = RES_FAX_MODEM;
 	general_options.ecm = AST_FAX_OPTFLAG_TRUE;
@@ -3979,7 +4024,7 @@
 static int unload_module(void)
 {
 	ast_cli_unregister_multiple(fax_cli, ARRAY_LEN(fax_cli));
-	
+
 	if (ast_custom_function_unregister(&acf_faxopt) < 0) {
 		ast_log(LOG_WARNING, "failed to unregister function '%s'\n", acf_faxopt.name);
 	}
@@ -4012,7 +4057,7 @@
 	if (!(faxregistry.container = ao2_container_alloc(FAX_MAXBUCKETS, session_hash_cb, session_cmp_cb))) {
 		return AST_MODULE_LOAD_DECLINE;
 	}
-	
+
 	if (set_config(config) < 0) {
 		ast_log(LOG_ERROR, "failed to load configuration file '%s'\n", config);
 		ao2_ref(faxregistry.container, -1);

Modified: trunk/res/res_fax_spandsp.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_fax_spandsp.c?view=diff&rev=349249&r1=349248&r2=349249
==============================================================================
--- trunk/res/res_fax_spandsp.c (original)
+++ trunk/res/res_fax_spandsp.c Wed Dec 28 12:59:16 2011
@@ -80,6 +80,9 @@
 static int spandsp_fax_gateway_start(struct ast_fax_session *s);
 static int spandsp_fax_gateway_process(struct ast_fax_session *s, const struct ast_frame *f);
 static void spandsp_fax_gateway_cleanup(struct ast_fax_session *s);
+static int spandsp_v21_detect(struct ast_fax_session *s, const struct ast_frame *f);
+static void spandsp_v21_cleanup(struct ast_fax_session *s);
+static void spandsp_v21_tone(void *data, int code, int level, int delay);
 
 static char *spandsp_fax_cli_show_capabilities(int fd);
 static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd);
@@ -98,7 +101,9 @@
 	 */
 	.version = "pre-20090220",
 #endif
-	.caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND | AST_FAX_TECH_RECEIVE | AST_FAX_TECH_GATEWAY,
+	.caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND
+		| AST_FAX_TECH_RECEIVE | AST_FAX_TECH_GATEWAY
+		| AST_FAX_TECH_V21_DETECT,
 	.new_session = spandsp_fax_new,
 	.destroy_session = spandsp_fax_destroy,
 	.read = spandsp_fax_read,
@@ -150,8 +155,12 @@
 
 	struct ast_timer *timer;
 	AST_LIST_HEAD(frame_queue, ast_frame) read_frames;
+
+	int v21_detected;
+	modem_connect_tones_rx_state_t *tone_state;
 };
 
+static int spandsp_v21_new(struct spandsp_pvt *p);
 static void session_destroy(struct spandsp_pvt *p);
 static int t38_tx_packet_handler(t38_core_state_t *t38_core_state, void *data, const uint8_t *buf, int len, int count);
 static void t30_phase_e_handler(t30_state_t *t30_state, void *data, int completion_code);
@@ -450,6 +459,20 @@
 	t30_set_supported_compressions(t30_state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION);
 }
 
+static int spandsp_v21_new(struct spandsp_pvt *p)
+{
+	/* XXX Here we use MODEM_CONNECT_TONES_FAX_CED_OR_PREAMBLE even though
+	 * we don't care about CED tones. Using MODEM_CONNECT_TONES_PREAMBLE
+	 * doesn't seem to work right all the time.
+	 */
+	p->tone_state = modem_connect_tones_rx_init(NULL, MODEM_CONNECT_TONES_FAX_CED_OR_PREAMBLE, spandsp_v21_tone, p);
+	if (!p->tone_state) {
+		return -1;
+	}
+
+	return 0;
+}
+
 /*! \brief create an instance of the spandsp tech_pvt for a fax session */
 static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_token *token)
 {
@@ -459,6 +482,15 @@
 	if ((!(p = ast_calloc(1, sizeof(*p))))) {
 		ast_log(LOG_ERROR, "Cannot initialize the spandsp private FAX technology structure.\n");
 		goto e_return;
+	}
+
+	if (s->details->caps & AST_FAX_TECH_V21_DETECT) {
+		if (spandsp_v21_new(p)) {
+			ast_log(LOG_ERROR, "Cannot initialize the spandsp private v21 technology structure.\n");
+			goto e_return;
+		}
+		s->state = AST_FAX_STATE_ACTIVE;
+		return p;
 	}
 
 	if (s->details->caps & AST_FAX_TECH_GATEWAY) {
@@ -513,6 +545,11 @@
 	return NULL;
 }
 
+static void spandsp_v21_cleanup(struct ast_fax_session *s) {
+	struct spandsp_pvt *p = s->tech_pvt;
+	modem_connect_tones_rx_free(p->tone_state);
+}
+
 /*! \brief Destroy a spandsp fax session.
  */
 static void spandsp_fax_destroy(struct ast_fax_session *s)
@@ -521,6 +558,8 @@
 
 	if (s->details->caps & AST_FAX_TECH_GATEWAY) {
 		spandsp_fax_gateway_cleanup(s);
+	} else if (s->details->caps & AST_FAX_TECH_V21_DETECT) {
+		spandsp_v21_cleanup(s);
 	} else {
 		session_destroy(p);
 	}
@@ -571,6 +610,36 @@
 	return &ast_null_frame;
 }
 
+static void spandsp_v21_tone(void *data, int code, int level, int delay)
+{
+	struct spandsp_pvt *p = data;
+
+	if (code == MODEM_CONNECT_TONES_FAX_PREAMBLE) {
+		p->v21_detected = 1;
+	}
+}
+
+static int spandsp_v21_detect(struct ast_fax_session *s, const struct ast_frame *f) {
+	struct spandsp_pvt *p = s->tech_pvt;
+
+	if (p->v21_detected) {
+		return 0;
+	}
+
+	/*invalid frame*/
+	if (!f->data.ptr || !f->datalen) {
+		return -1;
+	}
+
+	modem_connect_tones_rx(p->tone_state, f->data.ptr, f->samples);
+
+	if (p->v21_detected) {
+		s->details->option.v21_detected = 1;
+	}
+
+	return 0;
+}
+
 /*! \brief Write a frame to the spandsp fax stack.
  * \param s a fax session
  * \param f the frame to write
@@ -584,6 +653,10 @@
 static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame *f)
 {
 	struct spandsp_pvt *p = s->tech_pvt;
+
+	if (s->details->caps & AST_FAX_TECH_V21_DETECT) {
+		return spandsp_v21_detect(s, f);
+	}
 
 	if (s->details->caps & AST_FAX_TECH_GATEWAY) {
 		return spandsp_fax_gateway_process(s, f);
@@ -906,10 +979,10 @@
 /*! \brief */
 static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd)
 {
-	struct spandsp_pvt *p = s->tech_pvt;
-
 	ao2_lock(s);
 	if (s->details->caps & AST_FAX_TECH_GATEWAY) {
+		struct spandsp_pvt *p = s->tech_pvt;
+
 		ast_cli(fd, "%-22s : %d\n", "session", s->id);
 		ast_cli(fd, "%-22s : %s\n", "operation", "Gateway");
 		ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state));
@@ -920,7 +993,13 @@
 			ast_cli(fd, "%-22s : %d\n", "Data Rate", stats.bit_rate);
 			ast_cli(fd, "%-22s : %d\n", "Page Number", stats.pages_transferred + 1);
 		}
+	} else if (s->details->caps & AST_FAX_TECH_V21_DETECT) {
+		ast_cli(fd, "%-22s : %d\n", "session", s->id);
+		ast_cli(fd, "%-22s : %s\n", "operation", "V.21 Detect");
+		ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state));
 	} else {
+		struct spandsp_pvt *p = s->tech_pvt;
+
 		ast_cli(fd, "%-22s : %d\n", "session", s->id);
 		ast_cli(fd, "%-22s : %s\n", "operation", (s->details->caps & AST_FAX_TECH_RECEIVE) ? "Receive" : "Transmit");
 		ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state));




More information about the svn-commits mailing list