Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
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 <[email protected]>
24  */
25 
26 /*** MODULEINFO
27  <support_level>core</support_level>
28  ***/
29 
30 #include "asterisk.h"
31 
32 #include "asterisk/app.h"
33 #include "asterisk/conversions.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 
76 static const char app[] = "StreamEcho";
77 
78 static 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 
84  ast_frame_type2str(frame->frametype, frame_type, sizeof(frame_type));
85 
86  stream = pos < 0 ?
89 
90  media_type = ast_codec_media_type2str(ast_stream_get_type(stream));
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,
94  ast_stream_get_position(stream));
95 
96  return -1;
97 }
98 
99 static 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 ||
123  ast_format_get_type(frame->subclass.format) != type) {
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 
154 static 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 */
166  if (ast_channel_request_stream_topology_change(chan, topology, NULL)) {
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 &&
209  f->frametype != AST_FRAME_MODEM &&
210  f->frametype != AST_FRAME_NULL &&
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 
284 static 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);
294  AST_APP_ARG(type);
295  );
296 
297  parse = ast_strdupa(data);
298  AST_STANDARD_APP_ARGS(args, parse);
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 
315  type = ast_strlen_zero(args.type) ? AST_MEDIA_TYPE_VIDEO :
317 
318  topology = stream_echo_topology_alloc(
319  ast_channel_get_stream_topology(chan), num, type);
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 
335 static int unload_module(void)
336 {
338 }
339 
340 static int load_module(void)
341 {
343 }
344 
345 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Stream Echo Application");
static const char type[]
Definition: chan_ooh323.c:109
Main Channel structure associated with a channel.
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:567
enum ast_media_type ast_format_get_type(const struct ast_format *format)
Get the media type of a format.
Definition: format.c:354
Asterisk main include file. File version handling, generic pbx functions.
int ast_channel_is_multistream(struct ast_channel *chan)
Determine if a channel is multi-stream capable.
struct ast_stream_topology * ast_channel_get_stream_topology(const struct ast_channel *chan)
Retrieve the topology of streams on a channel.
media_type
Media types generate different "dummy answers" for not accepting the offer of a media stream...
Definition: sip.h:489
static int stream_echo_write(struct ast_channel *chan, struct ast_frame *frame, enum ast_media_type type, int one_to_one)
enum ast_media_type ast_stream_get_type(const struct ast_stream *stream)
Get the media type of a stream.
Definition: stream.c:316
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
#define LOG_WARNING
Definition: logger.h:274
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:347
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:363
Set when the stream has been removed/declined.
Definition: stream.h:78
static const char app[]
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:4307
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
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:11167
const char * args
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
#define NULL
Definition: resample.c:96
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
#define AST_FRAME_DTMF
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
static struct ast_stream_topology * stream_echo_topology_alloc(struct ast_stream_topology *original, unsigned int num, enum ast_media_type type)
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:5194
struct ast_frame_subclass subclass
Media Stream API.
#define ast_strlen_zero(foo)
Definition: strings.h:52
#define ast_log
Definition: astobj2.c:42
General Asterisk PBX channel definitions.
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.
static int unload_module(void)
Conversion utility functions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
static int stream_echo_write_error(struct ast_channel *chan, struct ast_frame *frame, int pos)
Set when the stream is sending and receiving media.
Definition: stream.h:82
#define LOG_ERROR
Definition: logger.h:285
struct ast_stream_topology * ast_stream_topology_alloc(void)
Create a stream topology.
Definition: stream.c:650
frame_type
Definition: codec_builtin.c:44
Set when the stream is sending media only.
Definition: stream.h:86
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1872
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_str_to_uint(const char *str, unsigned int *res)
Convert the given string to an unsigned integer.
Definition: conversions.c:56
int ast_stream_topology_get_count(const struct ast_stream_topology *topology)
Get the number of streams in a topology.
Definition: stream.c:765
static int stream_echo_perform(struct ast_channel *chan, struct ast_stream_topology *topology, enum ast_media_type type)
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
struct timeval delivery
void ast_stream_free(struct ast_stream *stream)
Destroy a media stream representation.
Definition: stream.c:292
int ast_waitfor(struct ast_channel *chan, int ms)
Wait for input on a channel.
Definition: channel.c:3171
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
static int stream_echo_exec(struct ast_channel *chan, const char *data)
const char * ast_channel_name(const struct ast_channel *chan)
#define ast_frfree(fr)
Data structure associated with a single frame of data.
ast_media_type
Types of media.
Definition: codec.h:30
enum ast_frame_type frametype
struct ast_format * format
void ast_stream_topology_free(struct ast_stream_topology *topology)
Unreference and destroy a stream topology.
Definition: stream.c:743
int ast_stream_get_position(const struct ast_stream *stream)
Get the position of the stream in the topology.
Definition: stream.c:500
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
static int load_module(void)
enum ast_stream_state ast_stream_get_state(const struct ast_stream *stream)
Get the current state of a stream.
Definition: stream.c:373
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626
#define AST_APP_ARG(name)
Define an application argument.