bridge_native_rtp.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2013, Digium, Inc.
00005  *
00006  * Joshua Colp <jcolp@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Native RTP bridging technology module
00022  *
00023  * \author Joshua Colp <jcolp@digium.com>
00024  *
00025  * \ingroup bridges
00026  */
00027 
00028 /*** MODULEINFO
00029    <support_level>core</support_level>
00030  ***/
00031 
00032 #include "asterisk.h"
00033 
00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 434509 $")
00035 
00036 #include <stdio.h>
00037 #include <stdlib.h>
00038 #include <string.h>
00039 #include <sys/types.h>
00040 #include <sys/stat.h>
00041 
00042 #include "asterisk/module.h"
00043 #include "asterisk/channel.h"
00044 #include "asterisk/bridge.h"
00045 #include "asterisk/bridge_technology.h"
00046 #include "asterisk/frame.h"
00047 #include "asterisk/rtp_engine.h"
00048 
00049 /*! \brief Internal structure which contains information about bridged RTP channels */
00050 struct native_rtp_bridge_data {
00051    /*! \brief Framehook used to intercept certain control frames */
00052    int id;
00053 };
00054 
00055 /*! \brief Internal helper function which gets all RTP information (glue and instances) relating to the given channels */
00056 static enum ast_rtp_glue_result native_rtp_bridge_get(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp_glue **glue0,
00057    struct ast_rtp_glue **glue1, struct ast_rtp_instance **instance0, struct ast_rtp_instance **instance1,
00058    struct ast_rtp_instance **vinstance0, struct ast_rtp_instance **vinstance1)
00059 {
00060    enum ast_rtp_glue_result audio_glue0_res;
00061    enum ast_rtp_glue_result video_glue0_res;
00062    enum ast_rtp_glue_result audio_glue1_res;
00063    enum ast_rtp_glue_result video_glue1_res;
00064 
00065    if (!(*glue0 = ast_rtp_instance_get_glue(ast_channel_tech(c0)->type)) ||
00066       !(*glue1 = ast_rtp_instance_get_glue(ast_channel_tech(c1)->type))) {
00067       return AST_RTP_GLUE_RESULT_FORBID;
00068    }
00069 
00070    audio_glue0_res = (*glue0)->get_rtp_info(c0, instance0);
00071    video_glue0_res = (*glue0)->get_vrtp_info ? (*glue0)->get_vrtp_info(c0, vinstance0) : AST_RTP_GLUE_RESULT_FORBID;
00072 
00073    audio_glue1_res = (*glue1)->get_rtp_info(c1, instance1);
00074    video_glue1_res = (*glue1)->get_vrtp_info ? (*glue1)->get_vrtp_info(c1, vinstance1) : AST_RTP_GLUE_RESULT_FORBID;
00075 
00076    /* Apply any limitations on direct media bridging that may be present */
00077    if (audio_glue0_res == audio_glue1_res && audio_glue1_res == AST_RTP_GLUE_RESULT_REMOTE) {
00078       if ((*glue0)->allow_rtp_remote && !((*glue0)->allow_rtp_remote(c0, *instance1))) {
00079          /* If the allow_rtp_remote indicates that remote isn't allowed, revert to local bridge */
00080          audio_glue0_res = audio_glue1_res = AST_RTP_GLUE_RESULT_LOCAL;
00081       } else if ((*glue1)->allow_rtp_remote && !((*glue1)->allow_rtp_remote(c1, *instance0))) {
00082          audio_glue0_res = audio_glue1_res = AST_RTP_GLUE_RESULT_LOCAL;
00083       }
00084    }
00085    if (video_glue0_res == video_glue1_res && video_glue1_res == AST_RTP_GLUE_RESULT_REMOTE) {
00086       if ((*glue0)->allow_vrtp_remote && !((*glue0)->allow_vrtp_remote(c0, *instance1))) {
00087          /* if the allow_vrtp_remote indicates that remote isn't allowed, revert to local bridge */
00088          video_glue0_res = video_glue1_res = AST_RTP_GLUE_RESULT_LOCAL;
00089       } else if ((*glue1)->allow_vrtp_remote && !((*glue1)->allow_vrtp_remote(c1, *instance0))) {
00090          video_glue0_res = video_glue1_res = AST_RTP_GLUE_RESULT_LOCAL;
00091       }
00092    }
00093 
00094    /* If we are carrying video, and both sides are not going to remotely bridge... fail the native bridge */
00095    if (video_glue0_res != AST_RTP_GLUE_RESULT_FORBID
00096       && (audio_glue0_res != AST_RTP_GLUE_RESULT_REMOTE
00097          || video_glue0_res != AST_RTP_GLUE_RESULT_REMOTE)) {
00098       audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID;
00099    }
00100    if (video_glue1_res != AST_RTP_GLUE_RESULT_FORBID
00101       && (audio_glue1_res != AST_RTP_GLUE_RESULT_REMOTE
00102          || video_glue1_res != AST_RTP_GLUE_RESULT_REMOTE)) {
00103       audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID;
00104    }
00105 
00106    /* The order of preference is: forbid, local, and remote. */
00107    if (audio_glue0_res == AST_RTP_GLUE_RESULT_FORBID ||
00108       audio_glue1_res == AST_RTP_GLUE_RESULT_FORBID) {
00109       /* If any sort of bridge is forbidden just completely bail out and go back to generic bridging */
00110       return AST_RTP_GLUE_RESULT_FORBID;
00111    } else if (audio_glue0_res == AST_RTP_GLUE_RESULT_LOCAL ||
00112       audio_glue1_res == AST_RTP_GLUE_RESULT_LOCAL) {
00113       return AST_RTP_GLUE_RESULT_LOCAL;
00114    } else {
00115       return AST_RTP_GLUE_RESULT_REMOTE;
00116    }
00117 }
00118 
00119 /*!
00120  * \internal
00121  * \brief Start native RTP bridging of two channels
00122  *
00123  * \param bridge The bridge that had native RTP bridging happening on it
00124  * \param target If remote RTP bridging, the channel that is unheld.
00125  *
00126  * \note Bridge must be locked when calling this function.
00127  */
00128 static void native_rtp_bridge_start(struct ast_bridge *bridge, struct ast_channel *target)
00129 {
00130    struct ast_bridge_channel *bc0 = AST_LIST_FIRST(&bridge->channels);
00131    struct ast_bridge_channel *bc1 = AST_LIST_LAST(&bridge->channels);
00132    enum ast_rtp_glue_result native_type;
00133    struct ast_rtp_glue *glue0, *glue1;
00134    RAII_VAR(struct ast_rtp_instance *, instance0, NULL, ao2_cleanup);
00135    RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ao2_cleanup);
00136    RAII_VAR(struct ast_rtp_instance *, vinstance0, NULL, ao2_cleanup);
00137    RAII_VAR(struct ast_rtp_instance *, vinstance1, NULL, ao2_cleanup);
00138    RAII_VAR(struct ast_rtp_instance *, tinstance0, NULL, ao2_cleanup);
00139    RAII_VAR(struct ast_rtp_instance *, tinstance1, NULL, ao2_cleanup);
00140    RAII_VAR(struct ast_format_cap *, cap0, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup);
00141    RAII_VAR(struct ast_format_cap *, cap1, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup);
00142 
00143    if (bc0 == bc1) {
00144       return;
00145    }
00146 
00147    ast_channel_lock_both(bc0->chan, bc1->chan);
00148    native_type = native_rtp_bridge_get(bc0->chan, bc1->chan, &glue0, &glue1, &instance0, &instance1, &vinstance0, &vinstance1);
00149 
00150    switch (native_type) {
00151    case AST_RTP_GLUE_RESULT_LOCAL:
00152       if (ast_rtp_instance_get_engine(instance0)->local_bridge) {
00153          ast_rtp_instance_get_engine(instance0)->local_bridge(instance0, instance1);
00154       }
00155       if (ast_rtp_instance_get_engine(instance1)->local_bridge) {
00156          ast_rtp_instance_get_engine(instance1)->local_bridge(instance1, instance0);
00157       }
00158       ast_rtp_instance_set_bridged(instance0, instance1);
00159       ast_rtp_instance_set_bridged(instance1, instance0);
00160       ast_verb(4, "Locally RTP bridged '%s' and '%s' in stack\n",
00161          ast_channel_name(bc0->chan), ast_channel_name(bc1->chan));
00162       break;
00163 
00164    case AST_RTP_GLUE_RESULT_REMOTE:
00165       if (glue0->get_codec) {
00166          glue0->get_codec(bc0->chan, cap0);
00167       }
00168       if (glue1->get_codec) {
00169          glue1->get_codec(bc1->chan, cap1);
00170       }
00171 
00172       /* If we have a target, it's the channel that received the UNHOLD or UPDATE_RTP_PEER frame and was told to resume */
00173       if (!target) {
00174          glue0->update_peer(bc0->chan, instance1, vinstance1, tinstance1, cap1, 0);
00175          glue1->update_peer(bc1->chan, instance0, vinstance0, tinstance0, cap0, 0);
00176          ast_verb(4, "Remotely bridged '%s' and '%s' - media will flow directly between them\n",
00177             ast_channel_name(bc0->chan), ast_channel_name(bc1->chan));
00178       } else {
00179          /*
00180           * If a target was provided, it is the recipient of an unhold or an update and needs to have
00181           * its media redirected to fit the current remote bridging needs. The other channel is either
00182           * already set up to handle the new media path or will have its own set of updates independent
00183           * of this pass.
00184           */
00185          if (bc0->chan == target) {
00186             glue0->update_peer(bc0->chan, instance1, vinstance1, tinstance1, cap1, 0);
00187          } else {
00188             glue1->update_peer(bc1->chan, instance0, vinstance0, tinstance0, cap0, 0);
00189          }
00190       }
00191       break;
00192    case AST_RTP_GLUE_RESULT_FORBID:
00193       break;
00194    }
00195 
00196    ast_channel_unlock(bc0->chan);
00197    ast_channel_unlock(bc1->chan);
00198 }
00199 
00200 static void native_rtp_bridge_stop(struct ast_bridge *bridge, struct ast_channel *target)
00201 {
00202    struct ast_bridge_channel *bc0 = AST_LIST_FIRST(&bridge->channels);
00203    struct ast_bridge_channel *bc1 = AST_LIST_LAST(&bridge->channels);
00204    enum ast_rtp_glue_result native_type;
00205    struct ast_rtp_glue *glue0, *glue1 = NULL;
00206    RAII_VAR(struct ast_rtp_instance *, instance0, NULL, ao2_cleanup);
00207    RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ao2_cleanup);
00208    RAII_VAR(struct ast_rtp_instance *, vinstance0, NULL, ao2_cleanup);
00209    RAII_VAR(struct ast_rtp_instance *, vinstance1, NULL, ao2_cleanup);
00210 
00211    if (bc0 == bc1) {
00212       return;
00213    }
00214 
00215    ast_channel_lock_both(bc0->chan, bc1->chan);
00216    native_type = native_rtp_bridge_get(bc0->chan, bc1->chan, &glue0, &glue1, &instance0, &instance1, &vinstance0, &vinstance1);
00217 
00218    switch (native_type) {
00219    case AST_RTP_GLUE_RESULT_LOCAL:
00220       if (ast_rtp_instance_get_engine(instance0)->local_bridge) {
00221          ast_rtp_instance_get_engine(instance0)->local_bridge(instance0, NULL);
00222       }
00223       if (instance1 && ast_rtp_instance_get_engine(instance1)->local_bridge) {
00224          ast_rtp_instance_get_engine(instance1)->local_bridge(instance1, NULL);
00225       }
00226       ast_rtp_instance_set_bridged(instance0, NULL);
00227       if (instance1) {
00228          ast_rtp_instance_set_bridged(instance1, NULL);
00229       }
00230       break;
00231    case AST_RTP_GLUE_RESULT_REMOTE:
00232       if (target) {
00233          /*
00234           * If a target was provided, it is being put on hold and should expect to
00235           * receive media from Asterisk instead of what it was previously connected to.
00236           */
00237          if (bc0->chan == target) {
00238             glue0->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0);
00239          } else {
00240             glue1->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0);
00241          }
00242       }
00243       break;
00244    case AST_RTP_GLUE_RESULT_FORBID:
00245       break;
00246    }
00247 
00248    if (!target && native_type != AST_RTP_GLUE_RESULT_FORBID) {
00249       glue0->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0);
00250       glue1->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0);
00251    }
00252 
00253    ast_debug(2, "Discontinued RTP bridging of '%s' and '%s' - media will flow through Asterisk core\n",
00254       ast_channel_name(bc0->chan), ast_channel_name(bc1->chan));
00255 
00256    ast_channel_unlock(bc0->chan);
00257    ast_channel_unlock(bc1->chan);
00258 }
00259 
00260 /*! \brief Frame hook that is called to intercept hold/unhold */
00261 static struct ast_frame *native_rtp_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data)
00262 {
00263    RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
00264 
00265    if (!f || (event != AST_FRAMEHOOK_EVENT_WRITE)) {
00266       return f;
00267    }
00268 
00269    bridge = ast_channel_get_bridge(chan);
00270 
00271    if (bridge) {
00272       /* native_rtp_bridge_start/stop are not being called from bridging
00273          core so we need to lock the bridge prior to calling these functions
00274          Unfortunately that means unlocking the channel, but as it
00275          should not be modified this should be okay...hopefully */
00276       ast_channel_unlock(chan);
00277       ast_bridge_lock(bridge);
00278       if (f->subclass.integer == AST_CONTROL_HOLD) {
00279          native_rtp_bridge_stop(bridge, chan);
00280       } else if ((f->subclass.integer == AST_CONTROL_UNHOLD) || (f->subclass.integer == AST_CONTROL_UPDATE_RTP_PEER)) {
00281          native_rtp_bridge_start(bridge, chan);
00282       }
00283       ast_bridge_unlock(bridge);
00284       ast_channel_lock(chan);
00285 
00286    }
00287 
00288    return f;
00289 }
00290 
00291 /*! \brief Callback function which informs upstream if we are consuming a frame of a specific type */
00292 static int native_rtp_framehook_consume(void *data, enum ast_frame_type type)
00293 {
00294    return (type == AST_FRAME_CONTROL ? 1 : 0);
00295 }
00296 
00297 /*! \brief Internal helper function which checks whether the channels are compatible with our native bridging */
00298 static int native_rtp_bridge_capable(struct ast_channel *chan)
00299 {
00300    return !ast_channel_has_hook_requiring_audio(chan);
00301 }
00302 
00303 static int native_rtp_bridge_compatible(struct ast_bridge *bridge)
00304 {
00305    struct ast_bridge_channel *bc0 = AST_LIST_FIRST(&bridge->channels);
00306    struct ast_bridge_channel *bc1 = AST_LIST_LAST(&bridge->channels);
00307    enum ast_rtp_glue_result native_type;
00308    struct ast_rtp_glue *glue0, *glue1;
00309    RAII_VAR(struct ast_rtp_instance *, instance0, NULL, ao2_cleanup);
00310    RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ao2_cleanup);
00311    RAII_VAR(struct ast_rtp_instance *, vinstance0, NULL, ao2_cleanup);
00312    RAII_VAR(struct ast_rtp_instance *, vinstance1, NULL, ao2_cleanup);
00313    RAII_VAR(struct ast_format_cap *, cap0, NULL, ao2_cleanup);
00314    RAII_VAR(struct ast_format_cap *, cap1, NULL, ao2_cleanup);
00315    int read_ptime0, read_ptime1, write_ptime0, write_ptime1;
00316 
00317    /* We require two channels before even considering native bridging */
00318    if (bridge->num_channels != 2) {
00319       ast_debug(1, "Bridge '%s' can not use native RTP bridge as two channels are required\n",
00320          bridge->uniqueid);
00321       return 0;
00322    }
00323 
00324    if (!native_rtp_bridge_capable(bc0->chan)) {
00325       ast_debug(1, "Bridge '%s' can not use native RTP bridge as channel '%s' has features which prevent it\n",
00326          bridge->uniqueid, ast_channel_name(bc0->chan));
00327       return 0;
00328    }
00329 
00330    if (!native_rtp_bridge_capable(bc1->chan)) {
00331       ast_debug(1, "Bridge '%s' can not use native RTP bridge as channel '%s' has features which prevent it\n",
00332          bridge->uniqueid, ast_channel_name(bc1->chan));
00333       return 0;
00334    }
00335 
00336    if ((native_type = native_rtp_bridge_get(bc0->chan, bc1->chan, &glue0, &glue1, &instance0, &instance1, &vinstance0, &vinstance1))
00337       == AST_RTP_GLUE_RESULT_FORBID) {
00338       ast_debug(1, "Bridge '%s' can not use native RTP bridge as it was forbidden while getting details\n",
00339          bridge->uniqueid);
00340       return 0;
00341    }
00342 
00343    if (ao2_container_count(bc0->features->dtmf_hooks) && ast_rtp_instance_dtmf_mode_get(instance0)) {
00344       ast_debug(1, "Bridge '%s' can not use native RTP bridge as channel '%s' has DTMF hooks\n",
00345          bridge->uniqueid, ast_channel_name(bc0->chan));
00346       return 0;
00347    }
00348 
00349    if (ao2_container_count(bc1->features->dtmf_hooks) && ast_rtp_instance_dtmf_mode_get(instance1)) {
00350       ast_debug(1, "Bridge '%s' can not use native RTP bridge as channel '%s' has DTMF hooks\n",
00351          bridge->uniqueid, ast_channel_name(bc1->chan));
00352       return 0;
00353    }
00354 
00355    if ((native_type == AST_RTP_GLUE_RESULT_LOCAL) && ((ast_rtp_instance_get_engine(instance0)->local_bridge !=
00356       ast_rtp_instance_get_engine(instance1)->local_bridge) ||
00357       (ast_rtp_instance_get_engine(instance0)->dtmf_compatible &&
00358          !ast_rtp_instance_get_engine(instance0)->dtmf_compatible(bc0->chan, instance0, bc1->chan, instance1)))) {
00359       ast_debug(1, "Bridge '%s' can not use local native RTP bridge as local bridge or DTMF is not compatible\n",
00360          bridge->uniqueid);
00361       return 0;
00362    }
00363 
00364    cap0 = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
00365    cap1 = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
00366    if (!cap0 || !cap1) {
00367       return 0;
00368    }
00369 
00370    /* Make sure that codecs match */
00371    if (glue0->get_codec) {
00372       glue0->get_codec(bc0->chan, cap0);
00373    }
00374    if (glue1->get_codec) {
00375       glue1->get_codec(bc1->chan, cap1);
00376    }
00377    if (ast_format_cap_count(cap0) != 0 && ast_format_cap_count(cap1) != 0 && !ast_format_cap_iscompatible(cap0, cap1)) {
00378       struct ast_str *codec_buf0 = ast_str_alloca(64);
00379       struct ast_str *codec_buf1 = ast_str_alloca(64);
00380       ast_debug(1, "Channel codec0 = %s is not codec1 = %s, cannot native bridge in RTP.\n",
00381          ast_format_cap_get_names(cap0, &codec_buf0), ast_format_cap_get_names(cap1, &codec_buf1));
00382       return 0;
00383    }
00384 
00385    read_ptime0 = ast_format_cap_get_format_framing(cap0, ast_channel_rawreadformat(bc0->chan));
00386    read_ptime1 = ast_format_cap_get_format_framing(cap1, ast_channel_rawreadformat(bc1->chan));
00387    write_ptime0 = ast_format_cap_get_format_framing(cap0, ast_channel_rawwriteformat(bc0->chan));
00388    write_ptime1 = ast_format_cap_get_format_framing(cap1, ast_channel_rawwriteformat(bc1->chan));
00389 
00390    if (read_ptime0 != write_ptime1 || read_ptime1 != write_ptime0) {
00391       ast_debug(1, "Packetization differs between RTP streams (%d != %d or %d != %d). Cannot native bridge in RTP\n",
00392             read_ptime0, write_ptime1, read_ptime1, write_ptime0);
00393       return 0;
00394    }
00395 
00396    return 1;
00397 }
00398 
00399 /*! \brief Helper function which adds frame hook to bridge channel */
00400 static int native_rtp_bridge_framehook_attach(struct ast_bridge_channel *bridge_channel)
00401 {
00402    struct native_rtp_bridge_data *data = ao2_alloc(sizeof(*data), NULL);
00403    static struct ast_framehook_interface hook = {
00404       .version = AST_FRAMEHOOK_INTERFACE_VERSION,
00405       .event_cb = native_rtp_framehook,
00406       .consume_cb = native_rtp_framehook_consume,
00407       .disable_inheritance = 1,
00408    };
00409 
00410    if (!data) {
00411       return -1;
00412    }
00413 
00414    ast_channel_lock(bridge_channel->chan);
00415    data->id = ast_framehook_attach(bridge_channel->chan, &hook);
00416    ast_channel_unlock(bridge_channel->chan);
00417    if (data->id < 0) {
00418       ao2_cleanup(data);
00419       return -1;
00420    }
00421 
00422    bridge_channel->tech_pvt = data;
00423 
00424    return 0;
00425 }
00426 
00427 /*! \brief Helper function which removes frame hook from bridge channel */
00428 static void native_rtp_bridge_framehook_detach(struct ast_bridge_channel *bridge_channel)
00429 {
00430    RAII_VAR(struct native_rtp_bridge_data *, data, bridge_channel->tech_pvt, ao2_cleanup);
00431 
00432    if (!data) {
00433       return;
00434    }
00435 
00436    ast_channel_lock(bridge_channel->chan);
00437    ast_framehook_detach(bridge_channel->chan, data->id);
00438    ast_channel_unlock(bridge_channel->chan);
00439    bridge_channel->tech_pvt = NULL;
00440 }
00441 
00442 static int native_rtp_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
00443 {
00444    native_rtp_bridge_framehook_detach(bridge_channel);
00445    if (native_rtp_bridge_framehook_attach(bridge_channel)) {
00446       return -1;
00447    }
00448 
00449    native_rtp_bridge_start(bridge, NULL);
00450    return 0;
00451 }
00452 
00453 static void native_rtp_bridge_unsuspend(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
00454 {
00455    native_rtp_bridge_join(bridge, bridge_channel);
00456 }
00457 
00458 static void native_rtp_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
00459 {
00460    native_rtp_bridge_framehook_detach(bridge_channel);
00461    native_rtp_bridge_stop(bridge, NULL);
00462 }
00463 
00464 static int native_rtp_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
00465 {
00466    return ast_bridge_queue_everyone_else(bridge, bridge_channel, frame);
00467 }
00468 
00469 static struct ast_bridge_technology native_rtp_bridge = {
00470    .name = "native_rtp",
00471    .capabilities = AST_BRIDGE_CAPABILITY_NATIVE,
00472    .preference = AST_BRIDGE_PREFERENCE_BASE_NATIVE,
00473    .join = native_rtp_bridge_join,
00474    .unsuspend = native_rtp_bridge_unsuspend,
00475    .leave = native_rtp_bridge_leave,
00476    .suspend = native_rtp_bridge_leave,
00477    .write = native_rtp_bridge_write,
00478    .compatible = native_rtp_bridge_compatible,
00479 };
00480 
00481 static int unload_module(void)
00482 {
00483    ast_bridge_technology_unregister(&native_rtp_bridge);
00484    return 0;
00485 }
00486 
00487 static int load_module(void)
00488 {
00489    if (ast_bridge_technology_register(&native_rtp_bridge)) {
00490       unload_module();
00491       return AST_MODULE_LOAD_DECLINE;
00492    }
00493    return AST_MODULE_LOAD_SUCCESS;
00494 }
00495 
00496 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Native RTP bridging module");

Generated on Thu Apr 16 06:27:16 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6