[asterisk-commits] irroot: branch irroot/distrotech-customers-10 r338901 - in /team/irroot/distr...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Sun Oct 2 05:54:24 CDT 2011


Author: irroot
Date: Sun Oct  2 05:54:21 2011
New Revision: 338901

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=338901
Log:
Add faxdetect framehook

Modified:
    team/irroot/distrotech-customers-10/include/asterisk/res_fax.h
    team/irroot/distrotech-customers-10/res/res_fax.c

Modified: team/irroot/distrotech-customers-10/include/asterisk/res_fax.h
URL: http://svnview.digium.com/svn/asterisk/team/irroot/distrotech-customers-10/include/asterisk/res_fax.h?view=diff&rev=338901&r1=338900&r2=338901
==============================================================================
--- team/irroot/distrotech-customers-10/include/asterisk/res_fax.h (original)
+++ team/irroot/distrotech-customers-10/include/asterisk/res_fax.h Sun Oct  2 05:54:21 2011
@@ -174,6 +174,8 @@
 	int gateway_id;
 	/*! the timeout for this gateway in seconds */
 	int gateway_timeout;
+	/*! the id of the faxdetect framehook for this channel */
+	int faxdetect_id;
 };
 
 struct ast_fax_tech;

Modified: team/irroot/distrotech-customers-10/res/res_fax.c
URL: http://svnview.digium.com/svn/asterisk/team/irroot/distrotech-customers-10/res/res_fax.c?view=diff&rev=338901&r1=338900&r2=338901
==============================================================================
--- team/irroot/distrotech-customers-10/res/res_fax.c (original)
+++ team/irroot/distrotech-customers-10/res/res_fax.c Sun Oct  2 05:54:21 2011
@@ -192,6 +192,9 @@
 					<enum name="gateway">
 						<para>R/W T38 fax gateway, with optional fax activity timeout in seconds (yes[,timeout]/no)</para>
 					</enum>
+					<enum name="faxdetect">
+						<para>R/W Enable FAX detect with optional timeout in seconds seconds (yes[,timeout]/no)</para>
+					</enum>
 					<enum name="pages">
 						<para>R/O Number of pages transferred.</para>
 					</enum>
@@ -291,6 +294,24 @@
 	struct ast_format peer_write_format;
 };
 
+/*! \brief used for fax detect framehook */
+struct fax_detect {
+	/*! \brief the start of our timeout counter */
+	struct timeval timeout_start;
+	/*! \brief faxdetect timeout */
+	int timeout;
+	/*! \brief DSP Processor */
+	struct ast_dsp *dsp;
+	/*! \brief original audio formats */
+	struct ast_format orig_format;
+	/*! \brief Noise limit to end faxdetect */
+	int noiselim;
+	/*! \brief result of the framehook e[CED]/f[CNG]/n[Noise]/t[T38] */
+	int result;
+	/*! \brief fax session details */
+	struct ast_fax_session_details *details;
+};
+
 static int fax_logger_level = -1;
 
 /*! \brief maximum buckets for res_fax ao2 containers */
@@ -475,6 +496,7 @@
 	d->minrate = general_options.minrate;
 	d->maxrate = general_options.maxrate;
 	d->gateway_id = -1;
+	d->faxdetect_id = -1;
 	d->gateway_timeout = 0;
 
 	return d;
@@ -3091,6 +3113,251 @@
 	return gateway->framehook;
 }
 
+/*! \brief destroy a FAX detect structure */
+static void destroy_faxdetect(void *data)
+{
+	struct fax_detect *faxdetect = data;
+
+	if (faxdetect->dsp) {
+		ast_dsp_free(faxdetect->dsp);
+		faxdetect->dsp = NULL;
+	}
+	ao2_ref(faxdetect->details, -1);
+}
+
+/*! \brief Create a new fax detect object.
+ * \param chan the channel attaching to
+ * \param timeout remove framehook in this time if set
+ * \param noiselim end faxdetect when noiselim ms of noise is detected
+ * \param dsp_detect_flag dsp faxmode detect flags
+ * \return NULL or a fax gateway object
+ */
+static struct fax_detect *fax_detect_new(struct ast_channel *chan, int timeout, int noiselim, int dsp_detect_flag)
+{
+	struct fax_detect *faxdetect = ao2_alloc(sizeof(*faxdetect), destroy_faxdetect);
+	if (!faxdetect) {
+		return NULL;
+	}
+
+	faxdetect->noiselim = noiselim;
+	faxdetect->result = 0;
+
+	if (timeout) {
+		faxdetect->timeout_start = ast_tvnow();
+	} else {
+		faxdetect->timeout_start.tv_sec = 0;
+		faxdetect->timeout_start.tv_usec = 0;
+	}
+
+	faxdetect->dsp = ast_dsp_new();
+	if (!faxdetect->dsp) {
+		ao2_ref(faxdetect, -1);
+		return NULL;
+	}
+
+	ast_dsp_set_features(faxdetect->dsp, DSP_FEATURE_FAX_DETECT);
+	if (noiselim) {
+		ast_dsp_set_faxmode(faxdetect->dsp, dsp_detect_flag || DSP_FAXMODE_DETECT_SQUELCH);
+		ast_dsp_set_threshold(faxdetect->dsp, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE));
+	} else {
+		ast_dsp_set_faxmode(faxdetect->dsp, dsp_detect_flag);
+	}
+	return faxdetect;
+}
+
+/*! \brief Deref the faxdetect data structure when the faxdetect framehook is detached
+ * \param data framehook data (faxdetect data)*/
+static void fax_detect_framehook_destroy(void *data) {
+	struct fax_detect *faxdetect = data;
+
+	ao2_ref(faxdetect, -1);
+}
+
+/*! \brief Fax Detect Framehook
+ *
+ * Listen for fax tones in audio path and enable jumping to a extension when detected.
+ *
+ * \param chan channel
+ * \param f frame to handle may be NULL
+ * \param event framehook event
+ * \param data framehook data (struct fax_detect *)
+ *
+ * \return processed frame or NULL when f is NULL or a null frame
+ */
+static struct ast_frame *fax_detect_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data) {
+	struct fax_detect *faxdetect = data;
+	struct ast_fax_session_details *details;
+	struct ast_control_t38_parameters *control_params;
+	struct ast_channel *peer;
+	int dspnoise;
+
+	details = faxdetect->details;
+
+	switch (event) {
+	case AST_FRAMEHOOK_EVENT_ATTACHED:
+		/* Setup format for DSP on ATTACH*/
+		ao2_lock(faxdetect);
+		ast_format_copy(&faxdetect->orig_format, &chan->readformat);
+		switch (chan->readformat.id) {
+			case AST_FORMAT_SLINEAR:
+			case AST_FORMAT_ALAW:
+			case AST_FORMAT_ULAW:
+				break;
+			default:
+				if (ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR)) {
+					ast_framehook_detach(chan, details->faxdetect_id);
+					details->faxdetect_id = -1;
+				}
+		}
+		ao2_unlock(faxdetect);
+		return NULL;
+	case AST_FRAMEHOOK_EVENT_DETACHED:
+		/* restore audio formats when we are detached */
+		ao2_lock(faxdetect);
+		ast_set_read_format(chan, &faxdetect->orig_format);
+		if ((peer = ast_bridged_channel(chan))) {
+			ast_channel_make_compatible(chan, peer);
+		}
+		ao2_unlock(faxdetect);
+		return NULL;
+	case AST_FRAMEHOOK_EVENT_READ:
+		if (f) {
+			break;
+		}
+	default:
+		return f;
+	};
+
+	if ((!ast_tvzero(faxdetect->timeout_start) &&
+	    (ast_tvdiff_ms(ast_tvnow(), faxdetect->timeout_start) > faxdetect->timeout))) {
+		ast_framehook_detach(chan, details->faxdetect_id);
+		details->faxdetect_id = -1;
+	}
+
+	/* only handle VOICE and CONTROL frames*/
+	switch (f->frametype) {
+	case AST_FRAME_VOICE:
+		/* We can only process some formats*/
+		switch (f->subclass.format.id) {
+			case AST_FORMAT_SLINEAR:
+			case AST_FORMAT_ALAW:
+			case AST_FORMAT_ULAW:
+				break;
+			default:
+				return f;
+		}
+		break;
+	case AST_FRAME_CONTROL:
+		if (f->subclass.integer == AST_CONTROL_T38_PARAMETERS) {
+			break;
+		}
+		return f;
+	default:
+		return f;
+	}
+
+	ao2_lock(faxdetect);
+	if (f->frametype == AST_FRAME_VOICE) {
+		f = ast_dsp_process(chan, faxdetect->dsp, f);
+		if (f->frametype == AST_FRAME_DTMF) {
+			faxdetect->result = f->subclass.integer;
+		} else if ((f->frametype == AST_FRAME_VOICE) && (faxdetect->noiselim > 0)) {
+			ast_dsp_noise(faxdetect->dsp, f, &dspnoise);
+			if (dspnoise > faxdetect->noiselim) {
+				faxdetect->result = 'n';
+			}
+		}
+	} else if ((f->frametype == AST_FRAME_CONTROL) && (f->datalen != sizeof(struct ast_control_t38_parameters))) {
+		control_params = f->data.ptr;
+		switch (control_params->request_response) {
+		case AST_T38_NEGOTIATED:
+		case AST_T38_REQUEST_NEGOTIATE:
+			faxdetect->result = 't';
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (faxdetect->result) {
+		const char *target_context = S_OR(chan->macrocontext, chan->context);
+		switch (faxdetect->result) {
+		case 'f':
+		case 't':
+			ast_channel_unlock(chan);
+			if (ast_exists_extension(chan, target_context, "fax", 1,
+			    S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
+				ast_channel_lock(chan);
+				ast_verbose(VERBOSE_PREFIX_2 "Redirecting '%s' to fax extension due to %s detection\n",
+					chan->name, (faxdetect->result == 'f') ? "CNG" : "T38");
+				pbx_builtin_setvar_helper(chan, "FAXEXTEN", chan->exten);
+				if (ast_async_goto(chan, target_context, "fax", 1)) {
+					ast_log(LOG_NOTICE, "Failed to async goto '%s' into fax of '%s'\n", chan->name, target_context);
+				}
+				ast_frfree(f);
+				f = &ast_null_frame;
+			} else {
+				ast_channel_lock(chan);
+				ast_log(LOG_NOTICE, "FAX %s detected but no fax extension\n",
+					(faxdetect->result == 'f') ? "CNG" : "T38");
+			}
+		}
+		ast_framehook_detach(chan, details->faxdetect_id);
+		details->faxdetect_id = -1;
+	}
+
+	ao2_unlock(faxdetect);
+
+	return f;
+}
+
+/*! \brief Attach a faxdetect framehook object to a channel.
+ * \param chan the channel to attach to
+ * \param timeout remove framehook in this time if set
+ * \param noiselim end faxdetect when noiselim ms of noise is detected
+ * \param dsp_detect_flag dsp faxmode detect flags
+ * \return the faxdetect structure or NULL on error
+ * \retval -1 error
+ */
+static struct fax_detect* fax_detect_attach(struct ast_channel *chan, int timeout, int noiselim, int dsp_detect_flags)
+{
+	struct fax_detect *faxdetect;
+	struct ast_fax_session_details *details;
+	struct ast_framehook_interface fr_hook = {
+		.version = AST_FRAMEHOOK_INTERFACE_VERSION,
+		.event_cb = fax_detect_framehook,
+		.destroy_cb = fax_detect_framehook_destroy,
+	};
+
+	if (!(details = find_or_create_details(chan))) {
+		ast_log(LOG_ERROR, "System cannot provide memory for session requirements.\n");
+		return NULL;
+	}
+
+	/* set up the frame hook*/
+	faxdetect = fax_detect_new(chan, timeout, noiselim, dsp_detect_flags);
+	if (!faxdetect) {
+		ao2_ref(details, -1);
+		return NULL;
+	}
+
+	fr_hook.data = faxdetect;
+	faxdetect->details = details;
+	ast_channel_lock(chan);
+	details->faxdetect_id = ast_framehook_attach(chan, &fr_hook);
+	ast_channel_unlock(chan);
+
+	if (details->faxdetect_id < 0) {
+		ao2_ref(details, -1);
+		ao2_ref(faxdetect, -1);
+		return NULL;
+	}
+
+	/* return it with ref held in framhook and return*/
+	ao2_ref(faxdetect, 1);
+	return faxdetect;
+}
+
 /*! \brief Faxdetect loop used by WaitFAX
  * \details Run DSP faxdetect on the channel for timeout seconds or until fax is detected
  * \param chan channel to run fax detect on
@@ -3100,33 +3367,13 @@
 {
 	int timeleft = timeout;
 	int res = 0;
-	int dspnoise = 0;
-	struct ast_dsp *dsp = NULL;
 	struct ast_frame *f, *dup_f;
-	enum ast_t38_state t38state = T38_STATE_UNKNOWN;
-	struct ast_format orig_read_format;
 	AST_LIST_HEAD_NOLOCK(, ast_frame) deferred_frames;
-
-	/* Setup DSP CNG processing */
-	ast_format_copy(&orig_read_format, &chan->readformat);
-	switch (chan->readformat.id) {
-		case AST_FORMAT_SLINEAR:
-		case AST_FORMAT_ALAW:
-		case AST_FORMAT_ULAW:
-			break;
-		default:
-			if (ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR)) {
-				return -1;
-			}
-	}
-
-	if (!(dsp = ast_dsp_new())) {
+	struct fax_detect *faxdetect = NULL;
+
+	if (!(faxdetect = fax_detect_attach(chan, 0, noiselim, DSP_FAXMODE_DETECT_CNG))) {
 		return -1;
 	}
-
-	ast_dsp_set_features(dsp, DSP_FEATURE_FAX_DETECT);
-	ast_dsp_set_faxmode(dsp, DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_SQUELCH);
-	ast_dsp_set_threshold(dsp, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE));
 
 	AST_LIST_HEAD_INIT_NOLOCK(&deferred_frames);
 
@@ -3135,18 +3382,18 @@
 			break;
 		}
 
-		if (dsp && (f->frametype == AST_FRAME_VOICE) && ((f->subclass.format.id == AST_FORMAT_SLINEAR) ||
-		    (f->subclass.format.id == AST_FORMAT_ULAW) || (f->subclass.format.id == AST_FORMAT_ALAW))) {
-			f = ast_dsp_process(chan, dsp, f);
-			if ((f->frametype ==  AST_FRAME_DTMF) && (f->subclass.integer == 'f')) {
+		ao2_lock(faxdetect);
+		switch (faxdetect->result) {
+			case 'f':
 				res = 1;
-			} else if ((f->frametype == AST_FRAME_VOICE) && (noiselim > 0)) {
-				ast_dsp_noise(dsp, f, &dspnoise);
-				if (dspnoise > noiselim) {
-					break;
-				}
-			}
-		}
+				break;
+			case 't':
+				res = 2;
+				break;
+			case 'n':
+				break;
+		}
+		ao2_unlock(faxdetect);
 
 		if (ast_is_deferrable_frame(f)) {
 			AST_LIST_INSERT_HEAD(&deferred_frames, f, frame_list);
@@ -3154,18 +3401,11 @@
 			ast_frfree(f);
 		}
 
-		t38state = ast_channel_get_t38_state(chan);
-		if ((t38state == T38_STATE_NEGOTIATING) || (t38state == T38_STATE_NEGOTIATED)) {
-			res = 2;
+		if (res) {
 			break;
-		} else if (res) {
-			break;
-		}
-	}
-
-	if (orig_read_format.id != chan->readformat.id) {
-		ast_set_read_format(chan, &orig_read_format);
-	}
+		}
+	}
+	ao2_ref(faxdetect, -1);
 
 	ast_channel_lock(chan);
 	while ((f = AST_LIST_REMOVE_HEAD(&deferred_frames, frame_list))) {
@@ -3176,10 +3416,6 @@
 		}
 	}
 	ast_channel_unlock(chan);
-
-	if (dsp) {
-		ast_dsp_free(dsp);
-	}
 
 	return res;
 }
@@ -3798,6 +4034,40 @@
 		} else {
 			ast_log(LOG_WARNING, "Unsupported value '%s' passed to FAXOPT(%s).\n", value, data);
 		}
+	} else if (!strcasecmp(data, "faxdetect")) {
+		const char *val = ast_skip_blanks(value);
+		char *timeout = strchr(val, ',');
+		unsigned int fdtimeout = 0;
+		struct fax_detect *faxdetect;
+
+		if (timeout) {
+			*timeout++ = '\0';
+		}
+
+		if (ast_true(val)) {
+			if (details->faxdetect_id < 0) {
+				if (timeout && (sscanf(timeout, "%u", &fdtimeout) == 1)) {
+					fdtimeout = fdtimeout * 1000;
+				}
+
+				faxdetect = fax_detect_attach(chan, fdtimeout, 0, DSP_FAXMODE_DETECT_CNG);
+
+				if (faxdetect && (details->faxdetect_id >= 0)) {
+					ast_debug(1, "Attached FAX detect to channel %s.\n", chan->name);
+				} else {
+					ast_log(LOG_ERROR, "Error attaching FAX detect to channel %s.\n", chan->name);
+					res = -1;
+				}
+				ao2_ref(faxdetect, -1);
+			} else {
+				ast_log(LOG_WARNING, "Attempt to attach a FAX detect on channel (%s) with FAX detect already running.\n", chan->name);
+			}
+		} else if (ast_false(val)) {
+			ast_framehook_detach(chan, details->faxdetect_id);
+			details->faxdetect_id = -1;
+		} else {
+			ast_log(LOG_WARNING, "Unsupported value '%s' passed to FAXOPT(%s).\n", value, data);
+		}
 	} else if (!strcasecmp(data, "headerinfo")) {
 		ast_string_field_set(details, headerinfo, value);
 	} else if (!strcasecmp(data, "localstationid")) {




More information about the asterisk-commits mailing list