Asterisk - The Open Source Telephony Project GIT-master-7e7a603
bridge_native_rtp.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2013, Digium, Inc.
5 *
6 * Joshua Colp <jcolp@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 Native RTP bridging technology module
22 *
23 * \author Joshua Colp <jcolp@digium.com>
24 *
25 * \ingroup bridges
26 */
27
28/*** MODULEINFO
29 <support_level>core</support_level>
30 ***/
31
32#include "asterisk.h"
33
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <sys/types.h>
38#include <sys/stat.h>
39
40#include "asterisk/module.h"
41#include "asterisk/channel.h"
42#include "asterisk/bridge.h"
44#include "asterisk/frame.h"
45#include "asterisk/rtp_engine.h"
46#include "asterisk/stream.h"
47
48/*! \brief Internal structure which contains bridged RTP channel hook data */
50 /*! \brief Framehook used to intercept certain control frames */
51 int id;
52 /*! \brief Set when this framehook has been detached */
53 unsigned int detached;
54};
55
57 /*! \brief RTP instance */
59 /*! \brief glue result */
61};
62
64 /*!
65 * \brief glue callbacks
66 *
67 * \note The glue data is considered valid if cb is not NULL.
68 */
72 /*! Combined glue result of both bridge channels. */
74};
75
76/*! \brief Internal structure which contains instance information about bridged RTP channels */
78 /*! \brief Channel's hook data */
80 /*!
81 * \brief Glue callbacks to bring remote channel streams back to Asterisk.
82 * \note NULL if channel streams are local.
83 */
85 /*! \brief Channel's cached RTP glue information */
87};
88
89/*! \brief Forward declarations */
90static int native_rtp_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
91static void native_rtp_bridge_unsuspend(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
92static void native_rtp_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
93static void native_rtp_bridge_suspend(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
94static int native_rtp_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame);
95static int native_rtp_bridge_compatible(struct ast_bridge *bridge);
96static void native_rtp_stream_topology_changed(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
97
99 .name = "native_rtp",
100 .capabilities = AST_BRIDGE_CAPABILITY_NATIVE,
103 .unsuspend = native_rtp_bridge_unsuspend,
105 .suspend = native_rtp_bridge_suspend,
107 .compatible = native_rtp_bridge_compatible,
108 .stream_topology_changed = native_rtp_stream_topology_changed,
109};
110
111static void rtp_glue_data_init(struct rtp_glue_data *glue)
112{
113 glue->cb = NULL;
114 glue->audio.instance = NULL;
116 glue->video.instance = NULL;
119}
120
121static void rtp_glue_data_destroy(struct rtp_glue_data *glue)
122{
123 if (!glue) {
124 return;
125 }
128}
129
130static void rtp_glue_data_reset(struct rtp_glue_data *glue)
131{
133 rtp_glue_data_init(glue);
134}
135
137{
138 ast_debug(2, "Destroying channel tech_pvt data %p\n", data);
139
140 /*
141 * hook_data will probably already have been unreferenced by the framehook detach
142 * and the pointer set to null.
143 */
144 ao2_cleanup(data->hook_data);
145
147 ast_free(data);
148}
149
151{
153
154 data = ast_calloc(1, sizeof(*data));
155 if (data) {
156 rtp_glue_data_init(&data->glue);
157 }
158 return data;
159}
160
161/*!
162 * \internal
163 * \brief Helper function which gets all RTP information (glue and instances) relating to the given channels
164 *
165 * \retval 0 on success.
166 * \retval -1 on error.
167 */
168static int rtp_glue_data_get(struct ast_channel *c0, struct rtp_glue_data *glue0,
169 struct ast_channel *c1, struct rtp_glue_data *glue1)
170{
171 struct ast_rtp_glue *cb0;
172 struct ast_rtp_glue *cb1;
173 enum ast_rtp_glue_result combined_result;
174
177 if (!cb0 || !cb1) {
178 /* One or both channels doesn't have any RTP glue registered. */
179 return -1;
180 }
181
182 /* The glue callbacks bump the RTP instance refcounts for us. */
183
184 glue0->cb = cb0;
185 glue0->audio.result = cb0->get_rtp_info(c0, &glue0->audio.instance);
186 glue0->video.result = cb0->get_vrtp_info
188
189 glue1->cb = cb1;
190 glue1->audio.result = cb1->get_rtp_info(c1, &glue1->audio.instance);
191 glue1->video.result = cb1->get_vrtp_info
193
194 /*
195 * Now determine the combined glue result.
196 */
197
198 /* Apply any limitations on direct media bridging that may be present */
199 if (glue0->audio.result == glue1->audio.result && glue1->audio.result == AST_RTP_GLUE_RESULT_REMOTE) {
200 if (glue0->cb->allow_rtp_remote && !glue0->cb->allow_rtp_remote(c0, glue1->audio.instance)) {
201 /* If the allow_rtp_remote indicates that remote isn't allowed, revert to local bridge */
203 } else if (glue1->cb->allow_rtp_remote && !glue1->cb->allow_rtp_remote(c1, glue0->audio.instance)) {
205 }
206 }
207 if (glue0->video.result == glue1->video.result && glue1->video.result == AST_RTP_GLUE_RESULT_REMOTE) {
208 if (glue0->cb->allow_vrtp_remote && !glue0->cb->allow_vrtp_remote(c0, glue1->video.instance)) {
209 /* If the allow_vrtp_remote indicates that remote isn't allowed, revert to local bridge */
211 } else if (glue1->cb->allow_vrtp_remote && !glue1->cb->allow_vrtp_remote(c1, glue0->video.instance)) {
213 }
214 }
215
216 /* If we are carrying video, and both sides are not going to remotely bridge... fail the native bridge */
221 }
226 }
227
228 /* The order of preference is: forbid, local, and remote. */
231 /* If any sort of bridge is forbidden just completely bail out and go back to generic bridging */
232 combined_result = AST_RTP_GLUE_RESULT_FORBID;
233 } else if (glue0->audio.result == AST_RTP_GLUE_RESULT_LOCAL
235 combined_result = AST_RTP_GLUE_RESULT_LOCAL;
236 } else {
237 combined_result = AST_RTP_GLUE_RESULT_REMOTE;
238 }
239 glue0->result = combined_result;
240 glue1->result = combined_result;
241
242 return 0;
243}
244
245/*!
246 * \internal
247 * \brief Get the current RTP native bridge combined glue result.
248 * \since 15.0.0
249 *
250 * \param c0 First bridge channel
251 * \param c1 Second bridge channel
252 *
253 * \note Both channels must be locked when calling this function.
254 *
255 * \return Current combined glue result.
256 */
258 struct ast_channel *c1)
259{
260 struct rtp_glue_data glue_a;
261 struct rtp_glue_data glue_b;
262 struct rtp_glue_data *glue0;
263 struct rtp_glue_data *glue1;
264 enum ast_rtp_glue_result combined_result;
265
266 rtp_glue_data_init(&glue_a);
267 glue0 = &glue_a;
268 rtp_glue_data_init(&glue_b);
269 glue1 = &glue_b;
270 if (rtp_glue_data_get(c0, glue0, c1, glue1)) {
272 }
273
274 combined_result = glue0->result;
277 return combined_result;
278}
279
280/*!
281 * \internal
282 * \brief Start native RTP bridging of two channels
283 *
284 * \param bridge The bridge that had native RTP bridging happening on it
285 * \param target If remote RTP bridging, the channel that is unheld.
286 *
287 * \note Bridge must be locked when calling this function.
288 */
289static void native_rtp_bridge_start(struct ast_bridge *bridge, struct ast_channel *target)
290{
293 struct native_rtp_bridge_channel_data *data0;
294 struct native_rtp_bridge_channel_data *data1;
295 struct rtp_glue_data *glue0;
296 struct rtp_glue_data *glue1;
297 struct ast_format_cap *cap0;
298 struct ast_format_cap *cap1;
299 enum ast_rtp_glue_result native_type;
300
301 if (bc0 == bc1) {
302 return;
303 }
304 data0 = bc0->tech_pvt;
305 data1 = bc1->tech_pvt;
306 if (!data0 || !data1) {
307 /* Not all channels are joined with the bridge tech yet */
308 return;
309 }
310 glue0 = &data0->glue;
311 glue1 = &data1->glue;
312
313 ast_channel_lock_both(bc0->chan, bc1->chan);
314
315 if (!glue0->cb || !glue1->cb) {
316 /*
317 * Somebody doesn't have glue data so the bridge isn't running
318 *
319 * Actually neither side should have glue data.
320 */
321 ast_assert(!glue0->cb && !glue1->cb);
322
323 if (rtp_glue_data_get(bc0->chan, glue0, bc1->chan, glue1)) {
324 /*
325 * This might happen if one of the channels got masqueraded
326 * at a critical time. It's a bit of a stretch even then
327 * since the channel is in a bridge.
328 */
329 goto done;
330 }
331 }
332
333 ast_debug(2, "Bridge '%s'. Tech starting '%s' and '%s' with target '%s'\n",
334 bridge->uniqueid, ast_channel_name(bc0->chan), ast_channel_name(bc1->chan),
335 target ? ast_channel_name(target) : "none");
336
337 native_type = glue0->result;
338
339 switch (native_type) {
343 }
346 }
349 ast_verb(4, "Locally RTP bridged '%s' and '%s' in stack\n",
351 break;
355 if (!cap0 || !cap1) {
356 ao2_cleanup(cap0);
357 ao2_cleanup(cap1);
358 break;
359 }
360
361 if (glue0->cb->get_codec) {
362 glue0->cb->get_codec(bc0->chan, cap0);
363 }
364 if (glue1->cb->get_codec) {
365 glue1->cb->get_codec(bc1->chan, cap1);
366 }
367
368 /*
369 * If we have a target, it's the channel that received the UNHOLD or
370 * UPDATE_RTP_PEER frame and was told to resume
371 */
372 if (!target) {
373 /* Send both channels to remote */
374 data0->remote_cb = glue0->cb;
375 data1->remote_cb = glue1->cb;
376 glue0->cb->update_peer(bc0->chan, glue1->audio.instance, glue1->video.instance, NULL, cap1, 0);
377 glue1->cb->update_peer(bc1->chan, glue0->audio.instance, glue0->video.instance, NULL, cap0, 0);
378 ast_verb(4, "Remotely bridged '%s' and '%s' - media will flow directly between them\n",
380 } else {
381 /*
382 * If a target was provided, it is the recipient of an unhold or an update and needs to have
383 * its media redirected to fit the current remote bridging needs. The other channel is either
384 * already set up to handle the new media path or will have its own set of updates independent
385 * of this pass.
386 */
387 ast_debug(2, "Bridge '%s'. Sending '%s' back to remote\n",
388 bridge->uniqueid, ast_channel_name(target));
389 if (bc0->chan == target) {
390 data0->remote_cb = glue0->cb;
391 glue0->cb->update_peer(bc0->chan, glue1->audio.instance, glue1->video.instance, NULL, cap1, 0);
392 } else {
393 data1->remote_cb = glue1->cb;
394 glue1->cb->update_peer(bc1->chan, glue0->audio.instance, glue0->video.instance, NULL, cap0, 0);
395 }
396 }
397
398 ao2_cleanup(cap0);
399 ao2_cleanup(cap1);
400 break;
402 break;
403 }
404
405 if (native_type != AST_RTP_GLUE_RESULT_REMOTE) {
406 /* Bring any remaining channels back to us. */
407 if (data0->remote_cb) {
408 ast_debug(2, "Bridge '%s'. Bringing back '%s' to us\n",
409 bridge->uniqueid, ast_channel_name(bc0->chan));
410 data0->remote_cb->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0);
411 data0->remote_cb = NULL;
412 }
413 if (data1->remote_cb) {
414 ast_debug(2, "Bridge '%s'. Bringing back '%s' to us\n",
415 bridge->uniqueid, ast_channel_name(bc1->chan));
416 data1->remote_cb->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0);
417 data1->remote_cb = NULL;
418 }
419 }
420
421done:
424}
425
426/*!
427 * \internal
428 * \brief Stop native RTP bridging of two channels
429 *
430 * \param bridge The bridge that had native RTP bridging happening on it
431 * \param target If remote RTP bridging, the channel that is held.
432 *
433 * \note The first channel to leave the bridge triggers the cleanup for both channels
434 */
435static void native_rtp_bridge_stop(struct ast_bridge *bridge, struct ast_channel *target)
436{
439 struct native_rtp_bridge_channel_data *data0;
440 struct native_rtp_bridge_channel_data *data1;
441 struct rtp_glue_data *glue0;
442 struct rtp_glue_data *glue1;
443
444 if (bc0 == bc1) {
445 return;
446 }
447 data0 = bc0->tech_pvt;
448 data1 = bc1->tech_pvt;
449 if (!data0 || !data1) {
450 /* Not all channels are joined with the bridge tech */
451 return;
452 }
453 glue0 = &data0->glue;
454 glue1 = &data1->glue;
455
456 ast_debug(2, "Bridge '%s'. Tech stopping '%s' and '%s' with target '%s'\n",
457 bridge->uniqueid, ast_channel_name(bc0->chan), ast_channel_name(bc1->chan),
458 target ? ast_channel_name(target) : "none");
459
460 if (!glue0->cb || !glue1->cb) {
461 /*
462 * Somebody doesn't have glue data so the bridge isn't running
463 *
464 * Actually neither side should have glue data.
465 */
466 ast_assert(!glue0->cb && !glue1->cb);
467 /* At most one channel can be left at the remote endpoint here. */
468 ast_assert(!data0->remote_cb || !data1->remote_cb);
469
470 /* Bring selected channel streams back to us */
471 if (data0->remote_cb && (!target || target == bc0->chan)) {
473 ast_debug(2, "Bridge '%s'. Bringing back '%s' to us\n",
474 bridge->uniqueid, ast_channel_name(bc0->chan));
475 data0->remote_cb->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0);
476 data0->remote_cb = NULL;
478 }
479 if (data1->remote_cb && (!target || target == bc1->chan)) {
481 ast_debug(2, "Bridge '%s'. Bringing back '%s' to us\n",
482 bridge->uniqueid, ast_channel_name(bc1->chan));
483 data1->remote_cb->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0);
484 data1->remote_cb = NULL;
486 }
487 return;
488 }
489
490 ast_channel_lock_both(bc0->chan, bc1->chan);
491
492 switch (glue0->result) {
496 }
499 }
502 break;
504 if (target) {
505 /*
506 * If a target was provided, it is being put on hold and should expect to
507 * receive media from Asterisk instead of what it was previously connected to.
508 */
509 ast_debug(2, "Bridge '%s'. Bringing back '%s' to us\n",
510 bridge->uniqueid, ast_channel_name(target));
511 if (bc0->chan == target) {
512 data0->remote_cb = NULL;
513 glue0->cb->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0);
514 } else {
515 data1->remote_cb = NULL;
516 glue1->cb->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0);
517 }
518 } else {
519 data0->remote_cb = NULL;
520 data1->remote_cb = NULL;
521 /*
522 * XXX We don't want to bring back the channels if we are
523 * switching to T.38. We have received a reinvite on one channel
524 * and we will be sending a reinvite on the other to start T.38.
525 * If we bring the streams back now we confuse the chan_pjsip
526 * channel driver processing the incoming T.38 reinvite with
527 * reinvite glare. I think this is really a bug in chan_pjsip
528 * that this exception case is working around.
529 */
532 ast_debug(2, "Bridge '%s'. Bringing back '%s' and '%s' to us\n",
533 bridge->uniqueid, ast_channel_name(bc0->chan),
534 ast_channel_name(bc1->chan));
535 glue0->cb->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0);
536 glue1->cb->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0);
537 } else {
538 ast_debug(2, "Bridge '%s'. Skip bringing back '%s' and '%s' to us\n",
539 bridge->uniqueid, ast_channel_name(bc0->chan),
540 ast_channel_name(bc1->chan));
541 }
542 }
543 break;
545 break;
546 }
547
548 rtp_glue_data_reset(glue0);
549 rtp_glue_data_reset(glue1);
550
551 ast_debug(2, "Discontinued RTP bridging of '%s' and '%s' - media will flow through Asterisk core\n",
553
556}
557
558/*!
559 * \internal
560 * \brief Frame hook that is called to intercept hold/unhold
561 */
562static struct ast_frame *native_rtp_framehook(struct ast_channel *chan,
563 struct ast_frame *f, enum ast_framehook_event event, void *data)
564{
565 struct ast_bridge *bridge;
566 struct native_rtp_framehook_data *native_data = data;
567
568 if (!f
571 return f;
572 }
573
574 bridge = ast_channel_get_bridge(chan);
575 if (bridge) {
576 /* native_rtp_bridge_start/stop are not being called from bridging
577 core so we need to lock the bridge prior to calling these functions
578 Unfortunately that means unlocking the channel, but as it
579 should not be modified this should be okay... hopefully...
580 unless this channel is being moved around right now and is in
581 the process of having this framehook removed (which is fine). To
582 ensure we then don't stop or start when we shouldn't we consult
583 the data provided. If this framehook has been detached then the
584 detached variable will be set. This is safe to check as it is only
585 manipulated with the bridge lock held. */
586 ast_channel_unlock(chan);
587 ast_bridge_lock(bridge);
588 if (!native_data->detached) {
589 switch (f->subclass.integer) {
590 case AST_CONTROL_HOLD:
591 native_rtp_bridge_stop(bridge, chan);
592 break;
595 native_rtp_bridge_start(bridge, chan);
596 break;
597 default:
598 break;
599 }
600 }
601 ast_bridge_unlock(bridge);
602 ao2_ref(bridge, -1);
603 ast_channel_lock(chan);
604 }
605
606 return f;
607}
608
609/*!
610 * \internal
611 * \brief Callback function which informs upstream if we are consuming a frame of a specific type
612 */
614{
615 return (type == AST_FRAME_CONTROL ? 1 : 0);
616}
617
618/*!
619 * \internal
620 * \brief Internal helper function which checks whether a channel is compatible with our native bridging
621 */
623{
626}
627
628/*!
629 * \internal
630 * \brief Internal helper function which checks whether both channels are compatible with our native bridging
631 */
632static int native_rtp_bridge_compatible_check(struct ast_bridge *bridge, struct ast_bridge_channel *bc0, struct ast_bridge_channel *bc1)
633{
634 enum ast_rtp_glue_result native_type;
635 int read_ptime0;
636 int read_ptime1;
637 int write_ptime0;
638 int write_ptime1;
639 struct rtp_glue_data glue_a;
640 struct rtp_glue_data glue_b;
641 RAII_VAR(struct ast_format_cap *, cap0, NULL, ao2_cleanup);
642 RAII_VAR(struct ast_format_cap *, cap1, NULL, ao2_cleanup);
645
646 ast_debug(1, "Bridge '%s'. Checking compatability for channels '%s' and '%s'\n",
647 bridge->uniqueid, ast_channel_name(bc0->chan), ast_channel_name(bc1->chan));
648
649 if (!native_rtp_bridge_capable(bc0->chan)) {
650 ast_debug(1, "Bridge '%s' can not use native RTP bridge as channel '%s' has features which prevent it\n",
651 bridge->uniqueid, ast_channel_name(bc0->chan));
652 return 0;
653 }
654
655 if (!native_rtp_bridge_capable(bc1->chan)) {
656 ast_debug(1, "Bridge '%s' can not use native RTP bridge as channel '%s' has features which prevent it\n",
657 bridge->uniqueid, ast_channel_name(bc1->chan));
658 return 0;
659 }
660
661 rtp_glue_data_init(&glue_a);
662 glue0 = &glue_a;
663 rtp_glue_data_init(&glue_b);
664 glue1 = &glue_b;
665 if (rtp_glue_data_get(bc0->chan, glue0, bc1->chan, glue1)) {
666 ast_debug(1, "Bridge '%s' can not use native RTP bridge as could not get details\n",
667 bridge->uniqueid);
668 return 0;
669 }
670 native_type = glue0->result;
671
672 if (native_type == AST_RTP_GLUE_RESULT_FORBID) {
673 ast_debug(1, "Bridge '%s' can not use native RTP bridge as it was forbidden while getting details\n",
674 bridge->uniqueid);
675 return 0;
676 }
677
679 && ast_rtp_instance_dtmf_mode_get(glue0->audio.instance)) {
680 ast_debug(1, "Bridge '%s' can not use native RTP bridge as channel '%s' has DTMF hooks\n",
681 bridge->uniqueid, ast_channel_name(bc0->chan));
682 return 0;
683 }
684
686 && ast_rtp_instance_dtmf_mode_get(glue1->audio.instance)) {
687 ast_debug(1, "Bridge '%s' can not use native RTP bridge as channel '%s' has DTMF hooks\n",
688 bridge->uniqueid, ast_channel_name(bc1->chan));
689 return 0;
690 }
691
692 if (native_type == AST_RTP_GLUE_RESULT_LOCAL
693 && (ast_rtp_instance_get_engine(glue0->audio.instance)->local_bridge
694 != ast_rtp_instance_get_engine(glue1->audio.instance)->local_bridge
695 || (ast_rtp_instance_get_engine(glue0->audio.instance)->dtmf_compatible
696 && !ast_rtp_instance_get_engine(glue0->audio.instance)->dtmf_compatible(bc0->chan,
697 glue0->audio.instance, bc1->chan, glue1->audio.instance)))) {
698 ast_debug(1, "Bridge '%s' can not use local native RTP bridge as local bridge or DTMF is not compatible\n",
699 bridge->uniqueid);
700 return 0;
701 }
702
705 if (!cap0 || !cap1) {
706 return 0;
707 }
708
709 /* Make sure that codecs match */
710 if (glue0->cb->get_codec) {
711 glue0->cb->get_codec(bc0->chan, cap0);
712 }
713 if (glue1->cb->get_codec) {
714 glue1->cb->get_codec(bc1->chan, cap1);
715 }
716 if (ast_format_cap_count(cap0) != 0
717 && ast_format_cap_count(cap1) != 0
718 && !ast_format_cap_iscompatible(cap0, cap1)) {
719 struct ast_str *codec_buf0 = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
720 struct ast_str *codec_buf1 = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
721
722 ast_debug(1, "Bridge '%s': Channel codec0 = %s is not codec1 = %s, cannot native bridge in RTP.\n",
723 bridge->uniqueid,
724 ast_format_cap_get_names(cap0, &codec_buf0),
725 ast_format_cap_get_names(cap1, &codec_buf1));
726 return 0;
727 }
728
729 if (glue0->audio.instance && glue1->audio.instance) {
730 unsigned int framing_inst0, framing_inst1;
731 framing_inst0 = ast_rtp_codecs_get_framing(ast_rtp_instance_get_codecs(glue0->audio.instance));
732 framing_inst1 = ast_rtp_codecs_get_framing(ast_rtp_instance_get_codecs(glue1->audio.instance));
733 if (framing_inst0 != framing_inst1) {
734 /* ptimes are asymmetric on the two call legs so we can't use the native bridge */
735 ast_debug(1, "Asymmetric ptimes on the two call legs (%u != %u). Cannot native bridge in RTP\n",
736 framing_inst0, framing_inst1);
737 return 0;
738 }
739 ast_debug(3, "Symmetric ptimes on the two call legs (%u). May be able to native bridge in RTP\n",
740 framing_inst0);
741 }
742
747
748 if (read_ptime0 != write_ptime1 || read_ptime1 != write_ptime0) {
749 ast_debug(1, "Bridge '%s': Packetization differs between RTP streams (%d != %d or %d != %d). Cannot native bridge in RTP\n",
750 bridge->uniqueid,
751 read_ptime0, write_ptime1, read_ptime1, write_ptime0);
752 return 0;
753 }
754 ast_debug(3, "Bridge '%s': Packetization comparison success between RTP streams (read_ptime0:%d == write_ptime1:%d and read_ptime1:%d == write_ptime0:%d).\n",
755 bridge->uniqueid,
756 read_ptime0, write_ptime1, read_ptime1, write_ptime0);
757
758 return 1;
759}
760
761/*!
762 * \internal
763 * \brief Called by the bridge core "compatible' callback
764 */
765static int native_rtp_bridge_compatible(struct ast_bridge *bridge)
766{
767 struct ast_bridge_channel *bc0;
768 struct ast_bridge_channel *bc1;
769 int is_compatible;
770
771 /* We require two channels before even considering native bridging */
772 if (bridge->num_channels != 2) {
773 ast_debug(1, "Bridge '%s' can not use native RTP bridge as two channels are required\n",
775 return 0;
776 }
777
780
781 ast_channel_lock_both(bc0->chan, bc1->chan);
782 is_compatible = native_rtp_bridge_compatible_check(bridge, bc0, bc1);
785
786 return is_compatible;
787}
788
789/*!
790 * \internal
791 * \brief Helper function which adds frame hook to bridge channel
792 */
794{
795 struct native_rtp_bridge_channel_data *data = bridge_channel->tech_pvt;
796 struct ast_framehook_interface hook = {
798 .event_cb = native_rtp_framehook,
799 .destroy_cb = __ao2_cleanup,
800 .consume_cb = native_rtp_framehook_consume,
801 .disable_inheritance = 1,
802 };
803
804 ast_assert(data->hook_data == NULL);
805 data->hook_data = ao2_alloc_options(sizeof(*data->hook_data), NULL,
807 if (!data->hook_data) {
808 return -1;
809 }
810
811 ast_debug(2, "Bridge '%s'. Attaching hook data %p to '%s'\n",
812 bridge_channel->bridge->uniqueid, data, ast_channel_name(bridge_channel->chan));
813
814 /* We're giving 1 ref to the framehook and keeping the one from the alloc for ourselves */
815 hook.data = ao2_bump(data->hook_data);
816
817 ast_channel_lock(bridge_channel->chan);
818 data->hook_data->id = ast_framehook_attach(bridge_channel->chan, &hook);
819 ast_channel_unlock(bridge_channel->chan);
820 if (data->hook_data->id < 0) {
821 /*
822 * We need to drop both the reference we hold in data,
823 * and the one the framehook would hold.
824 */
825 ao2_ref(data->hook_data, -2);
826 data->hook_data = NULL;
827
828 return -1;
829 }
830
831 return 0;
832}
833
834/*!
835 * \internal
836 * \brief Helper function which removes frame hook from bridge channel
837 */
839{
840 struct native_rtp_bridge_channel_data *data = bridge_channel->tech_pvt;
841
842 if (!data || !data->hook_data) {
843 return;
844 }
845
846 ast_debug(2, "Bridge '%s'. Detaching hook data %p from '%s'\n",
847 bridge_channel->bridge->uniqueid, data->hook_data, ast_channel_name(bridge_channel->chan));
848
849 ast_channel_lock(bridge_channel->chan);
850 ast_framehook_detach(bridge_channel->chan, data->hook_data->id);
851 data->hook_data->detached = 1;
852 ast_channel_unlock(bridge_channel->chan);
853 ao2_cleanup(data->hook_data);
854 data->hook_data = NULL;
855}
856
858 struct ast_stream_topology *existing_topology,
859 struct ast_stream_topology *requested_topology)
860{
861 struct ast_stream *stream;
862 const struct ast_format_cap *audio_formats = NULL;
863 struct ast_stream_topology *new_topology;
864 int i;
865
866 new_topology = ast_stream_topology_clone(requested_topology);
867 if (!new_topology) {
868 return NULL;
869 }
870
871 /* We find an existing stream with negotiated audio formats that we can place into
872 * any audio streams in the new topology to ensure that negotiation succeeds. Some
873 * endpoints incorrectly terminate the call if SDP negotiation fails.
874 */
875 for (i = 0; i < ast_stream_topology_get_count(existing_topology); ++i) {
876 stream = ast_stream_topology_get_stream(existing_topology, i);
877
880 continue;
881 }
882
883 audio_formats = ast_stream_get_formats(stream);
884 break;
885 }
886
887 if (audio_formats) {
888 for (i = 0; i < ast_stream_topology_get_count(new_topology); ++i) {
889 stream = ast_stream_topology_get_stream(new_topology, i);
890
893 continue;
894 }
895
896 /* We haven't actually modified audio_formats so this is safe */
897 ast_stream_set_formats(stream, (struct ast_format_cap *)audio_formats);
898 }
899 }
900
901 for (i = 0; i < ast_stream_topology_get_count(new_topology); ++i) {
902 stream = ast_stream_topology_get_stream(new_topology, i);
903
904 /* For both recvonly and sendonly the stream state reflects our state, that is we
905 * are receiving only and we are sending only. Since we are renegotiating a remote
906 * party we need to swap this to reflect what we will be doing. That is, if we are
907 * receiving from Alice then we want to be sending to Bob, so swap recvonly to
908 * sendonly.
909 */
912 } else if (ast_stream_get_state(stream) == AST_STREAM_STATE_SENDONLY) {
914 }
915 }
916
917 return new_topology;
918}
919
921 struct ast_bridge_channel *bridge_channel)
922{
923 struct ast_channel *c0 = bridge_channel->chan;
924 struct ast_channel *c1 = AST_LIST_FIRST(&bridge->channels)->chan;
925 struct ast_stream_topology *req_top;
926 struct ast_stream_topology *existing_top;
927 struct ast_stream_topology *new_top;
928
929 ast_bridge_channel_stream_map(bridge_channel);
930
932 == &native_rtp_bridge) {
933 return;
934 }
935
936 if (c0 == c1) {
937 c1 = AST_LIST_LAST(&bridge->channels)->chan;
938 }
939
940 if (c0 == c1) {
941 return;
942 }
943
944 /* If a party renegotiates we want to renegotiate their counterpart to a matching
945 * topology.
946 */
947 ast_channel_lock_both(c0, c1);
949 existing_top = ast_channel_get_stream_topology(c1);
950 new_top = native_rtp_request_stream_topology_update(existing_top, req_top);
953
954 if (!new_top) {
955 /* Failure. We'll just have to live with the current topology. */
956 return;
957 }
958
961}
962
963/*!
964 * \internal
965 * \brief Called by the bridge core 'join' callback for each channel joining he bridge
966 */
967static int native_rtp_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
968{
969 struct ast_stream_topology *req_top;
970 struct ast_stream_topology *existing_top;
971 struct ast_stream_topology *new_top;
972 struct ast_channel *c0 = AST_LIST_FIRST(&bridge->channels)->chan;
973 struct ast_channel *c1 = AST_LIST_LAST(&bridge->channels)->chan;
974
975 ast_debug(2, "Bridge '%s'. Channel '%s' is joining bridge tech\n",
977
979
981 /* The channel will rejoin when it is unsuspended */
982 return 0;
983 }
984
986 if (!bridge_channel->tech_pvt) {
987 return -1;
988 }
989
993 return -1;
994 }
995
996 if (c0 != c1) {
997 /* When both channels are joined we want to try to improve the experience by
998 * raising the number of streams so they match.
999 */
1000 ast_channel_lock_both(c0, c1);
1001 req_top = ast_channel_get_stream_topology(c0);
1002 existing_top = ast_channel_get_stream_topology(c1);
1003 if (ast_stream_topology_get_count(req_top) < ast_stream_topology_get_count(existing_top)) {
1004 SWAP(req_top, existing_top);
1005 SWAP(c0, c1);
1006 }
1007 new_top = native_rtp_request_stream_topology_update(existing_top, req_top);
1010
1011 if (new_top) {
1013 ast_stream_topology_free(new_top);
1014 }
1015 }
1016
1018 return 0;
1019}
1020
1021/*!
1022 * \internal
1023 * \brief Add the channel back into the bridge
1024 */
1026{
1027 ast_debug(2, "Bridge '%s'. Channel '%s' is unsuspended back to bridge tech\n",
1030}
1031
1032/*!
1033 * \internal
1034 * \brief Leave the bridge
1035 */
1037{
1038 ast_debug(2, "Bridge '%s'. Channel '%s' is leaving bridge tech\n",
1040
1041 if (!bridge_channel->tech_pvt) {
1042 return;
1043 }
1044
1047
1050}
1051
1052/*!
1053 * \internal
1054 * \brief Suspend the channel from the bridge
1055 */
1057{
1058 ast_debug(2, "Bridge '%s'. Channel '%s' is suspending from bridge tech\n",
1061}
1062
1064{
1065 const struct ast_control_t38_parameters *t38_parameters;
1066 int defer = 0;
1067
1068 if (!ast_bridge_queue_everyone_else(bridge, bridge_channel, frame)) {
1069 /* This frame was successfully queued so no need to defer */
1070 return 0;
1071 }
1072
1073 /* Depending on the frame defer it so when the next channel joins it receives it */
1074 switch (frame->frametype) {
1075 case AST_FRAME_CONTROL:
1076 switch (frame->subclass.integer) {
1078 t38_parameters = frame->data.ptr;
1079 switch (t38_parameters->request_response) {
1081 defer = -1;
1082 break;
1083 default:
1084 break;
1085 }
1086 break;
1087 default:
1088 break;
1089 }
1090 break;
1091 default:
1092 break;
1093 }
1094
1095 return defer;
1096}
1097
1098static int unload_module(void)
1099{
1101 return 0;
1102}
1103
1104static int load_module(void)
1105{
1107 unload_module();
1109 }
1111}
1112
1113AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Native RTP bridging module");
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition: astobj2.h:367
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ao2_alloc_options(data_size, destructor_fn, options)
Definition: astobj2.h:404
void __ao2_cleanup(void *obj)
Definition: astobj2.c:677
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
Bridging API.
#define ast_bridge_unlock(bridge)
Unlock the bridge.
Definition: bridge.h:481
@ AST_BRIDGE_CAPABILITY_NATIVE
Definition: bridge.h:90
#define ast_bridge_lock(bridge)
Lock the bridge.
Definition: bridge.h:470
int ast_bridge_queue_everyone_else(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
Queue the given frame to everyone else.
void ast_bridge_channel_stream_map(struct ast_bridge_channel *bridge_channel)
Maps a channel's stream topology to and from the bridge.
static void native_rtp_stream_topology_changed(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
static int native_rtp_bridge_capable(struct ast_channel *chan)
static int native_rtp_bridge_compatible_check(struct ast_bridge *bridge, struct ast_bridge_channel *bc0, struct ast_bridge_channel *bc1)
static void native_rtp_bridge_framehook_detach(struct ast_bridge_channel *bridge_channel)
static int native_rtp_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
Forward declarations.
static struct ast_frame * native_rtp_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data)
static struct ast_stream_topology * native_rtp_request_stream_topology_update(struct ast_stream_topology *existing_topology, struct ast_stream_topology *requested_topology)
static int native_rtp_framehook_consume(void *data, enum ast_frame_type type)
static struct native_rtp_bridge_channel_data * native_rtp_bridge_channel_data_alloc(void)
static void native_rtp_bridge_unsuspend(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
static void native_rtp_bridge_suspend(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
static void native_rtp_bridge_stop(struct ast_bridge *bridge, struct ast_channel *target)
static enum ast_rtp_glue_result rtp_glue_get_current_combined_result(struct ast_channel *c0, struct ast_channel *c1)
static struct ast_bridge_technology native_rtp_bridge
static int native_rtp_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
static void native_rtp_bridge_channel_data_free(struct native_rtp_bridge_channel_data *data)
static int rtp_glue_data_get(struct ast_channel *c0, struct rtp_glue_data *glue0, struct ast_channel *c1, struct rtp_glue_data *glue1)
static void native_rtp_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
static void rtp_glue_data_reset(struct rtp_glue_data *glue)
static int load_module(void)
static int unload_module(void)
static int native_rtp_bridge_compatible(struct ast_bridge *bridge)
static int native_rtp_bridge_framehook_attach(struct ast_bridge_channel *bridge_channel)
static void rtp_glue_data_destroy(struct rtp_glue_data *glue)
static void rtp_glue_data_init(struct rtp_glue_data *glue)
static void native_rtp_bridge_start(struct ast_bridge *bridge, struct ast_channel *target)
Channel Bridging API.
@ AST_BRIDGE_PREFERENCE_BASE_NATIVE
#define ast_bridge_technology_register(technology)
See __ast_bridge_technology_register()
int ast_bridge_technology_unregister(struct ast_bridge_technology *technology)
Unregister a bridge technology from use.
Definition: bridge.c:263
static const char type[]
Definition: chan_ooh323.c:109
General Asterisk PBX channel definitions.
const char * ast_channel_name(const struct ast_channel *chan)
void * ast_channel_get_stream_topology_change_source(struct ast_channel *chan)
Retrieve the source that initiated the last stream topology change.
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
struct ast_format * ast_channel_rawreadformat(struct ast_channel *chan)
int ast_channel_has_hook_requiring_audio(struct ast_channel *chan)
Check if the channel has any active hooks that require audio.
Definition: channel.c:2524
#define ast_channel_lock(chan)
Definition: channel.h:2922
#define ast_channel_lock_both(chan1, chan2)
Lock two channels.
Definition: channel.h:2929
struct ast_format * ast_channel_rawwriteformat(struct ast_channel *chan)
struct ast_stream_topology * ast_channel_get_stream_topology(const struct ast_channel *chan)
Retrieve the topology of streams on a channel.
struct ast_bridge * ast_channel_get_bridge(const struct ast_channel *chan)
Get the bridge associated with a channel.
Definition: channel.c:10534
const struct ast_channel_tech * ast_channel_tech(const struct ast_channel *chan)
#define ast_channel_unlock(chan)
Definition: channel.h:2923
ast_channel_state
ast_channel states
Definition: channelstate.h:35
@ AST_STATE_UP
Definition: channelstate.h:42
@ AST_MEDIA_TYPE_AUDIO
Definition: codec.h:32
#define AST_FORMAT_CAP_NAMES_LEN
Definition: format_cap.h:324
unsigned int ast_format_cap_get_format_framing(const struct ast_format_cap *cap, const struct ast_format *format)
Get the framing for a format.
Definition: format_cap.c:443
@ AST_FORMAT_CAP_FLAG_DEFAULT
Definition: format_cap.h:38
const char * ast_format_cap_get_names(const struct ast_format_cap *cap, struct ast_str **buf)
Get the names of codecs of a set of formats.
Definition: format_cap.c:734
int ast_format_cap_iscompatible(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2)
Determine if any joint capabilities exist between two capabilities structures.
Definition: format_cap.c:653
#define ast_format_cap_alloc(flags)
Allocate a new ast_format_cap structure.
Definition: format_cap.h:49
size_t ast_format_cap_count(const struct ast_format_cap *cap)
Get the number of formats present within the capabilities structure.
Definition: format_cap.c:395
int ast_framehook_attach(struct ast_channel *chan, struct ast_framehook_interface *i)
Attach an framehook onto a channel for frame interception.
Definition: framehook.c:132
ast_framehook_event
These are the types of events that the framehook's event callback can receive.
Definition: framehook.h:151
@ AST_FRAMEHOOK_EVENT_WRITE
Definition: framehook.h:153
int ast_framehook_detach(struct ast_channel *chan, int framehook_id)
Detach an framehook from a channel.
Definition: framehook.c:177
#define AST_FRAMEHOOK_INTERFACE_VERSION
Definition: framehook.h:227
Asterisk internal frame definitions.
@ AST_T38_REQUEST_NEGOTIATE
ast_frame_type
Frame types.
@ AST_FRAME_CONTROL
@ AST_CONTROL_UNHOLD
@ AST_CONTROL_T38_PARAMETERS
@ AST_CONTROL_HOLD
@ AST_CONTROL_UPDATE_RTP_PEER
#define ast_debug(level,...)
Log a DEBUG message.
#define ast_verb(level,...)
#define AST_LIST_LAST(head)
Returns the last entry contained in a list.
Definition: linkedlists.h:429
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:421
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
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
#define NULL
Definition: resample.c:96
Pluggable RTP Architecture.
enum ast_rtp_dtmf_mode ast_rtp_instance_dtmf_mode_get(struct ast_rtp_instance *instance)
Get the DTMF mode of an RTP instance.
Definition: rtp_engine.c:2150
ast_rtp_glue_result
Definition: rtp_engine.h:158
@ AST_RTP_GLUE_RESULT_LOCAL
Definition: rtp_engine.h:164
@ AST_RTP_GLUE_RESULT_REMOTE
Definition: rtp_engine.h:162
@ AST_RTP_GLUE_RESULT_FORBID
Definition: rtp_engine.h:160
struct ast_rtp_engine * ast_rtp_instance_get_engine(struct ast_rtp_instance *instance)
Get the RTP engine in use on an RTP instance.
Definition: rtp_engine.c:2737
struct ast_rtp_glue * ast_rtp_instance_get_glue(const char *type)
Get the RTP glue that binds a channel to the RTP engine.
Definition: rtp_engine.c:2219
struct ast_rtp_codecs * ast_rtp_instance_get_codecs(struct ast_rtp_instance *instance)
Get the codecs structure of an RTP instance.
Definition: rtp_engine.c:748
unsigned int ast_rtp_codecs_get_framing(struct ast_rtp_codecs *codecs)
Get the framing used for a set of codecs.
Definition: rtp_engine.c:1582
void ast_rtp_instance_set_bridged(struct ast_rtp_instance *instance, struct ast_rtp_instance *bridged)
Set the other RTP instance that an instance is bridged to.
Definition: rtp_engine.c:2257
Media Stream API.
@ AST_STREAM_STATE_RECVONLY
Set when the stream is receiving media only.
Definition: stream.h:90
@ AST_STREAM_STATE_REMOVED
Set when the stream has been removed/declined.
Definition: stream.h:78
@ AST_STREAM_STATE_SENDONLY
Set when the stream is sending media only.
Definition: stream.h:86
void ast_stream_set_state(struct ast_stream *stream, enum ast_stream_state state)
Set the state of a stream.
Definition: stream.c:380
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_set_formats(struct ast_stream *stream, struct ast_format_cap *caps)
Set the current negotiated formats of a stream.
Definition: stream.c:365
const struct ast_format_cap * ast_stream_get_formats(const struct ast_stream *stream)
Get the current negotiated formats of a stream.
Definition: stream.c:330
struct ast_stream_topology * ast_stream_topology_clone(const struct ast_stream_topology *topology)
Create a deep clone of an existing stream topology.
Definition: stream.c:667
#define ast_str_alloca(init_len)
Definition: strings.h:848
Structure that contains information regarding a channel in a bridge.
void * tech_pvt
Private information unique to the bridge technology.
unsigned int suspended
struct ast_bridge * bridge
Bridge this channel is participating in.
struct ast_bridge_features * features
struct ast_channel * chan
struct ao2_container * dtmf_hooks
Structure that is the essence of a bridge technology.
Structure that contains information about a bridge.
Definition: bridge.h:349
const ast_string_field uniqueid
Definition: bridge.h:401
struct ast_bridge_channels_list channels
Definition: bridge.h:363
unsigned int num_channels
Definition: bridge.h:373
Main Channel structure associated with a channel.
struct ast_bridge_channel * bridge_channel
struct ast_bridge * bridge
enum ast_control_t38 request_response
Format capabilities structure, holds formats + preference order + etc.
Definition: format_cap.c:54
Data structure associated with a single frame of data.
struct ast_frame_subclass subclass
union ast_frame::@226 data
enum ast_frame_type frametype
int(* local_bridge)(struct ast_rtp_instance *instance0, struct ast_rtp_instance *instance1)
Definition: rtp_engine.h:709
int(* dtmf_compatible)(struct ast_channel *chan0, struct ast_rtp_instance *instance0, struct ast_channel *chan1, struct ast_rtp_instance *instance1)
Definition: rtp_engine.h:717
int(* allow_vrtp_remote)(struct ast_channel *chan1, struct ast_rtp_instance *instance)
Used to prevent two channels from remotely bridging video rtp if the channel tech has a reason for pr...
Definition: rtp_engine.h:793
enum ast_rtp_glue_result(* get_rtp_info)(struct ast_channel *chan, struct ast_rtp_instance **instance)
Callback for retrieving the RTP instance carrying audio.
Definition: rtp_engine.h:775
void(* get_codec)(struct ast_channel *chan, struct ast_format_cap *result_cap)
Callback for retrieving codecs that the channel can do. Result returned in result_cap.
Definition: rtp_engine.h:807
int(* update_peer)(struct ast_channel *chan, struct ast_rtp_instance *instance, struct ast_rtp_instance *vinstance, struct ast_rtp_instance *tinstance, const struct ast_format_cap *cap, int nat_active)
Definition: rtp_engine.h:802
int(* allow_rtp_remote)(struct ast_channel *chan1, struct ast_rtp_instance *instance)
Used to prevent two channels from remotely bridging audio rtp if the channel tech has a reason for pr...
Definition: rtp_engine.h:781
enum ast_rtp_glue_result(* get_vrtp_info)(struct ast_channel *chan, struct ast_rtp_instance **instance)
Callback for retrieving the RTP instance carrying video.
Definition: rtp_engine.h:787
Support for dynamic strings.
Definition: strings.h:623
Definition: astman.c:222
Internal structure which contains instance information about bridged RTP channels.
struct native_rtp_framehook_data * hook_data
Channel's hook data.
struct rtp_glue_data glue
Channel's cached RTP glue information.
struct ast_rtp_glue * remote_cb
Glue callbacks to bring remote channel streams back to Asterisk.
Internal structure which contains bridged RTP channel hook data.
unsigned int detached
Set when this framehook has been detached.
int id
Framehook used to intercept certain control frames.
struct rtp_glue_stream audio
struct rtp_glue_stream video
enum ast_rtp_glue_result result
struct ast_rtp_glue * cb
glue callbacks
enum ast_rtp_glue_result result
glue result
struct ast_rtp_instance * instance
RTP instance.
int done
Definition: test_amihooks.c:48
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define ast_assert(a)
Definition: utils.h:739
#define SWAP(a, b)
Definition: utils.h:235