Asterisk - The Open Source Telephony Project GIT-master-7e7a603
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 <synopsis>
42 Echo media, up to 'N' streams of a type, and DTMF back to the calling party
43 </synopsis>
44 <syntax>
45 <parameter name="num" required="false">
46 <para>The number of streams of a type to echo back. If '0' is specified then
47 all streams of a type are removed.</para>
48 </parameter>
49 <parameter name="type" required="false">
50 <para>The media type of the stream(s) to add or remove (in the case of "num"
51 being '0'). This can be set to either "audio" or "video" (default). If "num"
52 is empty (i.e. not specified) then this parameter is ignored.</para>
53 </parameter>
54 </syntax>
55 <description>
56 <para>If a "num" (the number of streams) is not given then this simply echos
57 back any media or DTMF frames (note, however if '#' is detected then the
58 application exits) read from the calling channel back to itself. This means
59 for any relevant frame read from a particular stream it is written back out
60 to the associated write stream in a one to one fashion.
61 </para>
62 <para>However if a "num" is specified, and if the calling channel allows it
63 (a new offer is made requesting the allowance of additional streams) then any
64 any media received, like before, is echoed back onto each stream. However, in
65 this case a relevant frame received on a stream of the given "type" is also
66 echoed back out to the other streams of that same type. It should be noted that
67 when operating in this mode only the first stream found of the given "type" is
68 allowed from the original offer. And this first stream found is also the only
69 stream of that "type" granted read (send/receive) capabilities in the new offer
70 whereas the additional ones are set to receive only.</para>
71 <note><para>This does not echo CONTROL, MODEM, or NULL frames.</para></note>
72 </description>
73 </application>
74 ***/
75
76static const char app[] = "StreamEcho";
77
78static int stream_echo_write_error(struct ast_channel *chan, struct ast_frame *frame, int pos)
79{
80 char frame_type[32];
81 const char *media_type;
82 struct ast_stream *stream;
83
85
86 stream = pos < 0 ?
89
91
92 ast_log(LOG_ERROR, "%s - unable to write frame type '%s' to stream type '%s' at "
93 "position '%d'\n", ast_channel_name(chan), frame_type, media_type,
95
96 return -1;
97}
98
99static int stream_echo_write(struct ast_channel *chan, struct ast_frame *frame,
100 enum ast_media_type type, int one_to_one)
101{
102 int i;
103 int num;
104 struct ast_stream_topology *topology;
105
106 /*
107 * Since this is an echo application if we get a frame in on a stream
108 * we simply want to echo it back out onto the same stream number.
109 */
110 num = ast_channel_is_multistream(chan) ? frame->stream_num : -1;
111 if (ast_write_stream(chan, num, frame)) {
112 return stream_echo_write_error(chan, frame, num);
113 }
114
115 /*
116 * If the frame's type and given type don't match, or we are operating in
117 * a one to one stream echo mode then there is nothing left to do.
118 *
119 * Note, if the channel is not multi-stream capable then one_to_one will
120 * always be true, so it is safe to also not check for that here too.
121 */
122 if (one_to_one || !frame->subclass.format ||
124 return 0;
125 }
126
127 /*
128 * However, if we are operating in a single stream echoed to many stream
129 * mode, and the frame's type matches the given type then we also need to
130 * find the other streams of the same type and write out to those streams
131 * as well.
132 *
133 * If we are here, then it's accepted that whatever stream number the frame
134 * was read from for the given type is the only one set to send/receive,
135 * while the others of the same type are set to receive only. Since we
136 * shouldn't assume any order to the streams, we'll loop back through all
137 * streams in the channel's topology writing only to those of the same type.
138 * And, of course also not the stream which has already been written to.
139 */
140 topology = ast_channel_get_stream_topology(chan);
141
142 for (i = 0; i < ast_stream_topology_get_count(topology); ++i) {
143 struct ast_stream *stream = ast_stream_topology_get_stream(topology, i);
144 if (num != i && ast_stream_get_type(stream) == type) {
145 if (ast_write_stream(chan, i, frame)) {
146 return stream_echo_write_error(chan, frame, i);
147 }
148 }
149 }
150
151 return 0;
152}
153
154static int stream_echo_perform(struct ast_channel *chan,
155 struct ast_stream_topology *topology, enum ast_media_type type)
156{
157 int update_sent = 0;
158 int request_change = topology != NULL;
159 int one_to_one = 1;
160
161 while (ast_waitfor(chan, -1) > -1) {
162 struct ast_frame *f;
163
164 if (request_change) {
165 /* Request a change to the new topology */
167 ast_log(LOG_WARNING, "Request stream topology change not supported "
168 "by channel '%s'\n", ast_channel_name(chan));
169 }
170 request_change = 0;
171 }
172
173 f = ast_read_stream(chan);
174 if (!f) {
175 return -1;
176 }
177
178 if ((f->frametype == AST_FRAME_DTMF) && (f->subclass.integer == '#')) {
179 ast_frfree(f);
180 break;
181 }
182
183 f->delivery.tv_sec = 0;
184 f->delivery.tv_usec = 0;
185
186 if (f->frametype == AST_FRAME_CONTROL) {
187 if (f->subclass.integer == AST_CONTROL_VIDUPDATE && !update_sent) {
188 if (stream_echo_write(chan, f, type, one_to_one)) {
189 ast_frfree(f);
190 return -1;
191 }
192 update_sent = 1;
193 } else if (f->subclass.integer == AST_CONTROL_SRCCHANGE) {
194 update_sent = 0;
196 update_sent = 0;
197 one_to_one = 0; /* Switch writing to one to many */
198 }
199 } else if (f->frametype == AST_FRAME_VIDEO && !update_sent){
200 struct ast_frame frame = {
202 .subclass.integer = AST_CONTROL_VIDUPDATE,
203 };
204 stream_echo_write(chan, &frame, type, one_to_one);
205 update_sent = 1;
206 }
207
208 if (f->frametype != AST_FRAME_CONTROL &&
211 stream_echo_write(chan, f, type, one_to_one)) {
212 ast_frfree(f);
213 return -1;
214 }
215
216 ast_frfree(f);
217 }
218
219 return 0;
220}
221
223 struct ast_stream_topology *original, unsigned int num, enum ast_media_type type)
224{
225 int i, n = num;
227
228 if (!res) {
229 return NULL;
230 }
231
232 /*
233 * Clone every stream of a type not matching the given one. If the type
234 * matches clone only the first stream found for the given type. Then for
235 * that stream clone it again up to num - 1 times. Ignore any other streams
236 * of the same matched type in the original topology.
237 *
238 * So for instance if the original stream contains 1 audio stream and 2 video
239 * streams (video stream 'A' and video stream 'B'), num is '3', and the given
240 * type is 'video' then the resulting topology will contain a clone of the
241 * audio stream along with 3 clones of video stream 'A'. Video stream 'B' is
242 * not copied over.
243 */
244 for (i = 0; i < ast_stream_topology_get_count(original); ++i) {
245 struct ast_stream *stream = ast_stream_topology_get_stream(original, i);
246
247 if (!n && ast_stream_get_type(stream) == type) {
248 /* Don't copy any[more] of the given type */
249 continue;
250 }
251
253 /* Don't copy removed/declined streams */
254 continue;
255 }
256
257 do {
258 stream = ast_stream_clone(stream, NULL);
259
260 if (!stream || ast_stream_topology_append_stream(res, stream) < 0) {
261 ast_stream_free(stream);
263 return NULL;
264 }
265
266 if (ast_stream_get_type(stream) != type) {
267 /* Do not multiply non matching streams */
268 break;
269 }
270
271 /*
272 * Since num is not zero yet (i.e. this is first stream found to
273 * match on the type) and the types match then loop num - 1 times
274 * cloning the same stream.
275 */
276 ast_stream_set_state(stream, n == num ?
278 } while (--n);
279 }
280
281 return res;
282}
283
284static int stream_echo_exec(struct ast_channel *chan, const char *data)
285{
286 int res;
287 unsigned int num = 0;
288 enum ast_media_type type;
289 char *parse;
290 struct ast_stream_topology *topology;
291
293 AST_APP_ARG(num);
295 );
296
297 parse = ast_strdupa(data);
299
300 if (ast_strlen_zero(args.num)) {
301 /*
302 * If a number is not given then no topology is to be created
303 * and renegotiated. The app will just echo back each stream
304 * received to itself.
305 */
307 }
308
309 if (ast_str_to_uint(args.num, &num)) {
310 ast_log(LOG_ERROR, "Failed to parse the first parameter '%s' into a"
311 " greater than or equal to zero\n", args.num);
312 return -1;
313 }
314
317
320 if (!topology) {
321 ast_log(LOG_ERROR, "Unable to create '%u' streams of type '%s' to"
322 " the topology\n", num, ast_codec_media_type2str(type));
323 return -1;
324 }
325
326 res = stream_echo_perform(chan, topology, type);
327
328 if (ast_channel_get_stream_topology(chan) != topology) {
329 ast_stream_topology_free(topology);
330 }
331
332 return res;
333}
334
335static int unload_module(void)
336{
338}
339
340static int load_module(void)
341{
343}
344
345AST_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:10966
int ast_waitfor(struct ast_channel *chan, int ms)
Wait for input on a channel.
Definition: channel.c:3162
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:4262
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:5149
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:567
#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:626
#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:650
@ 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:748
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:788
int ast_stream_topology_get_count(const struct ast_stream_topology *topology)
Get the number of streams in a topology.
Definition: stream.c:765
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:743
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