[svn-commits] file: branch file/bridging-softmixgen2 r180679 - /team/file/bridging-softmixg...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Sat Mar 7 15:30:50 CST 2009


Author: file
Date: Sat Mar  7 15:30:47 2009
New Revision: 180679

URL: http://svn.digium.com/svn-view/asterisk?view=rev&rev=180679
Log:
Add my progress thus far for a rewritten bridge_softmix module. This differs quite a lot from the old one in that it does all the work in the same thread. It also segments channels out based on the codec they are using. For example 8kHz signed linear channels will be in one segment while 16kHz channels will be in another. While not complete yet the mixed audio from each segment will be translated and mixed into the other segment. This will minimize resampling as much as possible. If resampling is not available though each segment can be isolated. Channels in the same segment will be able to talk to eachother but they will not be able to talk to other segments.

Modified:
    team/file/bridging-softmixgen2/bridges/bridge_softmix.c

Modified: team/file/bridging-softmixgen2/bridges/bridge_softmix.c
URL: http://svn.digium.com/svn-view/asterisk/team/file/bridging-softmixgen2/bridges/bridge_softmix.c?view=diff&rev=180679&r1=180678&r2=180679
==============================================================================
--- team/file/bridging-softmixgen2/bridges/bridge_softmix.c (original)
+++ team/file/bridging-softmixgen2/bridges/bridge_softmix.c Sat Mar  7 15:30:47 2009
@@ -1,7 +1,7 @@
 /*
  * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C) 2007, Digium, Inc.
+ * Copyright (C) 2007 - 2009, Digium, Inc.
  *
  * Joshua Colp <jcolp at digium.com>
  *
@@ -24,9 +24,6 @@
  *
  * \ingroup bridges
  *
- * \todo This bridge operates in 8 kHz mode unless a define is uncommented.
- * This needs to be improved so the bridge moves between the dominant codec as needed depending
- * on channels present in the bridge and transcoding capabilities.
  */
 
 #include "asterisk.h"
@@ -48,50 +45,147 @@
 #include "asterisk/frame.h"
 #include "asterisk/options.h"
 #include "asterisk/logger.h"
+#include "asterisk/translate.h"
 #include "asterisk/slinfactory.h"
 #include "asterisk/astobj2.h"
 #include "asterisk/timing.h"
 
-/*! \brief Interval at which mixing will take place. Valid options are 10, 20, and 40. */
+/*! \brief Interval at which mixing will take place. Specified in milliseconds. */
 #define SOFTMIX_INTERVAL 20
 
-/*! \brief Size of the buffer used for sample manipulation */
-#define SOFTMIX_DATALEN (160 * (SOFTMIX_INTERVAL / 10))
-
-/*! \brief Number of samples we are dealing with */
-#define SOFTMIX_SAMPLES (SOFTMIX_DATALEN / 2)
-
-/*! \brief Define used to turn on 16 kHz audio support */
-/* #define SOFTMIX_16_SUPPORT */
-
-/*! \brief Structure which contains per-channel mixing information */
+/*! \brief Maximum number of samples allowed for our buffers. */
+#define SOFTMIX_SAMPLES ((16 * SOFTMIX_INTERVAL) + AST_FRIENDLY_OFFSET)
+
+/*! \brief Maximum number of bridge segments. Each segment right now represents a different sample rate. */
+#define SOFTMIX_SEGMENTS_MAX 2
+
+/*! \brief Enable isolated segments. If transcoding is not possible between segments then each side will be isolated from the other. */
+#define SOFTMIX_SEGMENTS_ISOLATE
+
+/*! \brief Structure used for a specific segment of the bridge */
+struct softmix_bridge_segment {
+	/*! Our position in the segment array */
+	unsigned int position;
+	/*! Format used for this segment */
+	int format;
+	/*! Number of channels in this segment */
+	unsigned int channels;
+	/*! Frame used for this segment */
+	struct ast_frame frame;
+	/*! Buffer used for audio */
+	short buf[SOFTMIX_SAMPLES];
+	/*! Translation paths to other segments */
+	struct ast_trans_pvt *paths[SOFTMIX_SEGMENTS_MAX];
+};
+
+/*! \brief Structure used for the conference bridge */
+struct softmix_bridge {
+	/*! File descriptor used for timing */
+	int timingfd;
+	/*! Segments present in this bridge */
+	struct softmix_bridge_segment segments[SOFTMIX_SEGMENTS_MAX];
+	/*! Bit to indicate that the timer has been activated */
+	unsigned int active:1;
+};
+
+/*! \brief Structure used for each individual channel */
 struct softmix_channel {
-	/*! Lock to protect this structure */
-	ast_mutex_t lock;
-	/*! Factory which contains audio read in from the channel */
+	/*! Segment this channel belongs to */
+	struct softmix_bridge_segment *segment;
+	/*! Factory used for audio received from the channel */
 	struct ast_slinfactory factory;
-	/*! Frame that contains mixed audio to be written out to the channel */
-	struct ast_frame frame;
-	/*! Bit used to indicate that the channel provided audio for this mixing interval */
-	int have_audio:1;
-	/*! Bit used to indicate that a frame is available to be written out to the channel */
-	int have_frame:1;
-	/*! Buffer containing final mixed audio from all sources */
-	short final_buf[SOFTMIX_DATALEN];
-	/*! Buffer containing only the audio from the channel */
-	short our_buf[SOFTMIX_DATALEN];
+	/*! Buffer used for audio */
+	short buf[SOFTMIX_SAMPLES];
+	/*! Bit to indicate we got audio from this channel */
+	unsigned int audio:1;
 };
+
+/*! \brief Internal function which sets up a bridge segment with initial values */
+static void softmix_bridge_segment_setup(struct softmix_bridge_segment *softmix_bridge_segment, unsigned int position, int format, unsigned int samples)
+{
+	softmix_bridge_segment->position = position;
+	softmix_bridge_segment->format = format;
+
+	softmix_bridge_segment->frame.frametype = AST_FRAME_VOICE;
+	softmix_bridge_segment->frame.subclass = format;
+	softmix_bridge_segment->frame.data.ptr = softmix_bridge_segment->buf;
+	softmix_bridge_segment->frame.samples = samples;
+	softmix_bridge_segment->frame.datalen = samples * 2;
+	softmix_bridge_segment->frame.offset = SOFTMIX_SAMPLES - samples;
+}
+
+/*! \brief Internal function used to start transcoding of a bridge segment */
+static int softmix_bridge_segment_transcoding_start(struct softmix_bridge *softmix_bridge, struct softmix_bridge_segment *softmix_bridge_segment)
+{
+	int i;
+
+	for (i = 0; i < SOFTMIX_SEGMENTS_MAX; i++) {
+		if (i == softmix_bridge_segment->position) {
+			continue;
+		}
+
+		if (!(softmix_bridge->segments[i].paths[softmix_bridge_segment->position] = ast_translator_build_path(
+			      softmix_bridge_segment->format, softmix_bridge->segments[i].format))) {
+			return -1;
+		}
+
+		if (!(softmix_bridge_segment->paths[i] = ast_translator_build_path(
+			      softmix_bridge->segments[i].format, softmix_bridge_segment->format))) {
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+/*! \brief Internal function used to stop transcoding of a bridge segment */
+static void softmix_bridge_segment_transcoding_stop(struct softmix_bridge *softmix_bridge, struct softmix_bridge_segment *softmix_bridge_segment)
+{
+	int i;
+
+	for (i = 0; i < SOFTMIX_SEGMENTS_MAX; i++) {
+		if (softmix_bridge_segment->paths[i]) {
+			ast_translator_free_path(softmix_bridge_segment->paths[i]);
+		}
+		softmix_bridge_segment->paths[i] = NULL;
+		if (softmix_bridge->segments[i].paths[softmix_bridge_segment->position]) {
+			ast_translator_free_path(softmix_bridge->segments[i].paths[softmix_bridge_segment->position]);
+		}
+		softmix_bridge->segments[i].paths[softmix_bridge_segment->position] = NULL;
+	}
+}
 
 /*! \brief Function called when a bridge is created */
 static int softmix_bridge_create(struct ast_bridge *bridge)
 {
-	int timingfd;
-
-	if ((timingfd = ast_timer_open()) < 0) {
-		return -1;
-	}
-
-	ast_timer_close(timingfd);
+	struct softmix_bridge *softmix_bridge;
+
+	if (!(softmix_bridge = ast_calloc(1, sizeof(*softmix_bridge)))) {
+		return -1;
+	}
+
+	if ((softmix_bridge->timingfd = ast_timer_open()) < 0) {
+		ast_free(softmix_bridge);
+		return -1;
+	}
+
+	softmix_bridge_segment_setup(&softmix_bridge->segments[0], 0, AST_FORMAT_SLINEAR, 160);
+	softmix_bridge_segment_setup(&softmix_bridge->segments[1], 1, AST_FORMAT_SLINEAR16, 320);
+
+	bridge->fds[0] = softmix_bridge->timingfd;
+	bridge->bridge_pvt = softmix_bridge;
+
+	return 0;
+}
+
+/*! \brief Function called when a bridge is destroyed */
+static int softmix_bridge_destroy(struct ast_bridge *bridge)
+{
+	struct softmix_bridge *softmix_bridge = bridge->bridge_pvt;
+
+	ast_timer_close(softmix_bridge->timingfd);
+
+	ast_free(softmix_bridge);
 
 	return 0;
 }
@@ -99,32 +193,54 @@
 /*! \brief Function called when a channel is joined into the bridge */
 static int softmix_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
 {
-	struct softmix_channel *sc = NULL;
-
-	/* Create a new softmix_channel structure and allocate various things on it */
-	if (!(sc = ast_calloc(1, sizeof(*sc)))) {
-		return -1;
-	}
-
-	/* Can't forget the lock */
-	ast_mutex_init(&sc->lock);
-
-	/* Setup smoother */
-	ast_slinfactory_init(&sc->factory);
-
-	/* Setup frame parameters */
-	sc->frame.frametype = AST_FRAME_VOICE;
-#ifdef SOFTMIX_16_SUPPORT
-	sc->frame.subclass = AST_FORMAT_SLINEAR16;
+	struct softmix_bridge *softmix_bridge = bridge->bridge_pvt;
+	struct softmix_channel *softmix_channel;
+
+	if (!(softmix_channel = ast_calloc(1, sizeof(*softmix_channel)))) {
+		return -1;
+	}
+
+	if (ast_format_rate(bridge_channel->chan->writeformat) == 16000) {
+		softmix_channel->segment = &softmix_bridge->segments[1];
+	} else if (ast_format_rate(bridge_channel->chan->writeformat) == 8000) {
+		softmix_channel->segment = &softmix_bridge->segments[0];
+	} else {
+		ast_log(LOG_ERROR, "Failed to join channel '%s' to bridge '%p' because of incompatible rate '%d'\n",
+			bridge_channel->chan->name, bridge, ast_format_rate(bridge_channel->chan->writeformat));
+		ast_free(softmix_channel);
+		return -1;
+	}
+
+	if (ast_set_read_format(bridge_channel->chan, softmix_channel->segment->format) || ast_set_write_format(bridge_channel->chan, softmix_channel->segment->format)) {
+		ast_log(LOG_ERROR, "Failed to join channel '%s' to bridge '%p' because of inability to transcode into '%s'\n",
+			bridge_channel->chan->name, bridge, ast_getformatname(softmix_channel->segment->format));
+		ast_free(softmix_channel);
+		return -1;
+	}
+
+	if (!softmix_channel->segment->channels && softmix_bridge_segment_transcoding_start(softmix_bridge, softmix_channel->segment)) {
+		softmix_bridge_segment_transcoding_stop(softmix_bridge, softmix_channel->segment);
+#ifndef SOFTMIX_SEGMENTS_ISOLATE
+		ast_log(LOG_ERROR, "Failed to join channel '%s' to bridge '%p' because segment could not be transcoded\n",
+			bridge_channel->chan->name, bridge);
+		ast_free(softmix_channel);
+		return -1;
 #else
-	sc->frame.subclass = AST_FORMAT_SLINEAR;
+		ast_debug(1, "Segment '%p' on bridge '%p' is going to be isolated from the rest of the bridge\n",
+			  softmix_channel->segment, bridge);
 #endif
-	sc->frame.data.ptr = sc->final_buf;
-	sc->frame.datalen = SOFTMIX_DATALEN;
-	sc->frame.samples = SOFTMIX_SAMPLES;
-
-	/* Can't forget to record our pvt structure within the bridged channel structure */
-	bridge_channel->bridge_pvt = sc;
+	}
+
+	softmix_channel->segment->channels++;
+
+	ast_slinfactory_init_rate(&softmix_channel->factory, ast_format_rate(softmix_channel->segment->format));
+
+	bridge_channel->bridge_pvt = softmix_channel;
+
+	if (!softmix_bridge->active) {
+		ast_timer_set_rate(softmix_bridge->timingfd, (1000 / SOFTMIX_INTERVAL));
+		softmix_bridge->active = 1;
+	}
 
 	return 0;
 }
@@ -132,16 +248,80 @@
 /*! \brief Function called when a channel leaves the bridge */
 static int softmix_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
 {
-	struct softmix_channel *sc = bridge_channel->bridge_pvt;
-
-	/* Drop mutex lock */
-	ast_mutex_destroy(&sc->lock);
-
-	/* Drop the factory */
-	ast_slinfactory_destroy(&sc->factory);
-
-	/* Eep! drop ourselves */
-	ast_free(sc);
+	struct softmix_channel *softmix_channel = bridge_channel->bridge_pvt;
+
+	softmix_channel->segment->channels--;
+
+	if (!softmix_channel->segment->channels) {
+		softmix_bridge_segment_transcoding_stop(bridge->bridge_pvt, softmix_channel->segment);
+	}
+
+	ast_slinfactory_destroy(&softmix_channel->factory);
+
+	ast_free(softmix_channel);
+
+	return 0;
+}
+
+/*! \brief Internal function which sees if audio is available and mixes it in */
+static void softmix_mix_audio(struct softmix_channel *softmix_channel)
+{
+	short *our_buf = softmix_channel->buf, *segment_buf = softmix_channel->segment->buf;
+	int i;
+
+	if (ast_slinfactory_available(&softmix_channel->factory) < softmix_channel->segment->frame.samples) {
+		return;
+	}
+
+	if (!ast_slinfactory_read(&softmix_channel->factory, softmix_channel->buf, softmix_channel->segment->frame.samples)) {
+		return;
+	}
+
+	for (i = 0; i < softmix_channel->segment->frame.samples; i++, our_buf++, segment_buf++) {
+		ast_slinear_saturated_add(segment_buf, our_buf);
+	}
+
+	softmix_channel->audio = 1;
+}
+
+/*! \brief Function called when the timing file descriptor wants us to go */
+static int softmix_bridge_fd(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, int fd)
+{
+	struct softmix_bridge *softmix_bridge = bridge->bridge_pvt;
+	int i;
+
+	ast_timer_ack(softmix_bridge->timingfd, 1);
+
+	/* Get any audio that we may not have gotten previously */
+	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+		struct softmix_channel *softmix_channel = bridge_channel->bridge_pvt;
+
+		if (!softmix_channel->audio) {
+			softmix_mix_audio(softmix_channel);
+		}
+	}
+
+	/* Exchange audio between any bridge segments */
+
+	/* Write out the audio from each frame to the individual channels */
+	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+		struct softmix_channel *softmix_channel = bridge_channel->bridge_pvt;
+
+		if (softmix_channel->audio) {
+			ast_write(bridge_channel->chan, &softmix_channel->segment->frame);
+		} else {
+		}
+
+		softmix_channel->audio = 0;
+	}
+
+	/* Go back to a clean slate of audio on the segments */
+	for (i = 0; i < SOFTMIX_SEGMENTS_MAX; i++) {
+		if (!softmix_bridge->segments[i].channels) {
+			continue;
+		}
+		memset(softmix_bridge->segments[i].buf, 0, sizeof(softmix_bridge->segments[i].buf));
+	}
 
 	return 0;
 }
@@ -149,145 +329,32 @@
 /*! \brief Function called when a channel writes a frame into the bridge */
 static enum ast_bridge_write_result softmix_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
 {
-	struct softmix_channel *sc = bridge_channel->bridge_pvt;
-
-	/* Only accept audio frames, all others are unsupported */
+	struct softmix_channel *softmix_channel = bridge_channel->bridge_pvt;
+
 	if (frame->frametype != AST_FRAME_VOICE) {
 		return AST_BRIDGE_WRITE_UNSUPPORTED;
 	}
 
-	ast_mutex_lock(&sc->lock);
-
-	/* If a frame was provided add it to the smoother */
-#ifdef SOFTMIX_16_SUPPORT
-	if (frame->frametype == AST_FRAME_VOICE && frame->subclass == AST_FORMAT_SLINEAR16) {
-#else
-	if (frame->frametype == AST_FRAME_VOICE && frame->subclass == AST_FORMAT_SLINEAR) {
-#endif
-		ast_slinfactory_feed(&sc->factory, frame);
-	}
-
-	/* If a frame is ready to be written out, do so */
-	if (sc->have_frame) {
-		ast_write(bridge_channel->chan, &sc->frame);
-		sc->have_frame = 0;
-	}
-
-	/* Alllll done */
-	ast_mutex_unlock(&sc->lock);
+	ast_slinfactory_feed(&softmix_channel->factory, frame);
+
+	if (!softmix_channel->audio) {
+		softmix_mix_audio(softmix_channel);
+	}
 
 	return AST_BRIDGE_WRITE_SUCCESS;
-}
-
-/*! \brief Function called when the channel's thread is poked */
-static int softmix_bridge_poke(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
-{
-	struct softmix_channel *sc = bridge_channel->bridge_pvt;
-
-	ast_mutex_lock(&sc->lock);
-
-	if (sc->have_frame) {
-		ast_write(bridge_channel->chan, &sc->frame);
-		sc->have_frame = 0;
-	}
-
-	ast_mutex_unlock(&sc->lock);
-
-	return 0;
-}
-
-/*! \brief Function which acts as the mixing thread */
-static int softmix_bridge_thread(struct ast_bridge *bridge)
-{
-	int timingfd;
-
-	if ((timingfd = ast_timer_open()) < 0) {
-		return -1;
-	}
-
-	ast_timer_set_rate(timingfd, (1000 / SOFTMIX_INTERVAL));
-
-	while (!bridge->stop && !bridge->refresh && bridge->array_num) {
-		struct ast_bridge_channel *bridge_channel = NULL;
-		short buf[SOFTMIX_DATALEN] = {0, };
-		int timeout = -1;
-
-		/* Go through pulling audio from each factory that has it available */
-		AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
-			struct softmix_channel *sc = bridge_channel->bridge_pvt;
-
-			ast_mutex_lock(&sc->lock);
-
-			/* Try to get audio from the factory if available */
-			if (ast_slinfactory_available(&sc->factory) >= SOFTMIX_SAMPLES && ast_slinfactory_read(&sc->factory, sc->our_buf, SOFTMIX_SAMPLES)) {
-				short *data1, *data2;
-				int i;
-
-				/* Put into the local final buffer */
-				for (i = 0, data1 = buf, data2 = sc->our_buf; i < SOFTMIX_DATALEN; i++, data1++, data2++)
-					ast_slinear_saturated_add(data1, data2);
-				/* Yay we have our own audio */
-				sc->have_audio = 1;
-			} else {
-				/* Awww we don't have audio ;( */
-				sc->have_audio = 0;
-			}
-			ast_mutex_unlock(&sc->lock);
-		}
-
-		/* Next step go through removing the channel's own audio and creating a good frame... */
-		AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
-			struct softmix_channel *sc = bridge_channel->bridge_pvt;
-			int i = 0;
-
-			/* Copy from local final buffer to our final buffer */
-			memcpy(sc->final_buf, buf, sizeof(sc->final_buf));
-
-			/* If we provided audio then take it out */
-			if (sc->have_audio) {
-				for (i = 0; i < SOFTMIX_DATALEN; i++) {
-					ast_slinear_saturated_subtract(&sc->final_buf[i], &sc->our_buf[i]);
-				}
-			}
-
-			/* The frame is now ready for use... */
-			sc->have_frame = 1;
-
-			/* Poke bridged channel thread just in case */
-			pthread_kill(bridge_channel->thread, SIGURG);
-		}
-
-		ao2_unlock(bridge);
-
-		/* Wait for the timing source to tell us to wake up and get things done */
-		ast_waitfor_n_fd(&timingfd, 1, &timeout, NULL);
-
-		ast_timer_ack(timingfd, 1);
-
-		ao2_lock(bridge);
-	}
-
-	ast_timer_set_rate(timingfd, 0);
-	ast_timer_close(timingfd);
-
-	return 0;
 }
 
 static struct ast_bridge_technology softmix_bridge = {
 	.name = "softmix",
-	.capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX | AST_BRIDGE_CAPABILITY_THREAD | AST_BRIDGE_CAPABILITY_MULTITHREADED,
+	.capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX | AST_BRIDGE_CAPABILITY_THREAD,
 	.preference = AST_BRIDGE_PREFERENCE_LOW,
-#ifdef SOFTMIX_16_SUPPORT
-	.formats = AST_FORMAT_SLINEAR16,
-#else
-	.formats = AST_FORMAT_SLINEAR,
-#endif
+	.formats = AST_FORMAT_AUDIO_MASK,
 	.create = softmix_bridge_create,
+	.destroy = softmix_bridge_destroy,
 	.join = softmix_bridge_join,
 	.leave = softmix_bridge_leave,
 	.write = softmix_bridge_write,
-	.thread = softmix_bridge_thread,
-	.poke = softmix_bridge_poke,
+	.fd = softmix_bridge_fd,
 };
 
 static int unload_module(void)




More information about the svn-commits mailing list