holding_bridge: ensure moh participants get frames
[asterisk/asterisk.git] / bridges / bridge_native_rtp.c
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 ASTERISK_REGISTER_FILE()
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41
42 #include "asterisk/module.h"
43 #include "asterisk/channel.h"
44 #include "asterisk/bridge.h"
45 #include "asterisk/bridge_technology.h"
46 #include "asterisk/frame.h"
47 #include "asterisk/rtp_engine.h"
48
49 /*! \brief Internal structure which contains information about bridged RTP channels */
50 struct native_rtp_bridge_data {
51         /*! \brief Framehook used to intercept certain control frames */
52         int id;
53         /*! \brief Set when this framehook has been detached */
54         unsigned int detached;
55 };
56
57 /*! \brief Internal helper function which gets all RTP information (glue and instances) relating to the given channels */
58 static enum ast_rtp_glue_result native_rtp_bridge_get(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp_glue **glue0,
59         struct ast_rtp_glue **glue1, struct ast_rtp_instance **instance0, struct ast_rtp_instance **instance1,
60         struct ast_rtp_instance **vinstance0, struct ast_rtp_instance **vinstance1)
61 {
62         enum ast_rtp_glue_result audio_glue0_res;
63         enum ast_rtp_glue_result video_glue0_res;
64         enum ast_rtp_glue_result audio_glue1_res;
65         enum ast_rtp_glue_result video_glue1_res;
66
67         if (!(*glue0 = ast_rtp_instance_get_glue(ast_channel_tech(c0)->type)) ||
68                 !(*glue1 = ast_rtp_instance_get_glue(ast_channel_tech(c1)->type))) {
69                 return AST_RTP_GLUE_RESULT_FORBID;
70         }
71
72         audio_glue0_res = (*glue0)->get_rtp_info(c0, instance0);
73         video_glue0_res = (*glue0)->get_vrtp_info ? (*glue0)->get_vrtp_info(c0, vinstance0) : AST_RTP_GLUE_RESULT_FORBID;
74
75         audio_glue1_res = (*glue1)->get_rtp_info(c1, instance1);
76         video_glue1_res = (*glue1)->get_vrtp_info ? (*glue1)->get_vrtp_info(c1, vinstance1) : AST_RTP_GLUE_RESULT_FORBID;
77
78         /* Apply any limitations on direct media bridging that may be present */
79         if (audio_glue0_res == audio_glue1_res && audio_glue1_res == AST_RTP_GLUE_RESULT_REMOTE) {
80                 if ((*glue0)->allow_rtp_remote && !((*glue0)->allow_rtp_remote(c0, *instance1))) {
81                         /* If the allow_rtp_remote indicates that remote isn't allowed, revert to local bridge */
82                         audio_glue0_res = audio_glue1_res = AST_RTP_GLUE_RESULT_LOCAL;
83                 } else if ((*glue1)->allow_rtp_remote && !((*glue1)->allow_rtp_remote(c1, *instance0))) {
84                         audio_glue0_res = audio_glue1_res = AST_RTP_GLUE_RESULT_LOCAL;
85                 }
86         }
87         if (video_glue0_res == video_glue1_res && video_glue1_res == AST_RTP_GLUE_RESULT_REMOTE) {
88                 if ((*glue0)->allow_vrtp_remote && !((*glue0)->allow_vrtp_remote(c0, *instance1))) {
89                         /* if the allow_vrtp_remote indicates that remote isn't allowed, revert to local bridge */
90                         video_glue0_res = video_glue1_res = AST_RTP_GLUE_RESULT_LOCAL;
91                 } else if ((*glue1)->allow_vrtp_remote && !((*glue1)->allow_vrtp_remote(c1, *instance0))) {
92                         video_glue0_res = video_glue1_res = AST_RTP_GLUE_RESULT_LOCAL;
93                 }
94         }
95
96         /* If we are carrying video, and both sides are not going to remotely bridge... fail the native bridge */
97         if (video_glue0_res != AST_RTP_GLUE_RESULT_FORBID
98                 && (audio_glue0_res != AST_RTP_GLUE_RESULT_REMOTE
99                         || video_glue0_res != AST_RTP_GLUE_RESULT_REMOTE)) {
100                 audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID;
101         }
102         if (video_glue1_res != AST_RTP_GLUE_RESULT_FORBID
103                 && (audio_glue1_res != AST_RTP_GLUE_RESULT_REMOTE
104                         || video_glue1_res != AST_RTP_GLUE_RESULT_REMOTE)) {
105                 audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID;
106         }
107
108         /* The order of preference is: forbid, local, and remote. */
109         if (audio_glue0_res == AST_RTP_GLUE_RESULT_FORBID ||
110                 audio_glue1_res == AST_RTP_GLUE_RESULT_FORBID) {
111                 /* If any sort of bridge is forbidden just completely bail out and go back to generic bridging */
112                 return AST_RTP_GLUE_RESULT_FORBID;
113         } else if (audio_glue0_res == AST_RTP_GLUE_RESULT_LOCAL ||
114                 audio_glue1_res == AST_RTP_GLUE_RESULT_LOCAL) {
115                 return AST_RTP_GLUE_RESULT_LOCAL;
116         } else {
117                 return AST_RTP_GLUE_RESULT_REMOTE;
118         }
119 }
120
121 /*!
122  * \internal
123  * \brief Start native RTP bridging of two channels
124  *
125  * \param bridge The bridge that had native RTP bridging happening on it
126  * \param target If remote RTP bridging, the channel that is unheld.
127  *
128  * \note Bridge must be locked when calling this function.
129  */
130 static void native_rtp_bridge_start(struct ast_bridge *bridge, struct ast_channel *target)
131 {
132         struct ast_bridge_channel *bc0 = AST_LIST_FIRST(&bridge->channels);
133         struct ast_bridge_channel *bc1 = AST_LIST_LAST(&bridge->channels);
134         enum ast_rtp_glue_result native_type;
135         struct ast_rtp_glue *glue0, *glue1;
136         RAII_VAR(struct ast_rtp_instance *, instance0, NULL, ao2_cleanup);
137         RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ao2_cleanup);
138         RAII_VAR(struct ast_rtp_instance *, vinstance0, NULL, ao2_cleanup);
139         RAII_VAR(struct ast_rtp_instance *, vinstance1, NULL, ao2_cleanup);
140         RAII_VAR(struct ast_rtp_instance *, tinstance0, NULL, ao2_cleanup);
141         RAII_VAR(struct ast_rtp_instance *, tinstance1, NULL, ao2_cleanup);
142         RAII_VAR(struct ast_format_cap *, cap0, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup);
143         RAII_VAR(struct ast_format_cap *, cap1, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup);
144
145         if (bc0 == bc1) {
146                 return;
147         }
148
149         ast_channel_lock_both(bc0->chan, bc1->chan);
150         native_type = native_rtp_bridge_get(bc0->chan, bc1->chan, &glue0, &glue1, &instance0, &instance1, &vinstance0, &vinstance1);
151
152         switch (native_type) {
153         case AST_RTP_GLUE_RESULT_LOCAL:
154                 if (ast_rtp_instance_get_engine(instance0)->local_bridge) {
155                         ast_rtp_instance_get_engine(instance0)->local_bridge(instance0, instance1);
156                 }
157                 if (ast_rtp_instance_get_engine(instance1)->local_bridge) {
158                         ast_rtp_instance_get_engine(instance1)->local_bridge(instance1, instance0);
159                 }
160                 ast_rtp_instance_set_bridged(instance0, instance1);
161                 ast_rtp_instance_set_bridged(instance1, instance0);
162                 ast_verb(4, "Locally RTP bridged '%s' and '%s' in stack\n",
163                         ast_channel_name(bc0->chan), ast_channel_name(bc1->chan));
164                 break;
165
166         case AST_RTP_GLUE_RESULT_REMOTE:
167                 if (glue0->get_codec) {
168                         glue0->get_codec(bc0->chan, cap0);
169                 }
170                 if (glue1->get_codec) {
171                         glue1->get_codec(bc1->chan, cap1);
172                 }
173
174                 /* If we have a target, it's the channel that received the UNHOLD or UPDATE_RTP_PEER frame and was told to resume */
175                 if (!target) {
176                         glue0->update_peer(bc0->chan, instance1, vinstance1, tinstance1, cap1, 0);
177                         glue1->update_peer(bc1->chan, instance0, vinstance0, tinstance0, cap0, 0);
178                         ast_verb(4, "Remotely bridged '%s' and '%s' - media will flow directly between them\n",
179                                 ast_channel_name(bc0->chan), ast_channel_name(bc1->chan));
180                 } else {
181                         /*
182                          * If a target was provided, it is the recipient of an unhold or an update and needs to have
183                          * its media redirected to fit the current remote bridging needs. The other channel is either
184                          * already set up to handle the new media path or will have its own set of updates independent
185                          * of this pass.
186                          */
187                         if (bc0->chan == target) {
188                                 glue0->update_peer(bc0->chan, instance1, vinstance1, tinstance1, cap1, 0);
189                         } else {
190                                 glue1->update_peer(bc1->chan, instance0, vinstance0, tinstance0, cap0, 0);
191                         }
192                 }
193                 break;
194         case AST_RTP_GLUE_RESULT_FORBID:
195                 break;
196         }
197
198         ast_channel_unlock(bc0->chan);
199         ast_channel_unlock(bc1->chan);
200 }
201
202 static void native_rtp_bridge_stop(struct ast_bridge *bridge, struct ast_channel *target)
203 {
204         struct ast_bridge_channel *bc0 = AST_LIST_FIRST(&bridge->channels);
205         struct ast_bridge_channel *bc1 = AST_LIST_LAST(&bridge->channels);
206         enum ast_rtp_glue_result native_type;
207         struct ast_rtp_glue *glue0, *glue1 = NULL;
208         RAII_VAR(struct ast_rtp_instance *, instance0, NULL, ao2_cleanup);
209         RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ao2_cleanup);
210         RAII_VAR(struct ast_rtp_instance *, vinstance0, NULL, ao2_cleanup);
211         RAII_VAR(struct ast_rtp_instance *, vinstance1, NULL, ao2_cleanup);
212
213         if (bc0 == bc1) {
214                 return;
215         }
216
217         ast_channel_lock_both(bc0->chan, bc1->chan);
218         native_type = native_rtp_bridge_get(bc0->chan, bc1->chan, &glue0, &glue1, &instance0, &instance1, &vinstance0, &vinstance1);
219
220         switch (native_type) {
221         case AST_RTP_GLUE_RESULT_LOCAL:
222                 if (ast_rtp_instance_get_engine(instance0)->local_bridge) {
223                         ast_rtp_instance_get_engine(instance0)->local_bridge(instance0, NULL);
224                 }
225                 if (instance1 && ast_rtp_instance_get_engine(instance1)->local_bridge) {
226                         ast_rtp_instance_get_engine(instance1)->local_bridge(instance1, NULL);
227                 }
228                 ast_rtp_instance_set_bridged(instance0, NULL);
229                 if (instance1) {
230                         ast_rtp_instance_set_bridged(instance1, NULL);
231                 }
232                 break;
233         case AST_RTP_GLUE_RESULT_REMOTE:
234                 if (target) {
235                         /*
236                          * If a target was provided, it is being put on hold and should expect to
237                          * receive media from Asterisk instead of what it was previously connected to.
238                          */
239                         if (bc0->chan == target) {
240                                 glue0->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0);
241                         } else {
242                                 glue1->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0);
243                         }
244                 }
245                 break;
246         case AST_RTP_GLUE_RESULT_FORBID:
247                 break;
248         }
249
250         if (!target && native_type != AST_RTP_GLUE_RESULT_FORBID) {
251                 glue0->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0);
252                 glue1->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0);
253         }
254
255         ast_debug(2, "Discontinued RTP bridging of '%s' and '%s' - media will flow through Asterisk core\n",
256                 ast_channel_name(bc0->chan), ast_channel_name(bc1->chan));
257
258         ast_channel_unlock(bc0->chan);
259         ast_channel_unlock(bc1->chan);
260 }
261
262 /*! \brief Frame hook that is called to intercept hold/unhold */
263 static struct ast_frame *native_rtp_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data)
264 {
265         RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
266         struct native_rtp_bridge_data *native_data = data;
267
268         if (!f || (event != AST_FRAMEHOOK_EVENT_WRITE)) {
269                 return f;
270         }
271
272         bridge = ast_channel_get_bridge(chan);
273
274         if (bridge) {
275                 /* native_rtp_bridge_start/stop are not being called from bridging
276                    core so we need to lock the bridge prior to calling these functions
277                    Unfortunately that means unlocking the channel, but as it
278                    should not be modified this should be okay... hopefully...
279                    unless this channel is being moved around right now and is in
280                    the process of having this framehook removed (which is fine). To
281                    ensure we then don't stop or start when we shouldn't we consult
282                    the data provided. If this framehook has been detached then the
283                    detached variable will be set. This is safe to check as it is only
284                    manipulated with the bridge lock held. */
285                 ast_channel_unlock(chan);
286                 ast_bridge_lock(bridge);
287                 if (!native_data->detached) {
288                         if (f->subclass.integer == AST_CONTROL_HOLD) {
289                                 native_rtp_bridge_stop(bridge, chan);
290                         } else if ((f->subclass.integer == AST_CONTROL_UNHOLD) ||
291                                 (f->subclass.integer == AST_CONTROL_UPDATE_RTP_PEER)) {
292                                 native_rtp_bridge_start(bridge, chan);
293                         }
294                 }
295                 ast_bridge_unlock(bridge);
296                 ast_channel_lock(chan);
297
298         }
299
300         return f;
301 }
302
303 /*! \brief Callback function which informs upstream if we are consuming a frame of a specific type */
304 static int native_rtp_framehook_consume(void *data, enum ast_frame_type type)
305 {
306         return (type == AST_FRAME_CONTROL ? 1 : 0);
307 }
308
309 /*! \brief Internal helper function which checks whether the channels are compatible with our native bridging */
310 static int native_rtp_bridge_capable(struct ast_channel *chan)
311 {
312         return !ast_channel_has_hook_requiring_audio(chan);
313 }
314
315 static int native_rtp_bridge_compatible(struct ast_bridge *bridge)
316 {
317         struct ast_bridge_channel *bc0 = AST_LIST_FIRST(&bridge->channels);
318         struct ast_bridge_channel *bc1 = AST_LIST_LAST(&bridge->channels);
319         enum ast_rtp_glue_result native_type;
320         struct ast_rtp_glue *glue0, *glue1;
321         RAII_VAR(struct ast_rtp_instance *, instance0, NULL, ao2_cleanup);
322         RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ao2_cleanup);
323         RAII_VAR(struct ast_rtp_instance *, vinstance0, NULL, ao2_cleanup);
324         RAII_VAR(struct ast_rtp_instance *, vinstance1, NULL, ao2_cleanup);
325         RAII_VAR(struct ast_format_cap *, cap0, NULL, ao2_cleanup);
326         RAII_VAR(struct ast_format_cap *, cap1, NULL, ao2_cleanup);
327         int read_ptime0, read_ptime1, write_ptime0, write_ptime1;
328
329         /* We require two channels before even considering native bridging */
330         if (bridge->num_channels != 2) {
331                 ast_debug(1, "Bridge '%s' can not use native RTP bridge as two channels are required\n",
332                         bridge->uniqueid);
333                 return 0;
334         }
335
336         if (!native_rtp_bridge_capable(bc0->chan)) {
337                 ast_debug(1, "Bridge '%s' can not use native RTP bridge as channel '%s' has features which prevent it\n",
338                         bridge->uniqueid, ast_channel_name(bc0->chan));
339                 return 0;
340         }
341
342         if (!native_rtp_bridge_capable(bc1->chan)) {
343                 ast_debug(1, "Bridge '%s' can not use native RTP bridge as channel '%s' has features which prevent it\n",
344                         bridge->uniqueid, ast_channel_name(bc1->chan));
345                 return 0;
346         }
347
348         if ((native_type = native_rtp_bridge_get(bc0->chan, bc1->chan, &glue0, &glue1, &instance0, &instance1, &vinstance0, &vinstance1))
349                 == AST_RTP_GLUE_RESULT_FORBID) {
350                 ast_debug(1, "Bridge '%s' can not use native RTP bridge as it was forbidden while getting details\n",
351                         bridge->uniqueid);
352                 return 0;
353         }
354
355         if (ao2_container_count(bc0->features->dtmf_hooks) && ast_rtp_instance_dtmf_mode_get(instance0)) {
356                 ast_debug(1, "Bridge '%s' can not use native RTP bridge as channel '%s' has DTMF hooks\n",
357                         bridge->uniqueid, ast_channel_name(bc0->chan));
358                 return 0;
359         }
360
361         if (ao2_container_count(bc1->features->dtmf_hooks) && ast_rtp_instance_dtmf_mode_get(instance1)) {
362                 ast_debug(1, "Bridge '%s' can not use native RTP bridge as channel '%s' has DTMF hooks\n",
363                         bridge->uniqueid, ast_channel_name(bc1->chan));
364                 return 0;
365         }
366
367         if ((native_type == AST_RTP_GLUE_RESULT_LOCAL) && ((ast_rtp_instance_get_engine(instance0)->local_bridge !=
368                 ast_rtp_instance_get_engine(instance1)->local_bridge) ||
369                 (ast_rtp_instance_get_engine(instance0)->dtmf_compatible &&
370                         !ast_rtp_instance_get_engine(instance0)->dtmf_compatible(bc0->chan, instance0, bc1->chan, instance1)))) {
371                 ast_debug(1, "Bridge '%s' can not use local native RTP bridge as local bridge or DTMF is not compatible\n",
372                         bridge->uniqueid);
373                 return 0;
374         }
375
376         cap0 = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
377         cap1 = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
378         if (!cap0 || !cap1) {
379                 return 0;
380         }
381
382         /* Make sure that codecs match */
383         if (glue0->get_codec) {
384                 glue0->get_codec(bc0->chan, cap0);
385         }
386         if (glue1->get_codec) {
387                 glue1->get_codec(bc1->chan, cap1);
388         }
389         if (ast_format_cap_count(cap0) != 0 && ast_format_cap_count(cap1) != 0 && !ast_format_cap_iscompatible(cap0, cap1)) {
390                 struct ast_str *codec_buf0 = ast_str_alloca(64);
391                 struct ast_str *codec_buf1 = ast_str_alloca(64);
392                 ast_debug(1, "Channel codec0 = %s is not codec1 = %s, cannot native bridge in RTP.\n",
393                         ast_format_cap_get_names(cap0, &codec_buf0), ast_format_cap_get_names(cap1, &codec_buf1));
394                 return 0;
395         }
396
397         read_ptime0 = ast_format_cap_get_format_framing(cap0, ast_channel_rawreadformat(bc0->chan));
398         read_ptime1 = ast_format_cap_get_format_framing(cap1, ast_channel_rawreadformat(bc1->chan));
399         write_ptime0 = ast_format_cap_get_format_framing(cap0, ast_channel_rawwriteformat(bc0->chan));
400         write_ptime1 = ast_format_cap_get_format_framing(cap1, ast_channel_rawwriteformat(bc1->chan));
401
402         if (read_ptime0 != write_ptime1 || read_ptime1 != write_ptime0) {
403                 ast_debug(1, "Packetization differs between RTP streams (%d != %d or %d != %d). Cannot native bridge in RTP\n",
404                                 read_ptime0, write_ptime1, read_ptime1, write_ptime0);
405                 return 0;
406         }
407
408         return 1;
409 }
410
411 /*! \brief Helper function which adds frame hook to bridge channel */
412 static int native_rtp_bridge_framehook_attach(struct ast_bridge_channel *bridge_channel)
413 {
414         struct native_rtp_bridge_data *data = ao2_alloc(sizeof(*data), NULL);
415         static struct ast_framehook_interface hook = {
416                 .version = AST_FRAMEHOOK_INTERFACE_VERSION,
417                 .event_cb = native_rtp_framehook,
418                 .destroy_cb = __ao2_cleanup,
419                 .consume_cb = native_rtp_framehook_consume,
420                 .disable_inheritance = 1,
421         };
422
423         if (!data) {
424                 return -1;
425         }
426
427         ast_channel_lock(bridge_channel->chan);
428         hook.data = ao2_bump(data);
429         data->id = ast_framehook_attach(bridge_channel->chan, &hook);
430         ast_channel_unlock(bridge_channel->chan);
431         if (data->id < 0) {
432                 /* We need to drop both the reference we hold, and the one the framehook would hold */
433                 ao2_ref(data, -2);
434                 return -1;
435         }
436
437         bridge_channel->tech_pvt = data;
438
439         return 0;
440 }
441
442 /*! \brief Helper function which removes frame hook from bridge channel */
443 static void native_rtp_bridge_framehook_detach(struct ast_bridge_channel *bridge_channel)
444 {
445         RAII_VAR(struct native_rtp_bridge_data *, data, bridge_channel->tech_pvt, ao2_cleanup);
446
447         if (!data) {
448                 return;
449         }
450
451         ast_channel_lock(bridge_channel->chan);
452         ast_framehook_detach(bridge_channel->chan, data->id);
453         data->detached = 1;
454         ast_channel_unlock(bridge_channel->chan);
455         bridge_channel->tech_pvt = NULL;
456 }
457
458 static int native_rtp_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
459 {
460         native_rtp_bridge_framehook_detach(bridge_channel);
461         if (native_rtp_bridge_framehook_attach(bridge_channel)) {
462                 return -1;
463         }
464
465         native_rtp_bridge_start(bridge, NULL);
466         return 0;
467 }
468
469 static void native_rtp_bridge_unsuspend(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
470 {
471         native_rtp_bridge_join(bridge, bridge_channel);
472 }
473
474 static void native_rtp_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
475 {
476         native_rtp_bridge_framehook_detach(bridge_channel);
477         native_rtp_bridge_stop(bridge, NULL);
478 }
479
480 static int native_rtp_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
481 {
482         return ast_bridge_queue_everyone_else(bridge, bridge_channel, frame);
483 }
484
485 static struct ast_bridge_technology native_rtp_bridge = {
486         .name = "native_rtp",
487         .capabilities = AST_BRIDGE_CAPABILITY_NATIVE,
488         .preference = AST_BRIDGE_PREFERENCE_BASE_NATIVE,
489         .join = native_rtp_bridge_join,
490         .unsuspend = native_rtp_bridge_unsuspend,
491         .leave = native_rtp_bridge_leave,
492         .suspend = native_rtp_bridge_leave,
493         .write = native_rtp_bridge_write,
494         .compatible = native_rtp_bridge_compatible,
495 };
496
497 static int unload_module(void)
498 {
499         ast_bridge_technology_unregister(&native_rtp_bridge);
500         return 0;
501 }
502
503 static int load_module(void)
504 {
505         if (ast_bridge_technology_register(&native_rtp_bridge)) {
506                 unload_module();
507                 return AST_MODULE_LOAD_DECLINE;
508         }
509         return AST_MODULE_LOAD_SUCCESS;
510 }
511
512 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Native RTP bridging module");