Asterisk - The Open Source Telephony Project GIT-master-8924258
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
app_stream_echo.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2017, Digium, Inc.
5 *
6 * Kevin Harwell <kharwell@digium.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*! \file
20 *
21 * \brief Stream echo application
22 *
23 * \author Kevin Harwell <kharwell@digium.com>
24 */
25
26/*** MODULEINFO
27 <support_level>core</support_level>
28 ***/
29
30#include "asterisk.h"
31
32#include "asterisk/app.h"
34#include "asterisk/file.h"
35#include "asterisk/module.h"
36#include "asterisk/channel.h"
37#include "asterisk/stream.h"
38
39/*** DOCUMENTATION
40 <application name="StreamEcho" language="en_US">
41 <since>
42 <version>15.0.0</version>
43 </since>
44 <synopsis>
45 Echo media, up to 'N' streams of a type, and DTMF back to the calling party
46 </synopsis>
47 <syntax>
48 <parameter name="num" required="false">
49 <para>The number of streams of a type to echo back. If '0' is specified then
50 all streams of a type are removed.</para>
51 </parameter>
52 <parameter name="type" required="false">
53 <para>The media type of the stream(s) to add or remove (in the case of "num"
54 being '0'). This can be set to either "audio" or "video" (default). If "num"
55 is empty (i.e. not specified) then this parameter is ignored.</para>
56 </parameter>
57 </syntax>
58 <description>
59 <para>If a "num" (the number of streams) is not given then this simply echos
60 back any media or DTMF frames (note, however if '#' is detected then the
61 application exits) read from the calling channel back to itself. This means
62 for any relevant frame read from a particular stream it is written back out
63 to the associated write stream in a one to one fashion.
64 </para>
65 <para>However if a "num" is specified, and if the calling channel allows it
66 (a new offer is made requesting the allowance of additional streams) then any
67 any media received, like before, is echoed back onto each stream. However, in
68 this case a relevant frame received on a stream of the given "type" is also
69 echoed back out to the other streams of that same type. It should be noted that
70 when operating in this mode only the first stream found of the given "type" is
71 allowed from the original offer. And this first stream found is also the only
72 stream of that "type" granted read (send/receive) capabilities in the new offer
73 whereas the additional ones are set to receive only.</para>
74 <note><para>This does not echo CONTROL, MODEM, or NULL frames.</para></note>
75 </description>
76 </application>
77 ***/
78
79static const char app[] = "StreamEcho";
80
81static int stream_echo_write_error(struct ast_channel *chan, struct ast_frame *frame, int pos)
82{
83 char frame_type[32];
84 const char *media_type;
85 struct ast_stream *stream;
86
88
89 stream = pos < 0 ?
92
94
95 ast_log(LOG_ERROR, "%s - unable to write frame type '%s' to stream type '%s' at "
96 "position '%d'\n", ast_channel_name(chan), frame_type, media_type,
98
99 return -1;
100}
101
102static int stream_echo_write(struct ast_channel *chan, struct ast_frame *frame,
103 enum ast_media_type type, int one_to_one)
104{
105 int i;
106 int num;
107 struct ast_stream_topology *topology;
108
109 /*
110 * Since this is an echo application if we get a frame in on a stream
111 * we simply want to echo it back out onto the same stream number.
112 */
113 num = ast_channel_is_multistream(chan) ? frame->stream_num : -1;
114 if (ast_write_stream(chan, num, frame)) {
115 return stream_echo_write_error(chan, frame, num);
116 }
117
118 /*
119 * If the frame's type and given type don't match, or we are operating in
120 * a one to one stream echo mode then there is nothing left to do.
121 *
122 * Note, if the channel is not multi-stream capable then one_to_one will
123 * always be true, so it is safe to also not check for that here too.
124 */
125 if (one_to_one || !frame->subclass.format ||
127 return 0;
128 }
129
130 /*
131 * However, if we are operating in a single stream echoed to many stream
132 * mode, and the frame's type matches the given type then we also need to
133 * find the other streams of the same type and write out to those streams
134 * as well.
135 *
136 * If we are here, then it's accepted that whatever stream number the frame
137 * was read from for the given type is the only one set to send/receive,
138 * while the others of the same type are set to receive only. Since we
139 * shouldn't assume any order to the streams, we'll loop back through all
140 * streams in the channel's topology writing only to those of the same type.
141 * And, of course also not the stream which has already been written to.
142 */
143 topology = ast_channel_get_stream_topology(chan);
144
145 for (i = 0; i < ast_stream_topology_get_count(topology); ++i) {
146 struct ast_stream *stream = ast_stream_topology_get_stream(topology, i);
147 if (num != i && ast_stream_get_type(stream) == type) {
148 if (ast_write_stream(chan, i, frame)) {
149 return stream_echo_write_error(chan, frame, i);
150 }
151 }
152 }
153
154 return 0;
155}
156
157static int stream_echo_perform(struct ast_channel *chan,
158 struct ast_stream_topology *topology, enum ast_media_type type)
159{
160 int update_sent = 0;
161 int request_change = topology != NULL;
162 int one_to_one = 1;
163
164 while (ast_waitfor(chan, -1) > -1) {
165 struct ast_frame *f;
166
167 if (request_change) {
168 /* Request a change to the new topology */
170 ast_log(LOG_WARNING, "Request stream topology change not supported "
171 "by channel '%s'\n", ast_channel_name(chan));
172 }
173 request_change = 0;
174 }
175
176 f = ast_read_stream(chan);
177 if (!f) {
178 return -1;
179 }
180
181 if ((f->frametype == AST_FRAME_DTMF) && (f->subclass.integer == '#')) {
182 ast_frfree(f);
183 break;
184 }
185
186 f->delivery.tv_sec = 0;
187 f->delivery.tv_usec = 0;
188
189 if (f->frametype == AST_FRAME_CONTROL) {
190 if (f->subclass.integer == AST_CONTROL_VIDUPDATE && !update_sent) {
191 if (stream_echo_write(chan, f, type, one_to_one)) {
192 ast_frfree(f);
193 return -1;
194 }
195 update_sent = 1;
196 } else if (f->subclass.integer == AST_CONTROL_SRCCHANGE) {
197 update_sent = 0;
199 update_sent = 0;
200 one_to_one = 0; /* Switch writing to one to many */
201 }
202 } else if (f->frametype == AST_FRAME_VIDEO && !update_sent){
203 struct ast_frame frame = {
205 .subclass.integer = AST_CONTROL_VIDUPDATE,
206 };
207 stream_echo_write(chan, &frame, type, one_to_one);
208 update_sent = 1;
209 }
210
211 if (f->frametype != AST_FRAME_CONTROL &&
214 stream_echo_write(chan, f, type, one_to_one)) {
215 ast_frfree(f);
216 return -1;
217 }
218
219 ast_frfree(f);
220 }
221
222 return 0;
223}
224
226 struct ast_stream_topology *original, unsigned int num, enum ast_media_type type)
227{
228 int i, n = num;
230
231 if (!res) {
232 return NULL;
233 }
234
235 /*
236 * Clone every stream of a type not matching the given one. If the type
237 * matches clone only the first stream found for the given type. Then for
238 * that stream clone it again up to num - 1 times. Ignore any other streams
239 * of the same matched type in the original topology.
240 *
241 * So for instance if the original stream contains 1 audio stream and 2 video
242 * streams (video stream 'A' and video stream 'B'), num is '3', and the given
243 * type is 'video' then the resulting topology will contain a clone of the
244 * audio stream along with 3 clones of video stream 'A'. Video stream 'B' is
245 * not copied over.
246 */
247 for (i = 0; i < ast_stream_topology_get_count(original); ++i) {
248 struct ast_stream *stream = ast_stream_topology_get_stream(original, i);
249
250 if (!n && ast_stream_get_type(stream) == type) {
251 /* Don't copy any[more] of the given type */
252 continue;
253 }
254
256 /* Don't copy removed/declined streams */
257 continue;
258 }
259
260 do {
261 stream = ast_stream_clone(stream, NULL);
262
263 if (!stream || ast_stream_topology_append_stream(res, stream) < 0) {
264 ast_stream_free(stream);
266 return NULL;
267 }
268
269 if (ast_stream_get_type(stream) != type) {
270 /* Do not multiply non matching streams */
271 break;
272 }
273
274 /*
275 * Since num is not zero yet (i.e. this is first stream found to
276 * match on the type) and the types match then loop num - 1 times
277 * cloning the same stream.
278 */
279 ast_stream_set_state(stream, n == num ?
281 } while (--n);
282 }
283
284 return res;
285}
286
287static int stream_echo_exec(struct ast_channel *chan, const char *data)
288{
289 int res;
290 unsigned int num = 0;
291 enum ast_media_type type;
292 char *parse;
293 struct ast_stream_topology *topology;
294
296 AST_APP_ARG(num);
298 );
299
300 parse = ast_strdupa(data);
302
303 if (ast_strlen_zero(args.num)) {
304 /*
305 * If a number is not given then no topology is to be created
306 * and renegotiated. The app will just echo back each stream
307 * received to itself.
308 */
310 }
311
312 if (ast_str_to_uint(args.num, &num)) {
313 ast_log(LOG_ERROR, "Failed to parse the first parameter '%s' into a"
314 " greater than or equal to zero\n", args.num);
315 return -1;
316 }
317
320
323 if (!topology) {
324 ast_log(LOG_ERROR, "Unable to create '%u' streams of type '%s' to"
325 " the topology\n", num, ast_codec_media_type2str(type));
326 return -1;
327 }
328
329 res = stream_echo_perform(chan, topology, type);
330
331 if (ast_channel_get_stream_topology(chan) != topology) {
332 ast_stream_topology_free(topology);
333 }
334
335 return res;
336}
337
338static int unload_module(void)
339{
341}
342
343static int load_module(void)
344{
346}
347
348AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Stream Echo Application");
static const char app[]
static int stream_echo_write(struct ast_channel *chan, struct ast_frame *frame, enum ast_media_type type, int one_to_one)
static int stream_echo_exec(struct ast_channel *chan, const char *data)
static int stream_echo_perform(struct ast_channel *chan, struct ast_stream_topology *topology, enum ast_media_type type)
static int stream_echo_write_error(struct ast_channel *chan, struct ast_frame *frame, int pos)
static int load_module(void)
static struct ast_stream_topology * stream_echo_topology_alloc(struct ast_stream_topology *original, unsigned int num, enum ast_media_type type)
static int unload_module(void)
Asterisk main include file. File version handling, generic pbx functions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
static const char type[]
Definition: chan_ooh323.c:109
General Asterisk PBX channel definitions.
const char * ast_channel_name(const struct ast_channel *chan)
int ast_channel_request_stream_topology_change(struct ast_channel *chan, struct ast_stream_topology *topology, void *change_source)
Request that the stream topology of a channel change.
Definition: channel.c:10992
int ast_waitfor(struct ast_channel *chan, int ms)
Wait for input on a channel.
Definition: channel.c:3190
struct ast_frame * ast_read_stream(struct ast_channel *chan)
Reads a frame, but does not filter to just the default streams.
Definition: channel.c:4279
struct ast_stream_topology * ast_channel_get_stream_topology(const struct ast_channel *chan)
Retrieve the topology of streams on a channel.
struct ast_stream * ast_channel_get_default_stream(struct ast_channel *chan, enum ast_media_type type)
Retrieve the default stream of a specific media type on a channel.
int ast_write_stream(struct ast_channel *chan, int stream_num, struct ast_frame *frame)
Write a frame to a stream This function writes the given frame to the indicated stream on the channel...
Definition: channel.c:5166
int ast_channel_is_multistream(struct ast_channel *chan)
Determine if a channel is multi-stream capable.
ast_media_type
Types of media.
Definition: codec.h:30
@ AST_MEDIA_TYPE_UNKNOWN
Definition: codec.h:31
@ AST_MEDIA_TYPE_VIDEO
Definition: codec.h:33
enum ast_media_type ast_media_type_from_str(const char *media_type_str)
Conversion function to take a media string and convert it to a media type.
Definition: codec.c:364
const char * ast_codec_media_type2str(enum ast_media_type type)
Conversion function to take a media type and turn it into a string.
Definition: codec.c:348
frame_type
Definition: codec_builtin.c:44
Conversion utility functions.
int ast_str_to_uint(const char *str, unsigned int *res)
Convert the given string to an unsigned integer.
Definition: conversions.c:56
Generic File Format Support. Should be included by clients of the file handling routines....
enum ast_media_type ast_format_get_type(const struct ast_format *format)
Get the media type of a format.
Definition: format.c:354
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define AST_FRAME_DTMF
char * ast_frame_type2str(enum ast_frame_type frame_type, char *ftype, size_t len)
Copy the discription of a frame type into the provided string.
Definition: main/frame.c:671
#define ast_frfree(fr)
@ AST_FRAME_VIDEO
@ AST_FRAME_NULL
@ AST_FRAME_MODEM
@ AST_FRAME_CONTROL
@ AST_CONTROL_VIDUPDATE
@ AST_CONTROL_STREAM_TOPOLOGY_CHANGED
@ AST_CONTROL_SRCCHANGE
#define LOG_ERROR
#define LOG_WARNING
Asterisk module definitions.
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:581
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
#define NULL
Definition: resample.c:96
Media Stream API.
struct ast_stream_topology * ast_stream_topology_alloc(void)
Create a stream topology.
Definition: stream.c:652
@ AST_STREAM_STATE_REMOVED
Set when the stream has been removed/declined.
Definition: stream.h:78
@ AST_STREAM_STATE_SENDRECV
Set when the stream is sending and receiving media.
Definition: stream.h:82
@ AST_STREAM_STATE_SENDONLY
Set when the stream is sending media only.
Definition: stream.h:86
int ast_stream_get_position(const struct ast_stream *stream)
Get the position of the stream in the topology.
Definition: stream.c:500
void ast_stream_set_state(struct ast_stream *stream, enum ast_stream_state state)
Set the state of a stream.
Definition: stream.c:380
int ast_stream_topology_append_stream(struct ast_stream_topology *topology, struct ast_stream *stream)
Append a stream to the topology.
Definition: stream.c:751
struct ast_stream * ast_stream_clone(const struct ast_stream *stream, const char *name)
Create a deep clone of an existing stream.
Definition: stream.c:257
struct ast_stream * ast_stream_topology_get_stream(const struct ast_stream_topology *topology, unsigned int position)
Get a specific stream from the topology.
Definition: stream.c:791
int ast_stream_topology_get_count(const struct ast_stream_topology *topology)
Get the number of streams in a topology.
Definition: stream.c:768
enum ast_stream_state ast_stream_get_state(const struct ast_stream *stream)
Get the current state of a stream.
Definition: stream.c:373
enum ast_media_type ast_stream_get_type(const struct ast_stream *stream)
Get the media type of a stream.
Definition: stream.c:316
void ast_stream_topology_free(struct ast_stream_topology *topology)
Unreference and destroy a stream topology.
Definition: stream.c:746
void ast_stream_free(struct ast_stream *stream)
Destroy a media stream representation.
Definition: stream.c:292
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
Main Channel structure associated with a channel.
struct ast_format * format
Data structure associated with a single frame of data.
struct ast_frame_subclass subclass
struct timeval delivery
enum ast_frame_type frametype
const char * args