[Asterisk-code-review] app stream echo: Added a multi-stream echo application (asterisk[master])

Kevin Harwell asteriskteam at digium.com
Mon May 15 13:33:06 CDT 2017


Kevin Harwell has uploaded a new change for review. ( https://gerrit.asterisk.org/5629 )

Change subject: app_stream_echo: Added a multi-stream echo application
......................................................................

app_stream_echo: Added a multi-stream echo application

If the channel does not have multi-stream support then this application acts
just like app_echo. However if it does have multi-stream support then each
stream is echoed back to itself (one-to-one). If a "num" (optional parameter)
is given, and the channel allows it, then the default stream is echoed back
onto "num" streams (one-to-many) for a given media type ("audio" or "video",
with "video" being the default).

Change-Id: I254144486734178e196c7f590a26ffc13543ff2c
---
A apps/app_stream_echo.c
1 file changed, 304 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/29/5629/1

diff --git a/apps/app_stream_echo.c b/apps/app_stream_echo.c
new file mode 100644
index 0000000..50a03c7
--- /dev/null
+++ b/apps/app_stream_echo.c
@@ -0,0 +1,304 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2017, Digium, Inc.
+ *
+ * Kevin Harwell <kharwell at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Stream echo application
+ *
+ * \author Kevin Harwell <kharwell at digium.com>
+ */
+
+/*** MODULEINFO
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include "asterisk/app.h"
+#include "asterisk/conversions.h"
+#include "asterisk/file.h"
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/stream.h"
+
+/*** DOCUMENTATION
+	<application name="StreamEcho" language="en_US">
+		<synopsis>
+			Echo media, up to 'N' streams, and DTMF back to the calling party
+		</synopsis>
+		<syntax>
+		<parameter name="num" required="false">
+			<para>The number of streams to echo the default stream media back onto.</para>
+		</parameter>
+		<parameter name="type" required="false">
+			<para>The media type of the stream to echo. Should be set to "audio" or "video"
+			with "video" being the default if not specified. Note, too this parameter is
+			ignored if "num" has not been given.</para>
+		</parameter>
+		</syntax>
+		<description>
+			<para>If no parameters are specified (e.g.. the number of streams) then
+			this simply echos back any media or DTMF frames (note, however if '#' is
+			detected then the application exits) read from the calling channel back
+			to itself. It does not echo CONTROL, MODEM, or NULL frames.</para>
+			<para>If the "num" parameter is specified, and if the calling channel
+			is capable, then any media received on the default stream is echoed
+			back on the other "num" streams for a given "type".</para>
+		</description>
+	</application>
+ ***/
+
+static const char app[] = "StreamEcho";
+
+static int stream_echo_write_error(struct ast_channel *chan, struct ast_frame *frame, int pos)
+{
+	char frame_type[32];
+	const char *media_type;
+	struct ast_stream *stream;
+
+	ast_frame_type2str(frame->frametype, frame_type, 32);
+
+	stream = pos < 0 ?
+		ast_channel_get_default_stream(chan, ast_format_get_type(frame->subclass.format)) :
+		ast_stream_topology_get_stream(ast_channel_get_stream_topology(chan), pos);
+
+	media_type = ast_codec_media_type2str(ast_stream_get_type(stream));
+
+	ast_log(LOG_ERROR, "%s - unable to write frame type '%s' to stream type '%s' at "
+		"position '%d'\n", ast_channel_name(chan), frame_type, media_type,
+		ast_stream_get_position(stream));
+
+	return -1;
+}
+
+static int stream_echo_write_one_to_many(struct ast_channel *chan,
+	struct ast_frame *frame, enum ast_media_type type)
+{
+	int i;
+	int pos;
+	struct ast_stream_topology *topology;
+	struct ast_stream *stream;
+
+	if (type != ast_format_get_type(frame->subclass.format)) {
+		/* Ignore other types */
+		return 0;
+	}
+
+	stream = ast_channel_get_default_stream(chan, type);
+	if (!stream) {
+		return -1;
+	}
+
+	pos = ast_stream_get_position(stream);
+	if (pos != frame->stream_num) {
+		/* Only receive and echo frames from the default stream */
+		return 0;
+	}
+
+	topology = ast_channel_get_stream_topology(chan);
+
+	/* Write the frame to every stream of the given type */
+	for (i = 0; i < ast_stream_topology_get_count(topology); ++i) {
+		struct ast_stream *s = ast_stream_topology_get_stream(topology, i);
+
+		if (s && ast_stream_get_type(s) == type) {
+			if (ast_write_stream(chan, i, frame)) {
+				return stream_echo_write_error(chan, frame, i);
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int stream_echo_write_one_to_one(struct ast_channel *chan, struct ast_frame *frame)
+{
+	int num = ast_channel_is_multistream(chan) ? frame->stream_num : -1;
+
+	return ast_write_stream(chan, num, frame) ?
+		stream_echo_write_error(chan, frame, num) : 0;
+}
+
+static int stream_echo_write(struct ast_channel *chan, struct ast_frame *frame,
+	enum ast_media_type type, int one_to_one)
+{
+	return one_to_one ? stream_echo_write_one_to_one(chan, frame) :
+		stream_echo_write_one_to_many(chan, frame, type);
+}
+
+static int stream_echo_perform(struct ast_channel *chan,
+	struct ast_stream_topology *topology, enum ast_media_type type)
+{
+	int update_sent = 0;
+	int request_change = topology == NULL;
+	int one_to_one = 1;
+
+	while (ast_waitfor(chan, -1) > -1) {
+		struct ast_frame *f;
+
+		if (request_change) {
+			/* Request a change to the new topology */
+			if (ast_channel_request_stream_topology_change(chan, topology, NULL)) {
+				ast_log(LOG_WARNING, "Request stream topology change not supported "
+					"by channel '%s'\n", ast_channel_name(chan));
+			}
+			request_change = 0;
+		}
+
+		f = ast_read(chan);
+		if (!f) {
+			return -1;
+		}
+
+		if ((f->frametype == AST_FRAME_DTMF) && (f->subclass.integer == '#')) {
+			ast_frfree(f);
+			break;
+		}
+
+		f->delivery.tv_sec = 0;
+		f->delivery.tv_usec = 0;
+
+		if (f->frametype == AST_FRAME_CONTROL) {
+			if (f->subclass.integer == AST_CONTROL_VIDUPDATE && !update_sent) {
+				if (stream_echo_write(chan, f, one_to_one, type)) {
+					ast_frfree(f);
+					return -1;
+				}
+				update_sent = 1;
+			} else if (f->subclass.integer == AST_CONTROL_STREAM_TOPOLOGY_CHANGED) {
+				update_sent = 0;
+				one_to_one = 0; /* Switch writing to one to many */
+			}
+		} else if (f->frametype == AST_FRAME_VIDEO && !update_sent){
+			struct ast_frame frame = {
+				.frametype = AST_FRAME_CONTROL,
+				.subclass.integer = AST_CONTROL_VIDUPDATE,
+			};
+			stream_echo_write(chan, &frame, one_to_one, type);
+			update_sent = 1;
+		}
+
+		if (f->frametype != AST_FRAME_CONTROL &&
+		    f->frametype != AST_FRAME_MODEM &&
+		    f->frametype != AST_FRAME_NULL &&
+		    stream_echo_write(chan, f, one_to_one, type)) {
+			ast_frfree(f);
+			return -1;
+		}
+
+		ast_frfree(f);
+	}
+
+	return 0;
+}
+
+static int stream_echo_topology_append_streams(struct ast_stream_topology *topology,
+	unsigned int num, enum ast_media_type type)
+{
+	unsigned int i;
+
+	for (i = 0; i < num; ++i) {
+		struct ast_stream *stream = ast_stream_alloc(NULL, type);
+
+		if (!stream) {
+			return -1;
+		}
+
+		ast_stream_set_state(stream, i == 0 ?
+			AST_STREAM_STATE_SENDRECV : AST_STREAM_STATE_RECVONLY);
+
+		if (ast_stream_topology_append_stream(topology, stream) < 0) {
+			ast_stream_free(stream);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int stream_echo_exec(struct ast_channel *chan, const char *data)
+{
+	int res;
+	unsigned int num = 0;
+	enum ast_media_type type;
+	char *parse;
+	struct ast_stream_topology *topology;
+
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(num);
+		AST_APP_ARG(type);
+	);
+
+	parse = ast_strdupa(data);
+	AST_STANDARD_APP_ARGS(args, parse);
+
+	if (ast_strlen_zero(args.num)) {
+		/*
+		 * If a number is not given then no topology is to be created
+		 * and renegotiated. The app will just echo back each stream
+		 * received to itself.
+		 */
+		return stream_echo_perform(chan, NULL, AST_MEDIA_TYPE_UNKNOWN);
+	}
+
+	/*
+	 * If a valid number is given then create a topology of 'num' streams
+	 * for a given media type. A topology change is then requested on the
+	 * channel. If the change takes place then the media received on the
+	 * default stream is then echoed back to each of the other streams of
+	 * the same media type.
+	 */
+	if (ast_str_to_uint(args.num, &num)) {
+		return -1;
+	}
+
+	type = ast_strlen_zero(args.type) ? AST_MEDIA_TYPE_VIDEO :
+		ast_media_type_from_str(args.type);
+
+	topology = ast_stream_topology_alloc();
+	if (!topology) {
+		return -1;
+	}
+
+	if (stream_echo_topology_append_streams(topology, num, type)) {
+		ast_log(LOG_ERROR, "Unable to append '%u' streams of type '%s' to"
+			" the topology\n", num, ast_codec_media_type2str(type));
+		ast_stream_topology_free(topology);
+		return -1;
+	}
+
+	res = stream_echo_perform(chan, topology, type);
+
+	if (ast_channel_get_stream_topology(chan) != topology) {
+		ast_stream_topology_free(topology);
+	}
+
+	return res;
+}
+
+static int unload_module(void)
+{
+	return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+	return ast_register_application_xml(app, stream_echo_exec);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Stream Echo Application");

-- 
To view, visit https://gerrit.asterisk.org/5629
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I254144486734178e196c7f590a26ffc13543ff2c
Gerrit-PatchSet: 1
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Owner: Kevin Harwell <kharwell at digium.com>



More information about the asterisk-code-review mailing list