[asterisk-commits] mnicholson: branch group/res_fax r236018 - /team/group/res_fax/res/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Dec 21 14:53:14 CST 2009


Author: mnicholson
Date: Mon Dec 21 14:53:12 2009
New Revision: 236018

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=236018
Log:
Initial commit of res_fax_spandsp code.  Hasn't been tested yet, but it compiles.

Modified:
    team/group/res_fax/res/res_fax_spandsp.c

Modified: team/group/res_fax/res/res_fax_spandsp.c
URL: http://svnview.digium.com/svn/asterisk/team/group/res_fax/res/res_fax_spandsp.c?view=diff&rev=236018&r1=236017&r2=236018
==============================================================================
--- team/group/res_fax/res/res_fax_spandsp.c (original)
+++ team/group/res_fax/res/res_fax_spandsp.c Mon Dec 21 14:53:12 2009
@@ -50,16 +50,543 @@
 #include "asterisk/channel.h"
 #include "asterisk/res_fax.h"
 
+#define SPANDSP_FAX_SAMPLES 160
+#define SPANDSP_FAX_POLL_TIME 20000 /* 20ms */
+
+static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_token *token);
+
+/*! \brief destroy a spandsp fax session */
+static void spandsp_fax_destroy(struct ast_fax_session *s);
+
+/*! \brief */
+static struct ast_frame *spandsp_fax_read(struct ast_fax_session *s);
+
+/*! \brief */
+static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame *f);
+
+/*! \brief */
+static int spandsp_fax_start(struct ast_fax_session *s);
+
+/*! \brief */
+static int spandsp_fax_cancel(struct ast_fax_session *s);
+
+/*! \brief */
+static int spandsp_fax_generate_silence(struct ast_fax_session *s);
+
+/*! \brief */
+static int spandsp_fax_switch_to_t38(struct ast_fax_session *s);
+
+/*! \brief */
+static char *spandsp_fax_cli_show_capabilities(int fd);
+
+/*! \brief */
+static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd);
+
+/*! \brief */
+static char *spandsp_fax_cli_show_stats(int fd);
+
+static struct ast_fax_tech spandsp_fax_tech = {
+	.type = "Spandsp",
+	.description = "Spandsp FAX Driver",
+#if SPANDSP_RELEASE_DATE >= 20090220
+	/* spandsp 0.0.6 */
+	.version = SPANDSP_RELEASE_DATETIME_STRING,
+#else
+	/* spandsp 0.0.5
+	 * TODO: maybe we should determine the version better way
+	 */
+	.version = "20090220",
+#endif
+	.caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND | AST_FAX_TECH_RECEIVE,
+	.new_session = spandsp_fax_new,
+	.destroy_session = spandsp_fax_destroy,
+	.read = spandsp_fax_read,
+	.write = spandsp_fax_write,
+	.start_session = spandsp_fax_start,
+	.cancel_session = spandsp_fax_cancel,
+	.generate_silence = spandsp_fax_generate_silence,
+	.switch_to_t38 = spandsp_fax_switch_to_t38,
+	.cli_show_capabilities = spandsp_fax_cli_show_capabilities,
+	.cli_show_session = spandsp_fax_cli_show_session,
+	.cli_show_stats = spandsp_fax_cli_show_stats,
+};
+
+struct spandsp_pvt {
+	struct ast_fax_session *session;  /* XXX this is unnecessary */
+	unsigned int ist38:1;
+	fax_state_t fax_state;
+	t38_terminal_state_t t38_state;
+	t30_state_t *t30_state;
+	t38_core_state_t *t38_core_state;
+
+	pthread_t session_thread;
+	int trigger_pipe[2];
+	AST_LIST_HEAD(frame_queue, ast_frame) read_frames;
+};
+
+static void session_destroy(struct spandsp_pvt *p);
+static void *session_thread(void *data);
+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);
+static void spandsp_log(int level, const char *msg);
+
+static void set_logging(logging_state_t *state);
+static void set_local_info(t30_state_t *t30_state, struct ast_fax_session_details *details);
+static void set_file(t30_state_t *t30_state, struct ast_fax_session_details *details);
+static void set_ecm(t30_state_t *t30_state, struct ast_fax_session_details *details);
+
+static void session_destroy(struct spandsp_pvt *p)
+{
+	struct ast_frame *f;
+
+	t30_terminate(p->t30_state);
+
+	if (p->session_thread != AST_PTHREADT_NULL) {
+		/* XXX perhaps s->state should be set here */
+		pthread_join(p->session_thread, NULL);
+	}
+
+	fax_release(&p->fax_state);
+	t38_terminal_release(&p->t38_state);
+
+	if (p->trigger_pipe[0] != -1) {
+		close(p->trigger_pipe[0]);
+		close(p->trigger_pipe[1]);
+	}
+
+	while ((f = AST_LIST_REMOVE_HEAD(&p->read_frames, frame_list))) {
+		ast_frfree(f);
+	}
+}
+
+static void *session_thread(void *data)
+{
+	struct ast_fax_session *s = data;
+	struct spandsp_pvt *p = s->tech_pvt;
+	struct timeval now, last_frame;
+	char byte = 0;
+
+	now = ast_tvnow();
+
+	while (s->state != AST_FAX_STATE_COMPLETE) {
+		if (p->ist38) {
+			last_frame = now;
+			now = ast_tvnow();
+			t38_terminal_send_timeout(&p->t38_state, ast_tvdiff_us(now, last_frame) / (1000000 / 8000));
+		} else {
+			write(p->trigger_pipe[1], &byte, 1);
+		}
+
+		usleep(SPANDSP_FAX_POLL_TIME);
+	}
+
+	return NULL;
+}
+
+/*! \brief
+ *
+ */
+static int t38_tx_packet_handler(t38_core_state_t *t38_core_state, void *data, const uint8_t *buf, int len, int count)
+{
+	struct spandsp_pvt *p = data;
+	struct ast_frame fax_frame = {
+		.frametype = AST_FRAME_MODEM,
+		.subclass.integer = AST_MODEM_T38,
+		.src = "res_fax_spandsp_t38",
+	};
+
+	struct ast_frame *f = &fax_frame;
+	char byte = 0;
+
+
+	/* TODO: Asterisk does not provide means of resending the same packet multiple
+	  times so count is ignored at the moment */
+
+	AST_FRAME_SET_BUFFER(f, buf, 0, len);
+
+	if (!(f = ast_frisolate(f))) {
+		return -1;
+	}
+
+	AST_LIST_LOCK(&p->read_frames);
+	AST_LIST_INSERT_TAIL(&p->read_frames, f, frame_list);
+	AST_LIST_UNLOCK(&p->read_frames);
+
+	write(p->trigger_pipe[1], &byte, 1);
+
+	return 0;
+}
+
+/*! \brief Phase E handler callback.
+ * \param t30_state the span t30 state
+ * \param data this will be the ast_fax_session
+ * \param completion_code the result of the fax session
+ *
+ * This function pulls stats from the spandsp stack and stores them for res_fax
+ * to use later.
+ */
+static void t30_phase_e_handler(t30_state_t *t30_state, void *data, int completion_code)
+{
+	struct ast_fax_session *s = data;
+	char headerinfo[T30_MAX_PAGE_HEADER_INFO + 1];
+	const char *c;
+	t30_stats_t stats;
+
+	s->state = AST_FAX_STATE_COMPLETE;
+
+	t30_get_transfer_statistics(t30_state, &stats);
+
+	if (completion_code == T30_ERR_OK) {
+		ast_string_field_set(s->details, result, "SUCCESS");
+	} else {
+		ast_string_field_set(s->details, result, "FAILED");
+		ast_string_field_set(s->details, error, t30_completion_code_to_str(completion_code));
+	}
+
+	ast_string_field_set(s->details, resultstr, t30_completion_code_to_str(completion_code));
+
+	if ((c = t30_get_tx_ident(t30_state))) {
+		ast_string_field_set(s->details, localstationid, c);
+	}
+
+	if ((c = t30_get_rx_ident(t30_state))) {
+		ast_string_field_set(s->details, remotestationid, c);
+	}
+
+#if SPANDSP_RELEASE_DATE >= 20090220
+	s->details->pages_transferred = (s->details.caps & AST_FAX_TECH_RECEIVE) ? stats.pages_rx : stats.pages_tx;
+#else
+	s->details->pages_transferred = stats.pages_transferred;
+#endif
+
+	ast_string_field_build(s->details, transfer_rate, "%d", stats.bit_rate);
+
+	ast_string_field_build(s->details, resolution, "%dx%d", stats.x_resolution, stats.y_resolution);
+
+	t30_get_tx_page_header_info(t30_state, headerinfo);
+	ast_string_field_set(s->details, headerinfo, headerinfo);
+}
+
+/*! \brief Send spandsp log messages to asterisk.
+ * \param level the spandsp logging level
+ * \param msg the log message
+ *
+ * \note This function is a callback function called by spandsp.
+ */
+static void spandsp_log(int level, const char *msg)
+{
+	if (level == SPAN_LOG_ERROR) {
+		ast_log(LOG_ERROR, "%s", msg);
+	} else if (level == SPAN_LOG_WARNING) {
+		ast_log(LOG_WARNING, "%s", msg);
+	} else {
+		ast_log(LOG_DEBUG, "%s", msg);
+	}
+}
+
+static void set_logging(logging_state_t *state)
+{
+	int level = SPAN_LOG_WARNING + option_debug;
+
+	span_log_set_message_handler(state, spandsp_log);
+	span_log_set_level(state, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | level);
+}
+
+static void set_local_info(t30_state_t *t30_state, struct ast_fax_session_details *details)
+{
+	if (!ast_strlen_zero(details->localstationid)) {
+		t30_set_tx_ident(t30_state, details->localstationid);
+	}
+
+	if (!ast_strlen_zero(details->headerinfo)) {
+		t30_set_tx_page_header_info(t30_state, details->headerinfo);
+	}
+}
+
+static void set_file(t30_state_t *t30_state, struct ast_fax_session_details *details)
+{
+	if (details->caps & AST_FAX_TECH_RECEIVE) {
+		t30_set_rx_file(t30_state, AST_LIST_FIRST(&details->documents)->filename, -1);
+	} else {
+		/* if not AST_FAX_TECH_RECEIVE, assume AST_FAX_TECH_SEND, this
+		 * should be safe because we ensure either RECEIVE or SEND is
+		 * indicated in spandsp_fax_new() */
+		t30_set_tx_file(t30_state, AST_LIST_FIRST(&details->documents)->filename, -1, -1);
+	}
+}
+
+static void set_ecm(t30_state_t *t30_state, struct ast_fax_session_details *details)
+{
+	t30_set_ecm_capability(t30_state, (details->option.ecm == AST_FAX_OPTFLAG_DEFAULT) ? 1 : details->option.ecm );
+	t30_set_supported_compressions(t30_state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION);
+}
+
+/*! \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)
+{
+	struct spandsp_pvt *p;
+	int caller_mode;
+
+	if ((!(p = ast_calloc(1, sizeof(*p))))) {
+		ast_log(LOG_ERROR, "Cannot initialize the spandsp private FAX technology structure.\n");
+		goto e_return;
+	}
+
+	AST_LIST_HEAD_INIT(&p->read_frames);
+	p->trigger_pipe[0] = -1;
+	p->session_thread = AST_PTHREADT_NULL;
+
+	if (s->details->caps & AST_FAX_TECH_RECEIVE) {
+		caller_mode = 0;
+	} else if (s->details->caps & AST_FAX_TECH_SEND) {
+		caller_mode = 1;
+	} else {
+		ast_log(LOG_ERROR, "Are we sending or receiving? The FAX requirements (capabilities: 0x%X) were not properly set.\n", s->details->caps);
+		goto e_free;
+	}
+
+	if (pipe(p->trigger_pipe) < 0) {
+		ast_log(LOG_ERROR, "Channel '%s' FAX session '%d' failed to create trigger pipe.\n", s->channame, s->id);
+		goto e_free;
+	}
+
+	s->fd = p->trigger_pipe[0];
+
+	if (s->details->caps & AST_FAX_TECH_T38) {
+		if ((s->details->caps & AST_FAX_TECH_AUDIO) == 0) {
+			/* audio mode was not requested, start in T.38 mode */
+			p->ist38 = 1;
+		}
+
+		/* init t38 stuff */
+		t38_terminal_init(&p->t38_state, caller_mode, t38_tx_packet_handler, p);
+		set_logging(&p->t38_state.logging);
+	}
+
+	if (s->details->caps & AST_FAX_TECH_AUDIO) {
+		/* init audio stuff */
+		fax_init(&p->fax_state, caller_mode);
+		set_logging(&p->fax_state.logging);
+	}
+
+	s->state = AST_FAX_STATE_INITIALIZED;
+	return p;
+
+e_free:
+	ast_free(p);
+e_return:
+	return NULL;
+}
+
+static void spandsp_fax_destroy(struct ast_fax_session *s)
+{
+	struct spandsp_pvt *p = s->tech_pvt;
+
+	session_destroy(p);
+	ast_free(p);
+	s->tech_pvt = NULL;
+	s->fd = -1;
+}
+
+/*! \brief Read a frame from the spandsp fax stack.
+ */
+static struct ast_frame *spandsp_fax_read(struct ast_fax_session *s)
+{
+	struct spandsp_pvt *p = s->tech_pvt;
+	uint8_t buffer[AST_FRIENDLY_OFFSET + SPANDSP_FAX_SAMPLES * sizeof(uint16_t)];
+	int16_t *buf = (int16_t *) (buffer + AST_FRIENDLY_OFFSET);
+	int samples;
+	char byte;
+
+	struct ast_frame fax_frame = {
+		.frametype = AST_FRAME_VIDEO,
+		.subclass.codec = AST_FORMAT_SLINEAR,
+		.src = "res_fax_spandsp_g711",
+	};
+
+	struct ast_frame *f = &fax_frame;
+
+	/* XXX do we need to lock here? */
+	if (s->state == AST_FAX_STATE_COMPLETE) {
+		ast_log(LOG_WARNING, "FAX session '%d' is in the '%s' state.\n", s->id, ast_fax_state_to_str(s->state));
+		return NULL;
+	}
+
+	read(p->trigger_pipe[0], &byte, 1);
+
+	if (p->ist38) {
+		AST_LIST_LOCK(&p->read_frames);
+		f = AST_LIST_REMOVE_HEAD(&p->read_frames, frame_list);
+		AST_LIST_UNLOCK(&p->read_frames);
+		return f;
+	} else {
+		if ((samples = fax_tx(&p->fax_state, buf, SPANDSP_FAX_SAMPLES)) > 0) {
+			f->samples = samples;
+			AST_FRAME_SET_BUFFER(f, buffer, AST_FRIENDLY_OFFSET, samples * sizeof(int16_t));
+			return ast_frisolate(f);
+		}
+	}
+
+	return NULL;
+}
+
+/*! \brief Write a frame to the spandsp fax stack.
+ * \param s a fax session
+ * \param f the frame to write
+ *
+ * \note res_fax does not currently use the return value of this function.
+ * Also the fax_rx() function never fails.
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame *f)
+{
+	struct spandsp_pvt *p = s->tech_pvt;
+
+	/* XXX do we need to lock here? */
+	if (s->state == AST_FAX_STATE_COMPLETE) {
+		ast_log(LOG_WARNING, "FAX session '%d' is in the '%s' state.\n", s->id, ast_fax_state_to_str(s->state));
+		return -1;
+	}
+
+	if (p->ist38) {
+		return t38_core_rx_ifp_packet(p->t38_core_state, f->data.ptr, f->datalen, f->seqno);
+	} else {
+		return fax_rx(&p->fax_state, f->data.ptr, f->samples);
+	}
+}
+
+static int spandsp_fax_start(struct ast_fax_session *s)
+{
+	struct spandsp_pvt *p = s->tech_pvt;
+
+	s->state = AST_FAX_STATE_OPEN;
+
+	if (p->ist38) {
+#if SPANDSP_RELEASE_DATE >= 20080725
+		/* for spandsp shaphots 0.0.6 and higher */
+		p->t30_state = &p->t38_state.t30;
+		p->t38_core_state = &p->t38_state.t38_fe.t38;
+#else
+		/* for spandsp releases 0.0.5 */
+		p->t30_state = &p->t38_state.t30_state;
+		p->t38_core_state = &p->t38_state.t38;
+#endif
+	} else {
+#if SPANDSP_RELEASE_DATE >= 20080725
+		/* for spandsp shaphots 0.0.6 and higher */
+		p->t30_state = &p->fax_state.t30;
+#else
+		/* for spandsp release 0.0.5 */
+		p->t30_state = &p->fax_state.t30_state;
+#endif
+	}
+
+	set_logging(&p->t30_state->logging);
+
+	/* set some parameters */
+	set_local_info(p->t30_state, s->details);
+	set_file(p->t30_state, s->details);
+	set_ecm(p->t30_state, s->details);
+
+	/* perhaps set_transmit_on_idle() should be called */
+
+	t30_set_phase_e_handler(p->t30_state, t30_phase_e_handler, s);
+
+	/* set T.38 parameters */
+	if (p->ist38) {
+		set_logging(&p->t38_core_state->logging);
+
+		t38_set_max_datagram_size(p->t38_core_state, s->details->their_t38_parameters.max_ifp);
+
+		if (s->details->their_t38_parameters.fill_bit_removal) {
+			t38_set_fill_bit_removal(p->t38_core_state, TRUE);
+		}
+
+		if (s->details->their_t38_parameters.transcoding_mmr) {
+			t38_set_mmr_transcoding(p->t38_core_state, TRUE);
+		}
+
+		if (s->details->their_t38_parameters.transcoding_jbig) {
+			t38_set_jbig_transcoding(p->t38_core_state, TRUE);
+		}
+
+		/* XXX create a thread for T.38 and stuffs */
+		if (ast_pthread_create(&p->session_thread, NULL, session_thread, s) < 0) {
+			ast_log(LOG_ERROR, "failed to create FAX thread for session: %d\n", s->id);
+			return -1;
+		}
+	}
+
+	s->state = AST_FAX_STATE_ACTIVE;
+
+	return 0;
+}
+
+static int spandsp_fax_cancel(struct ast_fax_session *s)
+{
+	struct spandsp_pvt *p = s->tech_pvt;
+	t30_terminate(p->t30_state);
+	return 0;
+}
+
+static int spandsp_fax_generate_silence(struct ast_fax_session *s)
+{
+	/* XXX figure out what to do here, and figure out if we even need this
+	 * perhaps we should call fax_set_transmit_on_idle() here or something
+	 */
+	return 0;
+}
+
+static int spandsp_fax_switch_to_t38(struct ast_fax_session *s)
+{
+	struct spandsp_pvt *p = s->tech_pvt;
+
+	/* prevent the phase E handler from running, this is not a real termination */
+	t30_set_phase_e_handler(p->t30_state, NULL, NULL);
+
+	t30_terminate(p->t30_state);
+
+	s->details->option.switch_to_t38 = 1;
+
+	p->ist38 = 1;
+	spandsp_fax_start(s);
+
+	return 0;
+}
+
+static char *spandsp_fax_cli_show_capabilities(int fd)
+{
+	ast_cli(fd, "SEND RECEIVE T.38 G.711\n\n");
+	return  CLI_SUCCESS;
+}
+
+static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd)
+{
+	return CLI_SUCCESS;
+}
+
+static char *spandsp_fax_cli_show_stats(int fd)
+{
+	return CLI_SUCCESS;
+}
 
 /*! \brief unload res_fax_spandsp */
 static int unload_module(void)
 {
+	ast_fax_tech_unregister(&spandsp_fax_tech);
 	return AST_MODULE_LOAD_SUCCESS;
 }
 
 /*! \brief load res_fax_spandsp */
 static int load_module(void)
 {
+	if (ast_fax_tech_register(&spandsp_fax_tech) < 0) {
+		ast_log(LOG_ERROR, "failed to register FAX technology\n");
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
 	/* prevent logging to stderr */
 	span_set_message_handler(NULL);
 




More information about the asterisk-commits mailing list