From 63de8343958b91c8836c5e6ddf1c0106b40e9fe6 Mon Sep 17 00:00:00 2001 From: Joshua Colp <jcolp@digium.com> Date: Thu, 2 Apr 2009 17:20:52 +0000 Subject: [PATCH] Merge in the RTP engine API. This API provides a generic way for multiple RTP stacks to be integrated into Asterisk. Right now there is only one present, res_rtp_asterisk, which is the existing Asterisk RTP stack. Functionality wise this commit performs the same as previously. API documentation can be viewed in the rtp_engine.h header file. Review: http://reviewboard.digium.com/r/209/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@186078 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- UPGRADE.txt | 6 +- apps/app_dial.c | 10 +- channels/chan_agent.c | 1 - channels/chan_bridge.c | 1 - channels/chan_gtalk.c | 95 +- channels/chan_h323.c | 105 +- channels/chan_jingle.c | 79 +- channels/chan_local.c | 1 - channels/chan_mgcp.c | 94 +- channels/chan_sip.c | 1070 ++++---- channels/chan_skinny.c | 99 +- channels/chan_unistim.c | 93 +- configs/sip.conf.sample | 2 + include/asterisk/_private.h | 1 + include/asterisk/rtp.h | 416 --- include/asterisk/rtp_engine.h | 1594 +++++++++++ include/asterisk/stun.h | 71 + main/Makefile | 4 +- main/asterisk.c | 2 - main/loader.c | 2 - main/rtp.c | 4865 --------------------------------- main/rtp_engine.c | 1572 +++++++++++ main/stun.c | 475 ++++ res/res_rtp_asterisk.c | 2579 +++++++++++++++++ 24 files changed, 7113 insertions(+), 6124 deletions(-) delete mode 100644 include/asterisk/rtp.h create mode 100644 include/asterisk/rtp_engine.h create mode 100644 include/asterisk/stun.h delete mode 100644 main/rtp.c create mode 100644 main/rtp_engine.c create mode 100644 main/stun.c create mode 100644 res/res_rtp_asterisk.c diff --git a/UPGRADE.txt b/UPGRADE.txt index 62551b0f9a..35b0d455aa 100644 --- a/UPGRADE.txt +++ b/UPGRADE.txt @@ -20,7 +20,11 @@ From 1.6.2 to 1.6.3: -* Nothing, yet! +* The usage of RTP inside of Asterisk has now become modularized. This means + the Asterisk RTP stack now exists as a loadable module, res_rtp_asterisk. + If you are not using autoload=yes in modules.conf you will need to ensure + it is set to load. If not, then any module which uses RTP (such as chan_sip) + will not be able to send or receive calls. From 1.6.1 to 1.6.2: diff --git a/apps/app_dial.c b/apps/app_dial.c index 8f6a49ba3c..96bb570813 100644 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -54,7 +54,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/utils.h" #include "asterisk/app.h" #include "asterisk/causes.h" -#include "asterisk/rtp.h" +#include "asterisk/rtp_engine.h" #include "asterisk/cdr.h" #include "asterisk/manager.h" #include "asterisk/privacy.h" @@ -745,7 +745,9 @@ static void do_forward(struct chanlist *o, char *new_cid_num, *new_cid_name; struct ast_channel *src; - ast_rtp_make_compatible(c, in, single); + if (single) { + ast_rtp_instance_early_bridge_make_compatible(c, in); + } if (ast_test_flag64(o, OPT_FORCECLID)) { new_cid_num = ast_strdup(S_OR(in->macroexten, in->exten)); new_cid_name = NULL; /* XXX no name ? */ @@ -1745,7 +1747,9 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags pbx_builtin_setvar_helper(tc, "DIALEDPEERNUMBER", numsubst); /* Setup outgoing SDP to match incoming one */ - ast_rtp_make_compatible(tc, chan, !outgoing && !rest); + if (!outgoing && !rest) { + ast_rtp_instance_early_bridge_make_compatible(tc, chan); + } /* Inherit specially named variables from parent channel */ ast_channel_inherit_variables(chan, tc); diff --git a/channels/chan_agent.c b/channels/chan_agent.c index 4e1c282401..b15f7a04e7 100644 --- a/channels/chan_agent.c +++ b/channels/chan_agent.c @@ -52,7 +52,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/pbx.h" #include "asterisk/sched.h" #include "asterisk/io.h" -#include "asterisk/rtp.h" #include "asterisk/acl.h" #include "asterisk/callerid.h" #include "asterisk/file.h" diff --git a/channels/chan_bridge.c b/channels/chan_bridge.c index 84909e795f..bd1d0fbeed 100644 --- a/channels/chan_bridge.c +++ b/channels/chan_bridge.c @@ -39,7 +39,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/pbx.h" #include "asterisk/sched.h" #include "asterisk/io.h" -#include "asterisk/rtp.h" #include "asterisk/acl.h" #include "asterisk/callerid.h" #include "asterisk/file.h" diff --git a/channels/chan_gtalk.c b/channels/chan_gtalk.c index d608cc05c5..f63cc20270 100644 --- a/channels/chan_gtalk.c +++ b/channels/chan_gtalk.c @@ -52,7 +52,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/pbx.h" #include "asterisk/sched.h" #include "asterisk/io.h" -#include "asterisk/rtp.h" +#include "asterisk/rtp_engine.h" +#include "asterisk/stun.h" #include "asterisk/acl.h" #include "asterisk/callerid.h" #include "asterisk/file.h" @@ -112,8 +113,8 @@ struct gtalk_pvt { char cid_name[80]; /*!< Caller ID name */ char exten[80]; /*!< Called extension */ struct ast_channel *owner; /*!< Master Channel */ - struct ast_rtp *rtp; /*!< RTP audio session */ - struct ast_rtp *vrtp; /*!< RTP video session */ + struct ast_rtp_instance *rtp; /*!< RTP audio session */ + struct ast_rtp_instance *vrtp; /*!< RTP video session */ int jointcapability; /*!< Supported capability at both ends (codecs ) */ int peercapability; struct gtalk_pvt *next; /* Next entity */ @@ -183,11 +184,6 @@ static int gtalk_sendhtml(struct ast_channel *ast, int subclass, const char *dat static struct gtalk_pvt *gtalk_alloc(struct gtalk *client, const char *us, const char *them, const char *sid); static char *gtalk_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); static char *gtalk_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); -/*----- RTP interface functions */ -static int gtalk_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, - struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active); -static enum ast_rtp_get_result gtalk_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); -static int gtalk_get_codec(struct ast_channel *chan); /*! \brief PBX interface structure for channel registration */ static const struct ast_channel_tech gtalk_tech = { @@ -197,7 +193,7 @@ static const struct ast_channel_tech gtalk_tech = { .requester = gtalk_request, .send_digit_begin = gtalk_digit_begin, .send_digit_end = gtalk_digit_end, - .bridge = ast_rtp_bridge, + .bridge = ast_rtp_instance_bridge, .call = gtalk_call, .hangup = gtalk_hangup, .answer = gtalk_answer, @@ -216,14 +212,6 @@ static struct sched_context *sched; /*!< The scheduling context */ static struct io_context *io; /*!< The IO context */ static struct in_addr __ourip; -/*! \brief RTP driver interface */ -static struct ast_rtp_protocol gtalk_rtp = { - type: "Gtalk", - get_rtp_info: gtalk_get_rtp_peer, - set_rtp_peer: gtalk_set_rtp_peer, - get_codec: gtalk_get_codec, -}; - static struct ast_cli_entry gtalk_cli[] = { AST_CLI_DEFINE(gtalk_do_reload, "Reload GoogleTalk configuration"), AST_CLI_DEFINE(gtalk_show_channels, "Show GoogleTalk channels"), @@ -371,7 +359,7 @@ static int add_codec_to_answer(const struct gtalk_pvt *p, int codec, iks *dcodec iks_insert_node(dcodecs, payload_gsm); res++; } - ast_rtp_lookup_code(p->rtp, 1, codec); + return res; } @@ -523,18 +511,19 @@ static int gtalk_answer(struct ast_channel *ast) return res; } -static enum ast_rtp_get_result gtalk_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) +static enum ast_rtp_glue_result gtalk_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) { struct gtalk_pvt *p = chan->tech_pvt; - enum ast_rtp_get_result res = AST_RTP_GET_FAILED; + enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_FORBID; if (!p) return res; ast_mutex_lock(&p->lock); if (p->rtp){ - *rtp = p->rtp; - res = AST_RTP_TRY_PARTIAL; + ao2_ref(p->rtp, +1); + *instance = p->rtp; + res = AST_RTP_GLUE_RESULT_LOCAL; } ast_mutex_unlock(&p->lock); @@ -547,7 +536,7 @@ static int gtalk_get_codec(struct ast_channel *chan) return p->peercapability; } -static int gtalk_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active) +static int gtalk_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *trtp, int codecs, int nat_active) { struct gtalk_pvt *p; @@ -567,6 +556,13 @@ static int gtalk_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, str return 0; } +static struct ast_rtp_glue gtalk_rtp_glue = { + .type = "Gtalk", + .get_rtp_info = gtalk_get_rtp_peer, + .get_codec = gtalk_get_codec, + .update_peer = gtalk_set_rtp_peer, +}; + static int gtalk_response(struct gtalk *client, char *from, ikspak *pak, const char *reasonstr, const char *reasonstr2) { iks *response = NULL, *error = NULL, *reason = NULL; @@ -617,13 +613,13 @@ static int gtalk_is_answered(struct gtalk *client, ikspak *pak) /* codec points to the first <payload-type/> tag */ codec = iks_first_tag(iks_first_tag(iks_first_tag(pak->x))); while (codec) { - ast_rtp_set_m_type(tmp->rtp, atoi(iks_find_attrib(codec, "id"))); - ast_rtp_set_rtpmap_type(tmp->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); + ast_rtp_codecs_payloads_set_m_type(ast_rtp_instance_get_codecs(tmp->rtp), tmp->rtp, atoi(iks_find_attrib(codec, "id"))); + ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(tmp->rtp), tmp->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); codec = iks_next_tag(codec); } /* Now gather all of the codecs that we are asked for */ - ast_rtp_get_current_formats(tmp->rtp, &tmp->peercapability, &peernoncodeccapability); + ast_rtp_codecs_payload_formats(ast_rtp_instance_get_codecs(tmp->rtp), &tmp->peercapability, &peernoncodeccapability); /* at this point, we received an awser from the remote Gtalk client, which allows us to compare capabilities */ @@ -810,7 +806,7 @@ static int gtalk_create_candidates(struct gtalk *client, struct gtalk_pvt *p, ch goto safeout; } - ast_rtp_get_us(p->rtp, &sin); + ast_rtp_instance_get_local_address(p->rtp, &sin); ast_find_ourip(&us, bindaddr); if (!strcmp(ast_inet_ntoa(us), "127.0.0.1")) { ast_log(LOG_WARNING, "Found a loopback IP on the system, check your network configuration or set the bindaddr attribute."); @@ -951,8 +947,9 @@ static struct gtalk_pvt *gtalk_alloc(struct gtalk *client, const char *us, const tmp->initiator = 1; } /* clear codecs */ - tmp->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); - ast_rtp_pt_clear(tmp->rtp); + tmp->rtp = ast_rtp_instance_new(NULL, sched, &bindaddr, NULL); + ast_rtp_instance_set_prop(tmp->rtp, AST_RTP_PROPERTY_RTCP, 1); + ast_rtp_codecs_payloads_clear(ast_rtp_instance_get_codecs(tmp->rtp), tmp->rtp); /* add user configured codec capabilites */ if (client->capability) @@ -1014,20 +1011,20 @@ static struct ast_channel *gtalk_new(struct gtalk *client, struct gtalk_pvt *i, /* Set Frame packetization */ if (i->rtp) - ast_rtp_codec_setpref(i->rtp, &i->prefs); + ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(i->rtp), i->rtp, &i->prefs); tmp->nativeformats = ast_codec_choose(&i->prefs, what, 1) | (i->jointcapability & AST_FORMAT_VIDEO_MASK); fmt = ast_best_codec(tmp->nativeformats); if (i->rtp) { - ast_rtp_setstun(i->rtp, 1); - ast_channel_set_fd(tmp, 0, ast_rtp_fd(i->rtp)); - ast_channel_set_fd(tmp, 1, ast_rtcp_fd(i->rtp)); + ast_rtp_instance_set_prop(i->rtp, AST_RTP_PROPERTY_STUN, 1); + ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(i->rtp, 0)); + ast_channel_set_fd(tmp, 1, ast_rtp_instance_fd(i->rtp, 1)); } if (i->vrtp) { - ast_rtp_setstun(i->rtp, 1); - ast_channel_set_fd(tmp, 2, ast_rtp_fd(i->vrtp)); - ast_channel_set_fd(tmp, 3, ast_rtcp_fd(i->vrtp)); + ast_rtp_instance_set_prop(i->vrtp, AST_RTP_PROPERTY_STUN, 1); + ast_channel_set_fd(tmp, 2, ast_rtp_instance_fd(i->vrtp, 0)); + ast_channel_set_fd(tmp, 3, ast_rtp_instance_fd(i->vrtp, 1)); } if (state == AST_STATE_RING) tmp->rings = 1; @@ -1142,9 +1139,9 @@ static void gtalk_free_pvt(struct gtalk *client, struct gtalk_pvt *p) if (p->owner) ast_log(LOG_WARNING, "Uh oh, there's an owner, this is going to be messy.\n"); if (p->rtp) - ast_rtp_destroy(p->rtp); + ast_rtp_instance_destroy(p->rtp); if (p->vrtp) - ast_rtp_destroy(p->vrtp); + ast_rtp_instance_destroy(p->vrtp); gtalk_free_candidates(p->theircandidates); ast_free(p); } @@ -1207,13 +1204,13 @@ static int gtalk_newcall(struct gtalk *client, ikspak *pak) codec = iks_first_tag(iks_first_tag(iks_first_tag(pak->x))); while (codec) { - ast_rtp_set_m_type(p->rtp, atoi(iks_find_attrib(codec, "id"))); - ast_rtp_set_rtpmap_type(p->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); + ast_rtp_codecs_payloads_set_m_type(ast_rtp_instance_get_codecs(p->rtp), p->rtp, atoi(iks_find_attrib(codec, "id"))); + ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(p->rtp), p->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); codec = iks_next_tag(codec); } /* Now gather all of the codecs that we are asked for */ - ast_rtp_get_current_formats(p->rtp, &p->peercapability, &peernoncodeccapability); + ast_rtp_codecs_payload_formats(ast_rtp_instance_get_codecs(p->rtp), &p->peercapability, &peernoncodeccapability); p->jointcapability = p->capability & p->peercapability; ast_mutex_unlock(&p->lock); @@ -1277,16 +1274,16 @@ static int gtalk_update_stun(struct gtalk *client, struct gtalk_pvt *p) p->ourcandidates->username); /* Find out the result of the STUN */ - ast_rtp_get_peer(p->rtp, &aux); + ast_rtp_instance_get_remote_address(p->rtp, &aux); /* If the STUN result is different from the IP of the hostname, lock on the stun IP of the hostname advertised by the remote client */ if (aux.sin_addr.s_addr && aux.sin_addr.s_addr != sin.sin_addr.s_addr) - ast_rtp_stun_request(p->rtp, &aux, username); + ast_rtp_instance_stun_request(p->rtp, &aux, username); else - ast_rtp_stun_request(p->rtp, &sin, username); + ast_rtp_instance_stun_request(p->rtp, &sin, username); if (aux.sin_addr.s_addr) { ast_debug(4, "Receiving RTP traffic from IP %s, matches with remote candidate's IP %s\n", ast_inet_ntoa(aux.sin_addr), tmp->ip); @@ -1387,7 +1384,7 @@ static struct ast_frame *gtalk_rtp_read(struct ast_channel *ast, struct gtalk_pv if (!p->rtp) return &ast_null_frame; - f = ast_rtp_read(p->rtp); + f = ast_rtp_instance_read(p->rtp, 0); gtalk_update_stun(p->parent, p); if (p->owner) { /* We already hold the channel lock */ @@ -1438,7 +1435,7 @@ static int gtalk_write(struct ast_channel *ast, struct ast_frame *frame) if (p) { ast_mutex_lock(&p->lock); if (p->rtp) { - res = ast_rtp_write(p->rtp, frame); + res = ast_rtp_instance_write(p->rtp, frame); } ast_mutex_unlock(&p->lock); } @@ -1447,7 +1444,7 @@ static int gtalk_write(struct ast_channel *ast, struct ast_frame *frame) if (p) { ast_mutex_lock(&p->lock); if (p->vrtp) { - res = ast_rtp_write(p->vrtp, frame); + res = ast_rtp_instance_write(p->vrtp, frame); } ast_mutex_unlock(&p->lock); } @@ -2062,7 +2059,7 @@ static int load_module(void) return 0; } - ast_rtp_proto_register(>alk_rtp); + ast_rtp_glue_register(>alk_rtp_glue); ast_cli_register_multiple(gtalk_cli, ARRAY_LEN(gtalk_cli)); /* Make sure we can register our channel type */ @@ -2086,7 +2083,7 @@ static int unload_module(void) ast_cli_unregister_multiple(gtalk_cli, ARRAY_LEN(gtalk_cli)); /* First, take us out of the channel loop */ ast_channel_unregister(>alk_tech); - ast_rtp_proto_unregister(>alk_rtp); + ast_rtp_glue_unregister(>alk_rtp_glue); if (!ast_mutex_lock(>alklock)) { /* Hangup all interfaces if they have an owner */ diff --git a/channels/chan_h323.c b/channels/chan_h323.c index 2342ecfbba..c3e074d143 100644 --- a/channels/chan_h323.c +++ b/channels/chan_h323.c @@ -76,7 +76,7 @@ extern "C" { #include "asterisk/utils.h" #include "asterisk/sched.h" #include "asterisk/io.h" -#include "asterisk/rtp.h" +#include "asterisk/rtp_engine.h" #include "asterisk/acl.h" #include "asterisk/callerid.h" #include "asterisk/cli.h" @@ -161,7 +161,7 @@ struct oh323_pvt { char accountcode[256]; /*!< Account code */ char rdnis[80]; /*!< Referring DNIS, if available */ int amaflags; /*!< AMA Flags */ - struct ast_rtp *rtp; /*!< RTP Session */ + struct ast_rtp_instance *rtp; /*!< RTP Session */ struct ast_dsp *vad; /*!< Used for in-band DTMF detection */ int nativeformats; /*!< Codec formats supported by a channel */ int needhangup; /*!< Send hangup when Asterisk is ready */ @@ -254,7 +254,7 @@ static const struct ast_channel_tech oh323_tech = { .write = oh323_write, .indicate = oh323_indicate, .fixup = oh323_fixup, - .bridge = ast_rtp_bridge, + .bridge = ast_rtp_instance_bridge, }; static const char* redirectingreason2str(int redirectingreason) @@ -381,8 +381,8 @@ static void __oh323_update_info(struct ast_channel *c, struct oh323_pvt *pvt) if (pvt->update_rtp_info > 0) { if (pvt->rtp) { ast_jb_configure(c, &global_jbconf); - ast_channel_set_fd(c, 0, ast_rtp_fd(pvt->rtp)); - ast_channel_set_fd(c, 1, ast_rtcp_fd(pvt->rtp)); + ast_channel_set_fd(c, 0, ast_rtp_instance_fd(pvt->rtp, 0)); + ast_channel_set_fd(c, 1, ast_rtp_instance_fd(pvt->rtp, 1)); ast_queue_frame(pvt->owner, &ast_null_frame); /* Tell Asterisk to apply changes */ } pvt->update_rtp_info = -1; @@ -444,7 +444,7 @@ static void __oh323_destroy(struct oh323_pvt *pvt) AST_SCHED_DEL(sched, pvt->DTMFsched); if (pvt->rtp) { - ast_rtp_destroy(pvt->rtp); + ast_rtp_instance_destroy(pvt->rtp); } /* Free dsp used for in-band DTMF detection */ @@ -510,7 +510,7 @@ static int oh323_digit_begin(struct ast_channel *c, char digit) if (h323debug) { ast_log(LOG_DTMF, "Begin sending out-of-band digit %c on %s\n", digit, c->name); } - ast_rtp_senddigit_begin(pvt->rtp, digit); + ast_rtp_instance_dtmf_begin(pvt->rtp, digit); ast_mutex_unlock(&pvt->lock); } else if (pvt->txDtmfDigit != digit) { /* in-band DTMF */ @@ -549,7 +549,7 @@ static int oh323_digit_end(struct ast_channel *c, char digit, unsigned int durat if (h323debug) { ast_log(LOG_DTMF, "End sending out-of-band digit %c on %s, duration %d\n", digit, c->name, duration); } - ast_rtp_senddigit_end(pvt->rtp, digit); + ast_rtp_instance_dtmf_end(pvt->rtp, digit); ast_mutex_unlock(&pvt->lock); } else { /* in-band DTMF */ @@ -747,11 +747,11 @@ static struct ast_frame *oh323_rtp_read(struct oh323_pvt *pvt) /* Only apply it for the first packet, we just need the correct ip/port */ if (pvt->options.nat) { - ast_rtp_setnat(pvt->rtp, pvt->options.nat); + ast_rtp_instance_set_prop(pvt->rtp, AST_RTP_PROPERTY_NAT, pvt->options.nat); pvt->options.nat = 0; } - f = ast_rtp_read(pvt->rtp); + f = ast_rtp_instance_read(pvt->rtp, 0); /* Don't send RFC2833 if we're not supposed to */ if (f && (f->frametype == AST_FRAME_DTMF) && !(pvt->options.dtmfmode & (H323_DTMF_RFC2833 | H323_DTMF_CISCO))) { return &ast_null_frame; @@ -808,7 +808,7 @@ static struct ast_frame *oh323_read(struct ast_channel *c) break; case 1: if (pvt->rtp) - fr = ast_rtcp_read(pvt->rtp); + fr = ast_rtp_instance_read(pvt->rtp, 1); else fr = &ast_null_frame; break; @@ -842,7 +842,7 @@ static int oh323_write(struct ast_channel *c, struct ast_frame *frame) if (pvt) { ast_mutex_lock(&pvt->lock); if (pvt->rtp && !pvt->recvonly) - res = ast_rtp_write(pvt->rtp, frame); + res = ast_rtp_instance_write(pvt->rtp, frame); __oh323_update_info(c, pvt); ast_mutex_unlock(&pvt->lock); } @@ -910,7 +910,7 @@ static int oh323_indicate(struct ast_channel *c, int condition, const void *data res = 0; break; case AST_CONTROL_SRCUPDATE: - ast_rtp_new_source(pvt->rtp); + ast_rtp_instance_new_source(pvt->rtp); res = 0; break; case AST_CONTROL_PROCEEDING: @@ -946,17 +946,17 @@ static int oh323_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) static int __oh323_rtp_create(struct oh323_pvt *pvt) { - struct in_addr our_addr; + struct sockaddr_in our_addr; if (pvt->rtp) return 0; - if (ast_find_ourip(&our_addr, bindaddr)) { + if (ast_find_ourip(&our_addr.sin_addr, bindaddr)) { ast_mutex_unlock(&pvt->lock); ast_log(LOG_ERROR, "Unable to locate local IP address for RTP stream\n"); return -1; } - pvt->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, our_addr); + pvt->rtp = ast_rtp_instance_new(NULL, sched, &our_addr, NULL); if (!pvt->rtp) { ast_mutex_unlock(&pvt->lock); ast_log(LOG_WARNING, "Unable to create RTP session: %s\n", strerror(errno)); @@ -965,24 +965,24 @@ static int __oh323_rtp_create(struct oh323_pvt *pvt) if (h323debug) ast_debug(1, "Created RTP channel\n"); - ast_rtp_setqos(pvt->rtp, tos, cos, "H323 RTP"); + ast_rtp_instance_set_qos(pvt->rtp, tos, cos, "H323 RTP"); if (h323debug) ast_debug(1, "Setting NAT on RTP to %d\n", pvt->options.nat); - ast_rtp_setnat(pvt->rtp, pvt->options.nat); + ast_rtp_instance_set_prop(pvt->rtp, AST_RTP_PROPERTY_NAT, pvt->options.nat); if (pvt->dtmf_pt[0] > 0) - ast_rtp_set_rtpmap_type(pvt->rtp, pvt->dtmf_pt[0], "audio", "telephone-event", 0); + ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(pvt->rtp), pvt->rtp, pvt->dtmf_pt[0], "audio", "telephone-event", 0); if (pvt->dtmf_pt[1] > 0) - ast_rtp_set_rtpmap_type(pvt->rtp, pvt->dtmf_pt[1], "audio", "cisco-telephone-event", 0); + ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(pvt->rtp), pvt->rtp, pvt->dtmf_pt[1], "audio", "cisco-telephone-event", 0); if (pvt->peercapability) - ast_rtp_codec_setpref(pvt->rtp, &pvt->peer_prefs); + ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(pvt->rtp), pvt->rtp, &pvt->peer_prefs); if (pvt->owner && !ast_channel_trylock(pvt->owner)) { ast_jb_configure(pvt->owner, &global_jbconf); - ast_channel_set_fd(pvt->owner, 0, ast_rtp_fd(pvt->rtp)); - ast_channel_set_fd(pvt->owner, 1, ast_rtcp_fd(pvt->rtp)); + ast_channel_set_fd(pvt->owner, 0, ast_rtp_instance_fd(pvt->rtp, 0)); + ast_channel_set_fd(pvt->owner, 1, ast_rtp_instance_fd(pvt->rtp, 1)); ast_queue_frame(pvt->owner, &ast_null_frame); /* Tell Asterisk to apply changes */ ast_channel_unlock(pvt->owner); } else @@ -1028,13 +1028,13 @@ static struct ast_channel *__oh323_new(struct oh323_pvt *pvt, int state, const c if (!pvt->rtp) __oh323_rtp_create(pvt); #if 0 - ast_channel_set_fd(ch, 0, ast_rtp_fd(pvt->rtp)); - ast_channel_set_fd(ch, 1, ast_rtcp_fd(pvt->rtp)); + ast_channel_set_fd(ch, 0, ast_rtp_instance_fd(pvt->rtp, 0)); + ast_channel_set_fd(ch, 1, ast_rtp_instance_fd(pvt->rtp, 1)); #endif #ifdef VIDEO_SUPPORT if (pvt->vrtp) { - ast_channel_set_fd(ch, 2, ast_rtp_fd(pvt->vrtp)); - ast_channel_set_fd(ch, 3, ast_rtcp_fd(pvt->vrtp)); + ast_channel_set_fd(ch, 2, ast_rtp_instance_fd(pvt->vrtp, 0)); + ast_channel_set_fd(ch, 3, ast_rtp_instance_fd(pvt->vrtp, 1)); } #endif #ifdef T38_SUPPORT @@ -1112,7 +1112,7 @@ static struct oh323_pvt *oh323_alloc(int callid) } if (!pvt->cd.call_token) { ast_log(LOG_ERROR, "Not enough memory to alocate call token\n"); - ast_rtp_destroy(pvt->rtp); + ast_rtp_instance_destroy(pvt->rtp); ast_free(pvt); return NULL; } @@ -1912,7 +1912,7 @@ static struct rtp_info *external_rtp_create(unsigned call_reference, const char return NULL; } /* figure out our local RTP port and tell the H.323 stack about it */ - ast_rtp_get_us(pvt->rtp, &us); + ast_rtp_instance_get_local_address(pvt->rtp, &us); ast_mutex_unlock(&pvt->lock); ast_copy_string(info->addr, ast_inet_ntoa(us.sin_addr), sizeof(info->addr)); @@ -1931,7 +1931,6 @@ static void setup_rtp_connection(unsigned call_reference, const char *remoteIp, { struct oh323_pvt *pvt; struct sockaddr_in them; - struct rtpPayloadType rtptype; int nativeformats_changed; enum { NEED_NONE, NEED_HOLD, NEED_UNHOLD } rtp_change = NEED_NONE; @@ -1953,7 +1952,7 @@ static void setup_rtp_connection(unsigned call_reference, const char *remoteIp, __oh323_rtp_create(pvt); if ((pt == 2) && (pvt->jointcapability & AST_FORMAT_G726_AAL2)) { - ast_rtp_set_rtpmap_type(pvt->rtp, pt, "audio", "G726-32", AST_RTP_OPT_G726_NONSTANDARD); + ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(pvt->rtp), pvt->rtp, pt, "audio", "G726-32", AST_RTP_OPT_G726_NONSTANDARD); } them.sin_family = AF_INET; @@ -1962,13 +1961,13 @@ static void setup_rtp_connection(unsigned call_reference, const char *remoteIp, them.sin_port = htons(remotePort); if (them.sin_addr.s_addr) { - ast_rtp_set_peer(pvt->rtp, &them); + ast_rtp_instance_set_remote_address(pvt->rtp, &them); if (pvt->recvonly) { pvt->recvonly = 0; rtp_change = NEED_UNHOLD; } } else { - ast_rtp_stop(pvt->rtp); + ast_rtp_instance_stop(pvt->rtp); if (!pvt->recvonly) { pvt->recvonly = 1; rtp_change = NEED_HOLD; @@ -1978,7 +1977,7 @@ static void setup_rtp_connection(unsigned call_reference, const char *remoteIp, /* Change native format to reflect information taken from OLC/OLCAck */ nativeformats_changed = 0; if (pt != 128 && pvt->rtp) { /* Payload type is invalid, so try to use previously decided */ - rtptype = ast_rtp_lookup_pt(pvt->rtp, pt); + struct ast_rtp_payload_type rtptype = ast_rtp_codecs_payload_lookup(ast_rtp_instance_get_codecs(pvt->rtp), pt); if (h323debug) ast_debug(1, "Native format is set to %d from %d by RTP payload type %d\n", rtptype.code, pvt->nativeformats, pt); if (pvt->nativeformats != rtptype.code) { @@ -2359,7 +2358,7 @@ static void cleanup_connection(unsigned call_reference, const char *call_token) } if (pvt->rtp) { /* Immediately stop RTP */ - ast_rtp_destroy(pvt->rtp); + ast_rtp_instance_destroy(pvt->rtp); pvt->rtp = NULL; } /* Free dsp used for in-band DTMF detection */ @@ -2421,7 +2420,7 @@ static void set_dtmf_payload(unsigned call_reference, const char *token, int pay return; } if (pvt->rtp) { - ast_rtp_set_rtpmap_type(pvt->rtp, payload, "audio", (is_cisco ? "cisco-telephone-event" : "telephone-event"), 0); + ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(pvt->rtp), pvt->rtp, payload, "audio", (is_cisco ? "cisco-telephone-event" : "telephone-event"), 0); } pvt->dtmf_pt[is_cisco ? 1 : 0] = payload; ast_mutex_unlock(&pvt->lock); @@ -2452,7 +2451,7 @@ static void set_peer_capabilities(unsigned call_reference, const char *token, in } } if (pvt->rtp) - ast_rtp_codec_setpref(pvt->rtp, &pvt->peer_prefs); + ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(pvt->rtp), pvt->rtp, &pvt->peer_prefs); } ast_mutex_unlock(&pvt->lock); } @@ -3113,19 +3112,19 @@ static int reload(void) static struct ast_cli_entry cli_h323_reload = AST_CLI_DEFINE(handle_cli_h323_reload, "Reload H.323 configuration"); -static enum ast_rtp_get_result oh323_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) +static enum ast_rtp_glue_result oh323_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) { struct oh323_pvt *pvt; - enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL; + enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_LOCAL; if (!(pvt = (struct oh323_pvt *)chan->tech_pvt)) - return AST_RTP_GET_FAILED; + return AST_RTP_GLUE_RESULT_FORBID; ast_mutex_lock(&pvt->lock); - *rtp = pvt->rtp; + *instance = pvt->rtp ? ao2_ref(pvt->rtp, +1), pvt->rtp : NULL; #if 0 if (pvt->options.bridge) { - res = AST_RTP_TRY_NATIVE; + res = AST_RTP_GLUE_RESULT_REMOTE; } #endif ast_mutex_unlock(&pvt->lock); @@ -3133,11 +3132,6 @@ static enum ast_rtp_get_result oh323_get_rtp_peer(struct ast_channel *chan, stru return res; } -static enum ast_rtp_get_result oh323_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) -{ - return AST_RTP_GET_FAILED; -} - static char *convertcap(int cap) { switch (cap) { @@ -3165,7 +3159,7 @@ static char *convertcap(int cap) } } -static int oh323_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active) +static int oh323_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *trtp, int codecs, int nat_active) { /* XXX Deal with Video */ struct oh323_pvt *pvt; @@ -3183,19 +3177,18 @@ static int oh323_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, str ast_log(LOG_ERROR, "No Private Structure, this is bad\n"); return -1; } - ast_rtp_get_peer(rtp, &them); - ast_rtp_get_us(rtp, &us); + ast_rtp_instance_get_remote_address(rtp, &them); + ast_rtp_instance_get_local_address(rtp, &us); #if 0 /* Native bridge still isn't ready */ h323_native_bridge(pvt->cd.call_token, ast_inet_ntoa(them.sin_addr), mode); #endif return 0; } -static struct ast_rtp_protocol oh323_rtp = { +static struct ast_rtp_glue oh323_rtp_glue = { .type = "H323", .get_rtp_info = oh323_get_rtp_peer, - .get_vrtp_info = oh323_get_vrtp_peer, - .set_rtp_peer = oh323_set_rtp_peer, + .update_peer = oh323_set_rtp_peer, }; static enum ast_module_load_result load_module(void) @@ -3250,7 +3243,7 @@ static enum ast_module_load_result load_module(void) } ast_cli_register_multiple(cli_h323, sizeof(cli_h323) / sizeof(struct ast_cli_entry)); - ast_rtp_proto_register(&oh323_rtp); + ast_rtp_glue_register(&oh323_rtp_glue); /* Register our callback functions */ h323_callback_register(setup_incoming_call, @@ -3271,7 +3264,7 @@ static enum ast_module_load_result load_module(void) /* start the h.323 listener */ if (h323_start_listener(h323_signalling_port, bindaddr)) { ast_log(LOG_ERROR, "Unable to create H323 listener.\n"); - ast_rtp_proto_unregister(&oh323_rtp); + ast_rtp_glue_unregister(&oh323_rtp_glue); ast_cli_unregister_multiple(cli_h323, sizeof(cli_h323) / sizeof(struct ast_cli_entry)); ast_cli_unregister(&cli_h323_reload); h323_end_process(); @@ -3310,7 +3303,7 @@ static int unload_module(void) ast_cli_unregister(&cli_h323_reload); ast_channel_unregister(&oh323_tech); - ast_rtp_proto_unregister(&oh323_rtp); + ast_rtp_glue_unregister(&oh323_rtp_glue); if (!ast_mutex_lock(&iflock)) { /* hangup all interfaces if they have an owner */ diff --git a/channels/chan_jingle.c b/channels/chan_jingle.c index d239fd717f..e1a60ae7e2 100644 --- a/channels/chan_jingle.c +++ b/channels/chan_jingle.c @@ -53,7 +53,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/pbx.h" #include "asterisk/sched.h" #include "asterisk/io.h" -#include "asterisk/rtp.h" +#include "asterisk/rtp_engine.h" #include "asterisk/acl.h" #include "asterisk/callerid.h" #include "asterisk/file.h" @@ -112,9 +112,9 @@ struct jingle_pvt { char exten[80]; /*!< Called extension */ struct ast_channel *owner; /*!< Master Channel */ char audio_content_name[100]; /*!< name attribute of content tag */ - struct ast_rtp *rtp; /*!< RTP audio session */ + struct ast_rtp_instance *rtp; /*!< RTP audio session */ char video_content_name[100]; /*!< name attribute of content tag */ - struct ast_rtp *vrtp; /*!< RTP video session */ + struct ast_rtp_instance *vrtp; /*!< RTP video session */ int jointcapability; /*!< Supported capability at both ends (codecs ) */ int peercapability; struct jingle_pvt *next; /* Next entity */ @@ -183,11 +183,6 @@ static int jingle_sendhtml(struct ast_channel *ast, int subclass, const char *da static struct jingle_pvt *jingle_alloc(struct jingle *client, const char *from, const char *sid); static char *jingle_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); static char *jingle_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); -/*----- RTP interface functions */ -static int jingle_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, - struct ast_rtp *vrtp, struct ast_rtp *tpeer, int codecs, int nat_active); -static enum ast_rtp_get_result jingle_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); -static int jingle_get_codec(struct ast_channel *chan); /*! \brief PBX interface structure for channel registration */ static const struct ast_channel_tech jingle_tech = { @@ -197,7 +192,7 @@ static const struct ast_channel_tech jingle_tech = { .requester = jingle_request, .send_digit_begin = jingle_digit_begin, .send_digit_end = jingle_digit_end, - .bridge = ast_rtp_bridge, + .bridge = ast_rtp_instance_bridge, .call = jingle_call, .hangup = jingle_hangup, .answer = jingle_answer, @@ -216,15 +211,6 @@ static struct sched_context *sched; /*!< The scheduling context */ static struct io_context *io; /*!< The IO context */ static struct in_addr __ourip; - -/*! \brief RTP driver interface */ -static struct ast_rtp_protocol jingle_rtp = { - type: "Jingle", - get_rtp_info: jingle_get_rtp_peer, - set_rtp_peer: jingle_set_rtp_peer, - get_codec: jingle_get_codec, -}; - static struct ast_cli_entry jingle_cli[] = { AST_CLI_DEFINE(jingle_do_reload, "Reload Jingle configuration"), AST_CLI_DEFINE(jingle_show_channels, "Show Jingle channels"), @@ -304,7 +290,6 @@ static void add_codec_to_answer(const struct jingle_pvt *p, int codec, iks *dcod iks_insert_attrib(payload_g723, "name", "G723"); iks_insert_node(dcodecs, payload_g723); } - ast_rtp_lookup_code(p->rtp, 1, codec); } static int jingle_accept_call(struct jingle *client, struct jingle_pvt *p) @@ -398,18 +383,19 @@ static int jingle_answer(struct ast_channel *ast) return res; } -static enum ast_rtp_get_result jingle_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) +static enum ast_rtp_glue_result jingle_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) { struct jingle_pvt *p = chan->tech_pvt; - enum ast_rtp_get_result res = AST_RTP_GET_FAILED; + enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_FORBID; if (!p) return res; ast_mutex_lock(&p->lock); if (p->rtp) { - *rtp = p->rtp; - res = AST_RTP_TRY_PARTIAL; + ao2_ref(p->rtp, +1); + *instance = p->rtp; + res = AST_RTP_GLUE_RESULT_LOCAL; } ast_mutex_unlock(&p->lock); @@ -422,7 +408,7 @@ static int jingle_get_codec(struct ast_channel *chan) return p->peercapability; } -static int jingle_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *tpeer, int codecs, int nat_active) +static int jingle_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *tpeer, int codecs, int nat_active) { struct jingle_pvt *p; @@ -442,6 +428,13 @@ static int jingle_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, st return 0; } +static struct ast_rtp_glue jingle_rtp_glue = { + .type = "Jingle", + .get_rtp_info = jingle_get_rtp_peer, + .get_codec = jingle_get_codec, + .update_peer = jingle_set_rtp_peer, +}; + static int jingle_response(struct jingle *client, ikspak *pak, const char *reasonstr, const char *reasonstr2) { iks *response = NULL, *error = NULL, *reason = NULL; @@ -621,7 +614,7 @@ static int jingle_create_candidates(struct jingle *client, struct jingle_pvt *p, goto safeout; } - ast_rtp_get_us(p->rtp, &sin); + ast_rtp_instance_get_local_address(p->rtp, &sin); ast_find_ourip(&us, bindaddr); /* Setup our first jingle candidate */ @@ -779,7 +772,7 @@ static struct jingle_pvt *jingle_alloc(struct jingle *client, const char *from, ast_copy_string(tmp->them, idroster, sizeof(tmp->them)); tmp->initiator = 1; } - tmp->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); + tmp->rtp = ast_rtp_instance_new(NULL, sched, &bindaddr, NULL); tmp->parent = client; if (!tmp->rtp) { ast_log(LOG_WARNING, "Out of RTP sessions?\n"); @@ -825,18 +818,18 @@ static struct ast_channel *jingle_new(struct jingle *client, struct jingle_pvt * /* Set Frame packetization */ if (i->rtp) - ast_rtp_codec_setpref(i->rtp, &i->prefs); + ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(i->rtp), i->rtp, &i->prefs); tmp->nativeformats = ast_codec_choose(&i->prefs, what, 1) | (i->jointcapability & AST_FORMAT_VIDEO_MASK); fmt = ast_best_codec(tmp->nativeformats); if (i->rtp) { - ast_channel_set_fd(tmp, 0, ast_rtp_fd(i->rtp)); - ast_channel_set_fd(tmp, 1, ast_rtcp_fd(i->rtp)); + ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(i->rtp, 0)); + ast_channel_set_fd(tmp, 1, ast_rtp_instance_fd(i->rtp, 1)); } if (i->vrtp) { - ast_channel_set_fd(tmp, 2, ast_rtp_fd(i->vrtp)); - ast_channel_set_fd(tmp, 3, ast_rtcp_fd(i->vrtp)); + ast_channel_set_fd(tmp, 2, ast_rtp_instance_fd(i->vrtp, 0)); + ast_channel_set_fd(tmp, 3, ast_rtp_instance_fd(i->vrtp, 1)); } if (state == AST_STATE_RING) tmp->rings = 1; @@ -942,9 +935,9 @@ static void jingle_free_pvt(struct jingle *client, struct jingle_pvt *p) if (p->owner) ast_log(LOG_WARNING, "Uh oh, there's an owner, this is going to be messy.\n"); if (p->rtp) - ast_rtp_destroy(p->rtp); + ast_rtp_instance_destroy(p->rtp); if (p->vrtp) - ast_rtp_destroy(p->vrtp); + ast_rtp_instance_destroy(p->vrtp); jingle_free_candidates(p->theircandidates); ast_free(p); } @@ -1009,8 +1002,8 @@ static int jingle_newcall(struct jingle *client, ikspak *pak) ast_copy_string(p->audio_content_name, iks_find_attrib(content, "name"), sizeof(p->audio_content_name)); while (codec) { - ast_rtp_set_m_type(p->rtp, atoi(iks_find_attrib(codec, "id"))); - ast_rtp_set_rtpmap_type(p->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); + ast_rtp_codecs_payloads_set_m_type(ast_rtp_instance_get_codecs(p->rtp), p->rtp, atoi(iks_find_attrib(codec, "id"))); + ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(p->rtp), p->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); codec = iks_next(codec); } } @@ -1025,8 +1018,8 @@ static int jingle_newcall(struct jingle *client, ikspak *pak) ast_copy_string(p->video_content_name, iks_find_attrib(content, "name"), sizeof(p->video_content_name)); while (codec) { - ast_rtp_set_m_type(p->rtp, atoi(iks_find_attrib(codec, "id"))); - ast_rtp_set_rtpmap_type(p->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); + ast_rtp_codecs_payloads_set_m_type(ast_rtp_instance_get_codecs(p->rtp), p->rtp, atoi(iks_find_attrib(codec, "id"))); + ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(p->rtp), p->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); codec = iks_next(codec); } } @@ -1079,7 +1072,7 @@ static int jingle_update_stun(struct jingle *client, struct jingle_pvt *p) sin.sin_port = htons(tmp->port); snprintf(username, sizeof(username), "%s:%s", tmp->ufrag, p->ourcandidates->ufrag); - ast_rtp_stun_request(p->rtp, &sin, username); + ast_rtp_instance_stun_request(p->rtp, &sin, username); tmp = tmp->next; } return 1; @@ -1169,7 +1162,7 @@ static struct ast_frame *jingle_rtp_read(struct ast_channel *ast, struct jingle_ if (!p->rtp) return &ast_null_frame; - f = ast_rtp_read(p->rtp); + f = ast_rtp_instance_read(p->rtp, 0); jingle_update_stun(p->parent, p); if (p->owner) { /* We already hold the channel lock */ @@ -1220,7 +1213,7 @@ static int jingle_write(struct ast_channel *ast, struct ast_frame *frame) if (p) { ast_mutex_lock(&p->lock); if (p->rtp) { - res = ast_rtp_write(p->rtp, frame); + res = ast_rtp_instance_write(p->rtp, frame); } ast_mutex_unlock(&p->lock); } @@ -1229,7 +1222,7 @@ static int jingle_write(struct ast_channel *ast, struct ast_frame *frame) if (p) { ast_mutex_lock(&p->lock); if (p->vrtp) { - res = ast_rtp_write(p->vrtp, frame); + res = ast_rtp_instance_write(p->vrtp, frame); } ast_mutex_unlock(&p->lock); } @@ -1879,7 +1872,7 @@ static int load_module(void) return 0; } - ast_rtp_proto_register(&jingle_rtp); + ast_rtp_glue_register(&jingle_rtp_glue); ast_cli_register_multiple(jingle_cli, ARRAY_LEN(jingle_cli)); /* Make sure we can register our channel type */ if (ast_channel_register(&jingle_tech)) { @@ -1902,7 +1895,7 @@ static int unload_module(void) ast_cli_unregister_multiple(jingle_cli, ARRAY_LEN(jingle_cli)); /* First, take us out of the channel loop */ ast_channel_unregister(&jingle_tech); - ast_rtp_proto_unregister(&jingle_rtp); + ast_rtp_glue_unregister(&jingle_rtp_glue); if (!ast_mutex_lock(&jinglelock)) { /* Hangup all interfaces if they have an owner */ diff --git a/channels/chan_local.c b/channels/chan_local.c index de161d6afd..e426e10fa3 100644 --- a/channels/chan_local.c +++ b/channels/chan_local.c @@ -39,7 +39,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/pbx.h" #include "asterisk/sched.h" #include "asterisk/io.h" -#include "asterisk/rtp.h" #include "asterisk/acl.h" #include "asterisk/callerid.h" #include "asterisk/file.h" diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c index 1c14829756..cad9d94971 100644 --- a/channels/chan_mgcp.c +++ b/channels/chan_mgcp.c @@ -52,7 +52,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/pbx.h" #include "asterisk/sched.h" #include "asterisk/io.h" -#include "asterisk/rtp.h" +#include "asterisk/rtp_engine.h" #include "asterisk/acl.h" #include "asterisk/callerid.h" #include "asterisk/cli.h" @@ -282,7 +282,7 @@ struct mgcp_subchannel { int id; struct ast_channel *owner; struct mgcp_endpoint *parent; - struct ast_rtp *rtp; + struct ast_rtp_instance *rtp; struct sockaddr_in tmpdest; char txident[80]; /*! \todo FIXME txident is replaced by rqnt_ident in endpoint. This should be obsoleted */ @@ -408,7 +408,7 @@ static int transmit_response(struct mgcp_subchannel *sub, char *msg, struct mgcp static int transmit_notify_request(struct mgcp_subchannel *sub, char *tone); static int transmit_modify_request(struct mgcp_subchannel *sub); static int transmit_notify_request_with_callerid(struct mgcp_subchannel *sub, char *tone, char *callernum, char *callername); -static int transmit_modify_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp *rtp, int codecs); +static int transmit_modify_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp_instance *rtp, int codecs); static int transmit_connection_del(struct mgcp_subchannel *sub); static int transmit_audit_endpoint(struct mgcp_endpoint *p); static void start_rtp(struct mgcp_subchannel *sub); @@ -447,7 +447,7 @@ static const struct ast_channel_tech mgcp_tech = { .fixup = mgcp_fixup, .send_digit_begin = mgcp_senddigit_begin, .send_digit_end = mgcp_senddigit_end, - .bridge = ast_rtp_bridge, + .bridge = ast_rtp_instance_bridge, }; static void mwi_event_cb(const struct ast_event *event, void *userdata) @@ -503,7 +503,7 @@ static int unalloc_sub(struct mgcp_subchannel *sub) sub->alreadygone = 0; memset(&sub->tmpdest, 0, sizeof(sub->tmpdest)); if (sub->rtp) { - ast_rtp_destroy(sub->rtp); + ast_rtp_instance_destroy(sub->rtp); sub->rtp = NULL; } dump_cmd_queues(NULL, sub); /* SC */ @@ -1003,7 +1003,7 @@ static int mgcp_hangup(struct ast_channel *ast) /* Reset temporary destination */ memset(&sub->tmpdest, 0, sizeof(sub->tmpdest)); if (sub->rtp) { - ast_rtp_destroy(sub->rtp); + ast_rtp_instance_destroy(sub->rtp); sub->rtp = NULL; } @@ -1203,7 +1203,7 @@ static struct ast_frame *mgcp_rtp_read(struct mgcp_subchannel *sub) /* Retrieve audio/etc from channel. Assumes sub->lock is already held. */ struct ast_frame *f; - f = ast_rtp_read(sub->rtp); + f = ast_rtp_instance_read(sub->rtp, 0); /* Don't send RFC2833 if we're not supposed to */ if (f && (f->frametype == AST_FRAME_DTMF) && !(sub->parent->dtmfmode & MGCP_DTMF_RFC2833)) return &ast_null_frame; @@ -1261,7 +1261,7 @@ static int mgcp_write(struct ast_channel *ast, struct ast_frame *frame) ast_mutex_lock(&sub->lock); if ((sub->parent->sub == sub) || !sub->parent->singlepath) { if (sub->rtp) { - res = ast_rtp_write(sub->rtp, frame); + res = ast_rtp_instance_write(sub->rtp, frame); } } ast_mutex_unlock(&sub->lock); @@ -1297,7 +1297,7 @@ static int mgcp_senddigit_begin(struct ast_channel *ast, char digit) res = -1; /* Let asterisk play inband indications */ } else if (p->dtmfmode & MGCP_DTMF_RFC2833) { ast_log(LOG_DEBUG, "Sending DTMF using RFC2833"); - ast_rtp_senddigit_begin(sub->rtp, digit); + ast_rtp_instance_dtmf_begin(sub->rtp, digit); } else { ast_log(LOG_ERROR, "Don't know about DTMF_MODE %d\n", p->dtmfmode); } @@ -1324,7 +1324,7 @@ static int mgcp_senddigit_end(struct ast_channel *ast, char digit, unsigned int tmp[2] = digit; tmp[3] = '\0'; transmit_notify_request(sub, tmp); - ast_rtp_senddigit_end(sub->rtp, digit); + ast_rtp_instance_dtmf_end(sub->rtp, digit); } else { ast_log(LOG_ERROR, "Don't know about DTMF_MODE %d\n", p->dtmfmode); } @@ -1453,7 +1453,7 @@ static int mgcp_indicate(struct ast_channel *ast, int ind, const void *data, siz ast_moh_stop(ast); break; case AST_CONTROL_SRCUPDATE: - ast_rtp_new_source(sub->rtp); + ast_rtp_instance_new_source(sub->rtp); break; case -1: transmit_notify_request(sub, ""); @@ -1481,7 +1481,7 @@ static struct ast_channel *mgcp_new(struct mgcp_subchannel *sub, int state) fmt = ast_best_codec(tmp->nativeformats); ast_string_field_build(tmp, name, "MGCP/%s@%s-%d", i->name, i->parent->name, sub->id); if (sub->rtp) - ast_channel_set_fd(tmp, 0, ast_rtp_fd(sub->rtp)); + ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(sub->rtp, 0)); if (i->dtmfmode & (MGCP_DTMF_INBAND | MGCP_DTMF_HYBRID)) { i->dsp = ast_dsp_new(); ast_dsp_set_features(i->dsp, DSP_FEATURE_DIGIT_DETECT); @@ -1874,12 +1874,12 @@ static int process_sdp(struct mgcp_subchannel *sub, struct mgcp_request *req) sin.sin_family = AF_INET; memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr)); sin.sin_port = htons(portno); - ast_rtp_set_peer(sub->rtp, &sin); + ast_rtp_instance_set_remote_address(sub->rtp, &sin); #if 0 printf("Peer RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); #endif /* Scan through the RTP payload types specified in a "m=" line: */ - ast_rtp_pt_clear(sub->rtp); + ast_rtp_codecs_payloads_clear(ast_rtp_instance_get_codecs(sub->rtp), sub->rtp); codecs = ast_strdupa(m + len); while (!ast_strlen_zero(codecs)) { if (sscanf(codecs, "%d%n", &codec, &len) != 1) { @@ -1888,7 +1888,7 @@ static int process_sdp(struct mgcp_subchannel *sub, struct mgcp_request *req) ast_log(LOG_WARNING, "Error in codec string '%s' at '%s'\n", m, codecs); return -1; } - ast_rtp_set_m_type(sub->rtp, codec); + ast_rtp_codecs_payloads_set_m_type(ast_rtp_instance_get_codecs(sub->rtp), sub->rtp, codec); codec_count++; codecs += len; } @@ -1901,11 +1901,11 @@ static int process_sdp(struct mgcp_subchannel *sub, struct mgcp_request *req) if (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) != 2) continue; /* Note: should really look at the 'freq' and '#chans' params too */ - ast_rtp_set_rtpmap_type(sub->rtp, codec, "audio", mimeSubtype, 0); + ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(sub->rtp), sub->rtp, codec, "audio", mimeSubtype, 0); } /* Now gather all of the codecs that were asked for: */ - ast_rtp_get_current_formats(sub->rtp, &peercapability, &peerNonCodecCapability); + ast_rtp_codecs_payload_formats(ast_rtp_instance_get_codecs(sub->rtp), &peercapability, &peerNonCodecCapability); p->capability = capability & peercapability; if (mgcpdebug) { ast_verbose("Capabilities: us - %d, them - %d, combined - %d\n", @@ -2043,7 +2043,7 @@ static int transmit_response(struct mgcp_subchannel *sub, char *msg, struct mgcp } -static int add_sdp(struct mgcp_request *resp, struct mgcp_subchannel *sub, struct ast_rtp *rtp) +static int add_sdp(struct mgcp_request *resp, struct mgcp_subchannel *sub, struct ast_rtp_instance *rtp) { int len; int codec; @@ -2066,9 +2066,9 @@ static int add_sdp(struct mgcp_request *resp, struct mgcp_subchannel *sub, struc ast_log(LOG_WARNING, "No way to add SDP without an RTP structure\n"); return -1; } - ast_rtp_get_us(sub->rtp, &sin); + ast_rtp_instance_get_local_address(sub->rtp, &sin); if (rtp) { - ast_rtp_get_peer(rtp, &dest); + ast_rtp_instance_get_remote_address(sub->rtp, &dest); } else { if (sub->tmpdest.sin_addr.s_addr) { dest.sin_addr = sub->tmpdest.sin_addr; @@ -2094,11 +2094,11 @@ static int add_sdp(struct mgcp_request *resp, struct mgcp_subchannel *sub, struc if (mgcpdebug) { ast_verbose("Answering with capability %d\n", x); } - codec = ast_rtp_lookup_code(sub->rtp, 1, x); + codec = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(sub->rtp), 1, x); if (codec > -1) { snprintf(costr, sizeof(costr), " %d", codec); strncat(m, costr, sizeof(m) - strlen(m) - 1); - snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(1, x, 0)); + snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype2(1, x, 0)); strncat(a, costr, sizeof(a) - strlen(a) - 1); } } @@ -2108,11 +2108,11 @@ static int add_sdp(struct mgcp_request *resp, struct mgcp_subchannel *sub, struc if (mgcpdebug) { ast_verbose("Answering with non-codec capability %d\n", x); } - codec = ast_rtp_lookup_code(sub->rtp, 0, x); + codec = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(sub->rtp), 0, x); if (codec > -1) { snprintf(costr, sizeof(costr), " %d", codec); strncat(m, costr, sizeof(m) - strlen(m) - 1); - snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(0, x, 0)); + snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype2(0, x, 0)); strncat(a, costr, sizeof(a) - strlen(a) - 1); if (x == AST_RTP_DTMF) { /* Indicate we support DTMF... Not sure about 16, @@ -2136,7 +2136,7 @@ static int add_sdp(struct mgcp_request *resp, struct mgcp_subchannel *sub, struc return 0; } -static int transmit_modify_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp *rtp, int codecs) +static int transmit_modify_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp_instance *rtp, int codecs) { struct mgcp_request resp; char local[256]; @@ -2147,13 +2147,13 @@ static int transmit_modify_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp if (ast_strlen_zero(sub->cxident) && rtp) { /* We don't have a CXident yet, store the destination and wait a bit */ - ast_rtp_get_peer(rtp, &sub->tmpdest); + ast_rtp_instance_get_remote_address(rtp, &sub->tmpdest); return 0; } ast_copy_string(local, "p:20", sizeof(local)); for (x = 1; x <= AST_FORMAT_AUDIO_MASK; x <<= 1) { if (p->capability & x) { - snprintf(tmp, sizeof(tmp), ", a:%s", ast_rtp_lookup_mime_subtype(1, x, 0)); + snprintf(tmp, sizeof(tmp), ", a:%s", ast_rtp_lookup_mime_subtype2(1, x, 0)); strncat(local, tmp, sizeof(local) - strlen(local) - 1); } } @@ -2172,7 +2172,7 @@ static int transmit_modify_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp return send_request(p, sub, &resp, oseq); /* SC */ } -static int transmit_connect_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp *rtp) +static int transmit_connect_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp_instance *rtp) { struct mgcp_request resp; char local[256]; @@ -2183,7 +2183,7 @@ static int transmit_connect_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp ast_copy_string(local, "p:20", sizeof(local)); for (x = 1; x <= AST_FORMAT_AUDIO_MASK; x <<= 1) { if (p->capability & x) { - snprintf(tmp, sizeof(tmp), ", a:%s", ast_rtp_lookup_mime_subtype(1, x, 0)); + snprintf(tmp, sizeof(tmp), ", a:%s", ast_rtp_lookup_mime_subtype2(1, x, 0)); strncat(local, tmp, sizeof(local) - strlen(local) - 1); } } @@ -2611,21 +2611,17 @@ static void start_rtp(struct mgcp_subchannel *sub) ast_mutex_lock(&sub->lock); /* check again to be on the safe side */ if (sub->rtp) { - ast_rtp_destroy(sub->rtp); + ast_rtp_instance_destroy(sub->rtp); sub->rtp = NULL; } /* Allocate the RTP now */ - sub->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); + sub->rtp = ast_rtp_instance_new(NULL, sched, &bindaddr, NULL); if (sub->rtp && sub->owner) - ast_channel_set_fd(sub->owner, 0, ast_rtp_fd(sub->rtp)); + ast_channel_set_fd(sub->owner, 0, ast_rtp_instance_fd(sub->rtp, 0)); if (sub->rtp) { - ast_rtp_setqos(sub->rtp, qos.tos_audio, qos.cos_audio, "MGCP RTP"); - ast_rtp_setnat(sub->rtp, sub->nat); + ast_rtp_instance_set_qos(sub->rtp, qos.tos_audio, qos.cos_audio, "MGCP RTP"); + ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_NAT, sub->nat); } -#if 0 - ast_rtp_set_callback(p->rtp, rtpready); - ast_rtp_set_data(p->rtp, p); -#endif /* Make a call*ID */ snprintf(sub->callid, sizeof(sub->callid), "%08lx%s", ast_random(), sub->txident); /* Transmit the connection create */ @@ -3940,22 +3936,22 @@ static struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v) return (gw_reload ? NULL : gw); } -static enum ast_rtp_get_result mgcp_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) +static enum ast_rtp_glue_result mgcp_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) { struct mgcp_subchannel *sub = NULL; if (!(sub = chan->tech_pvt) || !(sub->rtp)) - return AST_RTP_GET_FAILED; + return AST_RTP_GLUE_RESULT_FORBID; - *rtp = sub->rtp; + *instance = sub->rtp ? ao2_ref(sub->rtp, +1), sub->rtp : NULL; if (sub->parent->canreinvite) - return AST_RTP_TRY_NATIVE; + return AST_RTP_GLUE_RESULT_REMOTE; else - return AST_RTP_TRY_PARTIAL; + return AST_RTP_GLUE_RESULT_LOCAL; } -static int mgcp_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active) +static int mgcp_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *trtp, int codecs, int nat_active) { /* XXX Is there such thing as video support with MGCP? XXX */ struct mgcp_subchannel *sub; @@ -3967,10 +3963,10 @@ static int mgcp_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, stru return -1; } -static struct ast_rtp_protocol mgcp_rtp = { +static struct ast_rtp_glue mgcp_rtp_glue = { .type = "MGCP", .get_rtp_info = mgcp_get_rtp_peer, - .set_rtp_peer = mgcp_set_rtp_peer, + .update_peer = mgcp_set_rtp_peer, }; static void destroy_endpoint(struct mgcp_endpoint *e) @@ -3984,7 +3980,7 @@ static void destroy_endpoint(struct mgcp_endpoint *e) transmit_connection_del(sub); } if (sub->rtp) { - ast_rtp_destroy(sub->rtp); + ast_rtp_instance_destroy(sub->rtp); sub->rtp = NULL; } memset(sub->magic, 0, sizeof(sub->magic)); @@ -4276,7 +4272,7 @@ static int load_module(void) return AST_MODULE_LOAD_FAILURE; } - ast_rtp_proto_register(&mgcp_rtp); + ast_rtp_glue_register(&mgcp_rtp_glue); ast_cli_register_multiple(cli_mgcp, sizeof(cli_mgcp) / sizeof(struct ast_cli_entry)); /* And start the monitor for the first time */ @@ -4379,7 +4375,7 @@ static int unload_module(void) } close(mgcpsock); - ast_rtp_proto_unregister(&mgcp_rtp); + ast_rtp_glue_unregister(&mgcp_rtp_glue); ast_cli_unregister_multiple(cli_mgcp, sizeof(cli_mgcp) / sizeof(struct ast_cli_entry)); sched_context_destroy(sched); diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 8afe7766a2..4d0f06f4ad 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -229,7 +229,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/pbx.h" #include "asterisk/sched.h" #include "asterisk/io.h" -#include "asterisk/rtp.h" +#include "asterisk/rtp_engine.h" #include "asterisk/udptl.h" #include "asterisk/acl.h" #include "asterisk/manager.h" @@ -271,6 +271,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/ast_version.h" #include "asterisk/event.h" #include "asterisk/tcptls.h" +#include "asterisk/stun.h" /*** DOCUMENTATION <application name="SIPDtmfMode" language="en_US"> @@ -691,6 +692,7 @@ enum check_auth_result { AUTH_PEER_NOT_DYNAMIC = -6, AUTH_ACL_FAILED = -7, AUTH_BAD_TRANSPORT = -8, + AUTH_RTP_FAILED = 9, }; /*! \brief States for outbound registrations (with register= lines in sip.conf */ @@ -1011,6 +1013,7 @@ static const struct cfsip_options { #define DEFAULT_USERAGENT "Asterisk PBX" /*!< Default Useragent: header unless re-defined in sip.conf */ #define DEFAULT_SDPSESSION "Asterisk PBX" /*!< Default SDP session name, (s=) header unless re-defined in sip.conf */ #define DEFAULT_SDPOWNER "root" /*!< Default SDP username field in (o=) header unless re-defined in sip.conf */ +#define DEFAULT_ENGINE "asterisk" /*!< Default RTP engine to use for sessions */ #endif /*@}*/ @@ -1029,6 +1032,7 @@ static char default_mohinterpret[MAX_MUSICCLASS]; /*!< Global setting for moh c static char default_mohsuggest[MAX_MUSICCLASS]; /*!< Global setting for moh class to suggest when putting * a bridged channel on hold */ static char default_parkinglot[AST_MAX_CONTEXT]; /*!< Parkinglot */ +static char default_engine[256]; /*!< Default RTP engine */ static int default_maxcallbitrate; /*!< Maximum bitrate for call */ static struct ast_codec_pref default_prefs; /*!< Default codec prefs */ static unsigned int default_transports; /*!< Default Transports (enum sip_transport) that are acceptable */ @@ -1611,6 +1615,7 @@ struct sip_pvt { AST_STRING_FIELD(rpid_from); /*!< Our RPID From header */ AST_STRING_FIELD(url); /*!< URL to be sent with next message to peer */ AST_STRING_FIELD(parkinglot); /*!< Parkinglot */ + AST_STRING_FIELD(engine); /*!< RTP engine to use */ ); char via[128]; /*!< Via: header */ struct sip_socket socket; /*!< The socket used for this dialog */ @@ -1699,9 +1704,9 @@ struct sip_pvt { struct sip_peer *relatedpeer; /*!< If this dialog is related to a peer, which one Used in peerpoke, mwi subscriptions */ struct sip_registry *registry; /*!< If this is a REGISTER dialog, to which registry */ - struct ast_rtp *rtp; /*!< RTP Session */ - struct ast_rtp *vrtp; /*!< Video RTP session */ - struct ast_rtp *trtp; /*!< Text RTP session */ + struct ast_rtp_instance *rtp; /*!< RTP Session */ + struct ast_rtp_instance *vrtp; /*!< Video RTP session */ + struct ast_rtp_instance *trtp; /*!< Text RTP session */ struct sip_pkt *packets; /*!< Packets scheduled for re-transmission */ struct sip_history_head *history; /*!< History of this SIP dialog */ size_t history_entries; /*!< Number of entires in the history */ @@ -1844,6 +1849,7 @@ struct sip_peer { AST_STRING_FIELD(mohsuggest); /*!< Music on Hold class */ AST_STRING_FIELD(parkinglot); /*!< Parkinglot */ AST_STRING_FIELD(useragent); /*!< User agent in SIP request (saved from registration) */ + AST_STRING_FIELD(engine); /*!< RTP Engine to use */ ); struct sip_socket socket; /*!< Socket used for this peer */ unsigned int transports:3; /*!< Transports (enum sip_transport) that are acceptable for this peer */ @@ -2564,14 +2570,6 @@ static void handle_response_subscribe(struct sip_pvt *p, int resp, char *rest, s static int handle_response_register(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno); static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno); -/*----- RTP interface functions */ -static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active); -static enum ast_rtp_get_result sip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); -static enum ast_rtp_get_result sip_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); -static enum ast_rtp_get_result sip_get_trtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); -static int sip_get_codec(struct ast_channel *chan); -static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p, int *faxdetect); - /*------ T38 Support --------- */ static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans); static struct ast_udptl *sip_get_udptl_peer(struct ast_channel *chan); @@ -2592,6 +2590,9 @@ static enum st_refresher st_get_refresher(struct sip_pvt *); static enum st_mode st_get_mode(struct sip_pvt *); static struct sip_st_dlg* sip_st_alloc(struct sip_pvt *const p); +/*------- RTP Glue functions -------- */ +static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *instance, struct ast_rtp_instance *vinstance, struct ast_rtp_instance *tinstance, int codecs, int nat_active); + /*!--- SIP MWI Subscription support */ static int sip_subscribe_mwi(const char *value, int lineno); static void sip_subscribe_mwi_destroy(struct sip_subscription_mwi *mwi); @@ -2620,8 +2621,8 @@ static const struct ast_channel_tech sip_tech = { .fixup = sip_fixup, /* called with chan locked */ .send_digit_begin = sip_senddigit_begin, /* called with chan unlocked */ .send_digit_end = sip_senddigit_end, - .bridge = ast_rtp_bridge, /* XXX chan unlocked ? */ - .early_bridge = ast_rtp_early_bridge, + .bridge = ast_rtp_instance_bridge, /* XXX chan unlocked ? */ + .early_bridge = ast_rtp_instance_early_bridge, .send_text = sip_sendtext, /* called with chan locked */ .func_channel_read = acf_channel_read, .queryoption = sip_queryoption, @@ -2694,17 +2695,6 @@ static int map_s_x(const struct _map_x_s *table, const char *s, int errorvalue) return errorvalue; } - -/*! \brief Interface structure with callbacks used to connect to RTP module */ -static struct ast_rtp_protocol sip_rtp = { - .type = "SIP", - .get_rtp_info = sip_get_rtp_peer, - .get_vrtp_info = sip_get_vrtp_peer, - .get_trtp_info = sip_get_trtp_peer, - .set_rtp_peer = sip_set_rtp_peer, - .get_codec = sip_get_codec, -}; - /*! * duplicate a list of channel variables, \return the copy. */ @@ -4593,11 +4583,11 @@ static void do_setnat(struct sip_pvt *p, int natflags) if (p->rtp) { ast_debug(1, "Setting NAT on RTP to %s\n", mode); - ast_rtp_setnat(p->rtp, natflags); + ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_NAT, natflags); } if (p->vrtp) { ast_debug(1, "Setting NAT on VRTP to %s\n", mode); - ast_rtp_setnat(p->vrtp, natflags); + ast_rtp_instance_set_prop(p->vrtp, AST_RTP_PROPERTY_NAT, natflags); } if (p->udptl) { ast_debug(1, "Setting NAT on UDPTL to %s\n", mode); @@ -4605,7 +4595,7 @@ static void do_setnat(struct sip_pvt *p, int natflags) } if (p->trtp) { ast_debug(1, "Setting NAT on TRTP to %s\n", mode); - ast_rtp_setnat(p->trtp, natflags); + ast_rtp_instance_set_prop(p->trtp, AST_RTP_PROPERTY_NAT, natflags); } } @@ -4697,6 +4687,51 @@ static void copy_socket_data(struct sip_socket *to_sock, const struct sip_socket *to_sock = *from_sock; } +/*! \brief Initialize RTP portion of a dialog + * \returns -1 on failure, 0 on success + */ +static int dialog_initialize_rtp(struct sip_pvt *dialog) +{ + if (!sip_methods[dialog->method].need_rtp) { + return 0; + } + + if (!(dialog->rtp = ast_rtp_instance_new(dialog->engine, sched, &bindaddr, NULL))) { + return -1; + } + + if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT) && (dialog->capability & AST_FORMAT_VIDEO_MASK)) { + if (!(dialog->vrtp = ast_rtp_instance_new(dialog->engine, sched, &bindaddr, NULL))) { + return -1; + } + ast_rtp_instance_set_timeout(dialog->vrtp, global_rtptimeout); + ast_rtp_instance_set_hold_timeout(dialog->vrtp, global_rtpholdtimeout); + + ast_rtp_instance_set_prop(dialog->vrtp, AST_RTP_PROPERTY_RTCP, 1); + } + + if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_TEXTSUPPORT)) { + if (!(dialog->trtp = ast_rtp_instance_new(dialog->engine, sched, &bindaddr, NULL))) { + return -1; + } + ast_rtp_instance_set_timeout(dialog->trtp, global_rtptimeout); + ast_rtp_instance_set_hold_timeout(dialog->trtp, global_rtpholdtimeout); + + ast_rtp_instance_set_prop(dialog->trtp, AST_RTP_PROPERTY_RTCP, 1); + } + + ast_rtp_instance_set_timeout(dialog->rtp, global_rtptimeout); + ast_rtp_instance_set_hold_timeout(dialog->rtp, global_rtpholdtimeout); + + ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_RTCP, 1); + ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); + ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); + + ast_rtp_instance_set_qos(dialog->rtp, global_tos_audio, 0, "SIP RTP"); + + return 0; +} + /*! \brief Create address structure from peer reference. * This function copies data from peer to the dialog, so we don't have to look up the peer * again from memory or database during the life time of the dialog. @@ -4724,17 +4759,6 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer) ast_copy_flags(&dialog->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY); ast_copy_flags(&dialog->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY); dialog->capability = peer->capability; - if (!ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT_ALWAYS) && - (!ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT) || - !(dialog->capability & AST_FORMAT_VIDEO_MASK)) && - dialog->vrtp) { - ast_rtp_destroy(dialog->vrtp); - dialog->vrtp = NULL; - } - if (!ast_test_flag(&dialog->flags[1], SIP_PAGE2_TEXTSUPPORT) && dialog->trtp) { - ast_rtp_destroy(dialog->trtp); - dialog->trtp = NULL; - } dialog->prefs = peer->prefs; if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_T38SUPPORT)) { if (!dialog->udptl) { @@ -4750,29 +4774,28 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer) } do_setnat(dialog, ast_test_flag(&dialog->flags[0], SIP_NAT) & SIP_NAT_ROUTE); + ast_string_field_set(dialog, engine, peer->engine); + + if (dialog_initialize_rtp(dialog)) { + return -1; + } + if (dialog->rtp) { /* Audio */ - ast_rtp_setdtmf(dialog->rtp, ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); - ast_rtp_setdtmfcompensate(dialog->rtp, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); - ast_rtp_set_rtptimeout(dialog->rtp, peer->rtptimeout); - ast_rtp_set_rtpholdtimeout(dialog->rtp, peer->rtpholdtimeout); - ast_rtp_set_rtpkeepalive(dialog->rtp, peer->rtpkeepalive); + ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); + ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); + ast_rtp_instance_set_timeout(dialog->rtp, peer->rtptimeout); + ast_rtp_instance_set_hold_timeout(dialog->rtp, peer->rtpholdtimeout); /* Set Frame packetization */ - ast_rtp_codec_setpref(dialog->rtp, &dialog->prefs); + ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(dialog->rtp), dialog->rtp, &dialog->prefs); dialog->autoframing = peer->autoframing; } if (dialog->vrtp) { /* Video */ - ast_rtp_setdtmf(dialog->vrtp, 0); - ast_rtp_setdtmfcompensate(dialog->vrtp, 0); - ast_rtp_set_rtptimeout(dialog->vrtp, peer->rtptimeout); - ast_rtp_set_rtpholdtimeout(dialog->vrtp, peer->rtpholdtimeout); - ast_rtp_set_rtpkeepalive(dialog->vrtp, peer->rtpkeepalive); + ast_rtp_instance_set_timeout(dialog->vrtp, peer->rtptimeout); + ast_rtp_instance_set_hold_timeout(dialog->vrtp, peer->rtpholdtimeout); } if (dialog->trtp) { /* Realtime text */ - ast_rtp_setdtmf(dialog->trtp, 0); - ast_rtp_setdtmfcompensate(dialog->trtp, 0); - ast_rtp_set_rtptimeout(dialog->trtp, peer->rtptimeout); - ast_rtp_set_rtpholdtimeout(dialog->trtp, peer->rtpholdtimeout); - ast_rtp_set_rtpkeepalive(dialog->trtp, peer->rtpkeepalive); + ast_rtp_instance_set_timeout(dialog->trtp, peer->rtptimeout); + ast_rtp_instance_set_hold_timeout(dialog->trtp, peer->rtpholdtimeout); } ast_string_field_set(dialog, peername, peer->name); @@ -4786,6 +4809,7 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer) ast_string_field_set(dialog, fullcontact, peer->fullcontact); ast_string_field_set(dialog, context, peer->context); ast_string_field_set(dialog, parkinglot, peer->parkinglot); + ast_string_field_set(dialog, engine, peer->engine); ref_proxy(dialog, obproxy_get(dialog, peer)); dialog->callgroup = peer->callgroup; dialog->pickupgroup = peer->pickupgroup; @@ -4881,6 +4905,10 @@ static int create_addr(struct sip_pvt *dialog, const char *opeer, struct sockadd return res; } + if (dialog_initialize_rtp(dialog)) { + return -1; + } + do_setnat(dialog, ast_test_flag(&dialog->flags[0], SIP_NAT) & SIP_NAT_ROUTE); ast_string_field_set(dialog, tohost, peername); @@ -5155,15 +5183,13 @@ static void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist) p->notify_headers = NULL; } if (p->rtp) { - ast_rtp_destroy(p->rtp); + ast_rtp_instance_destroy(p->rtp); } if (p->vrtp) { - ast_rtp_destroy(p->vrtp); + ast_rtp_instance_destroy(p->vrtp); } if (p->trtp) { - while (ast_rtp_get_bridged(p->trtp)) - usleep(1); - ast_rtp_destroy(p->trtp); + ast_rtp_instance_destroy(p->trtp); } if (p->udptl) ast_udptl_destroy(p->udptl); @@ -5682,42 +5708,50 @@ static int sip_hangup(struct ast_channel *ast) if (!p->pendinginvite) { struct ast_channel *bridge = ast_bridged_channel(oldowner); - char *audioqos = ""; - char *videoqos = ""; - char *textqos = ""; + char quality_buf[AST_MAX_USER_FIELD], *quality; - if (p->rtp) - ast_rtp_set_vars(oldowner, p->rtp); + if (p->rtp) { + ast_rtp_instance_set_stats_vars(oldowner, p->rtp); + } if (bridge) { struct sip_pvt *q = bridge->tech_pvt; - if (IS_SIP_TECH(bridge->tech) && q) - ast_rtp_set_vars(bridge, q->rtp); + if (IS_SIP_TECH(bridge->tech) && q) { + ast_rtp_instance_set_stats_vars(bridge, q->rtp); + } + } + + if (p->do_history || oldowner) { + if (p->rtp && (quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) { + if (p->do_history) { + append_history(p, "RTCPaudio", "Quality:%s", quality); + } + if (oldowner) { + pbx_builtin_setvar_helper(oldowner, "RTPAUDIOQOS", quality); + } + } + if (p->vrtp && (quality = ast_rtp_instance_get_quality(p->vrtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) { + if (p->do_history) { + append_history(p, "RTCPvideo", "Quality:%s", quality); + } + if (oldowner) { + pbx_builtin_setvar_helper(oldowner, "RTPVIDEOQOS", quality); + } + } + if (p->trtp && (quality = ast_rtp_instance_get_quality(p->trtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) { + if (p->do_history) { + append_history(p, "RTCPtext", "Quality:%s", quality); + } + if (oldowner) { + pbx_builtin_setvar_helper(oldowner, "RTPTEXTQOS", quality); + } + } } - if (p->vrtp) - videoqos = ast_rtp_get_quality(p->vrtp, NULL, RTPQOS_SUMMARY); - if (p->trtp) - textqos = ast_rtp_get_quality(p->trtp, NULL, RTPQOS_SUMMARY); /* Send a hangup */ transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1); - /* Get RTCP quality before end of call */ - if (p->do_history) { - if (p->rtp) - append_history(p, "RTCPaudio", "Quality:%s", audioqos); - if (p->vrtp) - append_history(p, "RTCPvideo", "Quality:%s", videoqos); - if (p->trtp) - append_history(p, "RTCPtext", "Quality:%s", textqos); - } - if (p->rtp && oldowner) - pbx_builtin_setvar_helper(oldowner, "RTPAUDIOQOS", audioqos); - if (p->vrtp && oldowner) - pbx_builtin_setvar_helper(oldowner, "RTPVIDEOQOS", videoqos); - if (p->trtp && oldowner) - pbx_builtin_setvar_helper(oldowner, "RTPTEXTQOS", textqos); } else { /* Note we will need a BYE when this all settles out but we can't send one while we have "INVITE" outstanding. */ @@ -5772,7 +5806,10 @@ static int sip_answer(struct ast_channel *ast) ast_setstate(ast, AST_STATE_UP); ast_debug(1, "SIP answering channel: %s\n", ast->name); - ast_rtp_new_source(p->rtp); + if (p->t38.state == T38_PEER_DIRECT) { + change_t38_state(p, T38_ENABLED); + } + ast_rtp_instance_new_source(p->rtp); res = transmit_response_with_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL, FALSE); ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); } @@ -5807,7 +5844,7 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame) if ((ast->_state != AST_STATE_UP) && !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) && !ast_test_flag(&p->flags[0], SIP_OUTGOING)) { - ast_rtp_new_source(p->rtp); + ast_rtp_instance_new_source(p->rtp); p->invitestate = INV_EARLY_MEDIA; transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE); ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); @@ -5816,7 +5853,7 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame) transmit_reinvite_with_sdp(p, FALSE, FALSE); } else { p->lastrtptx = time(NULL); - res = ast_rtp_write(p->rtp, frame); + res = ast_rtp_instance_write(p->rtp, frame); } } sip_pvt_unlock(p); @@ -5835,7 +5872,7 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame) ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); } p->lastrtptx = time(NULL); - res = ast_rtp_write(p->vrtp, frame); + res = ast_rtp_instance_write(p->vrtp, frame); } sip_pvt_unlock(p); } @@ -5844,7 +5881,7 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame) if (p) { sip_pvt_lock(p); if (p->red) { - ast_red_buffer_t140(p->trtp, frame); + ast_rtp_red_buffer(p->trtp, frame); } else { if (p->trtp) { /* Activate text early media */ @@ -5856,7 +5893,7 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame) ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); } p->lastrtptx = time(NULL); - res = ast_rtp_write(p->trtp, frame); + res = ast_rtp_instance_write(p->trtp, frame); } } sip_pvt_unlock(p); @@ -5944,11 +5981,15 @@ static int sip_senddigit_begin(struct ast_channel *ast, char digit) sip_pvt_lock(p); switch (ast_test_flag(&p->flags[0], SIP_DTMF)) { case SIP_DTMF_INBAND: - res = -1; /* Tell Asterisk to generate inband indications */ + if (p->rtp && ast_rtp_instance_dtmf_mode_get(p->rtp) == AST_RTP_DTMF_MODE_INBAND) { + ast_rtp_instance_dtmf_begin(p->rtp, digit); + } else { + res = -1; /* Tell Asterisk to generate inband indications */ + } break; case SIP_DTMF_RFC2833: if (p->rtp) - ast_rtp_senddigit_begin(p->rtp, digit); + ast_rtp_instance_dtmf_begin(p->rtp, digit); break; default: break; @@ -5973,10 +6014,14 @@ static int sip_senddigit_end(struct ast_channel *ast, char digit, unsigned int d break; case SIP_DTMF_RFC2833: if (p->rtp) - ast_rtp_senddigit_end(p->rtp, digit); + ast_rtp_instance_dtmf_end(p->rtp, digit); break; case SIP_DTMF_INBAND: - res = -1; /* Tell Asterisk to stop inband indications */ + if (p->rtp && ast_rtp_instance_dtmf_mode_get(p->rtp) == AST_RTP_DTMF_MODE_INBAND) { + ast_rtp_instance_dtmf_end(p->rtp, digit); + } else { + res = -1; /* Tell Asterisk to stop inband indications */ + } break; } sip_pvt_unlock(p); @@ -6071,11 +6116,11 @@ static int sip_indicate(struct ast_channel *ast, int condition, const void *data res = -1; break; case AST_CONTROL_HOLD: - ast_rtp_new_source(p->rtp); + ast_rtp_instance_new_source(p->rtp); ast_moh_start(ast, data, p->mohinterpret); break; case AST_CONTROL_UNHOLD: - ast_rtp_new_source(p->rtp); + ast_rtp_instance_new_source(p->rtp); ast_moh_stop(ast); break; case AST_CONTROL_VIDUPDATE: /* Request a video frame update */ @@ -6121,7 +6166,7 @@ static int sip_indicate(struct ast_channel *ast, int condition, const void *data } break; case AST_CONTROL_SRCUPDATE: - ast_rtp_new_source(p->rtp); + ast_rtp_instance_new_source(p->rtp); break; case -1: res = -1; @@ -6235,23 +6280,29 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit ast_debug(3, "This channel will not be able to handle video.\n"); if ((ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) || (ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) { - i->vad = ast_dsp_new(); - ast_dsp_set_features(i->vad, DSP_FEATURE_DIGIT_DETECT); - if (global_relaxdtmf) - ast_dsp_set_digitmode(i->vad, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF); + if (!i->rtp || ast_rtp_instance_dtmf_mode_set(i->rtp, AST_RTP_DTMF_MODE_INBAND)) { + i->vad = ast_dsp_new(); + ast_dsp_set_features(i->vad, DSP_FEATURE_DIGIT_DETECT); + if (global_relaxdtmf) + ast_dsp_set_digitmode(i->vad, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF); + } + } else if (ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) { + if (i->rtp) { + ast_rtp_instance_dtmf_mode_set(i->rtp, AST_RTP_DTMF_MODE_RFC2833); + } } /* Set file descriptors for audio, video, realtime text and UDPTL as needed */ if (i->rtp) { - ast_channel_set_fd(tmp, 0, ast_rtp_fd(i->rtp)); - ast_channel_set_fd(tmp, 1, ast_rtcp_fd(i->rtp)); + ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(i->rtp, 0)); + ast_channel_set_fd(tmp, 1, ast_rtp_instance_fd(i->rtp, 1)); } if (needvideo && i->vrtp) { - ast_channel_set_fd(tmp, 2, ast_rtp_fd(i->vrtp)); - ast_channel_set_fd(tmp, 3, ast_rtcp_fd(i->vrtp)); + ast_channel_set_fd(tmp, 2, ast_rtp_instance_fd(i->vrtp, 0)); + ast_channel_set_fd(tmp, 3, ast_rtp_instance_fd(i->vrtp, 1)); } if (needtext && i->trtp) - ast_channel_set_fd(tmp, 4, ast_rtp_fd(i->trtp)); + ast_channel_set_fd(tmp, 4, ast_rtp_instance_fd(i->trtp, 0)); if (i->udptl) ast_channel_set_fd(tmp, 5, ast_udptl_fd(i->udptl)); @@ -6475,19 +6526,19 @@ static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p switch(ast->fdno) { case 0: - f = ast_rtp_read(p->rtp); /* RTP Audio */ + f = ast_rtp_instance_read(p->rtp, 0); /* RTP Audio */ break; case 1: - f = ast_rtcp_read(p->rtp); /* RTCP Control Channel */ + f = ast_rtp_instance_read(p->rtp, 1); /* RTCP Control Channel */ break; case 2: - f = ast_rtp_read(p->vrtp); /* RTP Video */ + f = ast_rtp_instance_read(p->vrtp, 0); /* RTP Video */ break; case 3: - f = ast_rtcp_read(p->vrtp); /* RTCP Control Channel for video */ + f = ast_rtp_instance_read(p->vrtp, 1); /* RTCP Control Channel for video */ break; case 4: - f = ast_rtp_read(p->trtp); /* RTP Text */ + f = ast_rtp_instance_read(p->trtp, 0); /* RTP Text */ if (sipdebug_text) { int i; unsigned char* arr = f->data.ptr; @@ -6694,50 +6745,11 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si p->ocseq = INITIAL_CSEQ; if (sip_methods[intended_method].need_rtp) { - p->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); - /* If the global videosupport flag is on, we always create a RTP interface for video */ - if (ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT)) - p->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); - if (ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT)) - p->trtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); - if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) - p->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr); - if (!p->rtp|| (ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) && !p->vrtp) - || (ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT) && !p->trtp)) { - ast_log(LOG_WARNING, "Unable to create RTP audio %s%ssession: %s\n", - ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "and video " : "", - ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT) ? "and text " : "", strerror(errno)); - if (p->chanvars) { - ast_variables_destroy(p->chanvars); - p->chanvars = NULL; - } - ao2_t_ref(p, -1, "failed to create RTP audio session, drop p"); - return NULL; - } - ast_rtp_setqos(p->rtp, global_tos_audio, global_cos_audio, "SIP RTP"); - ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); - ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); - ast_rtp_set_rtptimeout(p->rtp, global_rtptimeout); - ast_rtp_set_rtpholdtimeout(p->rtp, global_rtpholdtimeout); - ast_rtp_set_rtpkeepalive(p->rtp, global_rtpkeepalive); - if (p->vrtp) { - ast_rtp_setqos(p->vrtp, global_tos_video, global_cos_video, "SIP VRTP"); - ast_rtp_setdtmf(p->vrtp, 0); - ast_rtp_setdtmfcompensate(p->vrtp, 0); - ast_rtp_set_rtptimeout(p->vrtp, global_rtptimeout); - ast_rtp_set_rtpholdtimeout(p->vrtp, global_rtpholdtimeout); - ast_rtp_set_rtpkeepalive(p->vrtp, global_rtpkeepalive); - } - if (p->trtp) { - ast_rtp_setqos(p->trtp, global_tos_text, global_cos_text, "SIP TRTP"); - ast_rtp_setdtmf(p->trtp, 0); - ast_rtp_setdtmfcompensate(p->trtp, 0); - } - if (p->udptl) + if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && (p->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr))) { ast_udptl_setqos(p->udptl, global_tos_audio, global_cos_audio); + } p->maxcallbitrate = default_maxcallbitrate; p->autoframing = global_autoframing; - ast_rtp_codec_setpref(p->rtp, &p->prefs); } if (useglobal_nat && sin) { @@ -6769,6 +6781,7 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si } ast_string_field_set(p, context, sip_cfg.default_context); ast_string_field_set(p, parkinglot, default_parkinglot); + ast_string_field_set(p, engine, default_engine); AST_LIST_HEAD_INIT_NOLOCK(&p->request_queue); @@ -7403,7 +7416,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action int iterator; int sendonly = -1; int numberofports; - struct ast_rtp *newaudiortp, *newvideortp, *newtextrtp; /* Buffers for codec handling */ + struct ast_rtp_codecs newaudiortp, newvideortp, newtextrtp; int newjointcapability; /* Negotiated capability */ int newpeercapability; int newnoncodeccapability; @@ -7428,33 +7441,10 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action return -1; } - /* Initialize the temporary RTP structures we use to evaluate the offer from the peer */ -#ifdef LOW_MEMORY - newaudiortp = ast_threadstorage_get(&ts_audio_rtp, ast_rtp_alloc_size()); -#else - newaudiortp = alloca(ast_rtp_alloc_size()); -#endif - memset(newaudiortp, 0, ast_rtp_alloc_size()); - ast_rtp_new_init(newaudiortp); - ast_rtp_pt_clear(newaudiortp); - -#ifdef LOW_MEMORY - newvideortp = ast_threadstorage_get(&ts_video_rtp, ast_rtp_alloc_size()); -#else - newvideortp = alloca(ast_rtp_alloc_size()); -#endif - memset(newvideortp, 0, ast_rtp_alloc_size()); - ast_rtp_new_init(newvideortp); - ast_rtp_pt_clear(newvideortp); - -#ifdef LOW_MEMORY - newtextrtp = ast_threadstorage_get(&ts_text_rtp, ast_rtp_alloc_size()); -#else - newtextrtp = alloca(ast_rtp_alloc_size()); -#endif - memset(newtextrtp, 0, ast_rtp_alloc_size()); - ast_rtp_new_init(newtextrtp); - ast_rtp_pt_clear(newtextrtp); + /* Make sure that the codec structures are all cleared out */ + ast_rtp_codecs_payloads_clear(&newaudiortp, NULL); + ast_rtp_codecs_payloads_clear(&newvideortp, NULL); + ast_rtp_codecs_payloads_clear(&newtextrtp, NULL); /* Update our last rtprx when we receive an SDP, too */ p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */ @@ -7536,11 +7526,13 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action p->novideo = TRUE; p->notext = TRUE; - if (p->vrtp) - ast_rtp_pt_clear(newvideortp); /* Must be cleared in case no m=video line exists */ - - if (p->trtp) - ast_rtp_pt_clear(newtextrtp); /* Must be cleared in case no m=text line exists */ + if (p->vrtp) { + ast_rtp_codecs_payloads_clear(&newvideortp, NULL); + } + + if (p->trtp) { + ast_rtp_codecs_payloads_clear(&newtextrtp, NULL); + } /* Find media streams in this SDP offer */ while ((m = get_sdp_iterate(&iterator, req, "m"))[0] != '\0') { @@ -7565,7 +7557,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action } if (debug) ast_verbose("Found RTP audio format %d\n", codec); - ast_rtp_set_m_type(newaudiortp, codec); + + ast_rtp_codecs_payloads_set_m_type(&newaudiortp, NULL, codec); } } else if ((sscanf(m, "video %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) || (sscanf(m, "video %d RTP/AVP %n", &x, &len) == 1 && len >= 0)) { @@ -7581,7 +7574,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action } if (debug) ast_verbose("Found RTP video format %d\n", codec); - ast_rtp_set_m_type(newvideortp, codec); + ast_rtp_codecs_payloads_set_m_type(&newvideortp, NULL, codec); } } else if ((sscanf(m, "text %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) || (sscanf(m, "text %d RTP/AVP %n", &x, &len) == 1 && len > 0)) { @@ -7597,7 +7590,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action } if (debug) ast_verbose("Found RTP text format %d\n", codec); - ast_rtp_set_m_type(newtextrtp, codec); + ast_rtp_codecs_payloads_set_m_type(&newtextrtp, NULL, codec); } } else if (p->udptl && ( (sscanf(m, "image %d udptl t38%n", &x, &len) == 1 && len > 0) || (sscanf(m, "image %d UDPTL t38%n", &x, &len) == 1 && len > 0) )) { @@ -7662,10 +7655,10 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action if (udptlportno > 0) { sin.sin_port = htons(udptlportno); if (ast_test_flag(&p->flags[0], SIP_NAT) && ast_test_flag(&p->flags[1], SIP_PAGE2_UDPTL_DESTINATION)) { - struct sockaddr_in peer; - ast_rtp_get_peer(p->rtp, &peer); - if (peer.sin_addr.s_addr) { - memcpy(&sin.sin_addr, &peer.sin_addr, sizeof(sin.sin_addr)); + struct sockaddr_in remote_address; + ast_rtp_instance_get_remote_address(p->rtp, &remote_address); + if (remote_address.sin_addr.s_addr) { + memcpy(&sin, &remote_address, sizeof(sin)); if (debug) { ast_log(LOG_DEBUG, "Peer T.38 UDPTL is set behind NAT and with destination, destination address now %s\n", ast_inet_ntoa(sin.sin_addr)); } @@ -7685,7 +7678,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action if (p->rtp) { if (portno > 0) { sin.sin_port = htons(portno); - ast_rtp_set_peer(p->rtp, &sin); + ast_rtp_instance_set_remote_address(p->rtp, &sin); if (debug) ast_verbose("Peer audio RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); } else { @@ -7693,7 +7686,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action if (debug) ast_verbose("Got T.38 Re-invite without audio. Keeping RTP active during T.38 session. Callid %s\n", p->callid); } else { - ast_rtp_stop(p->rtp); + ast_rtp_instance_stop(p->rtp); if (debug) ast_verbose("Peer doesn't provide audio. Callid %s\n", p->callid); } @@ -7776,18 +7769,17 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action } } if (framing && p->autoframing) { - struct ast_codec_pref *pref = ast_rtp_codec_getpref(p->rtp); + struct ast_codec_pref *pref = &ast_rtp_instance_get_codecs(p->rtp)->pref; int codec_n; - int format = 0; - for (codec_n = 0; codec_n < MAX_RTP_PT; codec_n++) { - format = ast_rtp_codec_getformat(codec_n); - if (!format) /* non-codec or not found */ + for (codec_n = 0; codec_n < AST_RTP_MAX_PT; codec_n++) { + struct ast_rtp_payload_type format = ast_rtp_codecs_payload_lookup(ast_rtp_instance_get_codecs(p->rtp), codec_n); + if (!format.asterisk_format || !format.code) /* non-codec or not found */ continue; if (option_debug) - ast_log(LOG_DEBUG, "Setting framing for %d to %ld\n", format, framing); - ast_codec_pref_setsize(pref, format, framing); + ast_log(LOG_DEBUG, "Setting framing for %d to %ld\n", format.code, framing); + ast_codec_pref_setsize(pref, format.code, framing); } - ast_rtp_codec_setpref(p->rtp, pref); + ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(p->rtp), p->rtp, pref); } continue; } @@ -7799,7 +7791,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action sscanf(red_cp, "%u", &red_data_pt[red_num_gen]); red_cp = strtok(red_cp, "/"); - while (red_cp && red_num_gen++ < RED_MAX_GENERATION) { + while (red_cp && red_num_gen++ < AST_RED_MAX_GENERATION) { sscanf(red_cp, "%u", &red_data_pt[red_num_gen]); red_cp = strtok(NULL, "/"); } @@ -7808,15 +7800,15 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action } if (sscanf(a, "fmtp: %u %63s", &codec, fmtp_string) == 2) { - struct rtpPayloadType payload; + struct ast_rtp_payload_type payload; unsigned int handled = 0; - payload = ast_rtp_lookup_pt(newaudiortp, codec); + payload = ast_rtp_codecs_payload_lookup(&newaudiortp, codec); if (!payload.code) { /* it wasn't found, try the video rtp */ - payload = ast_rtp_lookup_pt(newvideortp, codec); + payload = ast_rtp_codecs_payload_lookup(&newvideortp, codec); } - if (payload.code && payload.isAstFormat) { + if (payload.code && payload.asterisk_format) { unsigned int bit_rate; switch (payload.code) { @@ -7824,7 +7816,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action if (sscanf(fmtp_string, "bitrate=%u", &bit_rate) == 1) { if (bit_rate != 32000) { ast_log(LOG_WARNING, "Got Siren7 offer at %d bps, but only 32000 bps supported; ignoring.\n", bit_rate); - ast_rtp_unset_m_type(newaudiortp, codec); + ast_rtp_codecs_payloads_unset(&newaudiortp, NULL, codec); } else { handled = 1; } @@ -7834,7 +7826,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action if (sscanf(fmtp_string, "bitrate=%u", &bit_rate) == 1) { if (bit_rate != 48000) { ast_log(LOG_WARNING, "Got Siren14 offer at %d bps, but only 48000 bps supported; ignoring.\n", bit_rate); - ast_rtp_unset_m_type(newaudiortp, codec); + ast_rtp_codecs_payloads_unset(&newaudiortp, NULL, codec); } else { handled = 1; } @@ -7856,24 +7848,24 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action /* Note: should really look at the '#chans' params too */ /* Note: This should all be done in the context of the m= above */ if (!strncasecmp(mimeSubtype, "H26", 3) || !strncasecmp(mimeSubtype, "MP4", 3)) { /* Video */ - if (ast_rtp_set_rtpmap_type_rate(newvideortp, codec, "video", mimeSubtype, 0, sample_rate) != -1) { + if (ast_rtp_codecs_payloads_set_rtpmap_type_rate(&newvideortp, NULL, codec, "video", mimeSubtype, 0, sample_rate) != -1) { if (debug) ast_verbose("Found video description format %s for ID %d\n", mimeSubtype, codec); found_rtpmap_codecs[last_rtpmap_codec] = codec; last_rtpmap_codec++; } else { - ast_rtp_unset_m_type(newvideortp, codec); + ast_rtp_codecs_payloads_unset(&newvideortp, NULL, codec); if (debug) ast_verbose("Found unknown media description format %s for ID %d\n", mimeSubtype, codec); } } else if (!strncasecmp(mimeSubtype, "T140", 4)) { /* Text */ if (p->trtp) { /* ast_verbose("Adding t140 mimeSubtype to textrtp struct\n"); */ - ast_rtp_set_rtpmap_type(newtextrtp, codec, "text", mimeSubtype, 0); + ast_rtp_codecs_payloads_set_rtpmap_type_rate(&newtextrtp, NULL, codec, "text", mimeSubtype, 0, sample_rate); } } else if (!strncasecmp(mimeSubtype, "RED", 3)) { /* Text with Redudancy */ if (p->trtp) { - ast_rtp_set_rtpmap_type(newtextrtp, codec, "text", mimeSubtype, 0); + ast_rtp_codecs_payloads_set_rtpmap_type_rate(&newtextrtp, NULL, codec, "text", mimeSubtype, 0, sample_rate); red_pt = codec; sprintf(red_fmtp, "fmtp:%d ", red_pt); @@ -7881,15 +7873,14 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action ast_verbose("RED submimetype has payload type: %d\n", red_pt); } } else { /* Must be audio?? */ - if (ast_rtp_set_rtpmap_type_rate(newaudiortp, codec, "audio", mimeSubtype, - ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0, - sample_rate) != -1) { + if (ast_rtp_codecs_payloads_set_rtpmap_type_rate(&newaudiortp, NULL, codec, "audio", mimeSubtype, + ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0, sample_rate) != -1) { if (debug) ast_verbose("Found audio description format %s for ID %d\n", mimeSubtype, codec); found_rtpmap_codecs[last_rtpmap_codec] = codec; last_rtpmap_codec++; } else { - ast_rtp_unset_m_type(newaudiortp, codec); + ast_rtp_codecs_payloads_unset(&newaudiortp, NULL, codec); if (debug) ast_verbose("Found unknown media description format %s for ID %d\n", mimeSubtype, codec); } @@ -8028,15 +8019,14 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action } /* Now gather all of the codecs that we are asked for: */ - ast_rtp_get_current_formats(newaudiortp, &peercapability, &peernoncodeccapability); - ast_rtp_get_current_formats(newvideortp, &vpeercapability, &vpeernoncodeccapability); - ast_rtp_get_current_formats(newtextrtp, &tpeercapability, &tpeernoncodeccapability); + ast_rtp_codecs_payload_formats(&newaudiortp, &peercapability, &peernoncodeccapability); + ast_rtp_codecs_payload_formats(&newvideortp, &vpeercapability, &vpeernoncodeccapability); + ast_rtp_codecs_payload_formats(&newtextrtp, &tpeercapability, &tpeernoncodeccapability); newjointcapability = p->capability & (peercapability | vpeercapability | tpeercapability); newpeercapability = (peercapability | vpeercapability | tpeercapability); newnoncodeccapability = p->noncodeccapability & peernoncodeccapability; - - + if (debug) { /* shame on whoever coded this.... */ char s1[SIPBUFSIZE], s2[SIPBUFSIZE], s3[SIPBUFSIZE], s4[SIPBUFSIZE], s5[SIPBUFSIZE]; @@ -8047,11 +8037,17 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action ast_getformatname_multiple(s3, SIPBUFSIZE, vpeercapability), ast_getformatname_multiple(s4, SIPBUFSIZE, tpeercapability), ast_getformatname_multiple(s5, SIPBUFSIZE, newjointcapability)); + } + + if (debug) { + struct ast_str *s1 = ast_str_alloca(SIPBUFSIZE); + struct ast_str *s2 = ast_str_alloca(SIPBUFSIZE); + struct ast_str *s3 = ast_str_alloca(SIPBUFSIZE); ast_verbose("Non-codec capabilities (dtmf): us - %s, peer - %s, combined - %s\n", - ast_rtp_lookup_mime_multiple(s1, SIPBUFSIZE, p->noncodeccapability, 0, 0), - ast_rtp_lookup_mime_multiple(s2, SIPBUFSIZE, peernoncodeccapability, 0, 0), - ast_rtp_lookup_mime_multiple(s3, SIPBUFSIZE, newnoncodeccapability, 0, 0)); + ast_rtp_lookup_mime_multiple2(s1, p->noncodeccapability, 0, 0), + ast_rtp_lookup_mime_multiple2(s2, peernoncodeccapability, 0, 0), + ast_rtp_lookup_mime_multiple2(s3, newnoncodeccapability, 0, 0)); } if (!newjointcapability) { /* If T.38 was not negotiated either, totally bail out... */ @@ -8082,11 +8078,13 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action p->red = 0; } - ast_rtp_pt_copy(p->rtp, newaudiortp); - if (p->vrtp) - ast_rtp_pt_copy(p->vrtp, newvideortp); - if (p->trtp) - ast_rtp_pt_copy(p->trtp, newtextrtp); + ast_rtp_codecs_payloads_copy(&newaudiortp, ast_rtp_instance_get_codecs(p->rtp), p->rtp); + if (p->vrtp) { + ast_rtp_codecs_payloads_copy(&newvideortp, ast_rtp_instance_get_codecs(p->vrtp), p->vrtp); + } + if (p->trtp) { + ast_rtp_codecs_payloads_copy(&newtextrtp, ast_rtp_instance_get_codecs(p->trtp), p->trtp); + } if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO) { ast_clear_flag(&p->flags[0], SIP_DTMF); @@ -8094,8 +8092,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action /* XXX Would it be reasonable to drop the DSP at this point? XXX */ ast_set_flag(&p->flags[0], SIP_DTMF_RFC2833); /* Since RFC2833 is now negotiated we need to change some properties of the RTP stream */ - ast_rtp_setdtmf(p->rtp, 1); - ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); + ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF, 1); + ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); } else { ast_set_flag(&p->flags[0], SIP_DTMF_INBAND); } @@ -8103,21 +8101,21 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action /* Setup audio port number */ if (p->rtp && sin.sin_port) { - ast_rtp_set_peer(p->rtp, &sin); + ast_rtp_instance_set_remote_address(p->rtp, &sin); if (debug) ast_verbose("Peer audio RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); } /* Setup video port number */ if (p->vrtp && vsin.sin_port) { - ast_rtp_set_peer(p->vrtp, &vsin); + ast_rtp_instance_set_remote_address(p->vrtp, &vsin); if (debug) ast_verbose("Peer video RTP is at port %s:%d\n", ast_inet_ntoa(vsin.sin_addr), ntohs(vsin.sin_port)); } /* Setup text port number */ if (p->trtp && tsin.sin_port) { - ast_rtp_set_peer(p->trtp, &tsin); + ast_rtp_instance_set_remote_address(p->trtp, &tsin); if (debug) ast_verbose("Peer text RTP is at port %s:%d\n", ast_inet_ntoa(tsin.sin_addr), ntohs(tsin.sin_port)); } @@ -8164,7 +8162,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action S_OR(p->mohsuggest, NULL), !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); if (sendonly) - ast_rtp_stop(p->rtp); + ast_rtp_instance_stop(p->rtp); /* RTCP needs to go ahead, even if we're on hold!!! */ /* Activate a re-invite */ ast_queue_frame(p->owner, &ast_null_frame); @@ -9001,19 +8999,19 @@ static void add_codec_to_sdp(const struct sip_pvt *p, int codec, if (debug) ast_verbose("Adding codec 0x%x (%s) to SDP\n", codec, ast_getformatname(codec)); - if ((rtp_code = ast_rtp_lookup_code(p->rtp, 1, codec)) == -1) + if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(p->rtp), 1, codec)) == -1) return; if (p->rtp) { - struct ast_codec_pref *pref = ast_rtp_codec_getpref(p->rtp); + struct ast_codec_pref *pref = &ast_rtp_instance_get_codecs(p->rtp)->pref; fmt = ast_codec_pref_getsize(pref, codec); } else /* I dont see how you couldn't have p->rtp, but good to check for and error out if not there like earlier code */ return; ast_str_append(m_buf, 0, " %d", rtp_code); ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code, - ast_rtp_lookup_mime_subtype(1, codec, + ast_rtp_lookup_mime_subtype2(1, codec, ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0), - ast_rtp_lookup_sample_rate(1, codec)); + ast_rtp_lookup_sample_rate2(1, codec)); switch (codec) { case AST_FORMAT_G729A: @@ -9060,13 +9058,13 @@ static void add_vcodec_to_sdp(const struct sip_pvt *p, int codec, if (debug) ast_verbose("Adding video codec 0x%x (%s) to SDP\n", codec, ast_getformatname(codec)); - if ((rtp_code = ast_rtp_lookup_code(p->vrtp, 1, codec)) == -1) + if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(p->vrtp), 1, codec)) == -1) return; ast_str_append(m_buf, 0, " %d", rtp_code); ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code, - ast_rtp_lookup_mime_subtype(1, codec, 0), - ast_rtp_lookup_sample_rate(1, codec)); + ast_rtp_lookup_mime_subtype2(1, codec, 0), + ast_rtp_lookup_sample_rate2(1, codec)); /* Add fmtp code here */ } @@ -9083,20 +9081,21 @@ static void add_tcodec_to_sdp(const struct sip_pvt *p, int codec, if (debug) ast_verbose("Adding text codec 0x%x (%s) to SDP\n", codec, ast_getformatname(codec)); - if ((rtp_code = ast_rtp_lookup_code(p->trtp, 1, codec)) == -1) + if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(p->trtp), 1, codec)) == -1) return; ast_str_append(m_buf, 0, " %d", rtp_code); ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code, - ast_rtp_lookup_mime_subtype(1, codec, 0), - ast_rtp_lookup_sample_rate(1, codec)); + ast_rtp_lookup_mime_subtype2(1, codec, 0), + ast_rtp_lookup_sample_rate2(1, codec)); /* Add fmtp code here */ if (codec == AST_FORMAT_T140RED) { - ast_str_append(a_buf, 0, "a=fmtp:%d %d/%d/%d\r\n", rtp_code, - ast_rtp_lookup_code(p->trtp, 1, AST_FORMAT_T140), - ast_rtp_lookup_code(p->trtp, 1, AST_FORMAT_T140), - ast_rtp_lookup_code(p->trtp, 1, AST_FORMAT_T140)); + int t140code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(p->trtp), 1, AST_FORMAT_T140); + ast_str_append(a_buf, 0, "a=fmtp:%d %d/%d/%d\r\n", rtp_code, + t140code, + t140code, + t140code); } } @@ -9139,14 +9138,14 @@ static void add_noncodec_to_sdp(const struct sip_pvt *p, int format, int rtp_code; if (debug) - ast_verbose("Adding non-codec 0x%x (%s) to SDP\n", format, ast_rtp_lookup_mime_subtype(0, format, 0)); - if ((rtp_code = ast_rtp_lookup_code(p->rtp, 0, format)) == -1) + ast_verbose("Adding non-codec 0x%x (%s) to SDP\n", format, ast_rtp_lookup_mime_subtype2(0, format, 0)); + if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(p->rtp), 0, format)) == -1) return; ast_str_append(m_buf, 0, " %d", rtp_code); ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code, - ast_rtp_lookup_mime_subtype(0, format, 0), - ast_rtp_lookup_sample_rate(0, format)); + ast_rtp_lookup_mime_subtype2(0, format, 0), + ast_rtp_lookup_sample_rate2(0, format)); if (format == AST_RTP_DTMF) /* Indicate we support DTMF and FLASH... */ ast_str_append(a_buf, 0, "a=fmtp:%d 0-16\r\n", rtp_code); } @@ -9159,11 +9158,11 @@ static void get_our_media_address(struct sip_pvt *p, int needvideo, struct sockaddr_in *dest, struct sockaddr_in *vdest) { /* First, get our address */ - ast_rtp_get_us(p->rtp, sin); + ast_rtp_instance_get_local_address(p->rtp, sin); if (p->vrtp) - ast_rtp_get_us(p->vrtp, vsin); + ast_rtp_instance_get_local_address(p->vrtp, vsin); if (p->trtp) - ast_rtp_get_us(p->trtp, tsin); + ast_rtp_instance_get_local_address(p->trtp, tsin); /* Now, try to figure out where we want them to send data */ /* Is this a re-invite to move the media out, then use the original offer from caller */ @@ -9594,7 +9593,7 @@ static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const if (p->rtp) { if (!p->autoframing && !ast_test_flag(&p->flags[0], SIP_OUTGOING)) { ast_debug(1, "Setting framing from config on incoming call\n"); - ast_rtp_codec_setpref(p->rtp, &p->prefs); + ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(p->rtp), p->rtp, &p->prefs); } try_suggested_sip_codec(p); if (p->t38.state == T38_PEER_DIRECT || p->t38.state == T38_ENABLED) { @@ -12087,12 +12086,6 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct sockaddr } if (peer) { - /*! \todo OEJ Remove this - there's never RTP in a REGISTER dialog... */ - /* Set Frame packetization */ - if (p->rtp) { - ast_rtp_codec_setpref(p->rtp, &peer->prefs); - p->autoframing = peer->autoframing; - } if (!peer->host_dynamic) { ast_log(LOG_ERROR, "Peer '%s' is trying to register, but not configured as host=dynamic\n", peer->name); res = AUTH_PEER_NOT_DYNAMIC; @@ -13024,7 +13017,7 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of, /* XXX what about p->prefs = peer->prefs; ? */ /* Set Frame packetization */ if (p->rtp) { - ast_rtp_codec_setpref(p->rtp, &peer->prefs); + ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(p->rtp), p->rtp, &peer->prefs); p->autoframing = peer->autoframing; } @@ -13046,6 +13039,7 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of, ast_string_field_set(p, mohinterpret, peer->mohinterpret); ast_string_field_set(p, mohsuggest, peer->mohsuggest); ast_string_field_set(p, parkinglot, peer->parkinglot); + ast_string_field_set(p, engine, peer->engine); if (peer->callingpres) /* Peer calling pres setting will override RPID */ p->callingpres = peer->callingpres; if (peer->maxms && peer->lastms) @@ -13113,17 +13107,6 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of, if (p->peercapability) p->jointcapability &= p->peercapability; p->maxcallbitrate = peer->maxcallbitrate; - if (!ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT_ALWAYS) && - (!ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) || - !(p->capability & AST_FORMAT_VIDEO_MASK)) && - p->vrtp) { - ast_rtp_destroy(p->vrtp); - p->vrtp = NULL; - } - if ((!ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT) || !(p->capability & AST_FORMAT_TEXT_MASK)) && p->trtp) { - ast_rtp_destroy(p->trtp); - p->trtp = NULL; - } if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) || (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) p->noncodeccapability |= AST_RTP_DTMF; @@ -13132,6 +13115,12 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of, p->jointnoncodeccapability = p->noncodeccapability; if (p->t38.peercapability) p->t38.jointcapability &= p->t38.peercapability; + if (!dialog_initialize_rtp(p)) { + ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(p->rtp), p->rtp, &peer->prefs); + p->autoframing = peer->autoframing; + } else { + res = AUTH_RTP_FAILED; + } } unref_peer(peer, "check_peer_ok: unref_peer: tossing temp ptr to peer from find_peer"); return res; @@ -13253,7 +13242,11 @@ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_requ /* Finally, apply the guest policy */ if (sip_cfg.allowguest) { replace_cid(p, rpid_num, calleridname); - res = AUTH_SUCCESSFUL; + if (!dialog_initialize_rtp(p)) { + res = AUTH_SUCCESSFUL; + } else { + res = AUTH_RTP_FAILED; + } } else if (sip_cfg.alwaysauthreject) res = AUTH_FAKE_AUTH; /* reject with fake authorization request */ else @@ -14050,7 +14043,20 @@ static int dialog_needdestroy(void *dialogobj, void *arg, int flags) */ return 0; } - + + /* We absolutely cannot destroy the rtp struct while a bridge is active or we WILL crash */ + if (dialog->rtp && ast_rtp_instance_get_bridged(dialog->rtp)) { + ast_debug(2, "Bridge still active. Delaying destroy of SIP dialog '%s' Method: %s\n", dialog->callid, sip_methods[dialog->method].text); + sip_pvt_unlock(dialog); + return 0; + } + + if (dialog->vrtp && ast_rtp_instance_get_bridged(dialog->vrtp)) { + ast_debug(2, "Bridge still active. Delaying destroy of SIP dialog '%s' Method: %s\n", dialog->callid, sip_methods[dialog->method].text); + sip_pvt_unlock(dialog); + return 0; + } + /* Check RTP timeouts and kill calls if we have a timeout set and do not get RTP */ check_rtp_timeout(dialog, *t); @@ -14059,13 +14065,13 @@ static int dialog_needdestroy(void *dialogobj, void *arg, int flags) - if that's the case, wait with destruction */ if (dialog->needdestroy && !dialog->packets && !dialog->owner) { /* We absolutely cannot destroy the rtp struct while a bridge is active or we WILL crash */ - if (dialog->rtp && ast_rtp_get_bridged(dialog->rtp)) { + if (dialog->rtp && ast_rtp_instance_get_bridged(dialog->rtp)) { ast_debug(2, "Bridge still active. Delaying destruction of SIP dialog '%s' Method: %s\n", dialog->callid, sip_methods[dialog->method].text); sip_pvt_unlock(dialog); return 0; } - if (dialog->vrtp && ast_rtp_get_bridged(dialog->vrtp)) { + if (dialog->vrtp && ast_rtp_instance_get_bridged(dialog->vrtp)) { ast_debug(2, "Bridge still active. Delaying destroy of SIP dialog '%s' Method: %s\n", dialog->callid, sip_methods[dialog->method].text); sip_pvt_unlock(dialog); return 0; @@ -14555,6 +14561,7 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct ast_cli(fd, " Sess-Refresh : %s\n", strefresher2str(peer->stimer.st_ref)); ast_cli(fd, " Sess-Expires : %d secs\n", peer->stimer.st_max_se); ast_cli(fd, " Min-Sess : %d secs\n", peer->stimer.st_min_se); + ast_cli(fd, " RTP Engine : %s\n", peer->engine); ast_cli(fd, "\n"); peer = unref_peer(peer, "sip_show_peer: unref_peer: done with peer ptr"); } else if (peer && type == 1) { /* manager listing */ @@ -14602,6 +14609,7 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct astman_append(s, "SIP-Sess-Refresh: %s\r\n", strefresher2str(peer->stimer.st_ref)); astman_append(s, "SIP-Sess-Expires: %d\r\n", peer->stimer.st_max_se); astman_append(s, "SIP-Sess-Min: %d\r\n", peer->stimer.st_min_se); + astman_append(s, "SIP-RTP-Engine: %s\r\n", peer->engine); /* - is enumerated */ astman_append(s, "SIP-DTMFmode: %s\r\n", dtmfmode2str(ast_test_flag(&peer->flags[0], SIP_DTMF))); @@ -14734,6 +14742,7 @@ static char *sip_show_user(struct ast_cli_entry *e, int cmd, struct ast_cli_args ast_cli(a->fd, " Sess-Refresh : %s\n", strefresher2str(user->stimer.st_ref)); ast_cli(a->fd, " Sess-Expires : %d secs\n", user->stimer.st_max_se); ast_cli(a->fd, " Sess-Min-SE : %d secs\n", user->stimer.st_min_se); + ast_cli(a->fd, " RTP Engine : %s\n", user->engine); ast_cli(a->fd, " Codec Order : ("); print_codec_to_cli(a->fd, &user->prefs); @@ -14888,11 +14897,10 @@ static int show_chanstats_cb(void *__cur, void *__arg, int flags) #define FORMAT2 "%-15.15s %-11.11s %-8.8s %-10.10s %-10.10s (%-2.2s) %-6.6s %-10.10s %-10.10s ( %%) %-6.6s\n" #define FORMAT "%-15.15s %-11.11s %-8.8s %-10.10u%-1.1s %-10.10u (%-2.2u%%) %-6.6u %-10.10u%-1.1s %-10.10u (%-2.2u%%) %-6.6u\n" struct sip_pvt *cur = __cur; - unsigned int rxcount; - unsigned int txcount; + struct ast_rtp_instance_stats stats; char durbuf[10]; - int duration; - int durh, durm, durs; + int duration; + int durh, durm, durs; struct ast_channel *c = cur->owner; struct __show_chan_arg *arg = __arg; int fd = arg->fd; @@ -14906,10 +14914,9 @@ static int show_chanstats_cb(void *__cur, void *__arg, int flags) ast_cli(fd, "%-15.15s %-11.11s (inv state: %s) -- %s\n", ast_inet_ntoa(cur->sa.sin_addr), cur->callid, invitestate2string[cur->invitestate].desc, "-- No RTP active"); return 0; /* don't care, we scan all channels */ } - rxcount = ast_rtp_get_qosvalue(cur->rtp, AST_RTP_RXCOUNT); - txcount = ast_rtp_get_qosvalue(cur->rtp, AST_RTP_TXCOUNT); - /* Find the duration of this channel */ + ast_rtp_instance_get_stats(cur->rtp, &stats, AST_RTP_INSTANCE_STAT_ALL); + if (c && c->cdr && !ast_tvzero(c->cdr->start)) { duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000); durh = duration / 3600; @@ -14919,21 +14926,21 @@ static int show_chanstats_cb(void *__cur, void *__arg, int flags) } else { durbuf[0] = '\0'; } - /* Print stats for every call with RTP */ + ast_cli(fd, FORMAT, ast_inet_ntoa(cur->sa.sin_addr), cur->callid, durbuf, - rxcount > (unsigned int) 100000 ? (unsigned int) (rxcount)/(unsigned int) 1000 : rxcount, - rxcount > (unsigned int) 100000 ? "K":" ", - ast_rtp_get_qosvalue(cur->rtp, AST_RTP_RXPLOSS), - rxcount > ast_rtp_get_qosvalue(cur->rtp, AST_RTP_RXPLOSS) ? (unsigned int) (ast_rtp_get_qosvalue(cur->rtp, AST_RTP_RXPLOSS) / rxcount * 100) : 0, - ast_rtp_get_qosvalue(cur->rtp, AST_RTP_RXJITTER), - txcount > (unsigned int) 100000 ? (unsigned int) (txcount)/(unsigned int) 1000 : txcount, - txcount > (unsigned int) 100000 ? "K":" ", - ast_rtp_get_qosvalue(cur->rtp, AST_RTP_TXPLOSS), - txcount > ast_rtp_get_qosvalue(cur->rtp, AST_RTP_TXPLOSS) ? (unsigned int) (ast_rtp_get_qosvalue(cur->rtp, AST_RTP_TXPLOSS)/ txcount * 100) : 0, - ast_rtp_get_qosvalue(cur->rtp, AST_RTP_TXJITTER) + stats.rxcount > (unsigned int) 100000 ? (unsigned int) (stats.rxcount)/(unsigned int) 1000 : stats.rxcount, + stats.rxcount > (unsigned int) 100000 ? "K":" ", + stats.rxploss, + stats.rxcount > stats.rxploss ? (stats.rxploss / stats.rxcount * 100) : 0, + stats.rxjitter, + stats.txcount > (unsigned int) 100000 ? (unsigned int) (stats.txcount)/(unsigned int) 1000 : stats.txcount, + stats.txcount > (unsigned int) 100000 ? "K":" ", + stats.txploss, + stats.txcount > stats.txploss ? (stats.txploss / stats.txcount * 100) : 0, + stats.txjitter ); arg->numchans++; @@ -16880,7 +16887,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru if (p->udptl && p->t38.state == T38_LOCAL_REINVITE) { change_t38_state(p, T38_DISABLED); /* Try to reset RTP timers */ - ast_rtp_set_rtptimers_onhold(p->rtp); + //ast_rtp_set_rtptimers_onhold(p->rtp); /* Trigger a reinvite back to audio */ transmit_reinvite_with_sdp(p, FALSE, FALSE); @@ -17300,11 +17307,11 @@ static void stop_media_flows(struct sip_pvt *p) { /* Immediately stop RTP, VRTP and UDPTL as applicable */ if (p->rtp) - ast_rtp_stop(p->rtp); + ast_rtp_instance_stop(p->rtp); if (p->vrtp) - ast_rtp_stop(p->vrtp); + ast_rtp_instance_stop(p->vrtp); if (p->trtp) - ast_rtp_stop(p->trtp); + ast_rtp_instance_stop(p->trtp); if (p->udptl) ast_udptl_stop(p->udptl); } @@ -19032,8 +19039,8 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int build_contact(p); /* Build our contact header */ if (p->rtp) { - ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); - ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); + ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); + ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); } if (!replace_id && gotdest) { /* No matching extension found */ @@ -19852,7 +19859,7 @@ static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req) static int acf_channel_read(struct ast_channel *chan, const char *funcname, char *preparse, char *buf, size_t buflen) { struct sip_pvt *p = chan->tech_pvt; - char *all = "", *parse = ast_strdupa(preparse); + char *parse = ast_strdupa(preparse); int res = 0; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(param); @@ -19890,61 +19897,70 @@ static int acf_channel_read(struct ast_channel *chan, const char *funcname, char args.type = "audio"; if (!strcasecmp(args.type, "audio")) - ast_rtp_get_peer(p->rtp, &sin); + ast_rtp_instance_get_remote_address(p->rtp, &sin); else if (!strcasecmp(args.type, "video")) - ast_rtp_get_peer(p->vrtp, &sin); + ast_rtp_instance_get_remote_address(p->vrtp, &sin); else if (!strcasecmp(args.type, "text")) - ast_rtp_get_peer(p->trtp, &sin); + ast_rtp_instance_get_remote_address(p->trtp, &sin); else return -1; snprintf(buf, buflen, "%s:%d", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); } else if (!strcasecmp(args.param, "rtpqos")) { - struct ast_rtp_quality qos; - struct ast_rtp *rtp = p->rtp; - - memset(&qos, 0, sizeof(qos)); + struct ast_rtp_instance *rtp = NULL; - if (ast_strlen_zero(args.type)) + if (ast_strlen_zero(args.type)) { args.type = "audio"; - if (ast_strlen_zero(args.field)) - args.field = "all"; - - if (!strcasecmp(args.type, "AUDIO")) { - all = ast_rtp_get_quality(rtp = p->rtp, &qos, RTPQOS_SUMMARY); - } else if (!strcasecmp(args.type, "VIDEO")) { - all = ast_rtp_get_quality(rtp = p->vrtp, &qos, RTPQOS_SUMMARY); - } else if (!strcasecmp(args.type, "TEXT")) { - all = ast_rtp_get_quality(rtp = p->trtp, &qos, RTPQOS_SUMMARY); + } + + if (!strcasecmp(args.type, "audio")) { + rtp = p->rtp; + } else if (!strcasecmp(args.type, "video")) { + rtp = p->vrtp; + } else if (!strcasecmp(args.type, "text")) { + rtp = p->trtp; } else { - return -1; + return -1; } - - if (!strcasecmp(args.field, "local_ssrc")) - snprintf(buf, buflen, "%u", qos.local_ssrc); - else if (!strcasecmp(args.field, "local_lostpackets")) - snprintf(buf, buflen, "%u", qos.local_lostpackets); - else if (!strcasecmp(args.field, "local_jitter")) - snprintf(buf, buflen, "%.0f", qos.local_jitter * 1000.0); - else if (!strcasecmp(args.field, "local_count")) - snprintf(buf, buflen, "%u", qos.local_count); - else if (!strcasecmp(args.field, "remote_ssrc")) - snprintf(buf, buflen, "%u", qos.remote_ssrc); - else if (!strcasecmp(args.field, "remote_lostpackets")) - snprintf(buf, buflen, "%u", qos.remote_lostpackets); - else if (!strcasecmp(args.field, "remote_jitter")) - snprintf(buf, buflen, "%.0f", qos.remote_jitter * 1000.0); - else if (!strcasecmp(args.field, "remote_count")) - snprintf(buf, buflen, "%u", qos.remote_count); - else if (!strcasecmp(args.field, "rtt")) - snprintf(buf, buflen, "%.0f", qos.rtt * 1000.0); - else if (!strcasecmp(args.field, "all")) - ast_copy_string(buf, all, buflen); - else if (!ast_rtp_get_qos(rtp, args.field, buf, buflen)) - ; - else { - ast_log(LOG_WARNING, "Unrecognized argument '%s' to %s\n", preparse, funcname); - return -1; + + if (ast_strlen_zero(args.field) || !strcasecmp(args.field, "all")) { + char quality_buf[AST_MAX_USER_FIELD], *quality; + + if (!(quality = ast_rtp_instance_get_quality(rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) { + return -1; + } + + ast_copy_string(buf, quality_buf, buflen); + return res; + } else { + struct ast_rtp_instance_stats stats; + + if (ast_rtp_instance_get_stats(rtp, &stats, AST_RTP_INSTANCE_STAT_ALL)) { + return -1; + } + + if (!strcasecmp(args.field, "local_ssrc")) { + snprintf(buf, buflen, "%u", stats.local_ssrc); + } else if (!strcasecmp(args.field, "local_lostpackets")) { + snprintf(buf, buflen, "%u", stats.rxploss); + } else if (!strcasecmp(args.field, "local_jitter")) { + snprintf(buf, buflen, "%u", stats.rxjitter); + } else if (!strcasecmp(args.field, "local_count")) { + snprintf(buf, buflen, "%u", stats.rxcount); + } else if (!strcasecmp(args.field, "remote_ssrc")) { + snprintf(buf, buflen, "%u", stats.remote_ssrc); + } else if (!strcasecmp(args.field, "remote_lostpackets")) { + snprintf(buf, buflen, "%u", stats.txploss); + } else if (!strcasecmp(args.field, "remote_jitter")) { + snprintf(buf, buflen, "%u", stats.txjitter); + } else if (!strcasecmp(args.field, "remote_count")) { + snprintf(buf, buflen, "%u", stats.txcount); + } else if (!strcasecmp(args.field, "rtt")) { + snprintf(buf, buflen, "%u", stats.rtt); + } else { + ast_log(LOG_WARNING, "Unrecognized argument '%s' to %s\n", preparse, funcname); + return -1; + } } } else { res = -1; @@ -19976,53 +19992,53 @@ static int handle_request_bye(struct sip_pvt *p, struct sip_request *req) /* Get RTCP quality before end of call */ if (p->do_history || p->owner) { + char quality_buf[AST_MAX_USER_FIELD], *quality; struct ast_channel *bridge = p->owner ? ast_bridged_channel(p->owner) : NULL; - char *videoqos, *textqos; - if (p->rtp) { + if (p->rtp && (quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) { if (p->do_history) { - char *audioqos, - *audioqos_jitter, - *audioqos_loss, - *audioqos_rtt; - - audioqos = ast_rtp_get_quality(p->rtp, NULL, RTPQOS_SUMMARY); - audioqos_jitter = ast_rtp_get_quality(p->rtp, NULL, RTPQOS_JITTER); - audioqos_loss = ast_rtp_get_quality(p->rtp, NULL, RTPQOS_LOSS); - audioqos_rtt = ast_rtp_get_quality(p->rtp, NULL, RTPQOS_RTT); - - append_history(p, "RTCPaudio", "Quality:%s", audioqos); - append_history(p, "RTCPaudioJitter", "Quality:%s", audioqos_jitter); - append_history(p, "RTCPaudioLoss", "Quality:%s", audioqos_loss); - append_history(p, "RTCPaudioRTT", "Quality:%s", audioqos_rtt); + append_history(p, "RTCPaudio", "Quality:%s", quality); + + if ((quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_JITTER, quality_buf, sizeof(quality_buf)))) { + append_history(p, "RTCPaudioJitter", "Quality:%s", quality); + } + if ((quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS, quality_buf, sizeof(quality_buf)))) { + append_history(p, "RTCPaudioLoss", "Quality:%s", quality); + } + if ((quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT, quality_buf, sizeof(quality_buf)))) { + append_history(p, "RTCPaudioRTT", "Quality:%s", quality); + } } - + if (p->owner) { - ast_rtp_set_vars(p->owner, p->rtp); + ast_rtp_instance_set_stats_vars(p->owner, p->rtp); } + } if (bridge) { struct sip_pvt *q = bridge->tech_pvt; - if (IS_SIP_TECH(bridge->tech) && q->rtp) - ast_rtp_set_vars(bridge, q->rtp); + if (IS_SIP_TECH(bridge->tech) && q->rtp) { + ast_rtp_instance_set_stats_vars(bridge, q->rtp); + } } - if (p->vrtp) { - videoqos = ast_rtp_get_quality(p->vrtp, NULL, RTPQOS_SUMMARY); - if (p->do_history) - append_history(p, "RTCPvideo", "Quality:%s", videoqos); - if (p->owner) - pbx_builtin_setvar_helper(p->owner, "RTPVIDEOQOS", videoqos); + if (p->vrtp && (quality = ast_rtp_instance_get_quality(p->vrtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) { + if (p->do_history) { + append_history(p, "RTCPvideo", "Quality:%s", quality); + } + if (p->owner) { + pbx_builtin_setvar_helper(p->owner, "RTPVIDEOQOS", quality); + } } - - if (p->trtp) { - textqos = ast_rtp_get_quality(p->trtp, NULL, RTPQOS_SUMMARY); - if (p->do_history) - append_history(p, "RTCPtext", "Quality:%s", textqos); - if (p->owner) - pbx_builtin_setvar_helper(p->owner, "RTPTEXTQOS", textqos); + if (p->trtp && (quality = ast_rtp_instance_get_quality(p->trtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) { + if (p->do_history) { + append_history(p, "RTCPtext", "Quality:%s", quality); + } + if (p->owner) { + pbx_builtin_setvar_helper(p->owner, "RTPTEXTQOS", quality); + } } } @@ -21211,15 +21227,8 @@ static void check_rtp_timeout(struct sip_pvt *dialog, time_t t) return; /* If we have no timers set, return now */ - if ((ast_rtp_get_rtpkeepalive(dialog->rtp) == 0) && (ast_rtp_get_rtptimeout(dialog->rtp) == 0) && (ast_rtp_get_rtpholdtimeout(dialog->rtp) == 0)) + if (!ast_rtp_instance_get_timeout(dialog->rtp) && !ast_rtp_instance_get_hold_timeout(dialog->rtp)) { return; - - /* Check AUDIO RTP keepalives */ - if (dialog->lastrtptx && ast_rtp_get_rtpkeepalive(dialog->rtp) && - (t > dialog->lastrtptx + ast_rtp_get_rtpkeepalive(dialog->rtp))) { - /* Need to send an empty RTP packet */ - dialog->lastrtptx = time(NULL); - ast_rtp_sendcng(dialog->rtp, 0); } /*! \todo Check video RTP keepalives @@ -21229,16 +21238,10 @@ static void check_rtp_timeout(struct sip_pvt *dialog, time_t t) */ /* Check AUDIO RTP timers */ - if (dialog->lastrtprx && (ast_rtp_get_rtptimeout(dialog->rtp) || ast_rtp_get_rtpholdtimeout(dialog->rtp)) && - (t > dialog->lastrtprx + ast_rtp_get_rtptimeout(dialog->rtp))) { - - /* Might be a timeout now -- see if we're on hold */ - struct sockaddr_in sin; - ast_rtp_get_peer(dialog->rtp, &sin); - if (!ast_test_flag(&dialog->flags[1], SIP_PAGE2_CALL_ONHOLD) || (ast_rtp_get_rtpholdtimeout(dialog->rtp) && - (t > dialog->lastrtprx + ast_rtp_get_rtpholdtimeout(dialog->rtp)))) { + if (dialog->lastrtprx && (ast_rtp_instance_get_timeout(dialog->rtp) || ast_rtp_instance_get_hold_timeout(dialog->rtp)) && (t > dialog->lastrtprx + ast_rtp_instance_get_timeout(dialog->rtp))) { + if (!ast_test_flag(&dialog->flags[1], SIP_PAGE2_CALL_ONHOLD) || (ast_rtp_instance_get_hold_timeout(dialog->rtp) && (t > dialog->lastrtprx + ast_rtp_instance_get_hold_timeout(dialog->rtp)))) { /* Needs a hangup */ - if (ast_rtp_get_rtptimeout(dialog->rtp)) { + if (ast_rtp_instance_get_timeout(dialog->rtp)) { while (dialog->owner && ast_channel_trylock(dialog->owner)) { sip_pvt_unlock(dialog); usleep(1); @@ -21253,11 +21256,11 @@ static void check_rtp_timeout(struct sip_pvt *dialog, time_t t) has already been requested and we don't want to repeatedly request hangups */ - ast_rtp_set_rtptimeout(dialog->rtp, 0); - ast_rtp_set_rtpholdtimeout(dialog->rtp, 0); + ast_rtp_instance_set_timeout(dialog->rtp, 0); + ast_rtp_instance_set_hold_timeout(dialog->rtp, 0); if (dialog->vrtp) { - ast_rtp_set_rtptimeout(dialog->vrtp, 0); - ast_rtp_set_rtpholdtimeout(dialog->vrtp, 0); + ast_rtp_instance_set_timeout(dialog->vrtp, 0); + ast_rtp_instance_set_hold_timeout(dialog->vrtp, 0); } } } @@ -22417,6 +22420,7 @@ static void set_peer_defaults(struct sip_peer *peer) ast_string_field_set(peer, language, default_language); ast_string_field_set(peer, mohinterpret, default_mohinterpret); ast_string_field_set(peer, mohsuggest, default_mohsuggest); + ast_string_field_set(peer, engine, default_engine); peer->addr.sin_family = AF_INET; peer->defaddr.sin_family = AF_INET; peer->capability = global_capability; @@ -22756,6 +22760,8 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str ast_string_field_set(peer, mohsuggest, v->value); } else if (!strcasecmp(v->name, "parkinglot")) { ast_string_field_set(peer, parkinglot, v->value); + } else if (!strcasecmp(v->name, "rtp_engine")) { + ast_string_field_set(peer, engine, v->value); } else if (!strcasecmp(v->name, "mailbox")) { add_peer_mailboxes(peer, v->value); } else if (!strcasecmp(v->name, "hasvoicemail")) { @@ -23205,6 +23211,7 @@ static int reload_config(enum channelreloadreason reason) ast_set_flag(&global_flags[0], SIP_DTMF_RFC2833); /*!< Default DTMF setting: RFC2833 */ ast_set_flag(&global_flags[0], SIP_NAT_RFC3581); /*!< NAT support if requested by device with rport */ ast_set_flag(&global_flags[0], SIP_CAN_REINVITE); /*!< Allow re-invites */ + ast_copy_string(default_engine, DEFAULT_ENGINE, sizeof(default_engine)); /* Debugging settings, always default to off */ dumphistory = FALSE; @@ -23945,156 +23952,176 @@ static int sip_set_udptl_peer(struct ast_channel *chan, struct ast_udptl *udptl) return 0; } -/*! \brief Returns null if we can't reinvite audio (part of RTP interface) */ -static enum ast_rtp_get_result sip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) +static enum ast_rtp_glue_result sip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) { - struct sip_pvt *p = NULL; - enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL; + struct sip_pvt *p = NULL; + enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_LOCAL; - if (!(p = chan->tech_pvt)) - return AST_RTP_GET_FAILED; - - sip_pvt_lock(p); - if (!(p->rtp)) { - sip_pvt_unlock(p); - return AST_RTP_GET_FAILED; + if (!(p = chan->tech_pvt)) { + return AST_RTP_GLUE_RESULT_FORBID; } - *rtp = p->rtp; + sip_pvt_lock(p); + if (!(p->rtp)) { + sip_pvt_unlock(p); + return AST_RTP_GLUE_RESULT_FORBID; + } - if (ast_rtp_getnat(*rtp) && !ast_test_flag(&p->flags[0], SIP_CAN_REINVITE_NAT)) - res = AST_RTP_TRY_PARTIAL; - else if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) - res = AST_RTP_TRY_NATIVE; - else if (ast_test_flag(&global_jbconf, AST_JB_FORCED)) - res = AST_RTP_GET_FAILED; + ao2_ref(p->rtp, +1); + *instance = p->rtp; - sip_pvt_unlock(p); + if (!ast_test_flag(&p->flags[0], SIP_CAN_REINVITE_NAT)) { + res = AST_RTP_GLUE_RESULT_LOCAL; + } else if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) { + res = AST_RTP_GLUE_RESULT_REMOTE; + } else if (ast_test_flag(&global_jbconf, AST_JB_FORCED)) { + res = AST_RTP_GLUE_RESULT_FORBID; + } - return res; + sip_pvt_unlock(p); + + return res; } -/*! \brief Returns null if we can't reinvite video (part of RTP interface) */ -static enum ast_rtp_get_result sip_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) +static enum ast_rtp_glue_result sip_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) { struct sip_pvt *p = NULL; - enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL; - - if (!(p = chan->tech_pvt)) - return AST_RTP_GET_FAILED; + enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_FORBID; + + if (!(p = chan->tech_pvt)) { + return AST_RTP_GLUE_RESULT_FORBID; + } sip_pvt_lock(p); if (!(p->vrtp)) { sip_pvt_unlock(p); - return AST_RTP_GET_FAILED; + return AST_RTP_GLUE_RESULT_FORBID; } - *rtp = p->vrtp; + ao2_ref(p->vrtp, +1); + *instance = p->vrtp; - if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) - res = AST_RTP_TRY_NATIVE; + if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) { + res = AST_RTP_GLUE_RESULT_REMOTE; + } sip_pvt_unlock(p); return res; } -/*! \brief Returns null if we can't reinvite text (part of RTP interface) */ -static enum ast_rtp_get_result sip_get_trtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) +static enum ast_rtp_glue_result sip_get_trtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) { - struct sip_pvt *p = NULL; - enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL; - - if (!(p = chan->tech_pvt)) - return AST_RTP_GET_FAILED; + struct sip_pvt *p = NULL; + enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_FORBID; - sip_pvt_lock(p); - if (!(p->trtp)) { - sip_pvt_unlock(p); - return AST_RTP_GET_FAILED; - } + if (!(p = chan->tech_pvt)) { + return AST_RTP_GLUE_RESULT_FORBID; + } - *rtp = p->trtp; + sip_pvt_lock(p); + if (!(p->trtp)) { + sip_pvt_unlock(p); + return AST_RTP_GLUE_RESULT_FORBID; + } - if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) - res = AST_RTP_TRY_NATIVE; + ao2_ref(p->trtp, +1); + *instance = p->trtp; - sip_pvt_unlock(p); + if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) { + res = AST_RTP_GLUE_RESULT_REMOTE; + } - return res; + sip_pvt_unlock(p); + + return res; } -/*! \brief Set the RTP peer for this call */ -static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active) +static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *instance, struct ast_rtp_instance *vinstance, struct ast_rtp_instance *tinstance, int codecs, int nat_active) { - struct sip_pvt *p; - int changed = 0; + struct sip_pvt *p; + int changed = 0; - p = chan->tech_pvt; - if (!p) - return -1; + p = chan->tech_pvt; + if (!p) + return -1; /* Disable early RTP bridge */ if (chan->_state != AST_STATE_UP && !sip_cfg.directrtpsetup) /* We are in early state */ return 0; - sip_pvt_lock(p); - if (p->alreadygone) { - /* If we're destroyed, don't bother */ - sip_pvt_unlock(p); - return 0; - } + sip_pvt_lock(p); + if (p->alreadygone) { + /* If we're destroyed, don't bother */ + sip_pvt_unlock(p); + return 0; + } - /* if this peer cannot handle reinvites of the media stream to devices - that are known to be behind a NAT, then stop the process now + /* if this peer cannot handle reinvites of the media stream to devices + that are known to be behind a NAT, then stop the process now */ - if (nat_active && !ast_test_flag(&p->flags[0], SIP_CAN_REINVITE_NAT)) { - sip_pvt_unlock(p); - return 0; - } + if (nat_active && !ast_test_flag(&p->flags[0], SIP_CAN_REINVITE_NAT)) { + sip_pvt_unlock(p); + return 0; + } - if (rtp) { - changed |= ast_rtp_get_peer(rtp, &p->redirip); - } else if (p->redirip.sin_addr.s_addr || ntohs(p->redirip.sin_port) != 0) { - memset(&p->redirip, 0, sizeof(p->redirip)); - changed = 1; - } - if (vrtp) { - changed |= ast_rtp_get_peer(vrtp, &p->vredirip); - } else if (p->vredirip.sin_addr.s_addr || ntohs(p->vredirip.sin_port) != 0) { - memset(&p->vredirip, 0, sizeof(p->vredirip)); - changed = 1; - } - if (trtp) { - changed |= ast_rtp_get_peer(trtp, &p->tredirip); - } else if (p->tredirip.sin_addr.s_addr || ntohs(p->tredirip.sin_port) != 0) { - memset(&p->tredirip, 0, sizeof(p->tredirip)); - changed = 1; - } - if (codecs && (p->redircodecs != codecs)) { - p->redircodecs = codecs; - changed = 1; - } - if (changed && !ast_test_flag(&p->flags[0], SIP_GOTREFER) && !ast_test_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER)) { - if (chan->_state != AST_STATE_UP) { /* We are in early state */ - if (p->do_history) - append_history(p, "ExtInv", "Initial invite sent with remote bridge proposal."); - ast_debug(1, "Early remote bridge setting SIP '%s' - Sending media to %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip.sin_addr)); - } else if (!p->pendinginvite) { /* We are up, and have no outstanding invite */ - ast_debug(3, "Sending reinvite on SIP '%s' - It's audio soon redirected to IP %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip.sin_addr)); - transmit_reinvite_with_sdp(p, FALSE, FALSE); - } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) { - ast_debug(3, "Deferring reinvite on SIP '%s' - It's audio will be redirected to IP %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip.sin_addr)); - /* We have a pending Invite. Send re-invite when we're done with the invite */ - ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); - } - } - /* Reset lastrtprx timer */ - p->lastrtprx = p->lastrtptx = time(NULL); - sip_pvt_unlock(p); - return 0; + if (instance) { + changed |= ast_rtp_instance_get_remote_address(instance, &p->redirip); + } else if (p->redirip.sin_addr.s_addr || ntohs(p->redirip.sin_port) != 0) { + memset(&p->redirip, 0, sizeof(p->redirip)); + changed = 1; + } + if (vinstance) { + changed |= ast_rtp_instance_get_remote_address(vinstance, &p->vredirip); + } else if (p->vredirip.sin_addr.s_addr || ntohs(p->vredirip.sin_port) != 0) { + memset(&p->vredirip, 0, sizeof(p->vredirip)); + changed = 1; + } + if (tinstance) { + changed |= ast_rtp_instance_get_remote_address(tinstance, &p->tredirip); + } else if (p->tredirip.sin_addr.s_addr || ntohs(p->tredirip.sin_port) != 0) { + memset(&p->tredirip, 0, sizeof(p->tredirip)); + changed = 1; + } + if (codecs && (p->redircodecs != codecs)) { + p->redircodecs = codecs; + changed = 1; + } + if (changed && !ast_test_flag(&p->flags[0], SIP_GOTREFER) && !ast_test_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER)) { + if (chan->_state != AST_STATE_UP) { /* We are in early state */ + if (p->do_history) + append_history(p, "ExtInv", "Initial invite sent with remote bridge proposal."); + ast_debug(1, "Early remote bridge setting SIP '%s' - Sending media to %s\n", p->callid, ast_inet_ntoa(instance ? p->redirip.sin_addr : p->ourip.sin_addr)); + } else if (!p->pendinginvite) { /* We are up, and have no outstanding invite */ + ast_debug(3, "Sending reinvite on SIP '%s' - It's audio soon redirected to IP %s\n", p->callid, ast_inet_ntoa(instance ? p->redirip.sin_addr : p->ourip.sin_addr)); + transmit_reinvite_with_sdp(p, FALSE, FALSE); + } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) { + ast_debug(3, "Deferring reinvite on SIP '%s' - It's audio will be redirected to IP %s\n", p->callid, ast_inet_ntoa(instance ? p->redirip.sin_addr : p->ourip.sin_addr)); + /* We have a pending Invite. Send re-invite when we're done with the invite */ + ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); + } + } + /* Reset lastrtprx timer */ + p->lastrtprx = p->lastrtptx = time(NULL); + sip_pvt_unlock(p); + return 0; } +static int sip_get_codec(struct ast_channel *chan) +{ + struct sip_pvt *p = chan->tech_pvt; + return p->peercapability ? p->peercapability : p->capability; +} + +static struct ast_rtp_glue sip_rtp_glue = { + .type = "SIP", + .get_rtp_info = sip_get_rtp_peer, + .get_vrtp_info = sip_get_vrtp_peer, + .get_trtp_info = sip_get_trtp_peer, + .update_peer = sip_set_rtp_peer, + .get_codec = sip_get_codec, +}; + static char *app_dtmfmode = "SIPDtmfMode"; static char *app_sipaddheader = "SIPAddHeader"; static char *app_sipremoveheader = "SIPRemoveHeader"; @@ -24140,7 +24167,7 @@ static int sip_dtmfmode(struct ast_channel *chan, void *data) } else ast_log(LOG_WARNING, "I don't know about this dtmf mode: %s\n", mode); if (p->rtp) - ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); + ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) { if (!p->vad) { p->vad = ast_dsp_new(); @@ -24288,13 +24315,6 @@ static int sip_sipredirect(struct sip_pvt *p, const char *dest) return 0; } -/*! \brief Return SIP UA's codec (part of the RTP interface) */ -static int sip_get_codec(struct ast_channel *chan) -{ - struct sip_pvt *p = chan->tech_pvt; - return p->jointcapability ? p->jointcapability : p->capability; -} - /*! \brief Send a poke to all known peers */ static void sip_poke_all_peers(void) { @@ -24502,12 +24522,12 @@ static int load_module(void) /* Register all CLI functions for SIP */ ast_cli_register_multiple(cli_sip, ARRAY_LEN(cli_sip)); - /* Tell the RTP subdriver that we're here */ - ast_rtp_proto_register(&sip_rtp); - /* Tell the UDPTL subdriver that we're here */ ast_udptl_proto_register(&sip_udptl); + /* Tell the RTP engine about our RTP glue */ + ast_rtp_glue_register(&sip_rtp_glue); + /* Register dialplan applications */ ast_register_application_xml(app_dtmfmode, sip_dtmfmode); ast_register_application_xml(app_sipaddheader, sip_addheader); @@ -24578,12 +24598,12 @@ static int unload_module(void) /* Unregister CLI commands */ ast_cli_unregister_multiple(cli_sip, ARRAY_LEN(cli_sip)); - /* Disconnect from the RTP subsystem */ - ast_rtp_proto_unregister(&sip_rtp); - /* Disconnect from UDPTL */ ast_udptl_proto_unregister(&sip_udptl); + /* Disconnect from RTP engine */ + ast_rtp_glue_unregister(&sip_rtp_glue); + /* Unregister AMI actions */ ast_manager_unregister("SIPpeers"); ast_manager_unregister("SIPshowpeer"); diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c index e8330fa824..f4104a89eb 100644 --- a/channels/chan_skinny.c +++ b/channels/chan_skinny.c @@ -49,7 +49,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/pbx.h" #include "asterisk/sched.h" #include "asterisk/io.h" -#include "asterisk/rtp.h" +#include "asterisk/rtp_engine.h" #include "asterisk/netsock.h" #include "asterisk/acl.h" #include "asterisk/callerid.h" @@ -1111,8 +1111,8 @@ static int matchdigittimeout = 3000; struct skinny_subchannel { ast_mutex_t lock; struct ast_channel *owner; - struct ast_rtp *rtp; - struct ast_rtp *vrtp; + struct ast_rtp_instance *rtp; + struct ast_rtp_instance *vrtp; unsigned int callid; /* time_t lastouttime; */ /* Unused */ int progress; @@ -1347,7 +1347,7 @@ static const struct ast_channel_tech skinny_tech = { .fixup = skinny_fixup, .send_digit_begin = skinny_senddigit_begin, .send_digit_end = skinny_senddigit_end, - .bridge = ast_rtp_bridge, + .bridge = ast_rtp_instance_bridge, }; static int skinny_extensionstate_cb(char *context, char* exten, int state, void *data); @@ -2557,46 +2557,48 @@ static void mwi_event_cb(const struct ast_event *event, void *userdata) /* I do not believe skinny can deal with video. Anyone know differently? */ /* Yes, it can. Currently 7985 and Cisco VT Advantage do video. */ -static enum ast_rtp_get_result skinny_get_vrtp_peer(struct ast_channel *c, struct ast_rtp **rtp) +static enum ast_rtp_glue_result skinny_get_vrtp_peer(struct ast_channel *c, struct ast_rtp_instance **instance) { struct skinny_subchannel *sub = NULL; if (!(sub = c->tech_pvt) || !(sub->vrtp)) - return AST_RTP_GET_FAILED; + return AST_RTP_GLUE_RESULT_FORBID; - *rtp = sub->vrtp; + ao2_ref(sub->vrtp, +1); + *instance = sub->vrtp; - return AST_RTP_TRY_NATIVE; + return AST_RTP_GLUE_RESULT_REMOTE; } -static enum ast_rtp_get_result skinny_get_rtp_peer(struct ast_channel *c, struct ast_rtp **rtp) +static enum ast_rtp_glue_result skinny_get_rtp_peer(struct ast_channel *c, struct ast_rtp_instance **instance) { struct skinny_subchannel *sub = NULL; struct skinny_line *l; - enum ast_rtp_get_result res = AST_RTP_TRY_NATIVE; + enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_REMOTE; if (skinnydebug) ast_verb(1, "skinny_get_rtp_peer() Channel = %s\n", c->name); if (!(sub = c->tech_pvt)) - return AST_RTP_GET_FAILED; + return AST_RTP_GLUE_RESULT_FORBID; ast_mutex_lock(&sub->lock); if (!(sub->rtp)){ ast_mutex_unlock(&sub->lock); - return AST_RTP_GET_FAILED; + return AST_RTP_GLUE_RESULT_FORBID; } - - *rtp = sub->rtp; + + ao2_ref(sub->rtp, +1); + *instance = sub->rtp; l = sub->parent; if (!l->canreinvite || l->nat){ - res = AST_RTP_TRY_PARTIAL; + res = AST_RTP_GLUE_RESULT_LOCAL; if (skinnydebug) - ast_verb(1, "skinny_get_rtp_peer() Using AST_RTP_TRY_PARTIAL \n"); + ast_verb(1, "skinny_get_rtp_peer() Using AST_RTP_GLUE_RESULT_LOCAL \n"); } ast_mutex_unlock(&sub->lock); @@ -2605,7 +2607,7 @@ static enum ast_rtp_get_result skinny_get_rtp_peer(struct ast_channel *c, struct } -static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active) +static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *trtp, int codecs, int nat_active) { struct skinny_subchannel *sub; struct skinny_line *l; @@ -2630,7 +2632,7 @@ static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp *rtp, struc s = d->session; if (rtp){ - ast_rtp_get_peer(rtp, &them); + ast_rtp_instance_get_remote_address(rtp, &them); /* Shutdown any early-media or previous media on re-invite */ if (!(req = req_alloc(sizeof(struct stop_media_transmission_message), STOP_MEDIA_TRANSMISSION_MESSAGE))) @@ -2654,7 +2656,7 @@ static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp *rtp, struc req->data.startmedia.conferenceId = htolel(sub->callid); req->data.startmedia.passThruPartyId = htolel(sub->callid); if (!(l->canreinvite) || (l->nat)){ - ast_rtp_get_us(rtp, &us); + ast_rtp_instance_get_local_address(rtp, &us); req->data.startmedia.remoteIp = htolel(d->ourip.s_addr); req->data.startmedia.remotePort = htolel(ntohs(us.sin_port)); } else { @@ -2675,11 +2677,11 @@ static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp *rtp, struc return 0; } -static struct ast_rtp_protocol skinny_rtp = { +static struct ast_rtp_glue skinny_rtp_glue = { .type = "Skinny", .get_rtp_info = skinny_get_rtp_peer, .get_vrtp_info = skinny_get_vrtp_peer, - .set_rtp_peer = skinny_set_rtp_peer, + .update_peer = skinny_set_rtp_peer, }; static char *handle_skinny_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) @@ -3559,29 +3561,36 @@ static void start_rtp(struct skinny_subchannel *sub) ast_mutex_lock(&sub->lock); /* Allocate the RTP */ - sub->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); + sub->rtp = ast_rtp_instance_new(NULL, sched, &bindaddr, NULL); if (hasvideo) - sub->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); - + sub->vrtp = ast_rtp_instance_new(NULL, sched, &bindaddr, NULL); + + if (sub->rtp) { + ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_RTCP, 1); + } + if (sub->vrtp) { + ast_rtp_instance_set_prop(sub->vrtp, AST_RTP_PROPERTY_RTCP, 1); + } + if (sub->rtp && sub->owner) { - ast_channel_set_fd(sub->owner, 0, ast_rtp_fd(sub->rtp)); - ast_channel_set_fd(sub->owner, 1, ast_rtcp_fd(sub->rtp)); + ast_channel_set_fd(sub->owner, 0, ast_rtp_instance_fd(sub->rtp, 0)); + ast_channel_set_fd(sub->owner, 1, ast_rtp_instance_fd(sub->rtp, 1)); } if (hasvideo && sub->vrtp && sub->owner) { - ast_channel_set_fd(sub->owner, 2, ast_rtp_fd(sub->vrtp)); - ast_channel_set_fd(sub->owner, 3, ast_rtcp_fd(sub->vrtp)); + ast_channel_set_fd(sub->owner, 2, ast_rtp_instance_fd(sub->vrtp, 0)); + ast_channel_set_fd(sub->owner, 3, ast_rtp_instance_fd(sub->vrtp, 1)); } if (sub->rtp) { - ast_rtp_setqos(sub->rtp, qos.tos_audio, qos.cos_audio, "Skinny RTP"); - ast_rtp_setnat(sub->rtp, l->nat); + ast_rtp_instance_set_qos(sub->rtp, qos.tos_audio, qos.cos_audio, "Skinny RTP"); + ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_NAT, l->nat); } if (sub->vrtp) { - ast_rtp_setqos(sub->vrtp, qos.tos_video, qos.cos_video, "Skinny VRTP"); - ast_rtp_setnat(sub->vrtp, l->nat); + ast_rtp_instance_set_qos(sub->vrtp, qos.tos_video, qos.cos_video, "Skinny VRTP"); + ast_rtp_instance_set_prop(sub->vrtp, AST_RTP_PROPERTY_NAT, l->nat); } /* Set Frame packetization */ if (sub->rtp) - ast_rtp_codec_setpref(sub->rtp, &l->prefs); + ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(sub->rtp), sub->rtp, &l->prefs); /* Create the RTP connection */ transmit_connect(d, sub); @@ -3852,7 +3861,7 @@ static int skinny_hangup(struct ast_channel *ast) sub->alreadygone = 0; sub->outgoing = 0; if (sub->rtp) { - ast_rtp_destroy(sub->rtp); + ast_rtp_instance_destroy(sub->rtp); sub->rtp = NULL; } ast_mutex_unlock(&sub->lock); @@ -3913,16 +3922,16 @@ static struct ast_frame *skinny_rtp_read(struct skinny_subchannel *sub) switch(ast->fdno) { case 0: - f = ast_rtp_read(sub->rtp); /* RTP Audio */ + f = ast_rtp_instance_read(sub->rtp, 0); /* RTP Audio */ break; case 1: - f = ast_rtcp_read(sub->rtp); /* RTCP Control Channel */ + f = ast_rtp_instance_read(sub->rtp, 1); /* RTCP Control Channel */ break; case 2: - f = ast_rtp_read(sub->vrtp); /* RTP Video */ + f = ast_rtp_instance_read(sub->vrtp, 0); /* RTP Video */ break; case 3: - f = ast_rtcp_read(sub->vrtp); /* RTCP Control Channel for video */ + f = ast_rtp_instance_read(sub->vrtp, 1); /* RTCP Control Channel for video */ break; #if 0 case 5: @@ -3979,7 +3988,7 @@ static int skinny_write(struct ast_channel *ast, struct ast_frame *frame) if (sub) { ast_mutex_lock(&sub->lock); if (sub->rtp) { - res = ast_rtp_write(sub->rtp, frame); + res = ast_rtp_instance_write(sub->rtp, frame); } ast_mutex_unlock(&sub->lock); } @@ -4253,7 +4262,7 @@ static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, s case AST_CONTROL_PROCEEDING: break; case AST_CONTROL_SRCUPDATE: - ast_rtp_new_source(sub->rtp); + ast_rtp_instance_new_source(sub->rtp); break; default: ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind); @@ -4312,7 +4321,7 @@ static struct ast_channel *skinny_new(struct skinny_line *l, int state) if (skinnydebug) ast_verb(1, "skinny_new: tmp->nativeformats=%d fmt=%d\n", tmp->nativeformats, fmt); if (sub->rtp) { - ast_channel_set_fd(tmp, 0, ast_rtp_fd(sub->rtp)); + ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(sub->rtp, 0)); } if (state == AST_STATE_RING) { tmp->rings = 1; @@ -5537,8 +5546,8 @@ static int handle_open_receive_channel_ack_message(struct skinny_req *req, struc l = sub->parent; if (sub->rtp) { - ast_rtp_set_peer(sub->rtp, &sin); - ast_rtp_get_us(sub->rtp, &us); + ast_rtp_instance_set_remote_address(sub->rtp, &sin); + ast_rtp_instance_get_local_address(sub->rtp, &us); } else { ast_log(LOG_ERROR, "No RTP structure, this is very bad\n"); return 0; @@ -7289,7 +7298,7 @@ static int load_module(void) return -1; } - ast_rtp_proto_register(&skinny_rtp); + ast_rtp_glue_register(&skinny_rtp_glue); ast_cli_register_multiple(cli_skinny, ARRAY_LEN(cli_skinny)); ast_manager_register2("SKINNYdevices", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_devices, @@ -7323,7 +7332,7 @@ static int unload_module(void) struct skinny_subchannel *sub; struct ast_context *con; - ast_rtp_proto_unregister(&skinny_rtp); + ast_rtp_glue_unregister(&skinny_rtp_glue); ast_channel_unregister(&skinny_tech); ast_cli_unregister_multiple(cli_skinny, ARRAY_LEN(cli_skinny)); diff --git a/channels/chan_unistim.c b/channels/chan_unistim.c index 818a32d71d..1cd94e02fc 100644 --- a/channels/chan_unistim.c +++ b/channels/chan_unistim.c @@ -60,7 +60,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/module.h" #include "asterisk/pbx.h" #include "asterisk/event.h" -#include "asterisk/rtp.h" +#include "asterisk/rtp_engine.h" #include "asterisk/netsock.h" #include "asterisk/acl.h" #include "asterisk/callerid.h" @@ -365,7 +365,7 @@ struct unistim_subchannel { /*! Unistim line */ struct unistim_line *parent; /*! RTP handle */ - struct ast_rtp *rtp; + struct ast_rtp_instance *rtp; int alreadygone; char ringvolume; char ringstyle; @@ -711,7 +711,7 @@ static const struct ast_channel_tech unistim_tech = { .send_digit_begin = unistim_senddigit_begin, .send_digit_end = unistim_senddigit_end, .send_text = unistim_sendtext, -/* .bridge = ast_rtp_bridge, */ + .bridge = ast_rtp_instance_bridge, }; static void display_last_error(const char *sz_msg) @@ -1854,7 +1854,7 @@ static void cancel_dial(struct unistimsession *pte) static void swap_subs(struct unistim_line *p, int a, int b) { /* struct ast_channel *towner; */ - struct ast_rtp *rtp; + struct ast_rtp_instance *rtp; int fds; if (unistimdebug) @@ -2056,30 +2056,29 @@ static void start_rtp(struct unistim_subchannel *sub) /* Allocate the RTP */ if (unistimdebug) ast_verb(0, "Starting RTP. Bind on %s\n", ast_inet_ntoa(sout.sin_addr)); - sub->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, sout.sin_addr); + sub->rtp = ast_rtp_instance_new(NULL, sched, &sout, NULL); if (!sub->rtp) { ast_log(LOG_WARNING, "Unable to create RTP session: %s binaddr=%s\n", strerror(errno), ast_inet_ntoa(sout.sin_addr)); ast_mutex_unlock(&sub->lock); return; } - if (sub->rtp && sub->owner) { - sub->owner->fds[0] = ast_rtp_fd(sub->rtp); - sub->owner->fds[1] = ast_rtcp_fd(sub->rtp); - } - if (sub->rtp) { - ast_rtp_setqos(sub->rtp, qos.tos_audio, qos.cos_audio, "UNISTIM RTP"); - ast_rtp_setnat(sub->rtp, sub->parent->parent->nat); + ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_RTCP, 1); + if (sub->owner) { + sub->owner->fds[0] = ast_rtp_instance_fd(sub->rtp, 0); + sub->owner->fds[1] = ast_rtp_instance_fd(sub->rtp, 1); } + ast_rtp_instance_set_qos(sub->rtp, qos.tos_audio, qos.cos_audio, "UNISTIM RTP"); + ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_NAT, sub->parent->parent->nat); /* Create the RTP connection */ - ast_rtp_get_us(sub->rtp, &us); + ast_rtp_instance_get_local_address(sub->rtp, &us); sin.sin_family = AF_INET; /* Setting up RTP for our side */ memcpy(&sin.sin_addr, &sub->parent->parent->session->sin.sin_addr, sizeof(sin.sin_addr)); sin.sin_port = htons(sub->parent->parent->rtp_port); - ast_rtp_set_peer(sub->rtp, &sin); + ast_rtp_instance_set_remote_address(sub->rtp, &sin); if (!(sub->owner->nativeformats & sub->owner->readformat)) { int fmt; fmt = ast_best_codec(sub->owner->nativeformats); @@ -2091,7 +2090,7 @@ static void start_rtp(struct unistim_subchannel *sub) sub->owner->readformat = fmt; sub->owner->writeformat = fmt; } - codec = ast_rtp_lookup_code(sub->rtp, 1, sub->owner->readformat); + codec = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(sub->rtp), 1, sub->owner->readformat); /* Setting up RTP of the phone */ if (public_ip.sin_family == 0) /* NAT IP override ? */ memcpy(&public, &us, sizeof(public)); /* No defined, using IP from recvmsg */ @@ -3724,7 +3723,7 @@ static int unistim_hangup(struct ast_channel *ast) if (sub->rtp) { if (unistimdebug) ast_verb(0, "Destroying RTP session\n"); - ast_rtp_destroy(sub->rtp); + ast_rtp_instance_destroy(sub->rtp); sub->rtp = NULL; } return 0; @@ -3769,7 +3768,7 @@ static int unistim_hangup(struct ast_channel *ast) if (sub->rtp) { if (unistimdebug) ast_verb(0, "Destroying RTP session\n"); - ast_rtp_destroy(sub->rtp); + ast_rtp_instance_destroy(sub->rtp); sub->rtp = NULL; } return 0; @@ -3794,7 +3793,7 @@ static int unistim_hangup(struct ast_channel *ast) if (sub->rtp) { if (unistimdebug) ast_verb(0, "Destroying RTP session\n"); - ast_rtp_destroy(sub->rtp); + ast_rtp_instance_destroy(sub->rtp); sub->rtp = NULL; } else if (unistimdebug) ast_verb(0, "No RTP session to destroy\n"); @@ -3921,10 +3920,10 @@ static struct ast_frame *unistim_rtp_read(const struct ast_channel *ast, switch (ast->fdno) { case 0: - f = ast_rtp_read(sub->rtp); /* RTP Audio */ + f = ast_rtp_instance_read(sub->rtp, 0); /* RTP Audio */ break; case 1: - f = ast_rtcp_read(sub->rtp); /* RTCP Control Channel */ + f = ast_rtp_instance_read(sub->rtp, 1); /* RTCP Control Channel */ break; default: f = &ast_null_frame; @@ -3990,7 +3989,7 @@ static int unistim_write(struct ast_channel *ast, struct ast_frame *frame) if (sub) { ast_mutex_lock(&sub->lock); if (sub->rtp) { - res = ast_rtp_write(sub->rtp, frame); + res = ast_rtp_instance_write(sub->rtp, frame); } ast_mutex_unlock(&sub->lock); } @@ -4455,8 +4454,8 @@ static struct ast_channel *unistim_new(struct unistim_subchannel *sub, int state if ((sub->rtp) && (sub->subtype == 0)) { if (unistimdebug) ast_verb(0, "New unistim channel with a previous rtp handle ?\n"); - tmp->fds[0] = ast_rtp_fd(sub->rtp); - tmp->fds[1] = ast_rtcp_fd(sub->rtp); + tmp->fds[0] = ast_rtp_instance_fd(sub->rtp, 0); + tmp->fds[1] = ast_rtp_instance_fd(sub->rtp, 1); } if (sub->rtp) ast_jb_configure(tmp, &global_jbconf); @@ -5526,51 +5525,19 @@ static int reload_config(void) return 0; } -static enum ast_rtp_get_result unistim_get_vrtp_peer(struct ast_channel *chan, - struct ast_rtp **rtp) -{ - return AST_RTP_TRY_NATIVE; -} - -static enum ast_rtp_get_result unistim_get_rtp_peer(struct ast_channel *chan, - struct ast_rtp **rtp) -{ - struct unistim_subchannel *sub; - enum ast_rtp_get_result res = AST_RTP_GET_FAILED; - - if (unistimdebug) - ast_verb(0, "unistim_get_rtp_peer called\n"); - - sub = chan->tech_pvt; - if (sub && sub->rtp) { - *rtp = sub->rtp; - res = AST_RTP_TRY_NATIVE; - } - - return res; -} - -static int unistim_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, - struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active) +static enum ast_rtp_glue_result unistim_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) { - struct unistim_subchannel *sub; - - if (unistimdebug) - ast_verb(0, "unistim_set_rtp_peer called\n"); - - sub = chan->tech_pvt; + struct unistim_subchannel *sub = chan->tech_pvt; - if (sub) - return 0; + ao2_ref(sub->rtp, +1); + *instance = sub->rtp; - return -1; + return AST_RTP_GLUE_RESULT_LOCAL; } -static struct ast_rtp_protocol unistim_rtp = { +static struct ast_rtp_glue unistim_rtp_glue = { .type = channel_type, .get_rtp_info = unistim_get_rtp_peer, - .get_vrtp_info = unistim_get_vrtp_peer, - .set_rtp_peer = unistim_set_rtp_peer, }; /*--- load_module: PBX load module - initialization ---*/ @@ -5603,7 +5570,7 @@ int load_module(void) goto chanreg_failed; } - ast_rtp_proto_register(&unistim_rtp); + ast_rtp_glue_register(&unistim_rtp_glue); ast_cli_register_multiple(unistim_cli, ARRAY_LEN(unistim_cli)); @@ -5634,7 +5601,7 @@ static int unload_module(void) ast_cli_unregister_multiple(unistim_cli, ARRAY_LEN(unistim_cli)); ast_channel_unregister(&unistim_tech); - ast_rtp_proto_unregister(&unistim_rtp); + ast_rtp_glue_unregister(&unistim_rtp_glue); ast_mutex_lock(&monlock); if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) { diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample index 37fcb7405a..3785618e3c 100644 --- a/configs/sip.conf.sample +++ b/configs/sip.conf.sample @@ -292,6 +292,8 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls ;contactpermit=172.16.0.0/255.255.0.0 ; restrict at what IPs your users may ; register their phones. +;engine=asterisk ; RTP engine to use when communicating with the device + ; ; If regcontext is specified, Asterisk will dynamically create and destroy a ; NoOp priority 1 extension for a given peer who registers or unregisters with diff --git a/include/asterisk/_private.h b/include/asterisk/_private.h index c709b1f152..c7e195fe6a 100644 --- a/include/asterisk/_private.h +++ b/include/asterisk/_private.h @@ -41,6 +41,7 @@ int ast_tps_init(void); /*!< Provided by taskprocessor.c */ int ast_timing_init(void); /*!< Provided by timing.c */ int ast_indications_init(void); /*!< Provided by indications.c */ int ast_indications_reload(void);/*!< Provided by indications.c */ +void ast_stun_init(void); /*!< Provided by stun.c */ /*! * \brief Reload asterisk modules. diff --git a/include/asterisk/rtp.h b/include/asterisk/rtp.h deleted file mode 100644 index c42906a064..0000000000 --- a/include/asterisk/rtp.h +++ /dev/null @@ -1,416 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2006, Digium, Inc. - * - * Mark Spencer <markster@digium.com> - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! - * \file rtp.h - * \brief Supports RTP and RTCP with Symmetric RTP support for NAT traversal. - * - * RTP is defined in RFC 3550. - */ - -#ifndef _ASTERISK_RTP_H -#define _ASTERISK_RTP_H - -#include "asterisk/network.h" - -#include "asterisk/frame.h" -#include "asterisk/io.h" -#include "asterisk/sched.h" -#include "asterisk/channel.h" -#include "asterisk/linkedlists.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -/* Codes for RTP-specific data - not defined by our AST_FORMAT codes */ -/*! DTMF (RFC2833) */ -#define AST_RTP_DTMF (1 << 0) -/*! 'Comfort Noise' (RFC3389) */ -#define AST_RTP_CN (1 << 1) -/*! DTMF (Cisco Proprietary) */ -#define AST_RTP_CISCO_DTMF (1 << 2) -/*! Maximum RTP-specific code */ -#define AST_RTP_MAX AST_RTP_CISCO_DTMF - -/*! Maxmum number of payload defintions for a RTP session */ -#define MAX_RTP_PT 256 - -/*! T.140 Redundancy Maxium number of generations */ -#define RED_MAX_GENERATION 5 - -#define FLAG_3389_WARNING (1 << 0) - -enum ast_rtp_options { - AST_RTP_OPT_G726_NONSTANDARD = (1 << 0), -}; - -enum ast_rtp_get_result { - /*! Failed to find the RTP structure */ - AST_RTP_GET_FAILED = 0, - /*! RTP structure exists but true native bridge can not occur so try partial */ - AST_RTP_TRY_PARTIAL, - /*! RTP structure exists and native bridge can occur */ - AST_RTP_TRY_NATIVE, -}; - -/*! \brief Variables used in ast_rtcp_get function */ -enum ast_rtp_qos_vars { - AST_RTP_TXCOUNT, - AST_RTP_RXCOUNT, - AST_RTP_TXJITTER, - AST_RTP_RXJITTER, - AST_RTP_RXPLOSS, - AST_RTP_TXPLOSS, - AST_RTP_RTT -}; - -struct ast_rtp; -/*! T.140 Redundancy structure*/ -struct rtp_red; - -/*! \brief The value of each payload format mapping: */ -struct rtpPayloadType { - int isAstFormat; /*!< whether the following code is an AST_FORMAT */ - int code; -}; - -/*! \brief This is the structure that binds a channel (SIP/Jingle/H.323) to the RTP subsystem -*/ -struct ast_rtp_protocol { - /*! Get RTP struct, or NULL if unwilling to transfer */ - enum ast_rtp_get_result (* const get_rtp_info)(struct ast_channel *chan, struct ast_rtp **rtp); - /*! Get RTP struct, or NULL if unwilling to transfer */ - enum ast_rtp_get_result (* const get_vrtp_info)(struct ast_channel *chan, struct ast_rtp **rtp); - /*! Get RTP struct, or NULL if unwilling to transfer */ - enum ast_rtp_get_result (* const get_trtp_info)(struct ast_channel *chan, struct ast_rtp **rtp); - /*! Set RTP peer */ - int (* const set_rtp_peer)(struct ast_channel *chan, struct ast_rtp *peer, struct ast_rtp *vpeer, struct ast_rtp *tpeer, int codecs, int nat_active); - int (* const get_codec)(struct ast_channel *chan); - const char * const type; - AST_LIST_ENTRY(ast_rtp_protocol) list; -}; - -enum ast_rtp_quality_type { - RTPQOS_SUMMARY = 0, - RTPQOS_JITTER, - RTPQOS_LOSS, - RTPQOS_RTT -}; - -/*! \brief RTCP quality report storage */ -struct ast_rtp_quality { - unsigned int local_ssrc; /*!< Our SSRC */ - unsigned int local_lostpackets; /*!< Our lost packets */ - double local_jitter; /*!< Our calculated jitter */ - unsigned int local_count; /*!< Number of received packets */ - unsigned int remote_ssrc; /*!< Their SSRC */ - unsigned int remote_lostpackets; /*!< Their lost packets */ - double remote_jitter; /*!< Their reported jitter */ - unsigned int remote_count; /*!< Number of transmitted packets */ - double rtt; /*!< Round trip time */ -}; - -/*! RTP callback structure */ -typedef int (*ast_rtp_callback)(struct ast_rtp *rtp, struct ast_frame *f, void *data); - -/*! - * \brief Get the amount of space required to hold an RTP session - * \return number of bytes required - */ -size_t ast_rtp_alloc_size(void); - -/*! - * \brief Initializate a RTP session. - * - * \param sched - * \param io - * \param rtcpenable - * \param callbackmode - * \return A representation (structure) of an RTP session. - */ -struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode); - -/*! - * \brief Initializate a RTP session using an in_addr structure. - * - * This fuction gets called by ast_rtp_new(). - * - * \param sched - * \param io - * \param rtcpenable - * \param callbackmode - * \param in - * \return A representation (structure) of an RTP session. - */ -struct ast_rtp *ast_rtp_new_with_bindaddr(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode, struct in_addr in); - -void ast_rtp_set_peer(struct ast_rtp *rtp, struct sockaddr_in *them); - -/* Copies from rtp to them and returns 1 if there was a change or 0 if it was already the same */ -int ast_rtp_get_peer(struct ast_rtp *rtp, struct sockaddr_in *them); - -void ast_rtp_get_us(struct ast_rtp *rtp, struct sockaddr_in *us); - -struct ast_rtp *ast_rtp_get_bridged(struct ast_rtp *rtp); - -/*! Destroy RTP session */ -void ast_rtp_destroy(struct ast_rtp *rtp); - -void ast_rtp_reset(struct ast_rtp *rtp); - -/*! Stop RTP session, do not destroy structure */ -void ast_rtp_stop(struct ast_rtp *rtp); - -void ast_rtp_set_callback(struct ast_rtp *rtp, ast_rtp_callback callback); - -void ast_rtp_set_data(struct ast_rtp *rtp, void *data); - -int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *f); - -struct ast_frame *ast_rtp_read(struct ast_rtp *rtp); - -struct ast_frame *ast_rtcp_read(struct ast_rtp *rtp); - -int ast_rtp_fd(struct ast_rtp *rtp); - -int ast_rtcp_fd(struct ast_rtp *rtp); - -int ast_rtp_senddigit_begin(struct ast_rtp *rtp, char digit); - -int ast_rtp_senddigit_end(struct ast_rtp *rtp, char digit); - -int ast_rtp_sendcng(struct ast_rtp *rtp, int level); - -int ast_rtp_setqos(struct ast_rtp *rtp, int tos, int cos, char *desc); - -void ast_rtp_new_source(struct ast_rtp *rtp); - -/*! \brief Setting RTP payload types from lines in a SDP description: */ -void ast_rtp_pt_clear(struct ast_rtp* rtp); -/*! \brief Set payload types to defaults */ -void ast_rtp_pt_default(struct ast_rtp* rtp); - -/*! \brief Copy payload types between RTP structures */ -void ast_rtp_pt_copy(struct ast_rtp *dest, struct ast_rtp *src); - -/*! \brief Activate payload type */ -void ast_rtp_set_m_type(struct ast_rtp* rtp, int pt); - -/*! \brief clear payload type */ -void ast_rtp_unset_m_type(struct ast_rtp* rtp, int pt); - -/*! \brief Set payload type to a known MIME media type for a codec - * - * \param rtp RTP structure to modify - * \param pt Payload type entry to modify - * \param mimeType top-level MIME type of media stream (typically "audio", "video", "text", etc.) - * \param mimeSubtype MIME subtype of media stream (typically a codec name) - * \param options Zero or more flags from the ast_rtp_options enum - * - * This function 'fills in' an entry in the list of possible formats for - * a media stream associated with an RTP structure. - * - * \retval 0 on success - * \retval -1 if the payload type is out of range - * \retval -2 if the mimeType/mimeSubtype combination was not found - */ -int ast_rtp_set_rtpmap_type(struct ast_rtp* rtp, int pt, - char *mimeType, char *mimeSubtype, - enum ast_rtp_options options); - -/*! \brief Set payload type to a known MIME media type for a codec with a specific sample rate - * - * \param rtp RTP structure to modify - * \param pt Payload type entry to modify - * \param mimeType top-level MIME type of media stream (typically "audio", "video", "text", etc.) - * \param mimeSubtype MIME subtype of media stream (typically a codec name) - * \param options Zero or more flags from the ast_rtp_options enum - * \param sample_rate The sample rate of the media stream - * - * This function 'fills in' an entry in the list of possible formats for - * a media stream associated with an RTP structure. - * - * \retval 0 on success - * \retval -1 if the payload type is out of range - * \retval -2 if the mimeType/mimeSubtype combination was not found - */ -int ast_rtp_set_rtpmap_type_rate(struct ast_rtp* rtp, int pt, - char *mimeType, char *mimeSubtype, - enum ast_rtp_options options, - unsigned int sample_rate); - -/*! \brief Mapping between RTP payload format codes and Asterisk codes: */ -struct rtpPayloadType ast_rtp_lookup_pt(struct ast_rtp* rtp, int pt); -int ast_rtp_lookup_code(struct ast_rtp* rtp, int isAstFormat, int code); - -void ast_rtp_get_current_formats(struct ast_rtp* rtp, - int* astFormats, int* nonAstFormats); - -/*! \brief Mapping an Asterisk code into a MIME subtype (string): */ -const char *ast_rtp_lookup_mime_subtype(int isAstFormat, int code, - enum ast_rtp_options options); - -/*! \brief Get the sample rate associated with known RTP payload types - * - * \param isAstFormat True if the value in the 'code' parameter is an AST_FORMAT value - * \param code Format code, either from AST_FORMAT list or from AST_RTP list - * - * \return the sample rate if the format was found, zero if it was not found - */ -unsigned int ast_rtp_lookup_sample_rate(int isAstFormat, int code); - -/*! \brief Build a string of MIME subtype names from a capability list */ -char *ast_rtp_lookup_mime_multiple(char *buf, size_t size, const int capability, - const int isAstFormat, enum ast_rtp_options options); - -void ast_rtp_setnat(struct ast_rtp *rtp, int nat); - -int ast_rtp_getnat(struct ast_rtp *rtp); - -/*! \brief Indicate whether this RTP session is carrying DTMF or not */ -void ast_rtp_setdtmf(struct ast_rtp *rtp, int dtmf); - -/*! \brief Compensate for devices that send RFC2833 packets all at once */ -void ast_rtp_setdtmfcompensate(struct ast_rtp *rtp, int compensate); - -/*! \brief Enable STUN capability */ -void ast_rtp_setstun(struct ast_rtp *rtp, int stun_enable); - -/*! \brief Generic STUN request - * send a generic stun request to the server specified. - * \param s the socket used to send the request - * \param dst the address of the STUN server - * \param username if non null, add the username in the request - * \param answer if non null, the function waits for a response and - * puts here the externally visible address. - * \return 0 on success, other values on error. - * The interface it may change in the future. - */ -int ast_stun_request(int s, struct sockaddr_in *dst, - const char *username, struct sockaddr_in *answer); - -/*! \brief Send STUN request for an RTP socket - * Deprecated, this is just a wrapper for ast_rtp_stun_request() - */ -void ast_rtp_stun_request(struct ast_rtp *rtp, struct sockaddr_in *suggestion, const char *username); - -/*! \brief The RTP bridge. - \arg \ref AstRTPbridge -*/ -int ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms); - -/*! \brief Register an RTP channel client */ -int ast_rtp_proto_register(struct ast_rtp_protocol *proto); - -/*! \brief Unregister an RTP channel client */ -void ast_rtp_proto_unregister(struct ast_rtp_protocol *proto); - -int ast_rtp_make_compatible(struct ast_channel *dest, struct ast_channel *src, int media); - -/*! \brief If possible, create an early bridge directly between the devices without - having to send a re-invite later */ -int ast_rtp_early_bridge(struct ast_channel *c0, struct ast_channel *c1); - -/*! \brief Get QOS stats on a RTP channel - * \since 1.6.1 - */ -int ast_rtp_get_qos(struct ast_rtp *rtp, const char *qos, char *buf, unsigned int buflen); - -/*! \brief Return RTP and RTCP QoS values - * \since 1.6.1 - */ -unsigned int ast_rtp_get_qosvalue(struct ast_rtp *rtp, enum ast_rtp_qos_vars value); - -/*! \brief Set RTPAUDIOQOS(...) variables on a channel when it is being hung up - * \since 1.6.1 - */ -void ast_rtp_set_vars(struct ast_channel *chan, struct ast_rtp *rtp); - -/*! \brief Return RTCP quality string - * - * \param rtp An rtp structure to get qos information about. - * - * \param qual An (optional) rtp quality structure that will be - * filled with the quality information described in - * the ast_rtp_quality structure. This structure is - * not dependent on any qtype, so a call for any - * type of information would yield the same results - * because ast_rtp_quality is not a data type - * specific to any qos type. - * - * \param qtype The quality type you'd like, default should be - * RTPQOS_SUMMARY which returns basic information - * about the call. The return from RTPQOS_SUMMARY - * is basically ast_rtp_quality in a string. The - * other types are RTPQOS_JITTER, RTPQOS_LOSS and - * RTPQOS_RTT which will return more specific - * statistics. - * \version 1.6.1 added qtype parameter - */ -char *ast_rtp_get_quality(struct ast_rtp *rtp, struct ast_rtp_quality *qual, enum ast_rtp_quality_type qtype); -/*! \brief Send an H.261 fast update request. Some devices need this rather than the XML message in SIP */ -int ast_rtcp_send_h261fur(void *data); - -void ast_rtp_init(void); /*! Initialize RTP subsystem */ -int ast_rtp_reload(void); /*! reload rtp configuration */ -void ast_rtp_new_init(struct ast_rtp *rtp); - -/*! \brief Set codec preference */ -void ast_rtp_codec_setpref(struct ast_rtp *rtp, struct ast_codec_pref *prefs); - -/*! \brief Get codec preference */ -struct ast_codec_pref *ast_rtp_codec_getpref(struct ast_rtp *rtp); - -/*! \brief get format from predefined dynamic payload format */ -int ast_rtp_codec_getformat(int pt); - -/*! \brief Set rtp timeout */ -void ast_rtp_set_rtptimeout(struct ast_rtp *rtp, int timeout); -/*! \brief Set rtp hold timeout */ -void ast_rtp_set_rtpholdtimeout(struct ast_rtp *rtp, int timeout); -/*! \brief set RTP keepalive interval */ -void ast_rtp_set_rtpkeepalive(struct ast_rtp *rtp, int period); -/*! \brief Get RTP keepalive interval */ -int ast_rtp_get_rtpkeepalive(struct ast_rtp *rtp); -/*! \brief Get rtp hold timeout */ -int ast_rtp_get_rtpholdtimeout(struct ast_rtp *rtp); -/*! \brief Get rtp timeout */ -int ast_rtp_get_rtptimeout(struct ast_rtp *rtp); -/* \brief Put RTP timeout timers on hold during another transaction, like T.38 */ -void ast_rtp_set_rtptimers_onhold(struct ast_rtp *rtp); - -/*! \brief Initalize t.140 redudancy - * \param ti time between each t140red frame is sent - * \param red_pt payloadtype for RTP packet - * \param pt payloadtype numbers for each generation including primary data - * \param num_gen number of redundant generations, primary data excluded - * \since 1.6.1 - */ -int ast_rtp_red_init(struct ast_rtp *rtp, int ti, int *pt, int num_gen); - -/*! \brief Buffer t.140 data */ -void ast_red_buffer_t140(struct ast_rtp *rtp, struct ast_frame *f); - - - -#if defined(__cplusplus) || defined(c_plusplus) -} -#endif - -#endif /* _ASTERISK_RTP_H */ diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h new file mode 100644 index 0000000000..edd7d1c47c --- /dev/null +++ b/include/asterisk/rtp_engine.h @@ -0,0 +1,1594 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2009, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * Joshua Colp <jcolp@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * \brief Pluggable RTP Architecture + * \author Joshua Colp <jcolp@digium.com> + * \ref AstRTPEngine + */ + +/*! + * \page AstRTPEngine Asterisk RTP Engine API + * + * The purpose of this API is to provide a way for multiple RTP stacks to be used inside + * of Asterisk without any module that uses RTP knowing any different. To the module each RTP + * stack behaves the same. + * + * An RTP session is called an instance and is made up of a combination of codec information, + * RTP engine, RTP properties, and address information. An engine name may be passed in to explicitly + * choose an RTP stack to be used but a default one will be used if none is provided. An address to use + * for RTP may also be provided but the underlying RTP engine may choose a different address depending on + * it's configuration. + * + * An RTP engine is the layer between the RTP engine core and the RTP stack itself. The RTP engine core provides + * a set of callbacks to do various things (such as write audio out) that the RTP engine has to have implemented. + * + * Glue is what binds an RTP instance to a channel. It is used to retrieve RTP instance information when + * performing remote or local bridging and is used to have the channel driver tell the remote side to change + * destination of the RTP stream. + * + * Statistics from an RTP instance can be retrieved using the ast_rtp_instance_get_stats API call. This essentially + * asks the RTP engine in use to fill in a structure with the requested values. It is not required for an RTP engine + * to support all statistic values. + * + * Properties allow behavior of the RTP engine and RTP engine core to be changed. For example, there is a property named + * AST_RTP_PROPERTY_NAT which is used to tell the RTP engine to enable symmetric RTP if it supports it. It is not required + * for an RTP engine to support all properties. + * + * Codec information is stored using a separate data structure which has it's own set of API calls to add/remove/retrieve + * information. They are used by the module after an RTP instance is created so that payload information is available for + * the RTP engine. + */ + +#ifndef _ASTERISK_RTP_ENGINE_H +#define _ASTERISK_RTP_ENGINE_H + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#include "asterisk/astobj2.h" + +/* Maximum number of payloads supported */ +#define AST_RTP_MAX_PT 256 + +/* Maximum number of generations */ +#define AST_RED_MAX_GENERATION 5 + +struct ast_rtp_instance; +struct ast_rtp_glue; + +/*! RTP Properties that can be set on an RTP instance */ +enum ast_rtp_property { + /*! Enable symmetric RTP support */ + AST_RTP_PROPERTY_NAT = 0, + /*! RTP instance will be carrying DTMF (using RFC2833) */ + AST_RTP_PROPERTY_DTMF, + /*! Expect unreliable DTMF from remote party */ + AST_RTP_PROPERTY_DTMF_COMPENSATE, + /*! Enable STUN support */ + AST_RTP_PROPERTY_STUN, + /*! Enable RTCP support */ + AST_RTP_PROPERTY_RTCP, + /*! Maximum number of RTP properties supported */ + AST_RTP_PROPERTY_MAX, +}; + +/*! Additional RTP options */ +enum ast_rtp_options { + /*! Remote side is using non-standard G.726 */ + AST_RTP_OPT_G726_NONSTANDARD = (1 << 0), +}; + +/*! RTP DTMF Modes */ +enum ast_rtp_dtmf_mode { + /*! No DTMF is being carried over the RTP stream */ + AST_RTP_DTMF_MODE_NONE = 0, + /*! DTMF is being carried out of band using RFC2833 */ + AST_RTP_DTMF_MODE_RFC2833, + /*! DTMF is being carried inband over the RTP stream */ + AST_RTP_DTMF_MODE_INBAND, +}; + +/*! Result codes when RTP glue is queried for information */ +enum ast_rtp_glue_result { + /*! No remote or local bridging is permitted */ + AST_RTP_GLUE_RESULT_FORBID = 0, + /*! Move RTP stream to be remote between devices directly */ + AST_RTP_GLUE_RESULT_REMOTE, + /*! Perform RTP engine level bridging if possible */ + AST_RTP_GLUE_RESULT_LOCAL, +}; + +/*! Field statistics that can be retrieved from an RTP instance */ +enum ast_rtp_instance_stat_field { + /*! Retrieve quality information */ + AST_RTP_INSTANCE_STAT_FIELD_QUALITY = 0, + /*! Retrieve quality information about jitter */ + AST_RTP_INSTANCE_STAT_FIELD_QUALITY_JITTER, + /*! Retrieve quality information about packet loss */ + AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS, + /*! Retrieve quality information about round trip time */ + AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT, +}; + +/*! Statistics that can be retrieved from an RTP instance */ +enum ast_rtp_instance_stat { + /*! Retrieve all statistics */ + AST_RTP_INSTANCE_STAT_ALL = 0, + /*! Retrieve number of packets transmitted */ + AST_RTP_INSTANCE_STAT_TXCOUNT, + /*! Retrieve number of packets received */ + AST_RTP_INSTANCE_STAT_RXCOUNT, + /*! Retrieve ALL statistics relating to packet loss */ + AST_RTP_INSTANCE_STAT_COMBINED_LOSS, + /*! Retrieve number of packets lost for transmitting */ + AST_RTP_INSTANCE_STAT_TXPLOSS, + /*! Retrieve number of packets lost for receiving */ + AST_RTP_INSTANCE_STAT_RXPLOSS, + /*! Retrieve maximum number of packets lost on remote side */ + AST_RTP_INSTANCE_STAT_REMOTE_MAXRXPLOSS, + /*! Retrieve minimum number of packets lost on remote side */ + AST_RTP_INSTANCE_STAT_REMOTE_MINRXPLOSS, + /*! Retrieve average number of packets lost on remote side */ + AST_RTP_INSTANCE_STAT_REMOTE_NORMDEVRXPLOSS, + /*! Retrieve standard deviation of packets lost on remote side */ + AST_RTP_INSTANCE_STAT_REMOTE_STDEVRXPLOSS, + /*! Retrieve maximum number of packets lost on local side */ + AST_RTP_INSTANCE_STAT_LOCAL_MAXRXPLOSS, + /*! Retrieve minimum number of packets lost on local side */ + AST_RTP_INSTANCE_STAT_LOCAL_MINRXPLOSS, + /*! Retrieve average number of packets lost on local side */ + AST_RTP_INSTANCE_STAT_LOCAL_NORMDEVRXPLOSS, + /*! Retrieve standard deviation of packets lost on local side */ + AST_RTP_INSTANCE_STAT_LOCAL_STDEVRXPLOSS, + /*! Retrieve ALL statistics relating to jitter */ + AST_RTP_INSTANCE_STAT_COMBINED_JITTER, + /*! Retrieve jitter on transmitted packets */ + AST_RTP_INSTANCE_STAT_TXJITTER, + /*! Retrieve jitter on received packets */ + AST_RTP_INSTANCE_STAT_RXJITTER, + /*! Retrieve maximum jitter on remote side */ + AST_RTP_INSTANCE_STAT_REMOTE_MAXJITTER, + /*! Retrieve minimum jitter on remote side */ + AST_RTP_INSTANCE_STAT_REMOTE_MINJITTER, + /*! Retrieve average jitter on remote side */ + AST_RTP_INSTANCE_STAT_REMOTE_NORMDEVJITTER, + /*! Retrieve standard deviation jitter on remote side */ + AST_RTP_INSTANCE_STAT_REMOTE_STDEVJITTER, + /*! Retrieve maximum jitter on local side */ + AST_RTP_INSTANCE_STAT_LOCAL_MAXJITTER, + /*! Retrieve minimum jitter on local side */ + AST_RTP_INSTANCE_STAT_LOCAL_MINJITTER, + /*! Retrieve average jitter on local side */ + AST_RTP_INSTANCE_STAT_LOCAL_NORMDEVJITTER, + /*! Retrieve standard deviation jitter on local side */ + AST_RTP_INSTANCE_STAT_LOCAL_STDEVJITTER, + /*! Retrieve ALL statistics relating to round trip time */ + AST_RTP_INSTANCE_STAT_COMBINED_RTT, + /*! Retrieve round trip time */ + AST_RTP_INSTANCE_STAT_RTT, + /*! Retrieve maximum round trip time */ + AST_RTP_INSTANCE_STAT_MAX_RTT, + /*! Retrieve minimum round trip time */ + AST_RTP_INSTANCE_STAT_MIN_RTT, + /*! Retrieve average round trip time */ + AST_RTP_INSTANCE_STAT_NORMDEVRTT, + /*! Retrieve standard deviation round trip time */ + AST_RTP_INSTANCE_STAT_STDEVRTT, + /*! Retrieve local SSRC */ + AST_RTP_INSTANCE_STAT_LOCAL_SSRC, + /*! Retrieve remote SSRC */ + AST_RTP_INSTANCE_STAT_REMOTE_SSRC, +}; + +/* Codes for RTP-specific data - not defined by our AST_FORMAT codes */ +/*! DTMF (RFC2833) */ +#define AST_RTP_DTMF (1 << 0) +/*! 'Comfort Noise' (RFC3389) */ +#define AST_RTP_CN (1 << 1) +/*! DTMF (Cisco Proprietary) */ +#define AST_RTP_CISCO_DTMF (1 << 2) +/*! Maximum RTP-specific code */ +#define AST_RTP_MAX AST_RTP_CISCO_DTMF + +/*! Structure that represents a payload */ +struct ast_rtp_payload_type { + /*! Is this an Asterisk value */ + int asterisk_format; + /*! Actual internal value of the payload */ + int code; +}; + +/*! Structure that represents statistics from an RTP instance */ +struct ast_rtp_instance_stats { + /*! Number of packets transmitted */ + unsigned int txcount; + /*! Number of packets received */ + unsigned int rxcount; + /*! Jitter on transmitted packets */ + unsigned int txjitter; + /*! Jitter on received packets */ + unsigned int rxjitter; + /*! Maximum jitter on remote side */ + double remote_maxjitter; + /*! Minimum jitter on remote side */ + double remote_minjitter; + /*! Average jitter on remote side */ + double remote_normdevjitter; + /*! Standard deviation jitter on remote side */ + double remote_stdevjitter; + /*! Maximum jitter on local side */ + double local_maxjitter; + /*! Minimum jitter on local side */ + double local_minjitter; + /*! Average jitter on local side */ + double local_normdevjitter; + /*! Standard deviation jitter on local side */ + double local_stdevjitter; + /*! Number of transmitted packets lost */ + unsigned int txploss; + /*! Number of received packets lost */ + unsigned int rxploss; + /*! Maximum number of packets lost on remote side */ + double remote_maxrxploss; + /*! Minimum number of packets lost on remote side */ + double remote_minrxploss; + /*! Average number of packets lost on remote side */ + double remote_normdevrxploss; + /*! Standard deviation packets lost on remote side */ + double remote_stdevrxploss; + /*! Maximum number of packets lost on local side */ + double local_maxrxploss; + /*! Minimum number of packets lost on local side */ + double local_minrxploss; + /*! Average number of packets lost on local side */ + double local_normdevrxploss; + /*! Standard deviation packets lost on local side */ + double local_stdevrxploss; + /*! Total round trip time */ + unsigned int rtt; + /*! Maximum round trip time */ + double maxrtt; + /*! Minimum round trip time */ + double minrtt; + /*! Average round trip time */ + double normdevrtt; + /*! Standard deviation round trip time */ + double stdevrtt; + /*! Our SSRC */ + unsigned int local_ssrc; + /*! Their SSRC */ + unsigned int remote_ssrc; +}; + +#define AST_RTP_STAT_SET(current_stat, combined, placement, value) \ +if (stat == current_stat || stat == AST_RTP_INSTANCE_STAT_ALL || (combined >= 0 && combined == current_stat)) { \ +placement = value; \ +if (stat == current_stat) { \ +return 0; \ +} \ +} + +#define AST_RTP_STAT_TERMINATOR(combined) \ +if (stat == combined) { \ +return 0; \ +} + +/*! Structure that represents an RTP stack (engine) */ +struct ast_rtp_engine { + /*! Name of the RTP engine, used when explicitly requested */ + const char *name; + /*! Module this RTP engine came from, used for reference counting */ + struct ast_module *mod; + /*! Callback for setting up a new RTP instance */ + int (*new)(struct ast_rtp_instance *instance, struct sched_context *sched, struct sockaddr_in *sin, void *data); + /*! Callback for destroying an RTP instance */ + int (*destroy)(struct ast_rtp_instance *instance); + /*! Callback for writing out a frame */ + int (*write)(struct ast_rtp_instance *instance, struct ast_frame *frame); + /*! Callback for stopping the RTP instance */ + void (*stop)(struct ast_rtp_instance *instance); + /*! Callback for starting RFC2833 DTMF transmission */ + int (*dtmf_begin)(struct ast_rtp_instance *instance, char digit); + /*! Callback for stopping RFC2833 DTMF transmission */ + int (*dtmf_end)(struct ast_rtp_instance *instance, char digit); + /*! Callback to indicate that a new source of media has come in */ + void (*new_source)(struct ast_rtp_instance *instance); + /*! Callback for setting an extended RTP property */ + int (*extended_prop_set)(struct ast_rtp_instance *instance, int property, void *value); + /*! Callback for getting an extended RTP property */ + void *(*extended_prop_get)(struct ast_rtp_instance *instance, int property); + /*! Callback for setting an RTP property */ + void (*prop_set)(struct ast_rtp_instance *instance, enum ast_rtp_property property, int value); + /*! Callback for setting a payload */ + void (*payload_set)(struct ast_rtp_instance *instance, int payload, int astformat, int format); + /*! Callback for setting packetization preferences */ + void (*packetization_set)(struct ast_rtp_instance *instance, struct ast_codec_pref *pref); + /*! Callback for setting the remote address that RTP is to be sent to */ + void (*remote_address_set)(struct ast_rtp_instance *instance, struct sockaddr_in *sin); + /*! Callback for changing DTMF mode */ + int (*dtmf_mode_set)(struct ast_rtp_instance *instance, enum ast_rtp_dtmf_mode dtmf_mode); + /*! Callback for retrieving statistics */ + int (*get_stat)(struct ast_rtp_instance *instance, struct ast_rtp_instance_stats *stats, enum ast_rtp_instance_stat stat); + /*! Callback for setting QoS values */ + int (*qos)(struct ast_rtp_instance *instance, int tos, int cos, const char *desc); + /*! Callback for retrieving a file descriptor to poll on, not always required */ + int (*fd)(struct ast_rtp_instance *instance, int rtcp); + /*! Callback for initializing RED support */ + int (*red_init)(struct ast_rtp_instance *instance, int buffer_time, int *payloads, int generations); + /*! Callback for buffering a frame using RED */ + int (*red_buffer)(struct ast_rtp_instance *instance, struct ast_frame *frame); + /*! Callback for reading a frame from the RTP engine */ + struct ast_frame *(*read)(struct ast_rtp_instance *instance, int rtcp); + /*! Callback to locally bridge two RTP instances */ + int (*local_bridge)(struct ast_rtp_instance *instance0, struct ast_rtp_instance *instance1); + /*! Callback to set the read format */ + int (*set_read_format)(struct ast_rtp_instance *instance, int format); + /*! Callback to set the write format */ + int (*set_write_format)(struct ast_rtp_instance *instance, int format); + /*! Callback to make two instances compatible */ + int (*make_compatible)(struct ast_channel *chan0, struct ast_rtp_instance *instance0, struct ast_channel *chan1, struct ast_rtp_instance *instance1); + /*! Callback to see if two instances are compatible with DTMF */ + int (*dtmf_compatible)(struct ast_channel *chan0, struct ast_rtp_instance *instance0, struct ast_channel *chan1, struct ast_rtp_instance *instance1); + /*! Callback to indicate that packets will now flow */ + int (*activate)(struct ast_rtp_instance *instance); + /*! Callback to request that the RTP engine send a STUN BIND request */ + void (*stun_request)(struct ast_rtp_instance *instance, struct sockaddr_in *suggestion, const char *username); + /*! Linked list information */ + AST_RWLIST_ENTRY(ast_rtp_engine) entry; +}; + +/*! Structure that represents codec and packetization information */ +struct ast_rtp_codecs { + /*! Codec packetization preferences */ + struct ast_codec_pref pref; + /*! Payloads present */ + struct ast_rtp_payload_type payloads[AST_RTP_MAX_PT]; +}; + +/*! Structure that represents the glue that binds an RTP instance to a channel */ +struct ast_rtp_glue { + /*! Name of the channel driver that this glue is responsible for */ + const char *type; + /*! Module that the RTP glue came from */ + struct ast_module *mod; + /*! + * \brief Callback for retrieving the RTP instance carrying audio + * \note This function increases the reference count on the returned RTP instance. + */ + enum ast_rtp_glue_result (*get_rtp_info)(struct ast_channel *chan, struct ast_rtp_instance **instance); + /*! + * \brief Callback for retrieving the RTP instance carrying video + * \note This function increases the reference count on the returned RTP instance. + */ + enum ast_rtp_glue_result (*get_vrtp_info)(struct ast_channel *chan, struct ast_rtp_instance **instance); + /*! + * \brief Callback for retrieving the RTP instance carrying text + * \note This function increases the reference count on the returned RTP instance. + */ + enum ast_rtp_glue_result (*get_trtp_info)(struct ast_channel *chan, struct ast_rtp_instance **instance); + /*! Callback for updating the destination that the remote side should send RTP to */ + int (*update_peer)(struct ast_channel *chan, struct ast_rtp_instance *instance, struct ast_rtp_instance *vinstance, struct ast_rtp_instance *tinstance, int codecs, int nat_active); + /*! Callback for retrieving codecs that the channel can do */ + int (*get_codec)(struct ast_channel *chan); + /*! Linked list information */ + AST_RWLIST_ENTRY(ast_rtp_glue) entry; +}; + +#define ast_rtp_engine_register(engine) ast_rtp_engine_register2(engine, ast_module_info->self) + +/*! + * \brief Register an RTP engine + * + * \param engine Structure of the RTP engine to register + * \param module Module that the RTP engine is part of + * + * \retval 0 success + * \retval -1 failure + * + * Example usage: + * + * \code + * ast_rtp_engine_register2(&example_rtp_engine, NULL); + * \endcode + * + * This registers the RTP engine declared as example_rtp_engine with the RTP engine core, but does not + * associate a module with it. + * + * \note It is recommended that you use the ast_rtp_engine_register macro so that the module is + * associated with the RTP engine and use counting is performed. + * + * \since 1.6.3 + */ +int ast_rtp_engine_register2(struct ast_rtp_engine *engine, struct ast_module *module); + +/*! + * \brief Unregister an RTP engine + * + * \param engine Structure of the RTP engine to unregister + * + * \retval 0 success + * \retval -1 failure + * + * Example usage: + * + * \code + * ast_rtp_engine_unregister(&example_rtp_engine); + * \endcode + * + * This unregisters the RTP engine declared as example_rtp_engine from the RTP engine core. If a module + * reference was provided when it was registered then this will only be called once the RTP engine is no longer in use. + * + * \since 1.6.3 + */ +int ast_rtp_engine_unregister(struct ast_rtp_engine *engine); + +#define ast_rtp_glue_register(glue) ast_rtp_glue_register2(glue, ast_module_info->self) + +/*! + * \brief Register RTP glue + * + * \param glue The glue to register + * \param module Module that the RTP glue is part of + * + * \retval 0 success + * \retval -1 failure + * + * Example usage: + * + * \code + * ast_rtp_glue_register2(&example_rtp_glue, NULL); + * \endcode + * + * This registers the RTP glue declared as example_rtp_glue with the RTP engine core, but does not + * associate a module with it. + * + * \note It is recommended that you use the ast_rtp_glue_register macro so that the module is + * associated with the RTP glue and use counting is performed. + * + * \since 1.6.3 + */ +int ast_rtp_glue_register2(struct ast_rtp_glue *glue, struct ast_module *module); + +/*! + * \brief Unregister RTP glue + * + * \param glue The glue to unregister + * + * \retval 0 success + * \retval -1 failure + * + * Example usage: + * + * \code + * ast_rtp_glue_unregister(&example_rtp_glue); + * \endcode + * + * This unregisters the RTP glue declared as example_rtp_gkue from the RTP engine core. If a module + * reference was provided when it was registered then this will only be called once the RTP engine is no longer in use. + * + * \since 1.6.3 + */ +int ast_rtp_glue_unregister(struct ast_rtp_glue *glue); + +/*! + * \brief Create a new RTP instance + * + * \param engine_name Name of the engine to use for the RTP instance + * \param sched Scheduler context that the RTP engine may want to use + * \param sin Address we want to bind to + * \param data Unique data for the engine + * + * \retval non-NULL success + * \retval NULL failure + * + * Example usage: + * + * \code + * struct ast_rtp_instance *instance = NULL; + * instance = ast_rtp_instance_new(NULL, sched, &sin, NULL); + * \endcode + * + * This creates a new RTP instance using the default engine and asks the RTP engine to bind to the address given + * in the sin structure. + * + * \note The RTP engine does not have to use the address provided when creating an RTP instance. It may choose to use + * another depending on it's own configuration. + * + * \since 1.6.3 + */ +struct ast_rtp_instance *ast_rtp_instance_new(const char *engine_name, struct sched_context *sched, struct sockaddr_in *sin, void *data); + +/*! + * \brief Destroy an RTP instance + * + * \param instance The RTP instance to destroy + * + * \retval 0 success + * \retval -1 failure + * + * Example usage: + * + * \code + * ast_rtp_instance_destroy(instance); + * \endcode + * + * This destroys the RTP instance pointed to by instance. Once this function returns instance no longer points to valid + * memory and may not be used again. + * + * \since 1.6.3 + */ +int ast_rtp_instance_destroy(struct ast_rtp_instance *instance); + +/*! + * \brief Set the data portion of an RTP instance + * + * \param instance The RTP instance to manipulate + * \param data Pointer to data + * + * Example usage: + * + * \code + * ast_rtp_instance_set_data(instance, blob); + * \endcode + * + * This sets the data pointer on the RTP instance pointed to by 'instance' to + * blob. + * + * \since 1.6.3 + */ +void ast_rtp_instance_set_data(struct ast_rtp_instance *instance, void *data); + +/*! + * \brief Get the data portion of an RTP instance + * + * \param instance The RTP instance we want the data portion from + * + * Example usage: + * + * \code + * struct *blob = ast_rtp_instance_get_data(instance); + ( \endcode + * + * This gets the data pointer on the RTP instance pointed to by 'instance'. + * + * \since 1.6.3 + */ +void *ast_rtp_instance_get_data(struct ast_rtp_instance *instance); + +/*! + * \brief Send a frame out over RTP + * + * \param instance The RTP instance to send frame out on + * + * \retval 0 success + * \retval -1 failure + * + * Example usage: + * + * \code + * ast_rtp_instance_write(instance, frame); + * \endcode + * + * This gives the frame pointed to by frame to the RTP engine being used for the instance + * and asks that it be transmitted to the current remote address set on the RTP instance. + * + * \since 1.6.3 + */ +int ast_rtp_instance_write(struct ast_rtp_instance *instance, struct ast_frame *frame); + +/*! + * \brief Receive a frame over RTP + * + * \param instance The RTP instance to receive frame on + * \param rtcp Whether to read in RTCP or not + * + * \retval non-NULL success + * \retval NULL failure + * + * Example usage: + * + * \code + * struct ast_frame *frame; + * frame = ast_rtp_instance_read(instance, 0); + * \endcode + * + * This asks the RTP engine to read in RTP from the instance and return it as an Asterisk frame. + * + * \since 1.6.3 + */ +struct ast_frame *ast_rtp_instance_read(struct ast_rtp_instance *instance, int rtcp); + +/*! + * \brief Set the address of the remote endpoint that we are sending RTP to + * + * \param instance The RTP instance to change the address on + * \param address Address to set it to + * + * \retval 0 success + * \retval -1 failure + * + * Example usage: + * + * \code + * ast_rtp_instance_set_remote_address(instance, &sin); + * \endcode + * + * This changes the remote address that RTP will be sent to on instance to the address given in the sin + * structure. + * + * \since 1.6.3 + */ +int ast_rtp_instance_set_remote_address(struct ast_rtp_instance *instance, struct sockaddr_in *address); + +/*! + * \brief Set the address that we are expecting to receive RTP on + * + * \param instance The RTP instance to change the address on + * \param address Address to set it to + * + * \retval 0 success + * \retval -1 failure + * + * Example usage: + * + * \code + * ast_rtp_instance_set_local_address(instance, &sin); + * \endcode + * + * This changes the local address that RTP is expected on to the address given in the sin + * structure. + * + * \since 1.6.3 + */ +int ast_rtp_instance_set_local_address(struct ast_rtp_instance *instance, struct sockaddr_in *address); + +/*! + * \brief Get the local address that we are expecting RTP on + * + * \param instance The RTP instance to get the address from + * \param address The variable to store the address in + * + * \retval 0 success + * \retval -1 failure + * + * Example usage: + * + * \code + * struct sockaddr_in sin; + * ast_rtp_instance_get_local_address(instance, &sin); + * \endcode + * + * This gets the local address that we are expecting RTP on and stores it in the 'sin' structure. + * + * \since 1.6.3 + */ +int ast_rtp_instance_get_local_address(struct ast_rtp_instance *instance, struct sockaddr_in *address); + +/*! + * \brief Get the address of the remote endpoint that we are sending RTP to + * + * \param instance The instance that we want to get the remote address for + * \param address A structure to put the address into + * + * \retval 0 success + * \retval -1 failure + * + * Example usage: + * + * \code + * struct sockaddr_in sin; + * ast_rtp_instance_get_remote_address(instance, &sin); + * \endcode + * + * This retrieves the current remote address set on the instance pointed to by instance and puts the value + * into the sin structure. + * + * \since 1.6.3 + */ +int ast_rtp_instance_get_remote_address(struct ast_rtp_instance *instance, struct sockaddr_in *address); + +/*! + * \brief Set the value of an RTP instance extended property + * + * \param instance The RTP instance to set the extended property on + * \param property The extended property to set + * \param value The value to set the extended property to + * + * \since 1.6.3 + */ +void ast_rtp_instance_set_extended_prop(struct ast_rtp_instance *instance, int property, void *value); + +/*! + * \brief Get the value of an RTP instance extended property + * + * \param instance The RTP instance to get the extended property on + * \param property The extended property to get + * + * \since 1.6.3 + */ +void *ast_rtp_instance_get_extended_prop(struct ast_rtp_instance *instance, int property); + +/*! + * \brief Set the value of an RTP instance property + * + * \param instance The RTP instance to set the property on + * \param property The property to modify + * \param value The value to set the property to + * + * Example usage: + * + * \code + * ast_rtp_instance_set_prop(instance, AST_RTP_PROPERTY_NAT, 1); + * \endcode + * + * This enables the AST_RTP_PROPERTY_NAT property on the instance pointed to by instance. + * + * \since 1.6.3 + */ +void ast_rtp_instance_set_prop(struct ast_rtp_instance *instance, enum ast_rtp_property property, int value); + +/*! + * \brief Get the value of an RTP instance property + * + * \param instance The RTP instance to get the property from + * \param property The property to get + * + * \retval Current value of the property + * + * Example usage: + * + * \code + * ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT); + * \endcode + * + * This returns the current value of the NAT property on the instance pointed to by instance. + * + * \since 1.6.3 + */ +int ast_rtp_instance_get_prop(struct ast_rtp_instance *instance, enum ast_rtp_property property); + +/*! + * \brief Get the codecs structure of an RTP instance + * + * \param instance The RTP instance to get the codecs structure from + * + * Example usage: + * + * \code + * struct ast_rtp_codecs *codecs = ast_rtp_instance_get_codecs(instance); + * \endcode + * + * This gets the codecs structure on the RTP instance pointed to by 'instance'. + * + * \since 1.6.3 + */ +struct ast_rtp_codecs *ast_rtp_instance_get_codecs(struct ast_rtp_instance *instance); + +/*! + * \brief Clear payload information from an RTP instance + * + * \param codecs The codecs structure that payloads will be cleared from + * \param instance Optionally the instance that the codecs structure belongs to + * + * Example usage: + * + * \code + * struct ast_rtp_codecs codecs; + * ast_rtp_codecs_payloads_clear(&codecs, NULL); + * \endcode + * + * This clears the codecs structure and puts it into a pristine state. + * + * \since 1.6.3 + */ +void ast_rtp_codecs_payloads_clear(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance); + +/*! + * \brief Set payload information on an RTP instance to the default + * + * \param codecs The codecs structure to set defaults on + * \param instance Optionally the instance that the codecs structure belongs to + * + * Example usage: + * + * \code + * struct ast_rtp_codecs codecs; + * ast_rtp_codecs_payloads_default(&codecs, NULL); + * \endcode + * + * This sets the default payloads on the codecs structure. + * + * \since 1.6.3 + */ +void ast_rtp_codecs_payloads_default(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance); + +/*! + * \brief Copy payload information from one RTP instance to another + * + * \param src The source codecs structure + * \param dst The destination codecs structure that the values from src will be copied to + * \param instance Optionally the instance that the dst codecs structure belongs to + * + * Example usage: + * + * \code + * ast_rtp_codecs_payloads_copy(&codecs0, &codecs1, NULL); + * \endcode + * + * This copies the payloads from the codecs0 structure to the codecs1 structure, overwriting any current values. + * + * \since 1.6.3 + */ +void ast_rtp_codecs_payloads_copy(struct ast_rtp_codecs *src, struct ast_rtp_codecs *dest, struct ast_rtp_instance *instance); + +/*! + * \brief Record payload information that was seen in an m= SDP line + * + * \param codecs The codecs structure to muck with + * \param instance Optionally the instance that the codecs structure belongs to + * \param payload Numerical payload that was seen in the m= SDP line + * + * Example usage: + * + * \code + * ast_rtp_codecs_payloads_set_m_type(&codecs, NULL, 0); + * \endcode + * + * This records that the numerical payload '0' was seen in the codecs structure. + * + * \since 1.6.3 + */ +void ast_rtp_codecs_payloads_set_m_type(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload); + +/*! + * \brief Record payload information that was seen in an a=rtpmap: SDP line + * + * \param codecs The codecs structure to muck with + * \param instance Optionally the instance that the codecs structure belongs to + * \param payload Numerical payload that was seen in the a=rtpmap: SDP line + * \param mimetype The string mime type that was seen + * \param mimesubtype The strin mime sub type that was seen + * \param options Optional options that may change the behavior of this specific payload + * + * \retval 0 success + * \retval -1 failure + * + * Example usage: + * + * \code + * ast_rtp_codecs_payloads_set_rtpmap_type(&codecs, NULL, 0, "audio", "PCMU", 0); + * \endcode + * + * This records that the numerical payload '0' was seen with mime type 'audio' and sub mime type 'PCMU' in the codecs structure. + * + * \since 1.6.3 + */ +int ast_rtp_codecs_payloads_set_rtpmap_type(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload, char *mimetype, char *mimesubtype, enum ast_rtp_options options); + +/*! + * \brief Set payload type to a known MIME media type for a codec with a specific sample rate + * + * \param rtp RTP structure to modify + * \param instance Optionally the instance that the codecs structure belongs to + * \param pt Payload type entry to modify + * \param mimetype top-level MIME type of media stream (typically "audio", "video", "text", etc.) + * \param mimesubtype MIME subtype of media stream (typically a codec name) + * \param options Zero or more flags from the ast_rtp_options enum + * \param sample_rate The sample rate of the media stream + * + * This function 'fills in' an entry in the list of possible formats for + * a media stream associated with an RTP structure. + * + * \retval 0 on success + * \retval -1 if the payload type is out of range + * \retval -2 if the mimeType/mimeSubtype combination was not found + * + * \since 1.6.3 + */ +int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int pt, + char *mimetype, char *mimesubtype, + enum ast_rtp_options options, + unsigned int sample_rate); + +/*! + * \brief Remove payload information + * + * \param codecs The codecs structure to muck with + * \param instance Optionally the instance that the codecs structure belongs to + * \param payload Numerical payload to unset + * + * Example usage: + * + * \code + * ast_rtp_codecs_payloads_unset(&codecs, NULL, 0); + * \endcode + * + * This clears the payload '0' from the codecs structure. It will be as if it was never set. + * + * \since 1.6.3 + */ +void ast_rtp_codecs_payloads_unset(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload); + +/*! + * \brief Retrieve payload information by payload + * + * \param codecs Codecs structure to look in + * \param payload Numerical payload to look up + * + * \retval Payload information + * + * Example usage: + * + * \code + * struct ast_rtp_payload_type payload_type; + * payload_type = ast_rtp_codecs_payload_lookup(&codecs, 0); + * \endcode + * + * This looks up the information for payload '0' from the codecs structure. + * + * \since 1.6.3 + */ +struct ast_rtp_payload_type ast_rtp_codecs_payload_lookup(struct ast_rtp_codecs *codecs, int payload); + +/*! + * \brief Get the sample rate associated with known RTP payload types + * + * \param asterisk_format True if the value in the 'code' parameter is an AST_FORMAT value + * \param code Format code, either from AST_FORMAT list or from AST_RTP list + * + * \return the sample rate if the format was found, zero if it was not found + * + * \since 1.6.3 + */ +unsigned int ast_rtp_lookup_sample_rate2(int asterisk_format, int code); + +/*! + * \brief Retrieve all formats that were found + * + * \param codecs Codecs structure to look in + * \param astFormats An integer to put the Asterisk formats in + * \param nonastformats An integer to put the non-Asterisk formats in + * + * Example usage: + * + * \code + * int astformats, nonastformats; + * ast_rtp_codecs_payload_Formats(&codecs, &astformats, &nonastformats); + * \endcode + * + * This retrieves all the formats known about in the codecs structure and puts the Asterisk ones in the integer + * pointed to by astformats and the non-Asterisk ones in the integer pointed to by nonastformats. + * + * \since 1.6.3 + */ +void ast_rtp_codecs_payload_formats(struct ast_rtp_codecs *codecs, int *astformats, int *nonastformats); + +/*! + * \brief Retrieve a payload based on whether it is an Asterisk format and the code + * + * \param codecs Codecs structure to look in + * \param asterisk_format Non-zero if the given code is an Asterisk format value + * \param code The format to look for + * + * \retval Numerical payload + * + * Example usage: + * + * \code + * int payload = ast_rtp_codecs_payload_code(&codecs, 1, AST_FORMAT_ULAW); + * \endcode + * + * This looks for the numerical payload for ULAW in the codecs structure. + * + * \since 1.6.3 + */ +int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, const int asterisk_format, const int code); + +/*! + * \brief Retrieve mime subtype information on a payload + * + * \param asterisk_format Non-zero if the given code is an Asterisk format value + * \param code Format to look up + * \param options Additional options that may change the result + * + * \retval Mime subtype success + * \retval NULL failure + * + * Example usage: + * + * \code + * const char *subtype = ast_rtp_lookup_mime_subtype2(1, AST_FORMAT_ULAW, 0); + * \endcode + * + * This looks up the mime subtype for the ULAW format. + * + * \since 1.6.3 + */ +const char *ast_rtp_lookup_mime_subtype2(const int asterisk_format, const int code, enum ast_rtp_options options); + +/*! + * \brief Convert formats into a string and put them into a buffer + * + * \param buf Buffer to put the mime output into + * \param capability Formats that we are looking up + * \param asterisk_format Non-zero if the given capability are Asterisk format capabilities + * \param options Additional options that may change the result + * + * \retval non-NULL success + * \retval NULL failure + * + * Example usage: + * + * \code + * char buf[256] = ""; + * char *mime = ast_rtp_lookup_mime_multiple2(&buf, sizeof(buf), AST_FORMAT_ULAW | AST_FORMAT_ALAW, 1, 0); + * \endcode + * + * This returns the mime values for ULAW and ALAW in the buffer pointed to by buf. + * + * \since 1.6.3 + */ +char *ast_rtp_lookup_mime_multiple2(struct ast_str *buf, const int capability, const int asterisk_format, enum ast_rtp_options options); + +/*! + * \brief Set codec packetization preferences + * + * \param codecs Codecs structure to muck with + * \param instance Optionally the instance that the codecs structure belongs to + * \param prefs Codec packetization preferences + * + * Example usage: + * + * \code + * ast_rtp_codecs_packetization_set(&codecs, NULL, &prefs); + * \endcode + * + * This sets the packetization preferences pointed to by prefs on the codecs structure pointed to by codecs. + * + * \since 1.6.3 + */ +void ast_rtp_codecs_packetization_set(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, struct ast_codec_pref *prefs); + +/*! + * \brief Begin sending a DTMF digit + * + * \param instance The RTP instance to send the DTMF on + * \param digit What DTMF digit to send + * + * \retval 0 success + * \retval -1 failure + * + * Example usage: + * + * \code + * ast_rtp_instance_dtmf_begin(instance, '1'); + * \endcode + * + * This starts sending the DTMF '1' on the RTP instance pointed to by instance. It will + * continue being sent until it is ended. + * + * \since 1.6.3 + */ +int ast_rtp_instance_dtmf_begin(struct ast_rtp_instance *instance, char digit); + +/*! + * \brief Stop sending a DTMF digit + * + * \param instance The RTP instance to stop the DTMF on + * \param digit What DTMF digit to stop + * + * \retval 0 success + * \retval -1 failure + * + * Example usage: + * + * \code + * ast_rtp_instance_dtmf_end(instance, '1'); + * \endcode + * + * This stops sending the DTMF '1' on the RTP instance pointed to by instance. + * + * \since 1.6.3 + */ +int ast_rtp_instance_dtmf_end(struct ast_rtp_instance *instance, char digit); + +/*! + * \brief Set the DTMF mode that should be used + * + * \param instance the RTP instance to set DTMF mode on + * \param dtmf_mode The DTMF mode that is in use + * + * \retval 0 success + * \retval -1 failure + * + * Example usage: + * + * \code + * ast_rtp_instance_dtmf_mode_set(instance, AST_RTP_DTMF_MODE_RFC2833); + * \endcode + * + * This sets the RTP instance to use RFC2833 for DTMF transmission and receiving. + * + * \since 1.6.3 + */ +int ast_rtp_instance_dtmf_mode_set(struct ast_rtp_instance *instance, enum ast_rtp_dtmf_mode dtmf_mode); + +/*! + * \brief Get the DTMF mode of an RTP instance + * + * \param instance The RTP instance to get the DTMF mode of + * + * \retval DTMF mode + * + * Example usage: + * + * \code + * enum ast_rtp_dtmf_mode dtmf_mode = ast_rtp_instance_dtmf_mode_get(instance); + * \endcode + * + * This gets the DTMF mode set on the RTP instance pointed to by 'instance'. + * + * \since 1.6.3 + */ +enum ast_rtp_dtmf_mode ast_rtp_instance_dtmf_mode_get(struct ast_rtp_instance *instance); + +/*! + * \brief Indicate a new source of audio has dropped in + * + * \param instance Instance that the new media source is feeding into + * + * Example usage: + * + * \code + * ast_rtp_instance_new_source(instance); + * \endcode + * + * This indicates that a new source of media is feeding the instance pointed to by + * instance. + * + * \since 1.6.3 + */ +void ast_rtp_instance_new_source(struct ast_rtp_instance *instance); + +/*! + * \brief Set QoS parameters on an RTP session + * + * \param instance Instance to set the QoS parameters on + * \param tos Terms of service value + * \param cos Class of service value + * \param desc What is setting the QoS values + * + * \retval 0 success + * \retval -1 failure + * + * Example usage: + * + * \code + * ast_rtp_instance_set_qos(instance, 0, 0, "Example"); + * \endcode + * + * This sets the TOS and COS values to 0 on the instance pointed to by instance. + * + * \since 1.6.3 + */ +int ast_rtp_instance_set_qos(struct ast_rtp_instance *instance, int tos, int cos, const char *desc); + +/*! + * \brief Stop an RTP instance + * + * \param instance Instance that media is no longer going to at this time + * + * Example usage: + * + * \code + * ast_rtp_instance_stop(instance); + * \endcode + * + * This tells the RTP engine being used for the instance pointed to by instance + * that media is no longer going to it at this time, but may in the future. + * + * \since 1.6.3 + */ +void ast_rtp_instance_stop(struct ast_rtp_instance *instance); + +/*! + * \brief Get the file descriptor for an RTP session (or RTCP) + * + * \param instance Instance to get the file descriptor for + * \param rtcp Whether to retrieve the file descriptor for RTCP or not + * + * \retval fd success + * \retval -1 failure + * + * Example usage: + * + * \code + * int rtp_fd = ast_rtp_instance_fd(instance, 0); + * \endcode + * + * This retrieves the file descriptor for the socket carrying media on the instance + * pointed to by instance. + * + * \since 1.6.3 + */ +int ast_rtp_instance_fd(struct ast_rtp_instance *instance, int rtcp); + +/*! + * \brief Get the RTP glue that binds a channel to the RTP engine + * + * \param type Name of the glue we want + * + * \retval non-NULL success + * \retval NULL failure + * + * Example usage: + * + * \code + * struct ast_rtp_glue *glue = ast_rtp_instance_get_glue("Example"); + * \endcode + * + * This retrieves the RTP glue that has the name 'Example'. + * + * \since 1.6.3 + */ +struct ast_rtp_glue *ast_rtp_instance_get_glue(const char *type); + +/*! + * \brief Bridge two channels that use RTP instances + * + * \param c0 First channel part of the bridge + * \param c1 Second channel part of the bridge + * \param flags Bridging flags + * \param fo If a frame needs to be passed up it is stored here + * \param rc Channel that passed the above frame up + * \param timeoutms How long the channels should be bridged for + * + * \retval Bridge result + * + * \note This should only be used by channel drivers in their technology declaration. + * + * \since 1.6.3 + */ +enum ast_bridge_result ast_rtp_instance_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms); + +/*! + * \brief Get the other RTP instance that an instance is bridged to + * + * \param instance The RTP instance that we want + * + * \retval non-NULL success + * \retval NULL failure + * + * Example usage: + * + * \code + * struct ast_rtp_instance *bridged = ast_rtp_instance_get_bridged(instance0); + * \endcode + * + * This gets the RTP instance that instance0 is bridged to. + * + * \since 1.6.3 + */ +struct ast_rtp_instance *ast_rtp_instance_get_bridged(struct ast_rtp_instance *instance); + +/*! + * \brief Make two channels compatible for early bridging + * + * \param c0 First channel part of the bridge + * \param c1 Second channel part of the bridge + * + * \since 1.6.3 + */ +void ast_rtp_instance_early_bridge_make_compatible(struct ast_channel *c0, struct ast_channel *c1); + +/*! + * \brief Early bridge two channels that use RTP instances + * + * \param c0 First channel part of the bridge + * \param c1 Second channel part of the bridge + * + * \retval 0 success + * \retval -1 failure + * + * \note This should only be used by channel drivers in their technology declaration. + * + * \since 1.6.3 + */ +int ast_rtp_instance_early_bridge(struct ast_channel *c0, struct ast_channel *c1); + +/*! + * \brief Initialize RED support on an RTP instance + * + * \param instance The instance to initialize RED support on + * \param buffer_time How long to buffer before sending + * \param payloads Payload values + * \param generations Number of generations + * + * \retval 0 success + * \retval -1 failure + * + * \since 1.6.3 + */ +int ast_rtp_red_init(struct ast_rtp_instance *instance, int buffer_time, int *payloads, int generations); + +/*! + * \brief Buffer a frame in an RTP instance for RED + * + * \param instance The instance to buffer the frame on + * \param frame Frame that we want to buffer + * + * \retval 0 success + * \retval -1 failure + * + * \since 1.6.3 + */ +int ast_rtp_red_buffer(struct ast_rtp_instance *instance, struct ast_frame *frame); + +/*! + * \brief Retrieve statistics about an RTP instance + * + * \param instance Instance to get statistics on + * \param stats Structure to put results into + * \param stat What statistic(s) to retrieve + * + * \retval 0 success + * \retval -1 failure + * + * Example usage: + * + * \code + * struct ast_rtp_instance_stats stats; + * ast_rtp_instance_get_stats(instance, &stats, AST_RTP_INSTANCE_STAT_ALL); + * \endcode + * + * This retrieves all statistics the underlying RTP engine supports and puts the values into the + * stats structure. + * + * \since 1.6.3 + */ +int ast_rtp_instance_get_stats(struct ast_rtp_instance *instance, struct ast_rtp_instance_stats *stats, enum ast_rtp_instance_stat stat); + +/*! + * \brief Set standard statistics from an RTP instance on a channel + * + * \param chan Channel to set the statistics on + * \param instance The RTP instance that statistics will be retrieved from + * + * Example usage: + * + * \code + * ast_rtp_instance_set_stats_vars(chan, rtp); + * \endcode + * + * This retrieves standard statistics from the RTP instance rtp and sets it on the channel pointed to + * by chan. + * + * \since 1.6.3 + */ +void ast_rtp_instance_set_stats_vars(struct ast_channel *chan, struct ast_rtp_instance *instance); + +/*! + * \brief Retrieve quality statistics about an RTP instance + * + * \param instance Instance to get statistics on + * \param field What quality statistic to retrieve + * \param buf What buffer to put the result into + * \param size Size of the above buffer + * + * \retval non-NULL success + * \retval NULL failure + * + * Example usage: + * + * \code + * char quality[AST_MAX_USER_FIELD]; + * ast_rtp_instance_get_quality(instance, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, &buf, sizeof(buf)); + * \endcode + * + * This retrieves general quality statistics and places a text representation into the buf pointed to by buf. + * + * \since 1.6.3 + */ +char *ast_rtp_instance_get_quality(struct ast_rtp_instance *instance, enum ast_rtp_instance_stat_field field, char *buf, size_t size); + +/*! + * \brief Request that the underlying RTP engine provide audio frames in a specific format + * + * \param instance The RTP instance to change read format on + * \param format Format that frames are wanted in + * + * \retval 0 success + * \retval -1 failure + * + * Example usage: + * + * \code + * ast_rtp_instance_set_read_format(instance, AST_FORMAT_ULAW); + * \endcode + * + * This requests that the RTP engine provide audio frames in the ULAW format. + * + * \since 1.6.3 + */ +int ast_rtp_instance_set_read_format(struct ast_rtp_instance *instance, int format); + +/*! + * \brief Tell underlying RTP engine that audio frames will be provided in a specific format + * + * \param instance The RTP instance to change write format on + * \param format Format that frames will be provided in + * + * \retval 0 success + * \retval -1 failure + * + * Example usage: + * + * \code + * ast_rtp_instance_set_write_format(instance, AST_FORMAT_ULAW); + * \endcode + * + * This tells the underlying RTP engine that audio frames will be provided to it in ULAW format. + * + * \since 1.6.3 + */ +int ast_rtp_instance_set_write_format(struct ast_rtp_instance *instance, int format); + +/*! + * \brief Request that the underlying RTP engine make two RTP instances compatible with eachother + * + * \param chan Our own Asterisk channel + * \param instance The first RTP instance + * \param peer The peer Asterisk channel + * + * \retval 0 success + * \retval -1 failure + * + * Example usage: + * + * \code + * ast_rtp_instance_make_compatible(instance, peer); + * \endcode + * + * This makes the RTP instance for 'peer' compatible with 'instance' and vice versa. + * + * \since 1.6.3 + */ +int ast_rtp_instance_make_compatible(struct ast_channel *chan, struct ast_rtp_instance *instance, struct ast_channel *peer); + +/*! + * \brief Indicate to the RTP engine that packets are now expected to be sent/received on the RTP instance + * + * \param instance The RTP instance + * + * \retval 0 success + * \retval -1 failure + * + * Example usage: + * + * \code + * ast_rtp_instance_activate(instance); + * \endcode + * + * This tells the underlying RTP engine of instance that packets will now flow. + * + * \since 1.6.3 + */ +int ast_rtp_instance_activate(struct ast_rtp_instance *instance); + +/*! + * \brief Request that the underlying RTP engine send a STUN BIND request + * + * \param instance The RTP instance + * \param suggestion The suggested destination + * \param username Optionally a username for the request + * + * Example usage: + * + * \code + * ast_rtp_instance_stun_request(instance, NULL, NULL); + * \endcode + * + * This requests that the RTP engine send a STUN BIND request on the session pointed to by + * 'instance'. + * + * \since 1.6.3 + */ +void ast_rtp_instance_stun_request(struct ast_rtp_instance *instance, struct sockaddr_in *suggestion, const char *username); + +/*! + * \brief Set the RTP timeout value + * + * \param instance The RTP instance + * \param timeout Value to set the timeout to + * + * Example usage: + * + * \code + * ast_rtp_instance_set_timeout(instance, 5000); + * \endcode + * + * This sets the RTP timeout value on 'instance' to be 5000. + * + * \since 1.6.3 + */ +void ast_rtp_instance_set_timeout(struct ast_rtp_instance *instance, int timeout); + +/*! + * \brief Set the RTP timeout value for when the instance is on hold + * + * \param instance The RTP instance + * \param timeout Value to set the timeout to + * + * Example usage: + * + * \code + * ast_rtp_instance_set_hold_timeout(instance, 5000); + * \endcode + * + * This sets the RTP hold timeout value on 'instance' to be 5000. + * + * \since 1.6.3 + */ +void ast_rtp_instance_set_hold_timeout(struct ast_rtp_instance *instance, int timeout); + +/*! + * \brief Get the RTP timeout value + * + * \param instance The RTP instance + * + * \retval timeout value + * + * Example usage: + * + * \code + * int timeout = ast_rtp_instance_get_timeout(instance); + * \endcode + * + * This gets the RTP timeout value for the RTP instance pointed to by 'instance'. + * + * \since 1.6.3 + */ +int ast_rtp_instance_get_timeout(struct ast_rtp_instance *instance); + +/*! + * \brief Get the RTP timeout value for when an RTP instance is on hold + * + * \param instance The RTP instance + * + * \retval timeout value + * + * Example usage: + * + * \code + * int timeout = ast_rtp_instance_get_hold_timeout(instance); + * \endcode + * + * This gets the RTP hold timeout value for the RTP instance pointed to by 'instance'. + * + * \since 1.6.3 + */ +int ast_rtp_instance_get_hold_timeout(struct ast_rtp_instance *instance); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif /* _ASTERISK_RTP_ENGINE_H */ diff --git a/include/asterisk/stun.h b/include/asterisk/stun.h new file mode 100644 index 0000000000..11921f8148 --- /dev/null +++ b/include/asterisk/stun.h @@ -0,0 +1,71 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2008, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file stun.h + * \brief STUN support. + * + * STUN is defined in RFC 3489. + */ + +#ifndef _ASTERISK_STUN_H +#define _ASTERISK_STUN_H + +#include "asterisk/network.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +enum ast_stun_result { + AST_STUN_IGNORE = 0, + AST_STUN_ACCEPT, +}; + +struct stun_attr; + +/*! \brief Generic STUN request + * send a generic stun request to the server specified. + * \param s the socket used to send the request + * \param dst the address of the STUN server + * \param username if non null, add the username in the request + * \param answer if non null, the function waits for a response and + * puts here the externally visible address. + * \return 0 on success, other values on error. + * The interface it may change in the future. + */ +int ast_stun_request(int s, struct sockaddr_in *dst, const char *username, struct sockaddr_in *answer); + +/*! \brief callback type to be invoked on stun responses. */ +typedef int (stun_cb_f)(struct stun_attr *attr, void *arg); + +/*! \brief handle an incoming STUN message. + * + * Do some basic sanity checks on packet size and content, + * try to extract a bit of information, and possibly reply. + * At the moment this only processes BIND requests, and returns + * the externally visible address of the request. + * If a callback is specified, invoke it with the attribute. + */ +int ast_stun_handle_packet(int s, struct sockaddr_in *src, unsigned char *data, size_t len, stun_cb_f *stun_cb, void *arg); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif /* _ASTERISK_STUN_H */ diff --git a/main/Makefile b/main/Makefile index 3e6179229c..6817197999 100644 --- a/main/Makefile +++ b/main/Makefile @@ -20,7 +20,7 @@ include $(ASTTOPDIR)/Makefile.moddir_rules OBJS= tcptls.o io.o sched.o logger.o frame.o loader.o config.o channel.o \ translate.o file.o pbx.o cli.o md5.o term.o heap.o \ ulaw.o alaw.o callerid.o fskmodem.o image.o app.o \ - cdr.o tdd.o acl.o rtp.o udptl.o manager.o asterisk.o \ + cdr.o tdd.o acl.o udptl.o manager.o asterisk.o \ dsp.o chanvars.o indications.o autoservice.o db.o privacy.o \ astmm.o enum.o srv.o dns.o aescrypt.o aestab.o aeskey.o \ utils.o plc.o jitterbuf.o dnsmgr.o devicestate.o \ @@ -29,7 +29,7 @@ OBJS= tcptls.o io.o sched.o logger.o frame.o loader.o config.o channel.o \ strcompat.o threadstorage.o dial.o event.o adsistub.o audiohook.o \ astobj2.o hashtab.o global_datastores.o version.o \ features.o taskprocessor.o timing.o datastore.o xml.o xmldoc.o \ - strings.o bridging.o poll.o + strings.o bridging.o poll.o rtp_engine.o stun.o # we need to link in the objects statically, not as a library, because # otherwise modules will not have them available if none of the static diff --git a/main/asterisk.c b/main/asterisk.c index fdef5e156e..20c85b47fe 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -120,7 +120,6 @@ int daemon(int, int); /* defined in libresolv of all places */ #include "asterisk/cdr.h" #include "asterisk/pbx.h" #include "asterisk/enum.h" -#include "asterisk/rtp.h" #include "asterisk/http.h" #include "asterisk/udptl.h" #include "asterisk/app.h" @@ -3579,7 +3578,6 @@ int main(int argc, char *argv[]) exit(1); } - ast_rtp_init(); ast_dsp_init(); ast_udptl_init(); diff --git a/main/loader.c b/main/loader.c index 5f2fe86786..4e07e843b5 100644 --- a/main/loader.c +++ b/main/loader.c @@ -43,7 +43,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/manager.h" #include "asterisk/cdr.h" #include "asterisk/enum.h" -#include "asterisk/rtp.h" #include "asterisk/http.h" #include "asterisk/lock.h" #include "asterisk/features.h" @@ -243,7 +242,6 @@ static struct reload_classes { { "extconfig", read_config_maps }, { "enum", ast_enum_reload }, { "manager", reload_manager }, - { "rtp", ast_rtp_reload }, { "http", ast_http_reload }, { "logger", logger_reload }, { "features", ast_features_reload }, diff --git a/main/rtp.c b/main/rtp.c deleted file mode 100644 index 38ff9ad3ab..0000000000 --- a/main/rtp.c +++ /dev/null @@ -1,4865 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2006, Digium, Inc. - * - * Mark Spencer <markster@digium.com> - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! - * \file - * - * \brief Supports RTP and RTCP with Symmetric RTP support for NAT traversal. - * - * \author Mark Spencer <markster@digium.com> - * - * \note RTP is defined in RFC 3550. - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include <sys/time.h> -#include <signal.h> -#include <fcntl.h> -#include <math.h> - -#include "asterisk/rtp.h" -#include "asterisk/pbx.h" -#include "asterisk/frame.h" -#include "asterisk/channel.h" -#include "asterisk/acl.h" -#include "asterisk/config.h" -#include "asterisk/lock.h" -#include "asterisk/utils.h" -#include "asterisk/netsock.h" -#include "asterisk/cli.h" -#include "asterisk/manager.h" -#include "asterisk/unaligned.h" - -#define MAX_TIMESTAMP_SKEW 640 - -#define RTP_SEQ_MOD (1<<16) /*!< A sequence number can't be more than 16 bits */ -#define RTCP_DEFAULT_INTERVALMS 5000 /*!< Default milli-seconds between RTCP reports we send */ -#define RTCP_MIN_INTERVALMS 500 /*!< Min milli-seconds between RTCP reports we send */ -#define RTCP_MAX_INTERVALMS 60000 /*!< Max milli-seconds between RTCP reports we send */ - -#define RTCP_PT_FUR 192 -#define RTCP_PT_SR 200 -#define RTCP_PT_RR 201 -#define RTCP_PT_SDES 202 -#define RTCP_PT_BYE 203 -#define RTCP_PT_APP 204 - -#define RTP_MTU 1200 - -#define DEFAULT_DTMF_TIMEOUT 3000 /*!< samples */ - -static int dtmftimeout = DEFAULT_DTMF_TIMEOUT; - -static int rtpstart = 5000; /*!< First port for RTP sessions (set in rtp.conf) */ -static int rtpend = 31000; /*!< Last port for RTP sessions (set in rtp.conf) */ -static int rtpdebug; /*!< Are we debugging? */ -static int rtcpdebug; /*!< Are we debugging RTCP? */ -static int rtcpstats; /*!< Are we debugging RTCP? */ -static int rtcpinterval = RTCP_DEFAULT_INTERVALMS; /*!< Time between rtcp reports in millisecs */ -static int stundebug; /*!< Are we debugging stun? */ -static struct sockaddr_in rtpdebugaddr; /*!< Debug packets to/from this host */ -static struct sockaddr_in rtcpdebugaddr; /*!< Debug RTCP packets to/from this host */ -#ifdef SO_NO_CHECK -static int nochecksums; -#endif -static int strictrtp; - -enum strict_rtp_state { - STRICT_RTP_OPEN = 0, /*! No RTP packets should be dropped, all sources accepted */ - STRICT_RTP_LEARN, /*! Accept next packet as source */ - STRICT_RTP_CLOSED, /*! Drop all RTP packets not coming from source that was learned */ -}; - -/* Uncomment this to enable more intense native bridging, but note: this is currently buggy */ -/* #define P2P_INTENSE */ - -/*! - * \brief Structure representing a RTP session. - * - * RTP session is defined on page 9 of RFC 3550: "An association among a set of participants communicating with RTP. A participant may be involved in multiple RTP sessions at the same time [...]" - * - */ - -/*! \brief RTP session description */ -struct ast_rtp { - int s; - struct ast_frame f; - unsigned char rawdata[8192 + AST_FRIENDLY_OFFSET]; - unsigned int ssrc; /*!< Synchronization source, RFC 3550, page 10. */ - unsigned int themssrc; /*!< Their SSRC */ - unsigned int rxssrc; - unsigned int lastts; - unsigned int lastrxts; - unsigned int lastividtimestamp; - unsigned int lastovidtimestamp; - unsigned int lastitexttimestamp; - unsigned int lastotexttimestamp; - unsigned int lasteventseqn; - int lastrxseqno; /*!< Last received sequence number */ - unsigned short seedrxseqno; /*!< What sequence number did they start with?*/ - unsigned int seedrxts; /*!< What RTP timestamp did they start with? */ - unsigned int rxcount; /*!< How many packets have we received? */ - unsigned int rxoctetcount; /*!< How many octets have we received? should be rxcount *160*/ - unsigned int txcount; /*!< How many packets have we sent? */ - unsigned int txoctetcount; /*!< How many octets have we sent? (txcount*160)*/ - unsigned int cycles; /*!< Shifted count of sequence number cycles */ - double rxjitter; /*!< Interarrival jitter at the moment */ - double rxtransit; /*!< Relative transit time for previous packet */ - int lasttxformat; - int lastrxformat; - - int rtptimeout; /*!< RTP timeout time (negative or zero means disabled, negative value means temporarily disabled) */ - int rtpholdtimeout; /*!< RTP timeout when on hold (negative or zero means disabled, negative value means temporarily disabled). */ - int rtpkeepalive; /*!< Send RTP comfort noice packets for keepalive */ - - /* DTMF Reception Variables */ - char resp; - unsigned int lastevent; - int dtmfcount; - unsigned int dtmfsamples; - /* DTMF Transmission Variables */ - unsigned int lastdigitts; - char sending_digit; /*!< boolean - are we sending digits */ - char send_digit; /*!< digit we are sending */ - int send_payload; - int send_duration; - int nat; - unsigned int flags; - struct sockaddr_in us; /*!< Socket representation of the local endpoint. */ - struct sockaddr_in them; /*!< Socket representation of the remote endpoint. */ - struct timeval rxcore; - struct timeval txcore; - double drxcore; /*!< The double representation of the first received packet */ - struct timeval lastrx; /*!< timeval when we last received a packet */ - struct timeval dtmfmute; - struct ast_smoother *smoother; - int *ioid; - unsigned short seqno; /*!< Sequence number, RFC 3550, page 13. */ - unsigned short rxseqno; - struct sched_context *sched; - struct io_context *io; - void *data; - ast_rtp_callback callback; -#ifdef P2P_INTENSE - ast_mutex_t bridge_lock; -#endif - struct rtpPayloadType current_RTP_PT[MAX_RTP_PT]; - int rtp_lookup_code_cache_isAstFormat; /*!< a cache for the result of rtp_lookup_code(): */ - int rtp_lookup_code_cache_code; - int rtp_lookup_code_cache_result; - struct ast_rtcp *rtcp; - struct ast_codec_pref pref; - struct ast_rtp *bridged; /*!< Who we are Packet bridged to */ - - enum strict_rtp_state strict_rtp_state; /*!< Current state that strict RTP protection is in */ - struct sockaddr_in strict_rtp_address; /*!< Remote address information for strict RTP purposes */ - - int set_marker_bit:1; /*!< Whether to set the marker bit or not */ - struct rtp_red *red; -}; - -static struct ast_frame *red_t140_to_red(struct rtp_red *red); -static int red_write(const void *data); - -struct rtp_red { - struct ast_frame t140; /*!< Primary data */ - struct ast_frame t140red; /*!< Redundant t140*/ - unsigned char pt[RED_MAX_GENERATION]; /*!< Payload types for redundancy data */ - unsigned char ts[RED_MAX_GENERATION]; /*!< Time stamps */ - unsigned char len[RED_MAX_GENERATION]; /*!< length of each generation */ - int num_gen; /*!< Number of generations */ - int schedid; /*!< Timer id */ - int ti; /*!< How long to buffer data before send */ - unsigned char t140red_data[64000]; - unsigned char buf_data[64000]; /*!< buffered primary data */ - int hdrlen; - long int prev_ts; -}; - -/* Forward declarations */ -static int ast_rtcp_write(const void *data); -static void timeval2ntp(struct timeval tv, unsigned int *msw, unsigned int *lsw); -static int ast_rtcp_write_sr(const void *data); -static int ast_rtcp_write_rr(const void *data); -static unsigned int ast_rtcp_calc_interval(struct ast_rtp *rtp); -static int ast_rtp_senddigit_continuation(struct ast_rtp *rtp); -int ast_rtp_senddigit_end(struct ast_rtp *rtp, char digit); - -#define FLAG_3389_WARNING (1 << 0) -#define FLAG_NAT_ACTIVE (3 << 1) -#define FLAG_NAT_INACTIVE (0 << 1) -#define FLAG_NAT_INACTIVE_NOWARN (1 << 1) -#define FLAG_HAS_DTMF (1 << 3) -#define FLAG_P2P_SENT_MARK (1 << 4) -#define FLAG_P2P_NEED_DTMF (1 << 5) -#define FLAG_CALLBACK_MODE (1 << 6) -#define FLAG_DTMF_COMPENSATE (1 << 7) -#define FLAG_HAS_STUN (1 << 8) - -/*! - * \brief Structure defining an RTCP session. - * - * The concept "RTCP session" is not defined in RFC 3550, but since - * this structure is analogous to ast_rtp, which tracks a RTP session, - * it is logical to think of this as a RTCP session. - * - * RTCP packet is defined on page 9 of RFC 3550. - * - */ -struct ast_rtcp { - int rtcp_info; - int s; /*!< Socket */ - struct sockaddr_in us; /*!< Socket representation of the local endpoint. */ - struct sockaddr_in them; /*!< Socket representation of the remote endpoint. */ - unsigned int soc; /*!< What they told us */ - unsigned int spc; /*!< What they told us */ - unsigned int themrxlsr; /*!< The middle 32 bits of the NTP timestamp in the last received SR*/ - struct timeval rxlsr; /*!< Time when we got their last SR */ - struct timeval txlsr; /*!< Time when we sent or last SR*/ - unsigned int expected_prior; /*!< no. packets in previous interval */ - unsigned int received_prior; /*!< no. packets received in previous interval */ - int schedid; /*!< Schedid returned from ast_sched_add() to schedule RTCP-transmissions*/ - unsigned int rr_count; /*!< number of RRs we've sent, not including report blocks in SR's */ - unsigned int sr_count; /*!< number of SRs we've sent */ - unsigned int lastsrtxcount; /*!< Transmit packet count when last SR sent */ - double accumulated_transit; /*!< accumulated a-dlsr-lsr */ - double rtt; /*!< Last reported rtt */ - unsigned int reported_jitter; /*!< The contents of their last jitter entry in the RR */ - unsigned int reported_lost; /*!< Reported lost packets in their RR */ - char quality[AST_MAX_USER_FIELD]; - char quality_jitter[AST_MAX_USER_FIELD]; - char quality_loss[AST_MAX_USER_FIELD]; - char quality_rtt[AST_MAX_USER_FIELD]; - - double reported_maxjitter; - double reported_minjitter; - double reported_normdev_jitter; - double reported_stdev_jitter; - unsigned int reported_jitter_count; - - double reported_maxlost; - double reported_minlost; - double reported_normdev_lost; - double reported_stdev_lost; - - double rxlost; - double maxrxlost; - double minrxlost; - double normdev_rxlost; - double stdev_rxlost; - unsigned int rxlost_count; - - double maxrxjitter; - double minrxjitter; - double normdev_rxjitter; - double stdev_rxjitter; - unsigned int rxjitter_count; - double maxrtt; - double minrtt; - double normdevrtt; - double stdevrtt; - unsigned int rtt_count; - int sendfur; -}; - -/*! - * \brief STUN support code - * - * This code provides some support for doing STUN transactions. - * Eventually it should be moved elsewhere as other protocols - * than RTP can benefit from it - e.g. SIP. - * STUN is described in RFC3489 and it is based on the exchange - * of UDP packets between a client and one or more servers to - * determine the externally visible address (and port) of the client - * once it has gone through the NAT boxes that connect it to the - * outside. - * The simplest request packet is just the header defined in - * struct stun_header, and from the response we may just look at - * one attribute, STUN_MAPPED_ADDRESS, that we find in the response. - * By doing more transactions with different server addresses we - * may determine more about the behaviour of the NAT boxes, of - * course - the details are in the RFC. - * - * All STUN packets start with a simple header made of a type, - * length (excluding the header) and a 16-byte random transaction id. - * Following the header we may have zero or more attributes, each - * structured as a type, length and a value (whose format depends - * on the type, but often contains addresses). - * Of course all fields are in network format. - */ - -typedef struct { unsigned int id[4]; } __attribute__((packed)) stun_trans_id; - -struct stun_header { - unsigned short msgtype; - unsigned short msglen; - stun_trans_id id; - unsigned char ies[0]; -} __attribute__((packed)); - -struct stun_attr { - unsigned short attr; - unsigned short len; - unsigned char value[0]; -} __attribute__((packed)); - -/* - * The format normally used for addresses carried by STUN messages. - */ -struct stun_addr { - unsigned char unused; - unsigned char family; - unsigned short port; - unsigned int addr; -} __attribute__((packed)); - -#define STUN_IGNORE (0) -#define STUN_ACCEPT (1) - -/*! \brief STUN message types - * 'BIND' refers to transactions used to determine the externally - * visible addresses. 'SEC' refers to transactions used to establish - * a session key for subsequent requests. - * 'SEC' functionality is not supported here. - */ - -#define STUN_BINDREQ 0x0001 -#define STUN_BINDRESP 0x0101 -#define STUN_BINDERR 0x0111 -#define STUN_SECREQ 0x0002 -#define STUN_SECRESP 0x0102 -#define STUN_SECERR 0x0112 - -/*! \brief Basic attribute types in stun messages. - * Messages can also contain custom attributes (codes above 0x7fff) - */ -#define STUN_MAPPED_ADDRESS 0x0001 -#define STUN_RESPONSE_ADDRESS 0x0002 -#define STUN_CHANGE_REQUEST 0x0003 -#define STUN_SOURCE_ADDRESS 0x0004 -#define STUN_CHANGED_ADDRESS 0x0005 -#define STUN_USERNAME 0x0006 -#define STUN_PASSWORD 0x0007 -#define STUN_MESSAGE_INTEGRITY 0x0008 -#define STUN_ERROR_CODE 0x0009 -#define STUN_UNKNOWN_ATTRIBUTES 0x000a -#define STUN_REFLECTED_FROM 0x000b - -/*! \brief helper function to print message names */ -static const char *stun_msg2str(int msg) -{ - switch (msg) { - case STUN_BINDREQ: - return "Binding Request"; - case STUN_BINDRESP: - return "Binding Response"; - case STUN_BINDERR: - return "Binding Error Response"; - case STUN_SECREQ: - return "Shared Secret Request"; - case STUN_SECRESP: - return "Shared Secret Response"; - case STUN_SECERR: - return "Shared Secret Error Response"; - } - return "Non-RFC3489 Message"; -} - -/*! \brief helper function to print attribute names */ -static const char *stun_attr2str(int msg) -{ - switch (msg) { - case STUN_MAPPED_ADDRESS: - return "Mapped Address"; - case STUN_RESPONSE_ADDRESS: - return "Response Address"; - case STUN_CHANGE_REQUEST: - return "Change Request"; - case STUN_SOURCE_ADDRESS: - return "Source Address"; - case STUN_CHANGED_ADDRESS: - return "Changed Address"; - case STUN_USERNAME: - return "Username"; - case STUN_PASSWORD: - return "Password"; - case STUN_MESSAGE_INTEGRITY: - return "Message Integrity"; - case STUN_ERROR_CODE: - return "Error Code"; - case STUN_UNKNOWN_ATTRIBUTES: - return "Unknown Attributes"; - case STUN_REFLECTED_FROM: - return "Reflected From"; - } - return "Non-RFC3489 Attribute"; -} - -/*! \brief here we store credentials extracted from a message */ -struct stun_state { - const char *username; - const char *password; -}; - -static int stun_process_attr(struct stun_state *state, struct stun_attr *attr) -{ - if (stundebug) - ast_verbose("Found STUN Attribute %s (%04x), length %d\n", - stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len)); - switch (ntohs(attr->attr)) { - case STUN_USERNAME: - state->username = (const char *) (attr->value); - break; - case STUN_PASSWORD: - state->password = (const char *) (attr->value); - break; - default: - if (stundebug) - ast_verbose("Ignoring STUN attribute %s (%04x), length %d\n", - stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len)); - } - return 0; -} - -/*! \brief append a string to an STUN message */ -static void append_attr_string(struct stun_attr **attr, int attrval, const char *s, int *len, int *left) -{ - int size = sizeof(**attr) + strlen(s); - if (*left > size) { - (*attr)->attr = htons(attrval); - (*attr)->len = htons(strlen(s)); - memcpy((*attr)->value, s, strlen(s)); - (*attr) = (struct stun_attr *)((*attr)->value + strlen(s)); - *len += size; - *left -= size; - } -} - -/*! \brief append an address to an STUN message */ -static void append_attr_address(struct stun_attr **attr, int attrval, struct sockaddr_in *sock_in, int *len, int *left) -{ - int size = sizeof(**attr) + 8; - struct stun_addr *addr; - if (*left > size) { - (*attr)->attr = htons(attrval); - (*attr)->len = htons(8); - addr = (struct stun_addr *)((*attr)->value); - addr->unused = 0; - addr->family = 0x01; - addr->port = sock_in->sin_port; - addr->addr = sock_in->sin_addr.s_addr; - (*attr) = (struct stun_attr *)((*attr)->value + 8); - *len += size; - *left -= size; - } -} - -/*! \brief wrapper to send an STUN message */ -static int stun_send(int s, struct sockaddr_in *dst, struct stun_header *resp) -{ - return sendto(s, resp, ntohs(resp->msglen) + sizeof(*resp), 0, - (struct sockaddr *)dst, sizeof(*dst)); -} - -/*! \brief helper function to generate a random request id */ -static void stun_req_id(struct stun_header *req) -{ - int x; - for (x = 0; x < 4; x++) - req->id.id[x] = ast_random(); -} - -size_t ast_rtp_alloc_size(void) -{ - return sizeof(struct ast_rtp); -} - -/*! \brief callback type to be invoked on stun responses. */ -typedef int (stun_cb_f)(struct stun_attr *attr, void *arg); - -/*! \brief handle an incoming STUN message. - * - * Do some basic sanity checks on packet size and content, - * try to extract a bit of information, and possibly reply. - * At the moment this only processes BIND requests, and returns - * the externally visible address of the request. - * If a callback is specified, invoke it with the attribute. - */ -static int stun_handle_packet(int s, struct sockaddr_in *src, - unsigned char *data, size_t len, stun_cb_f *stun_cb, void *arg) -{ - struct stun_header *hdr = (struct stun_header *)data; - struct stun_attr *attr; - struct stun_state st; - int ret = STUN_IGNORE; - int x; - - /* On entry, 'len' is the length of the udp payload. After the - * initial checks it becomes the size of unprocessed options, - * while 'data' is advanced accordingly. - */ - if (len < sizeof(struct stun_header)) { - ast_debug(1, "Runt STUN packet (only %d, wanting at least %d)\n", (int) len, (int) sizeof(struct stun_header)); - return -1; - } - len -= sizeof(struct stun_header); - data += sizeof(struct stun_header); - x = ntohs(hdr->msglen); /* len as advertised in the message */ - if (stundebug) - ast_verbose("STUN Packet, msg %s (%04x), length: %d\n", stun_msg2str(ntohs(hdr->msgtype)), ntohs(hdr->msgtype), x); - if (x > len) { - ast_debug(1, "Scrambled STUN packet length (got %d, expecting %d)\n", x, (int)len); - } else - len = x; - memset(&st, 0, sizeof(st)); - while (len) { - if (len < sizeof(struct stun_attr)) { - ast_debug(1, "Runt Attribute (got %d, expecting %d)\n", (int)len, (int) sizeof(struct stun_attr)); - break; - } - attr = (struct stun_attr *)data; - /* compute total attribute length */ - x = ntohs(attr->len) + sizeof(struct stun_attr); - if (x > len) { - ast_debug(1, "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n", x, (int)len); - break; - } - if (stun_cb) - stun_cb(attr, arg); - if (stun_process_attr(&st, attr)) { - ast_debug(1, "Failed to handle attribute %s (%04x)\n", stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr)); - break; - } - /* Clear attribute id: in case previous entry was a string, - * this will act as the terminator for the string. - */ - attr->attr = 0; - data += x; - len -= x; - } - /* Null terminate any string. - * XXX NOTE, we write past the size of the buffer passed by the - * caller, so this is potentially dangerous. The only thing that - * saves us is that usually we read the incoming message in a - * much larger buffer in the struct ast_rtp - */ - *data = '\0'; - - /* Now prepare to generate a reply, which at the moment is done - * only for properly formed (len == 0) STUN_BINDREQ messages. - */ - if (len == 0) { - unsigned char respdata[1024]; - struct stun_header *resp = (struct stun_header *)respdata; - int resplen = 0; /* len excluding header */ - int respleft = sizeof(respdata) - sizeof(struct stun_header); - - resp->id = hdr->id; - resp->msgtype = 0; - resp->msglen = 0; - attr = (struct stun_attr *)resp->ies; - switch (ntohs(hdr->msgtype)) { - case STUN_BINDREQ: - if (stundebug) - ast_verbose("STUN Bind Request, username: %s\n", - st.username ? st.username : "<none>"); - if (st.username) - append_attr_string(&attr, STUN_USERNAME, st.username, &resplen, &respleft); - append_attr_address(&attr, STUN_MAPPED_ADDRESS, src, &resplen, &respleft); - resp->msglen = htons(resplen); - resp->msgtype = htons(STUN_BINDRESP); - stun_send(s, src, resp); - ret = STUN_ACCEPT; - break; - default: - if (stundebug) - ast_verbose("Dunno what to do with STUN message %04x (%s)\n", ntohs(hdr->msgtype), stun_msg2str(ntohs(hdr->msgtype))); - } - } - return ret; -} - -/*! \brief Extract the STUN_MAPPED_ADDRESS from the stun response. - * This is used as a callback for stun_handle_response - * when called from ast_stun_request. - */ -static int stun_get_mapped(struct stun_attr *attr, void *arg) -{ - struct stun_addr *addr = (struct stun_addr *)(attr + 1); - struct sockaddr_in *sa = (struct sockaddr_in *)arg; - - if (ntohs(attr->attr) != STUN_MAPPED_ADDRESS || ntohs(attr->len) != 8) - return 1; /* not us. */ - sa->sin_port = addr->port; - sa->sin_addr.s_addr = addr->addr; - return 0; -} - -/*! \brief Generic STUN request - * Send a generic stun request to the server specified, - * possibly waiting for a reply and filling the 'reply' field with - * the externally visible address. Note that in this case the request - * will be blocking. - * (Note, the interface may change slightly in the future). - * - * \param s the socket used to send the request - * \param dst the address of the STUN server - * \param username if non null, add the username in the request - * \param answer if non null, the function waits for a response and - * puts here the externally visible address. - * \return 0 on success, other values on error. - */ -int ast_stun_request(int s, struct sockaddr_in *dst, - const char *username, struct sockaddr_in *answer) -{ - struct stun_header *req; - unsigned char reqdata[1024]; - int reqlen, reqleft; - struct stun_attr *attr; - int res = 0; - int retry; - - req = (struct stun_header *)reqdata; - stun_req_id(req); - reqlen = 0; - reqleft = sizeof(reqdata) - sizeof(struct stun_header); - req->msgtype = 0; - req->msglen = 0; - attr = (struct stun_attr *)req->ies; - if (username) - append_attr_string(&attr, STUN_USERNAME, username, &reqlen, &reqleft); - req->msglen = htons(reqlen); - req->msgtype = htons(STUN_BINDREQ); - for (retry = 0; retry < 3; retry++) { /* XXX make retries configurable */ - /* send request, possibly wait for reply */ - unsigned char reply_buf[1024]; - fd_set rfds; - struct timeval to = { 3, 0 }; /* timeout, make it configurable */ - struct sockaddr_in src; - socklen_t srclen; - - res = stun_send(s, dst, req); - if (res < 0) { - ast_log(LOG_WARNING, "ast_stun_request send #%d failed error %d, retry\n", - retry, res); - continue; - } - if (answer == NULL) - break; - FD_ZERO(&rfds); - FD_SET(s, &rfds); - res = ast_select(s + 1, &rfds, NULL, NULL, &to); - if (res <= 0) /* timeout or error */ - continue; - memset(&src, '\0', sizeof(src)); - srclen = sizeof(src); - /* XXX pass -1 in the size, because stun_handle_packet might - * write past the end of the buffer. - */ - res = recvfrom(s, reply_buf, sizeof(reply_buf) - 1, - 0, (struct sockaddr *)&src, &srclen); - if (res < 0) { - ast_log(LOG_WARNING, "ast_stun_request recvfrom #%d failed error %d, retry\n", - retry, res); - continue; - } - memset(answer, '\0', sizeof(struct sockaddr_in)); - stun_handle_packet(s, &src, reply_buf, res, - stun_get_mapped, answer); - res = 0; /* signal regular exit */ - break; - } - return res; -} - -/*! \brief send a STUN BIND request to the given destination. - * Optionally, add a username if specified. - */ -void ast_rtp_stun_request(struct ast_rtp *rtp, struct sockaddr_in *suggestion, const char *username) -{ - ast_stun_request(rtp->s, suggestion, username, NULL); -} - -/*! \brief List of current sessions */ -static AST_RWLIST_HEAD_STATIC(protos, ast_rtp_protocol); - -static void timeval2ntp(struct timeval when, unsigned int *msw, unsigned int *lsw) -{ - unsigned int sec, usec, frac; - sec = when.tv_sec + 2208988800u; /* Sec between 1900 and 1970 */ - usec = when.tv_usec; - frac = (usec << 12) + (usec << 8) - ((usec * 3650) >> 6); - *msw = sec; - *lsw = frac; -} - -int ast_rtp_fd(struct ast_rtp *rtp) -{ - return rtp->s; -} - -int ast_rtcp_fd(struct ast_rtp *rtp) -{ - if (rtp->rtcp) - return rtp->rtcp->s; - return -1; -} - -unsigned int ast_rtcp_calc_interval(struct ast_rtp *rtp) -{ - unsigned int interval; - /*! \todo XXX Do a more reasonable calculation on this one - * Look in RFC 3550 Section A.7 for an example*/ - interval = rtcpinterval; - return interval; -} - -/* \brief Put RTP timeout timers on hold during another transaction, like T.38 */ -void ast_rtp_set_rtptimers_onhold(struct ast_rtp *rtp) -{ - rtp->rtptimeout = (-1) * rtp->rtptimeout; - rtp->rtpholdtimeout = (-1) * rtp->rtpholdtimeout; -} - -/*! \brief Set rtp timeout */ -void ast_rtp_set_rtptimeout(struct ast_rtp *rtp, int timeout) -{ - rtp->rtptimeout = timeout; -} - -/*! \brief Set rtp hold timeout */ -void ast_rtp_set_rtpholdtimeout(struct ast_rtp *rtp, int timeout) -{ - rtp->rtpholdtimeout = timeout; -} - -/*! \brief set RTP keepalive interval */ -void ast_rtp_set_rtpkeepalive(struct ast_rtp *rtp, int period) -{ - rtp->rtpkeepalive = period; -} - -/*! \brief Get rtp timeout */ -int ast_rtp_get_rtptimeout(struct ast_rtp *rtp) -{ - if (rtp->rtptimeout < 0) /* We're not checking, but remembering the setting (during T.38 transmission) */ - return 0; - return rtp->rtptimeout; -} - -/*! \brief Get rtp hold timeout */ -int ast_rtp_get_rtpholdtimeout(struct ast_rtp *rtp) -{ - if (rtp->rtptimeout < 0) /* We're not checking, but remembering the setting (during T.38 transmission) */ - return 0; - return rtp->rtpholdtimeout; -} - -/*! \brief Get RTP keepalive interval */ -int ast_rtp_get_rtpkeepalive(struct ast_rtp *rtp) -{ - return rtp->rtpkeepalive; -} - -void ast_rtp_set_data(struct ast_rtp *rtp, void *data) -{ - rtp->data = data; -} - -void ast_rtp_set_callback(struct ast_rtp *rtp, ast_rtp_callback callback) -{ - rtp->callback = callback; -} - -void ast_rtp_setnat(struct ast_rtp *rtp, int nat) -{ - rtp->nat = nat; -} - -int ast_rtp_getnat(struct ast_rtp *rtp) -{ - return ast_test_flag(rtp, FLAG_NAT_ACTIVE); -} - -void ast_rtp_setdtmf(struct ast_rtp *rtp, int dtmf) -{ - ast_set2_flag(rtp, dtmf ? 1 : 0, FLAG_HAS_DTMF); -} - -void ast_rtp_setdtmfcompensate(struct ast_rtp *rtp, int compensate) -{ - ast_set2_flag(rtp, compensate ? 1 : 0, FLAG_DTMF_COMPENSATE); -} - -void ast_rtp_setstun(struct ast_rtp *rtp, int stun_enable) -{ - ast_set2_flag(rtp, stun_enable ? 1 : 0, FLAG_HAS_STUN); -} - -static void rtp_bridge_lock(struct ast_rtp *rtp) -{ -#ifdef P2P_INTENSE - ast_mutex_lock(&rtp->bridge_lock); -#endif - return; -} - -static void rtp_bridge_unlock(struct ast_rtp *rtp) -{ -#ifdef P2P_INTENSE - ast_mutex_unlock(&rtp->bridge_lock); -#endif - return; -} - -/*! \brief Calculate normal deviation */ -static double normdev_compute(double normdev, double sample, unsigned int sample_count) -{ - normdev = normdev * sample_count + sample; - sample_count++; - - return normdev / sample_count; -} - -static double stddev_compute(double stddev, double sample, double normdev, double normdev_curent, unsigned int sample_count) -{ -/* - for the formula check http://www.cs.umd.edu/~austinjp/constSD.pdf - return sqrt( (sample_count*pow(stddev,2) + sample_count*pow((sample-normdev)/(sample_count+1),2) + pow(sample-normdev_curent,2)) / (sample_count+1)); - we can compute the sigma^2 and that way we would have to do the sqrt only 1 time at the end and would save another pow 2 compute - optimized formula -*/ -#define SQUARE(x) ((x) * (x)) - - stddev = sample_count * stddev; - sample_count++; - - return stddev + - ( sample_count * SQUARE( (sample - normdev) / sample_count ) ) + - ( SQUARE(sample - normdev_curent) / sample_count ); - -#undef SQUARE -} - -static struct ast_frame *send_dtmf(struct ast_rtp *rtp, enum ast_frame_type type) -{ - if (((ast_test_flag(rtp, FLAG_DTMF_COMPENSATE) && type == AST_FRAME_DTMF_END) || - (type == AST_FRAME_DTMF_BEGIN)) && ast_tvcmp(ast_tvnow(), rtp->dtmfmute) < 0) { - ast_debug(1, "Ignore potential DTMF echo from '%s'\n", ast_inet_ntoa(rtp->them.sin_addr)); - rtp->resp = 0; - rtp->dtmfsamples = 0; - return &ast_null_frame; - } - ast_debug(1, "Sending dtmf: %d (%c), at %s\n", rtp->resp, rtp->resp, ast_inet_ntoa(rtp->them.sin_addr)); - if (rtp->resp == 'X') { - rtp->f.frametype = AST_FRAME_CONTROL; - rtp->f.subclass = AST_CONTROL_FLASH; - } else { - rtp->f.frametype = type; - rtp->f.subclass = rtp->resp; - } - rtp->f.datalen = 0; - rtp->f.samples = 0; - rtp->f.mallocd = 0; - rtp->f.src = "RTP"; - return &rtp->f; - -} - -static inline int rtp_debug_test_addr(struct sockaddr_in *addr) -{ - if (rtpdebug == 0) - return 0; - if (rtpdebugaddr.sin_addr.s_addr) { - if (((ntohs(rtpdebugaddr.sin_port) != 0) - && (rtpdebugaddr.sin_port != addr->sin_port)) - || (rtpdebugaddr.sin_addr.s_addr != addr->sin_addr.s_addr)) - return 0; - } - return 1; -} - -static inline int rtcp_debug_test_addr(struct sockaddr_in *addr) -{ - if (rtcpdebug == 0) - return 0; - if (rtcpdebugaddr.sin_addr.s_addr) { - if (((ntohs(rtcpdebugaddr.sin_port) != 0) - && (rtcpdebugaddr.sin_port != addr->sin_port)) - || (rtcpdebugaddr.sin_addr.s_addr != addr->sin_addr.s_addr)) - return 0; - } - return 1; -} - - -static struct ast_frame *process_cisco_dtmf(struct ast_rtp *rtp, unsigned char *data, int len) -{ - unsigned int event; - char resp = 0; - struct ast_frame *f = NULL; - unsigned char seq; - unsigned int flags; - unsigned int power; - - /* We should have at least 4 bytes in RTP data */ - if (len < 4) - return f; - - /* The format of Cisco RTP DTMF packet looks like next: - +0 - sequence number of DTMF RTP packet (begins from 1, - wrapped to 0) - +1 - set of flags - +1 (bit 0) - flaps by different DTMF digits delimited by audio - or repeated digit without audio??? - +2 (+4,+6,...) - power level? (rises from 0 to 32 at begin of tone - then falls to 0 at its end) - +3 (+5,+7,...) - detected DTMF digit (0..9,*,#,A-D,...) - Repeated DTMF information (bytes 4/5, 6/7) is history shifted right - by each new packet and thus provides some redudancy. - - Sample of Cisco RTP DTMF packet is (all data in hex): - 19 07 00 02 12 02 20 02 - showing end of DTMF digit '2'. - - The packets - 27 07 00 02 0A 02 20 02 - 28 06 20 02 00 02 0A 02 - shows begin of new digit '2' with very short pause (20 ms) after - previous digit '2'. Bit +1.0 flips at begin of new digit. - - Cisco RTP DTMF packets comes as replacement of audio RTP packets - so its uses the same sequencing and timestamping rules as replaced - audio packets. Repeat interval of DTMF packets is 20 ms and not rely - on audio framing parameters. Marker bit isn't used within stream of - DTMFs nor audio stream coming immediately after DTMF stream. Timestamps - are not sequential at borders between DTMF and audio streams, - */ - - seq = data[0]; - flags = data[1]; - power = data[2]; - event = data[3] & 0x1f; - - if (option_debug > 2 || rtpdebug) - ast_debug(0, "Cisco DTMF Digit: %02x (len=%d, seq=%d, flags=%02x, power=%d, history count=%d)\n", event, len, seq, flags, power, (len - 4) / 2); - if (event < 10) { - resp = '0' + event; - } else if (event < 11) { - resp = '*'; - } else if (event < 12) { - resp = '#'; - } else if (event < 16) { - resp = 'A' + (event - 12); - } else if (event < 17) { - resp = 'X'; - } - if ((!rtp->resp && power) || (rtp->resp && (rtp->resp != resp))) { - rtp->resp = resp; - /* Why we should care on DTMF compensation at reception? */ - if (!ast_test_flag(rtp, FLAG_DTMF_COMPENSATE)) { - f = send_dtmf(rtp, AST_FRAME_DTMF_BEGIN); - rtp->dtmfsamples = 0; - } - } else if ((rtp->resp == resp) && !power) { - f = send_dtmf(rtp, AST_FRAME_DTMF_END); - f->samples = rtp->dtmfsamples * 8; - rtp->resp = 0; - } else if (rtp->resp == resp) - rtp->dtmfsamples += 20 * 8; - rtp->dtmfcount = dtmftimeout; - return f; -} - -/*! - * \brief Process RTP DTMF and events according to RFC 2833. - * - * RFC 2833 is "RTP Payload for DTMF Digits, Telephony Tones and Telephony Signals". - * - * \param rtp - * \param data - * \param len - * \param seqno - * \param timestamp - * \returns - */ -static struct ast_frame *process_rfc2833(struct ast_rtp *rtp, unsigned char *data, int len, unsigned int seqno, unsigned int timestamp) -{ - unsigned int event; - unsigned int event_end; - unsigned int samples; - char resp = 0; - struct ast_frame *f = NULL; - - /* Figure out event, event end, and samples */ - event = ntohl(*((unsigned int *)(data))); - event >>= 24; - event_end = ntohl(*((unsigned int *)(data))); - event_end <<= 8; - event_end >>= 24; - samples = ntohl(*((unsigned int *)(data))); - samples &= 0xFFFF; - - /* Print out debug if turned on */ - if (rtpdebug || option_debug > 2) - ast_debug(0, "- RTP 2833 Event: %08x (len = %d)\n", event, len); - - /* Figure out what digit was pressed */ - if (event < 10) { - resp = '0' + event; - } else if (event < 11) { - resp = '*'; - } else if (event < 12) { - resp = '#'; - } else if (event < 16) { - resp = 'A' + (event - 12); - } else if (event < 17) { /* Event 16: Hook flash */ - resp = 'X'; - } else { - /* Not a supported event */ - ast_log(LOG_DEBUG, "Ignoring RTP 2833 Event: %08x. Not a DTMF Digit.\n", event); - return &ast_null_frame; - } - - if (ast_test_flag(rtp, FLAG_DTMF_COMPENSATE)) { - if ((rtp->lastevent != timestamp) || (rtp->resp && rtp->resp != resp)) { - rtp->resp = resp; - rtp->dtmfcount = 0; - f = send_dtmf(rtp, AST_FRAME_DTMF_END); - f->len = 0; - rtp->lastevent = timestamp; - } - } else { - if ((!(rtp->resp) && (!(event_end & 0x80))) || (rtp->resp && rtp->resp != resp)) { - rtp->resp = resp; - f = send_dtmf(rtp, AST_FRAME_DTMF_BEGIN); - rtp->dtmfcount = dtmftimeout; - } else if ((event_end & 0x80) && (rtp->lastevent != seqno) && rtp->resp) { - f = send_dtmf(rtp, AST_FRAME_DTMF_END); - f->len = ast_tvdiff_ms(ast_samp2tv(samples, 8000), ast_tv(0, 0)); /* XXX hard coded 8kHz */ - rtp->resp = 0; - rtp->dtmfcount = 0; - rtp->lastevent = seqno; - } - } - - rtp->dtmfsamples = samples; - - return f; -} - -/*! - * \brief Process Comfort Noise RTP. - * - * This is incomplete at the moment. - * -*/ -static struct ast_frame *process_rfc3389(struct ast_rtp *rtp, unsigned char *data, int len) -{ - struct ast_frame *f = NULL; - /* Convert comfort noise into audio with various codecs. Unfortunately this doesn't - totally help us out becuase we don't have an engine to keep it going and we are not - guaranteed to have it every 20ms or anything */ - if (rtpdebug) - ast_debug(0, "- RTP 3389 Comfort noise event: Level %d (len = %d)\n", rtp->lastrxformat, len); - - if (!(ast_test_flag(rtp, FLAG_3389_WARNING))) { - ast_log(LOG_NOTICE, "Comfort noise support incomplete in Asterisk (RFC 3389). Please turn off on client if possible. Client IP: %s\n", - ast_inet_ntoa(rtp->them.sin_addr)); - ast_set_flag(rtp, FLAG_3389_WARNING); - } - - /* Must have at least one byte */ - if (!len) - return NULL; - if (len < 24) { - rtp->f.data.ptr = rtp->rawdata + AST_FRIENDLY_OFFSET; - rtp->f.datalen = len - 1; - rtp->f.offset = AST_FRIENDLY_OFFSET; - memcpy(rtp->f.data.ptr, data + 1, len - 1); - } else { - rtp->f.data.ptr = NULL; - rtp->f.offset = 0; - rtp->f.datalen = 0; - } - rtp->f.frametype = AST_FRAME_CNG; - rtp->f.subclass = data[0] & 0x7f; - rtp->f.datalen = len - 1; - rtp->f.samples = 0; - rtp->f.delivery.tv_usec = rtp->f.delivery.tv_sec = 0; - f = &rtp->f; - return f; -} - -static int rtpread(int *id, int fd, short events, void *cbdata) -{ - struct ast_rtp *rtp = cbdata; - struct ast_frame *f; - f = ast_rtp_read(rtp); - if (f) { - if (rtp->callback) - rtp->callback(rtp, f, rtp->data); - } - return 1; -} - -struct ast_frame *ast_rtcp_read(struct ast_rtp *rtp) -{ - socklen_t len; - int position, i, packetwords; - int res; - struct sockaddr_in sock_in; - unsigned int rtcpdata[8192 + AST_FRIENDLY_OFFSET]; - unsigned int *rtcpheader; - int pt; - struct timeval now; - unsigned int length; - int rc; - double rttsec; - uint64_t rtt = 0; - unsigned int dlsr; - unsigned int lsr; - unsigned int msw; - unsigned int lsw; - unsigned int comp; - struct ast_frame *f = &ast_null_frame; - - double reported_jitter; - double reported_normdev_jitter_current; - double normdevrtt_current; - double reported_lost; - double reported_normdev_lost_current; - - if (!rtp || !rtp->rtcp) - return &ast_null_frame; - - len = sizeof(sock_in); - - res = recvfrom(rtp->rtcp->s, rtcpdata + AST_FRIENDLY_OFFSET, sizeof(rtcpdata) - sizeof(unsigned int) * AST_FRIENDLY_OFFSET, - 0, (struct sockaddr *)&sock_in, &len); - rtcpheader = (unsigned int *)(rtcpdata + AST_FRIENDLY_OFFSET); - - if (res < 0) { - ast_assert(errno != EBADF); - if (errno != EAGAIN) { - ast_log(LOG_WARNING, "RTCP Read error: %s. Hanging up.\n", strerror(errno)); - return NULL; - } - return &ast_null_frame; - } - - packetwords = res / 4; - - if (rtp->nat) { - /* Send to whoever sent to us */ - if ((rtp->rtcp->them.sin_addr.s_addr != sock_in.sin_addr.s_addr) || - (rtp->rtcp->them.sin_port != sock_in.sin_port)) { - memcpy(&rtp->rtcp->them, &sock_in, sizeof(rtp->rtcp->them)); - if (option_debug || rtpdebug) - ast_debug(0, "RTCP NAT: Got RTCP from other end. Now sending to address %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); - } - } - - ast_debug(1, "Got RTCP report of %d bytes\n", res); - - /* Process a compound packet */ - position = 0; - while (position < packetwords) { - i = position; - length = ntohl(rtcpheader[i]); - pt = (length & 0xff0000) >> 16; - rc = (length & 0x1f000000) >> 24; - length &= 0xffff; - - if ((i + length) > packetwords) { - if (option_debug || rtpdebug) - ast_log(LOG_DEBUG, "RTCP Read too short\n"); - return &ast_null_frame; - } - - if (rtcp_debug_test_addr(&sock_in)) { - ast_verbose("\n\nGot RTCP from %s:%d\n", ast_inet_ntoa(sock_in.sin_addr), ntohs(sock_in.sin_port)); - ast_verbose("PT: %d(%s)\n", pt, (pt == 200) ? "Sender Report" : (pt == 201) ? "Receiver Report" : (pt == 192) ? "H.261 FUR" : "Unknown"); - ast_verbose("Reception reports: %d\n", rc); - ast_verbose("SSRC of sender: %u\n", rtcpheader[i + 1]); - } - - i += 2; /* Advance past header and ssrc */ - - switch (pt) { - case RTCP_PT_SR: - gettimeofday(&rtp->rtcp->rxlsr,NULL); /* To be able to populate the dlsr */ - rtp->rtcp->spc = ntohl(rtcpheader[i+3]); - rtp->rtcp->soc = ntohl(rtcpheader[i + 4]); - rtp->rtcp->themrxlsr = ((ntohl(rtcpheader[i]) & 0x0000ffff) << 16) | ((ntohl(rtcpheader[i + 1]) & 0xffff0000) >> 16); /* Going to LSR in RR*/ - - if (rtcp_debug_test_addr(&sock_in)) { - ast_verbose("NTP timestamp: %lu.%010lu\n", (unsigned long) ntohl(rtcpheader[i]), (unsigned long) ntohl(rtcpheader[i + 1]) * 4096); - ast_verbose("RTP timestamp: %lu\n", (unsigned long) ntohl(rtcpheader[i + 2])); - ast_verbose("SPC: %lu\tSOC: %lu\n", (unsigned long) ntohl(rtcpheader[i + 3]), (unsigned long) ntohl(rtcpheader[i + 4])); - } - i += 5; - if (rc < 1) - break; - /* Intentional fall through */ - case RTCP_PT_RR: - /* Don't handle multiple reception reports (rc > 1) yet */ - /* Calculate RTT per RFC */ - gettimeofday(&now, NULL); - timeval2ntp(now, &msw, &lsw); - if (ntohl(rtcpheader[i + 4]) && ntohl(rtcpheader[i + 5])) { /* We must have the LSR && DLSR */ - comp = ((msw & 0xffff) << 16) | ((lsw & 0xffff0000) >> 16); - lsr = ntohl(rtcpheader[i + 4]); - dlsr = ntohl(rtcpheader[i + 5]); - rtt = comp - lsr - dlsr; - - /* Convert end to end delay to usec (keeping the calculation in 64bit space) - sess->ee_delay = (eedelay * 1000) / 65536; */ - if (rtt < 4294) { - rtt = (rtt * 1000000) >> 16; - } else { - rtt = (rtt * 1000) >> 16; - rtt *= 1000; - } - rtt = rtt / 1000.; - rttsec = rtt / 1000.; - rtp->rtcp->rtt = rttsec; - - if (comp - dlsr >= lsr) { - rtp->rtcp->accumulated_transit += rttsec; - - if (rtp->rtcp->rtt_count == 0) - rtp->rtcp->minrtt = rttsec; - - if (rtp->rtcp->maxrtt<rttsec) - rtp->rtcp->maxrtt = rttsec; - - if (rtp->rtcp->minrtt>rttsec) - rtp->rtcp->minrtt = rttsec; - - normdevrtt_current = normdev_compute(rtp->rtcp->normdevrtt, rttsec, rtp->rtcp->rtt_count); - - rtp->rtcp->stdevrtt = stddev_compute(rtp->rtcp->stdevrtt, rttsec, rtp->rtcp->normdevrtt, normdevrtt_current, rtp->rtcp->rtt_count); - - rtp->rtcp->normdevrtt = normdevrtt_current; - - rtp->rtcp->rtt_count++; - } else if (rtcp_debug_test_addr(&sock_in)) { - ast_verbose("Internal RTCP NTP clock skew detected: " - "lsr=%u, now=%u, dlsr=%u (%d:%03dms), " - "diff=%d\n", - lsr, comp, dlsr, dlsr / 65536, - (dlsr % 65536) * 1000 / 65536, - dlsr - (comp - lsr)); - } - } - - rtp->rtcp->reported_jitter = ntohl(rtcpheader[i + 3]); - reported_jitter = (double) rtp->rtcp->reported_jitter; - - if (rtp->rtcp->reported_jitter_count == 0) - rtp->rtcp->reported_minjitter = reported_jitter; - - if (reported_jitter < rtp->rtcp->reported_minjitter) - rtp->rtcp->reported_minjitter = reported_jitter; - - if (reported_jitter > rtp->rtcp->reported_maxjitter) - rtp->rtcp->reported_maxjitter = reported_jitter; - - reported_normdev_jitter_current = normdev_compute(rtp->rtcp->reported_normdev_jitter, reported_jitter, rtp->rtcp->reported_jitter_count); - - rtp->rtcp->reported_stdev_jitter = stddev_compute(rtp->rtcp->reported_stdev_jitter, reported_jitter, rtp->rtcp->reported_normdev_jitter, reported_normdev_jitter_current, rtp->rtcp->reported_jitter_count); - - rtp->rtcp->reported_normdev_jitter = reported_normdev_jitter_current; - - rtp->rtcp->reported_lost = ntohl(rtcpheader[i + 1]) & 0xffffff; - - reported_lost = (double) rtp->rtcp->reported_lost; - - /* using same counter as for jitter */ - if (rtp->rtcp->reported_jitter_count == 0) - rtp->rtcp->reported_minlost = reported_lost; - - if (reported_lost < rtp->rtcp->reported_minlost) - rtp->rtcp->reported_minlost = reported_lost; - - if (reported_lost > rtp->rtcp->reported_maxlost) - rtp->rtcp->reported_maxlost = reported_lost; - - reported_normdev_lost_current = normdev_compute(rtp->rtcp->reported_normdev_lost, reported_lost, rtp->rtcp->reported_jitter_count); - - rtp->rtcp->reported_stdev_lost = stddev_compute(rtp->rtcp->reported_stdev_lost, reported_lost, rtp->rtcp->reported_normdev_lost, reported_normdev_lost_current, rtp->rtcp->reported_jitter_count); - - rtp->rtcp->reported_normdev_lost = reported_normdev_lost_current; - - rtp->rtcp->reported_jitter_count++; - - if (rtcp_debug_test_addr(&sock_in)) { - ast_verbose(" Fraction lost: %ld\n", (((long) ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24)); - ast_verbose(" Packets lost so far: %d\n", rtp->rtcp->reported_lost); - ast_verbose(" Highest sequence number: %ld\n", (long) (ntohl(rtcpheader[i + 2]) & 0xffff)); - ast_verbose(" Sequence number cycles: %ld\n", (long) (ntohl(rtcpheader[i + 2]) & 0xffff) >> 16); - ast_verbose(" Interarrival jitter: %u\n", rtp->rtcp->reported_jitter); - ast_verbose(" Last SR(our NTP): %lu.%010lu\n",(unsigned long) ntohl(rtcpheader[i + 4]) >> 16,((unsigned long) ntohl(rtcpheader[i + 4]) << 16) * 4096); - ast_verbose(" DLSR: %4.4f (sec)\n",ntohl(rtcpheader[i + 5])/65536.0); - if (rtt) - ast_verbose(" RTT: %lu(sec)\n", (unsigned long) rtt); - } - - if (rtt) { - manager_event(EVENT_FLAG_REPORTING, "RTCPReceived", "From: %s:%d\r\n" - "PT: %d(%s)\r\n" - "ReceptionReports: %d\r\n" - "SenderSSRC: %u\r\n" - "FractionLost: %ld\r\n" - "PacketsLost: %d\r\n" - "HighestSequence: %ld\r\n" - "SequenceNumberCycles: %ld\r\n" - "IAJitter: %u\r\n" - "LastSR: %lu.%010lu\r\n" - "DLSR: %4.4f(sec)\r\n" - "RTT: %llu(sec)\r\n", - ast_inet_ntoa(sock_in.sin_addr), ntohs(sock_in.sin_port), - pt, (pt == 200) ? "Sender Report" : (pt == 201) ? "Receiver Report" : (pt == 192) ? "H.261 FUR" : "Unknown", - rc, - rtcpheader[i + 1], - (((long) ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24), - rtp->rtcp->reported_lost, - (long) (ntohl(rtcpheader[i + 2]) & 0xffff), - (long) (ntohl(rtcpheader[i + 2]) & 0xffff) >> 16, - rtp->rtcp->reported_jitter, - (unsigned long) ntohl(rtcpheader[i + 4]) >> 16, ((unsigned long) ntohl(rtcpheader[i + 4]) << 16) * 4096, - ntohl(rtcpheader[i + 5])/65536.0, - (unsigned long long)rtt); - } else { - manager_event(EVENT_FLAG_REPORTING, "RTCPReceived", "From: %s:%d\r\n" - "PT: %d(%s)\r\n" - "ReceptionReports: %d\r\n" - "SenderSSRC: %u\r\n" - "FractionLost: %ld\r\n" - "PacketsLost: %d\r\n" - "HighestSequence: %ld\r\n" - "SequenceNumberCycles: %ld\r\n" - "IAJitter: %u\r\n" - "LastSR: %lu.%010lu\r\n" - "DLSR: %4.4f(sec)\r\n", - ast_inet_ntoa(sock_in.sin_addr), ntohs(sock_in.sin_port), - pt, (pt == 200) ? "Sender Report" : (pt == 201) ? "Receiver Report" : (pt == 192) ? "H.261 FUR" : "Unknown", - rc, - rtcpheader[i + 1], - (((long) ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24), - rtp->rtcp->reported_lost, - (long) (ntohl(rtcpheader[i + 2]) & 0xffff), - (long) (ntohl(rtcpheader[i + 2]) & 0xffff) >> 16, - rtp->rtcp->reported_jitter, - (unsigned long) ntohl(rtcpheader[i + 4]) >> 16, - ((unsigned long) ntohl(rtcpheader[i + 4]) << 16) * 4096, - ntohl(rtcpheader[i + 5])/65536.0); - } - break; - case RTCP_PT_FUR: - if (rtcp_debug_test_addr(&sock_in)) - ast_verbose("Received an RTCP Fast Update Request\n"); - rtp->f.frametype = AST_FRAME_CONTROL; - rtp->f.subclass = AST_CONTROL_VIDUPDATE; - rtp->f.datalen = 0; - rtp->f.samples = 0; - rtp->f.mallocd = 0; - rtp->f.src = "RTP"; - f = &rtp->f; - break; - case RTCP_PT_SDES: - if (rtcp_debug_test_addr(&sock_in)) - ast_verbose("Received an SDES from %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); - break; - case RTCP_PT_BYE: - if (rtcp_debug_test_addr(&sock_in)) - ast_verbose("Received a BYE from %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); - break; - default: - ast_debug(1, "Unknown RTCP packet (pt=%d) received from %s:%d\n", pt, ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); - break; - } - position += (length + 1); - } - rtp->rtcp->rtcp_info = 1; - return f; -} - -static void calc_rxstamp(struct timeval *when, struct ast_rtp *rtp, unsigned int timestamp, int mark) -{ - struct timeval now; - double transit; - double current_time; - double d; - double dtv; - double prog; - - double normdev_rxjitter_current; - if ((!rtp->rxcore.tv_sec && !rtp->rxcore.tv_usec) || mark) { - gettimeofday(&rtp->rxcore, NULL); - rtp->drxcore = (double) rtp->rxcore.tv_sec + (double) rtp->rxcore.tv_usec / 1000000; - /* map timestamp to a real time */ - rtp->seedrxts = timestamp; /* Their RTP timestamp started with this */ - rtp->rxcore.tv_sec -= timestamp / 8000; - rtp->rxcore.tv_usec -= (timestamp % 8000) * 125; - /* Round to 0.1ms for nice, pretty timestamps */ - rtp->rxcore.tv_usec -= rtp->rxcore.tv_usec % 100; - if (rtp->rxcore.tv_usec < 0) { - /* Adjust appropriately if necessary */ - rtp->rxcore.tv_usec += 1000000; - rtp->rxcore.tv_sec -= 1; - } - } - - gettimeofday(&now,NULL); - /* rxcore is the mapping between the RTP timestamp and _our_ real time from gettimeofday() */ - when->tv_sec = rtp->rxcore.tv_sec + timestamp / 8000; - when->tv_usec = rtp->rxcore.tv_usec + (timestamp % 8000) * 125; - if (when->tv_usec >= 1000000) { - when->tv_usec -= 1000000; - when->tv_sec += 1; - } - prog = (double)((timestamp-rtp->seedrxts)/8000.); - dtv = (double)rtp->drxcore + (double)(prog); - current_time = (double)now.tv_sec + (double)now.tv_usec/1000000; - transit = current_time - dtv; - d = transit - rtp->rxtransit; - rtp->rxtransit = transit; - if (d<0) - d=-d; - rtp->rxjitter += (1./16.) * (d - rtp->rxjitter); - if (rtp->rtcp && rtp->rxjitter > rtp->rtcp->maxrxjitter) - rtp->rtcp->maxrxjitter = rtp->rxjitter; - if (rtp->rtcp->rxjitter_count == 1) - rtp->rtcp->minrxjitter = rtp->rxjitter; - if (rtp->rtcp && rtp->rxjitter < rtp->rtcp->minrxjitter) - rtp->rtcp->minrxjitter = rtp->rxjitter; - - normdev_rxjitter_current = normdev_compute(rtp->rtcp->normdev_rxjitter,rtp->rxjitter,rtp->rtcp->rxjitter_count); - rtp->rtcp->stdev_rxjitter = stddev_compute(rtp->rtcp->stdev_rxjitter,rtp->rxjitter,rtp->rtcp->normdev_rxjitter,normdev_rxjitter_current,rtp->rtcp->rxjitter_count); - - rtp->rtcp->normdev_rxjitter = normdev_rxjitter_current; - rtp->rtcp->rxjitter_count++; -} - -/*! \brief Perform a Packet2Packet RTP write */ -static int bridge_p2p_rtp_write(struct ast_rtp *rtp, struct ast_rtp *bridged, unsigned int *rtpheader, int len, int hdrlen) -{ - int res = 0, payload = 0, bridged_payload = 0, mark; - struct rtpPayloadType rtpPT; - int reconstruct = ntohl(rtpheader[0]); - - /* Get fields from packet */ - payload = (reconstruct & 0x7f0000) >> 16; - mark = (((reconstruct & 0x800000) >> 23) != 0); - - /* Check what the payload value should be */ - rtpPT = ast_rtp_lookup_pt(rtp, payload); - - /* If the payload is DTMF, and we are listening for DTMF - then feed it into the core */ - if (ast_test_flag(rtp, FLAG_P2P_NEED_DTMF) && !rtpPT.isAstFormat && rtpPT.code == AST_RTP_DTMF) - return -1; - - /* Otherwise adjust bridged payload to match */ - bridged_payload = ast_rtp_lookup_code(bridged, rtpPT.isAstFormat, rtpPT.code); - - /* If the payload coming in is not one of the negotiated ones then send it to the core, this will cause formats to change and the bridge to break */ - if (!bridged->current_RTP_PT[bridged_payload].code) - return -1; - - - /* If the mark bit has not been sent yet... do it now */ - if (!ast_test_flag(rtp, FLAG_P2P_SENT_MARK)) { - mark = 1; - ast_set_flag(rtp, FLAG_P2P_SENT_MARK); - } - - /* Reconstruct part of the packet */ - reconstruct &= 0xFF80FFFF; - reconstruct |= (bridged_payload << 16); - reconstruct |= (mark << 23); - rtpheader[0] = htonl(reconstruct); - - /* Send the packet back out */ - res = sendto(bridged->s, (void *)rtpheader, len, 0, (struct sockaddr *)&bridged->them, sizeof(bridged->them)); - if (res < 0) { - if (!bridged->nat || (bridged->nat && (ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) { - ast_debug(1, "RTP Transmission error of packet to %s:%d: %s\n", ast_inet_ntoa(bridged->them.sin_addr), ntohs(bridged->them.sin_port), strerror(errno)); - } else if (((ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug) && !ast_test_flag(bridged, FLAG_NAT_INACTIVE_NOWARN)) { - if (option_debug || rtpdebug) - ast_debug(0, "RTP NAT: Can't write RTP to private address %s:%d, waiting for other end to send audio...\n", ast_inet_ntoa(bridged->them.sin_addr), ntohs(bridged->them.sin_port)); - ast_set_flag(bridged, FLAG_NAT_INACTIVE_NOWARN); - } - return 0; - } else if (rtp_debug_test_addr(&bridged->them)) - ast_verbose("Sent RTP P2P packet to %s:%u (type %-2.2d, len %-6.6u)\n", ast_inet_ntoa(bridged->them.sin_addr), ntohs(bridged->them.sin_port), bridged_payload, len - hdrlen); - - return 0; -} - -struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) -{ - int res; - struct sockaddr_in sock_in; - socklen_t len; - unsigned int seqno; - int version; - int payloadtype; - int hdrlen = 12; - int padding; - int mark; - int ext; - int cc; - unsigned int ssrc; - unsigned int timestamp; - unsigned int *rtpheader; - struct rtpPayloadType rtpPT; - struct ast_rtp *bridged = NULL; - int prev_seqno; - - /* If time is up, kill it */ - if (rtp->sending_digit) - ast_rtp_senddigit_continuation(rtp); - - len = sizeof(sock_in); - - /* Cache where the header will go */ - res = recvfrom(rtp->s, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET, - 0, (struct sockaddr *)&sock_in, &len); - - /* If strict RTP protection is enabled see if we need to learn this address or if the packet should be dropped */ - if (rtp->strict_rtp_state == STRICT_RTP_LEARN) { - /* Copy over address that this packet was received on */ - memcpy(&rtp->strict_rtp_address, &sock_in, sizeof(rtp->strict_rtp_address)); - /* Now move over to actually protecting the RTP port */ - rtp->strict_rtp_state = STRICT_RTP_CLOSED; - ast_debug(1, "Learned remote address is %s:%d for strict RTP purposes, now protecting the port.\n", ast_inet_ntoa(rtp->strict_rtp_address.sin_addr), ntohs(rtp->strict_rtp_address.sin_port)); - } else if (rtp->strict_rtp_state == STRICT_RTP_CLOSED) { - /* If the address we previously learned doesn't match the address this packet came in on simply drop it */ - if ((rtp->strict_rtp_address.sin_addr.s_addr != sock_in.sin_addr.s_addr) || (rtp->strict_rtp_address.sin_port != sock_in.sin_port)) { - ast_debug(1, "Received RTP packet from %s:%d, dropping due to strict RTP protection. Expected it to be from %s:%d\n", ast_inet_ntoa(sock_in.sin_addr), ntohs(sock_in.sin_port), ast_inet_ntoa(rtp->strict_rtp_address.sin_addr), ntohs(rtp->strict_rtp_address.sin_port)); - return &ast_null_frame; - } - } - - rtpheader = (unsigned int *)(rtp->rawdata + AST_FRIENDLY_OFFSET); - if (res < 0) { - ast_assert(errno != EBADF); - if (errno != EAGAIN) { - ast_log(LOG_WARNING, "RTP Read error: %s. Hanging up.\n", strerror(errno)); - return NULL; - } - return &ast_null_frame; - } - - if (res < hdrlen) { - ast_log(LOG_WARNING, "RTP Read too short\n"); - return &ast_null_frame; - } - - /* Get fields */ - seqno = ntohl(rtpheader[0]); - - /* Check RTP version */ - version = (seqno & 0xC0000000) >> 30; - if (!version) { - /* If the two high bits are 0, this might be a - * STUN message, so process it. stun_handle_packet() - * answers to requests, and it returns STUN_ACCEPT - * if the request is valid. - */ - if ((stun_handle_packet(rtp->s, &sock_in, rtp->rawdata + AST_FRIENDLY_OFFSET, res, NULL, NULL) == STUN_ACCEPT) && - (!rtp->them.sin_port && !rtp->them.sin_addr.s_addr)) { - memcpy(&rtp->them, &sock_in, sizeof(rtp->them)); - } - return &ast_null_frame; - } - -#if 0 /* Allow to receive RTP stream with closed transmission path */ - /* If we don't have the other side's address, then ignore this */ - if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port) - return &ast_null_frame; -#endif - - /* Send to whoever send to us if NAT is turned on */ - if (rtp->nat) { - if ((rtp->them.sin_addr.s_addr != sock_in.sin_addr.s_addr) || - (rtp->them.sin_port != sock_in.sin_port)) { - rtp->them = sock_in; - if (rtp->rtcp) { - int h = 0; - memcpy(&rtp->rtcp->them, &sock_in, sizeof(rtp->rtcp->them)); - h = ntohs(rtp->them.sin_port); - rtp->rtcp->them.sin_port = htons(h + 1); - } - rtp->rxseqno = 0; - ast_set_flag(rtp, FLAG_NAT_ACTIVE); - if (option_debug || rtpdebug) - ast_debug(0, "RTP NAT: Got audio from other end. Now sending to address %s:%d\n", ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port)); - } - } - - /* If we are bridged to another RTP stream, send direct */ - if ((bridged = ast_rtp_get_bridged(rtp)) && !bridge_p2p_rtp_write(rtp, bridged, rtpheader, res, hdrlen)) - return &ast_null_frame; - - if (version != 2) - return &ast_null_frame; - - payloadtype = (seqno & 0x7f0000) >> 16; - padding = seqno & (1 << 29); - mark = seqno & (1 << 23); - ext = seqno & (1 << 28); - cc = (seqno & 0xF000000) >> 24; - seqno &= 0xffff; - timestamp = ntohl(rtpheader[1]); - ssrc = ntohl(rtpheader[2]); - - if (!mark && rtp->rxssrc && rtp->rxssrc != ssrc) { - if (option_debug || rtpdebug) - ast_debug(0, "Forcing Marker bit, because SSRC has changed\n"); - mark = 1; - } - - rtp->rxssrc = ssrc; - - if (padding) { - /* Remove padding bytes */ - res -= rtp->rawdata[AST_FRIENDLY_OFFSET + res - 1]; - } - - if (cc) { - /* CSRC fields present */ - hdrlen += cc*4; - } - - if (ext) { - /* RTP Extension present */ - hdrlen += (ntohl(rtpheader[hdrlen/4]) & 0xffff) << 2; - hdrlen += 4; - if (option_debug) { - int profile; - profile = (ntohl(rtpheader[3]) & 0xffff0000) >> 16; - if (profile == 0x505a) - ast_debug(1, "Found Zfone extension in RTP stream - zrtp - not supported.\n"); - else - ast_debug(1, "Found unknown RTP Extensions %x\n", profile); - } - } - - if (res < hdrlen) { - ast_log(LOG_WARNING, "RTP Read too short (%d, expecting %d)\n", res, hdrlen); - return &ast_null_frame; - } - - rtp->rxcount++; /* Only count reasonably valid packets, this'll make the rtcp stats more accurate */ - - if (rtp->rxcount==1) { - /* This is the first RTP packet successfully received from source */ - rtp->seedrxseqno = seqno; - } - - /* Do not schedule RR if RTCP isn't run */ - if (rtp->rtcp && rtp->rtcp->them.sin_addr.s_addr && rtp->rtcp->schedid < 1) { - /* Schedule transmission of Receiver Report */ - rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, rtp); - } - if ((int)rtp->lastrxseqno - (int)seqno > 100) /* if so it would indicate that the sender cycled; allow for misordering */ - rtp->cycles += RTP_SEQ_MOD; - - prev_seqno = rtp->lastrxseqno; - - rtp->lastrxseqno = seqno; - - if (!rtp->themssrc) - rtp->themssrc = ntohl(rtpheader[2]); /* Record their SSRC to put in future RR */ - - if (rtp_debug_test_addr(&sock_in)) - ast_verbose("Got RTP packet from %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", - ast_inet_ntoa(sock_in.sin_addr), ntohs(sock_in.sin_port), payloadtype, seqno, timestamp,res - hdrlen); - - rtpPT = ast_rtp_lookup_pt(rtp, payloadtype); - if (!rtpPT.isAstFormat) { - struct ast_frame *f = NULL; - - /* This is special in-band data that's not one of our codecs */ - if (rtpPT.code == AST_RTP_DTMF) { - /* It's special -- rfc2833 process it */ - if (rtp_debug_test_addr(&sock_in)) { - unsigned char *data; - unsigned int event; - unsigned int event_end; - unsigned int duration; - data = rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen; - event = ntohl(*((unsigned int *)(data))); - event >>= 24; - event_end = ntohl(*((unsigned int *)(data))); - event_end <<= 8; - event_end >>= 24; - duration = ntohl(*((unsigned int *)(data))); - duration &= 0xFFFF; - ast_verbose("Got RTP RFC2833 from %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u, mark %d, event %08x, end %d, duration %-5.5d) \n", ast_inet_ntoa(sock_in.sin_addr), ntohs(sock_in.sin_port), payloadtype, seqno, timestamp, res - hdrlen, (mark?1:0), event, ((event_end & 0x80)?1:0), duration); - } - f = process_rfc2833(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno, timestamp); - } else if (rtpPT.code == AST_RTP_CISCO_DTMF) { - /* It's really special -- process it the Cisco way */ - if (rtp->lastevent <= seqno || (rtp->lastevent >= 65530 && seqno <= 6)) { - f = process_cisco_dtmf(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen); - rtp->lastevent = seqno; - } - } else if (rtpPT.code == AST_RTP_CN) { - /* Comfort Noise */ - f = process_rfc3389(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen); - } else { - ast_log(LOG_NOTICE, "Unknown RTP codec %d received from '%s'\n", payloadtype, ast_inet_ntoa(rtp->them.sin_addr)); - } - return f ? f : &ast_null_frame; - } - rtp->lastrxformat = rtp->f.subclass = rtpPT.code; - rtp->f.frametype = (rtp->f.subclass & AST_FORMAT_AUDIO_MASK) ? AST_FRAME_VOICE : (rtp->f.subclass & AST_FORMAT_VIDEO_MASK) ? AST_FRAME_VIDEO : AST_FRAME_TEXT; - - rtp->rxseqno = seqno; - - if (rtp->dtmfcount) { - rtp->dtmfcount -= (timestamp - rtp->lastrxts); - - if (rtp->dtmfcount < 0) { - rtp->dtmfcount = 0; - } - - if (rtp->resp && !rtp->dtmfcount) { - struct ast_frame *f; - f = send_dtmf(rtp, AST_FRAME_DTMF_END); - rtp->resp = 0; - return f; - } - } - - /* Record received timestamp as last received now */ - rtp->lastrxts = timestamp; - - rtp->f.mallocd = 0; - rtp->f.datalen = res - hdrlen; - rtp->f.data.ptr = rtp->rawdata + hdrlen + AST_FRIENDLY_OFFSET; - rtp->f.offset = hdrlen + AST_FRIENDLY_OFFSET; - rtp->f.seqno = seqno; - - if (rtp->f.subclass == AST_FORMAT_T140 && (int)seqno - (prev_seqno+1) > 0 && (int)seqno - (prev_seqno+1) < 10) { - unsigned char *data = rtp->f.data.ptr; - - memmove(rtp->f.data.ptr+3, rtp->f.data.ptr, rtp->f.datalen); - rtp->f.datalen +=3; - *data++ = 0xEF; - *data++ = 0xBF; - *data = 0xBD; - } - - if (rtp->f.subclass == AST_FORMAT_T140RED) { - unsigned char *data = rtp->f.data.ptr; - unsigned char *header_end; - int num_generations; - int header_length; - int length; - int diff =(int)seqno - (prev_seqno+1); /* if diff = 0, no drop*/ - int x; - - rtp->f.subclass = AST_FORMAT_T140; - header_end = memchr(data, ((*data) & 0x7f), rtp->f.datalen); - header_end++; - - header_length = header_end - data; - num_generations = header_length / 4; - length = header_length; - - if (!diff) { - for (x = 0; x < num_generations; x++) - length += data[x * 4 + 3]; - - if (!(rtp->f.datalen - length)) - return &ast_null_frame; - - rtp->f.data.ptr += length; - rtp->f.datalen -= length; - } else if (diff > num_generations && diff < 10) { - length -= 3; - rtp->f.data.ptr += length; - rtp->f.datalen -= length; - - data = rtp->f.data.ptr; - *data++ = 0xEF; - *data++ = 0xBF; - *data = 0xBD; - } else { - for ( x = 0; x < num_generations - diff; x++) - length += data[x * 4 + 3]; - - rtp->f.data.ptr += length; - rtp->f.datalen -= length; - } - } - - if (rtp->f.subclass & AST_FORMAT_AUDIO_MASK) { - rtp->f.samples = ast_codec_get_samples(&rtp->f); - if (rtp->f.subclass == AST_FORMAT_SLINEAR) - ast_frame_byteswap_be(&rtp->f); - calc_rxstamp(&rtp->f.delivery, rtp, timestamp, mark); - /* Add timing data to let ast_generic_bridge() put the frame into a jitterbuf */ - ast_set_flag(&rtp->f, AST_FRFLAG_HAS_TIMING_INFO); - rtp->f.ts = timestamp / 8; - rtp->f.len = rtp->f.samples / ((ast_format_rate(rtp->f.subclass) / 1000)); - } else if (rtp->f.subclass & AST_FORMAT_VIDEO_MASK) { - /* Video -- samples is # of samples vs. 90000 */ - if (!rtp->lastividtimestamp) - rtp->lastividtimestamp = timestamp; - rtp->f.samples = timestamp - rtp->lastividtimestamp; - rtp->lastividtimestamp = timestamp; - rtp->f.delivery.tv_sec = 0; - rtp->f.delivery.tv_usec = 0; - /* Pass the RTP marker bit as bit 0 in the subclass field. - * This is ok because subclass is actually a bitmask, and - * the low bits represent audio formats, that are not - * involved here since we deal with video. - */ - if (mark) - rtp->f.subclass |= 0x1; - } else { - /* TEXT -- samples is # of samples vs. 1000 */ - if (!rtp->lastitexttimestamp) - rtp->lastitexttimestamp = timestamp; - rtp->f.samples = timestamp - rtp->lastitexttimestamp; - rtp->lastitexttimestamp = timestamp; - rtp->f.delivery.tv_sec = 0; - rtp->f.delivery.tv_usec = 0; - } - rtp->f.src = "RTP"; - return &rtp->f; -} - -/* The following array defines the MIME Media type (and subtype) for each - of our codecs, or RTP-specific data type. */ -static const struct mimeType { - struct rtpPayloadType payloadType; - char *type; - char *subtype; - unsigned int sample_rate; -} mimeTypes[] = { - {{1, AST_FORMAT_G723_1}, "audio", "G723", 8000}, - {{1, AST_FORMAT_GSM}, "audio", "GSM", 8000}, - {{1, AST_FORMAT_ULAW}, "audio", "PCMU", 8000}, - {{1, AST_FORMAT_ULAW}, "audio", "G711U", 8000}, - {{1, AST_FORMAT_ALAW}, "audio", "PCMA", 8000}, - {{1, AST_FORMAT_ALAW}, "audio", "G711A", 8000}, - {{1, AST_FORMAT_G726}, "audio", "G726-32", 8000}, - {{1, AST_FORMAT_ADPCM}, "audio", "DVI4", 8000}, - {{1, AST_FORMAT_SLINEAR}, "audio", "L16", 8000}, - {{1, AST_FORMAT_LPC10}, "audio", "LPC", 8000}, - {{1, AST_FORMAT_G729A}, "audio", "G729", 8000}, - {{1, AST_FORMAT_G729A}, "audio", "G729A", 8000}, - {{1, AST_FORMAT_G729A}, "audio", "G.729", 8000}, - {{1, AST_FORMAT_SPEEX}, "audio", "speex", 8000}, - {{1, AST_FORMAT_ILBC}, "audio", "iLBC", 8000}, - /* this is the sample rate listed in the RTP profile for the G.722 - codec, *NOT* the actual sample rate of the media stream - */ - {{1, AST_FORMAT_G722}, "audio", "G722", 8000}, - {{1, AST_FORMAT_G726_AAL2}, "audio", "AAL2-G726-32", 8000}, - {{0, AST_RTP_DTMF}, "audio", "telephone-event", 8000}, - {{0, AST_RTP_CISCO_DTMF}, "audio", "cisco-telephone-event", 8000}, - {{0, AST_RTP_CN}, "audio", "CN", 8000}, - {{1, AST_FORMAT_JPEG}, "video", "JPEG", 90000}, - {{1, AST_FORMAT_PNG}, "video", "PNG", 90000}, - {{1, AST_FORMAT_H261}, "video", "H261", 90000}, - {{1, AST_FORMAT_H263}, "video", "H263", 90000}, - {{1, AST_FORMAT_H263_PLUS}, "video", "h263-1998", 90000}, - {{1, AST_FORMAT_H264}, "video", "H264", 90000}, - {{1, AST_FORMAT_MP4_VIDEO}, "video", "MP4V-ES", 90000}, - {{1, AST_FORMAT_T140RED}, "text", "RED", 1000}, - {{1, AST_FORMAT_T140}, "text", "T140", 1000}, - {{1, AST_FORMAT_SIREN7}, "audio", "G7221", 16000}, - {{1, AST_FORMAT_SIREN14}, "audio", "G7221", 32000}, -}; - -/*! - * \brief Mapping between Asterisk codecs and rtp payload types - * - * Static (i.e., well-known) RTP payload types for our "AST_FORMAT..."s: - * also, our own choices for dynamic payload types. This is our master - * table for transmission - * - * See http://www.iana.org/assignments/rtp-parameters for a list of - * assigned values - */ -static const struct rtpPayloadType static_RTP_PT[MAX_RTP_PT] = { - [0] = {1, AST_FORMAT_ULAW}, -#ifdef USE_DEPRECATED_G726 - [2] = {1, AST_FORMAT_G726}, /* Technically this is G.721, but if Cisco can do it, so can we... */ -#endif - [3] = {1, AST_FORMAT_GSM}, - [4] = {1, AST_FORMAT_G723_1}, - [5] = {1, AST_FORMAT_ADPCM}, /* 8 kHz */ - [6] = {1, AST_FORMAT_ADPCM}, /* 16 kHz */ - [7] = {1, AST_FORMAT_LPC10}, - [8] = {1, AST_FORMAT_ALAW}, - [9] = {1, AST_FORMAT_G722}, - [10] = {1, AST_FORMAT_SLINEAR}, /* 2 channels */ - [11] = {1, AST_FORMAT_SLINEAR}, /* 1 channel */ - [13] = {0, AST_RTP_CN}, - [16] = {1, AST_FORMAT_ADPCM}, /* 11.025 kHz */ - [17] = {1, AST_FORMAT_ADPCM}, /* 22.050 kHz */ - [18] = {1, AST_FORMAT_G729A}, - [19] = {0, AST_RTP_CN}, /* Also used for CN */ - [26] = {1, AST_FORMAT_JPEG}, - [31] = {1, AST_FORMAT_H261}, - [34] = {1, AST_FORMAT_H263}, - [97] = {1, AST_FORMAT_ILBC}, - [98] = {1, AST_FORMAT_H263_PLUS}, - [99] = {1, AST_FORMAT_H264}, - [101] = {0, AST_RTP_DTMF}, - [102] = {1, AST_FORMAT_SIREN7}, - [103] = {1, AST_FORMAT_H263_PLUS}, - [104] = {1, AST_FORMAT_MP4_VIDEO}, - [105] = {1, AST_FORMAT_T140RED}, /* Real time text chat (with redundancy encoding) */ - [106] = {1, AST_FORMAT_T140}, /* Real time text chat */ - [110] = {1, AST_FORMAT_SPEEX}, - [111] = {1, AST_FORMAT_G726}, - [112] = {1, AST_FORMAT_G726_AAL2}, - [115] = {1, AST_FORMAT_SIREN14}, - [121] = {0, AST_RTP_CISCO_DTMF}, /* Must be type 121 */ -}; - -void ast_rtp_pt_clear(struct ast_rtp* rtp) -{ - int i; - - if (!rtp) - return; - - rtp_bridge_lock(rtp); - - for (i = 0; i < MAX_RTP_PT; ++i) { - rtp->current_RTP_PT[i].isAstFormat = 0; - rtp->current_RTP_PT[i].code = 0; - } - - rtp->rtp_lookup_code_cache_isAstFormat = 0; - rtp->rtp_lookup_code_cache_code = 0; - rtp->rtp_lookup_code_cache_result = 0; - - rtp_bridge_unlock(rtp); -} - -void ast_rtp_pt_default(struct ast_rtp* rtp) -{ - int i; - - rtp_bridge_lock(rtp); - - /* Initialize to default payload types */ - for (i = 0; i < MAX_RTP_PT; ++i) { - rtp->current_RTP_PT[i].isAstFormat = static_RTP_PT[i].isAstFormat; - rtp->current_RTP_PT[i].code = static_RTP_PT[i].code; - } - - rtp->rtp_lookup_code_cache_isAstFormat = 0; - rtp->rtp_lookup_code_cache_code = 0; - rtp->rtp_lookup_code_cache_result = 0; - - rtp_bridge_unlock(rtp); -} - -void ast_rtp_pt_copy(struct ast_rtp *dest, struct ast_rtp *src) -{ - unsigned int i; - - rtp_bridge_lock(dest); - rtp_bridge_lock(src); - - for (i = 0; i < MAX_RTP_PT; ++i) { - dest->current_RTP_PT[i].isAstFormat = - src->current_RTP_PT[i].isAstFormat; - dest->current_RTP_PT[i].code = - src->current_RTP_PT[i].code; - } - dest->rtp_lookup_code_cache_isAstFormat = 0; - dest->rtp_lookup_code_cache_code = 0; - dest->rtp_lookup_code_cache_result = 0; - - rtp_bridge_unlock(src); - rtp_bridge_unlock(dest); -} - -/*! \brief Get channel driver interface structure */ -static struct ast_rtp_protocol *get_proto(struct ast_channel *chan) -{ - struct ast_rtp_protocol *cur = NULL; - - AST_RWLIST_RDLOCK(&protos); - AST_RWLIST_TRAVERSE(&protos, cur, list) { - if (cur->type == chan->tech->type) - break; - } - AST_RWLIST_UNLOCK(&protos); - - return cur; -} - -int ast_rtp_early_bridge(struct ast_channel *c0, struct ast_channel *c1) -{ - struct ast_rtp *destp = NULL, *srcp = NULL; /* Audio RTP Channels */ - struct ast_rtp *vdestp = NULL, *vsrcp = NULL; /* Video RTP channels */ - struct ast_rtp *tdestp = NULL, *tsrcp = NULL; /* Text RTP channels */ - struct ast_rtp_protocol *destpr = NULL, *srcpr = NULL; - enum ast_rtp_get_result audio_dest_res = AST_RTP_GET_FAILED, video_dest_res = AST_RTP_GET_FAILED, text_dest_res = AST_RTP_GET_FAILED; - enum ast_rtp_get_result audio_src_res = AST_RTP_GET_FAILED, video_src_res = AST_RTP_GET_FAILED, text_src_res = AST_RTP_GET_FAILED; - int srccodec, destcodec, nat_active = 0; - - /* Lock channels */ - ast_channel_lock(c0); - if (c1) { - while (ast_channel_trylock(c1)) { - ast_channel_unlock(c0); - usleep(1); - ast_channel_lock(c0); - } - } - - /* Find channel driver interfaces */ - destpr = get_proto(c0); - if (c1) - srcpr = get_proto(c1); - if (!destpr) { - ast_debug(1, "Channel '%s' has no RTP, not doing anything\n", c0->name); - ast_channel_unlock(c0); - if (c1) - ast_channel_unlock(c1); - return -1; - } - if (!srcpr) { - ast_debug(1, "Channel '%s' has no RTP, not doing anything\n", c1 ? c1->name : "<unspecified>"); - ast_channel_unlock(c0); - if (c1) - ast_channel_unlock(c1); - return -1; - } - - /* Get audio, video and text interface (if native bridge is possible) */ - audio_dest_res = destpr->get_rtp_info(c0, &destp); - video_dest_res = destpr->get_vrtp_info ? destpr->get_vrtp_info(c0, &vdestp) : AST_RTP_GET_FAILED; - text_dest_res = destpr->get_trtp_info ? destpr->get_trtp_info(c0, &tdestp) : AST_RTP_GET_FAILED; - if (srcpr) { - audio_src_res = srcpr->get_rtp_info(c1, &srcp); - video_src_res = srcpr->get_vrtp_info ? srcpr->get_vrtp_info(c1, &vsrcp) : AST_RTP_GET_FAILED; - text_src_res = srcpr->get_trtp_info ? srcpr->get_trtp_info(c1, &tsrcp) : AST_RTP_GET_FAILED; - } - - /* Check if bridge is still possible (In SIP canreinvite=no stops this, like NAT) */ - if (audio_dest_res != AST_RTP_TRY_NATIVE || (video_dest_res != AST_RTP_GET_FAILED && video_dest_res != AST_RTP_TRY_NATIVE)) { - /* Somebody doesn't want to play... */ - ast_channel_unlock(c0); - if (c1) - ast_channel_unlock(c1); - return -1; - } - if (audio_src_res == AST_RTP_TRY_NATIVE && (video_src_res == AST_RTP_GET_FAILED || video_src_res == AST_RTP_TRY_NATIVE) && srcpr->get_codec) - srccodec = srcpr->get_codec(c1); - else - srccodec = 0; - if (audio_dest_res == AST_RTP_TRY_NATIVE && (video_dest_res == AST_RTP_GET_FAILED || video_dest_res == AST_RTP_TRY_NATIVE) && destpr->get_codec) - destcodec = destpr->get_codec(c0); - else - destcodec = 0; - /* Ensure we have at least one matching codec */ - if (srcp && !(srccodec & destcodec)) { - ast_channel_unlock(c0); - ast_channel_unlock(c1); - return 0; - } - /* Consider empty media as non-existent */ - if (audio_src_res == AST_RTP_TRY_NATIVE && !srcp->them.sin_addr.s_addr) - srcp = NULL; - if (srcp && (srcp->nat || ast_test_flag(srcp, FLAG_NAT_ACTIVE))) - nat_active = 1; - /* Bridge media early */ - if (destpr->set_rtp_peer(c0, srcp, vsrcp, tsrcp, srccodec, nat_active)) - ast_log(LOG_WARNING, "Channel '%s' failed to setup early bridge to '%s'\n", c0->name, c1 ? c1->name : "<unspecified>"); - ast_channel_unlock(c0); - if (c1) - ast_channel_unlock(c1); - ast_debug(1, "Setting early bridge SDP of '%s' with that of '%s'\n", c0->name, c1 ? c1->name : "<unspecified>"); - return 0; -} - -int ast_rtp_make_compatible(struct ast_channel *dest, struct ast_channel *src, int media) -{ - struct ast_rtp *destp = NULL, *srcp = NULL; /* Audio RTP Channels */ - struct ast_rtp *vdestp = NULL, *vsrcp = NULL; /* Video RTP channels */ - struct ast_rtp *tdestp = NULL, *tsrcp = NULL; /* Text RTP channels */ - struct ast_rtp_protocol *destpr = NULL, *srcpr = NULL; - enum ast_rtp_get_result audio_dest_res = AST_RTP_GET_FAILED, video_dest_res = AST_RTP_GET_FAILED, text_dest_res = AST_RTP_GET_FAILED; - enum ast_rtp_get_result audio_src_res = AST_RTP_GET_FAILED, video_src_res = AST_RTP_GET_FAILED, text_src_res = AST_RTP_GET_FAILED; - int srccodec, destcodec; - - /* Lock channels */ - ast_channel_lock(dest); - while (ast_channel_trylock(src)) { - ast_channel_unlock(dest); - usleep(1); - ast_channel_lock(dest); - } - - /* Find channel driver interfaces */ - if (!(destpr = get_proto(dest))) { - ast_debug(1, "Channel '%s' has no RTP, not doing anything\n", dest->name); - ast_channel_unlock(dest); - ast_channel_unlock(src); - return 0; - } - if (!(srcpr = get_proto(src))) { - ast_debug(1, "Channel '%s' has no RTP, not doing anything\n", src->name); - ast_channel_unlock(dest); - ast_channel_unlock(src); - return 0; - } - - /* Get audio and video interface (if native bridge is possible) */ - audio_dest_res = destpr->get_rtp_info(dest, &destp); - video_dest_res = destpr->get_vrtp_info ? destpr->get_vrtp_info(dest, &vdestp) : AST_RTP_GET_FAILED; - text_dest_res = destpr->get_trtp_info ? destpr->get_trtp_info(dest, &tdestp) : AST_RTP_GET_FAILED; - audio_src_res = srcpr->get_rtp_info(src, &srcp); - video_src_res = srcpr->get_vrtp_info ? srcpr->get_vrtp_info(src, &vsrcp) : AST_RTP_GET_FAILED; - text_src_res = srcpr->get_trtp_info ? srcpr->get_trtp_info(src, &tsrcp) : AST_RTP_GET_FAILED; - - /* Ensure we have at least one matching codec */ - if (srcpr->get_codec) - srccodec = srcpr->get_codec(src); - else - srccodec = 0; - if (destpr->get_codec) - destcodec = destpr->get_codec(dest); - else - destcodec = 0; - - /* Check if bridge is still possible (In SIP canreinvite=no stops this, like NAT) */ - if (audio_dest_res != AST_RTP_TRY_NATIVE || (video_dest_res != AST_RTP_GET_FAILED && video_dest_res != AST_RTP_TRY_NATIVE) || audio_src_res != AST_RTP_TRY_NATIVE || (video_src_res != AST_RTP_GET_FAILED && video_src_res != AST_RTP_TRY_NATIVE) || !(srccodec & destcodec)) { - /* Somebody doesn't want to play... */ - ast_channel_unlock(dest); - ast_channel_unlock(src); - return 0; - } - ast_rtp_pt_copy(destp, srcp); - if (vdestp && vsrcp) - ast_rtp_pt_copy(vdestp, vsrcp); - if (tdestp && tsrcp) - ast_rtp_pt_copy(tdestp, tsrcp); - if (media) { - /* Bridge early */ - if (destpr->set_rtp_peer(dest, srcp, vsrcp, tsrcp, srccodec, ast_test_flag(srcp, FLAG_NAT_ACTIVE))) - ast_log(LOG_WARNING, "Channel '%s' failed to setup early bridge to '%s'\n", dest->name, src->name); - } - ast_channel_unlock(dest); - ast_channel_unlock(src); - ast_debug(1, "Seeded SDP of '%s' with that of '%s'\n", dest->name, src->name); - return 1; -} - -/*! \brief Make a note of a RTP payload type that was seen in a SDP "m=" line. - * By default, use the well-known value for this type (although it may - * still be set to a different value by a subsequent "a=rtpmap:" line) - */ -void ast_rtp_set_m_type(struct ast_rtp* rtp, int pt) -{ - if (pt < 0 || pt > MAX_RTP_PT || static_RTP_PT[pt].code == 0) - return; /* bogus payload type */ - - rtp_bridge_lock(rtp); - rtp->current_RTP_PT[pt] = static_RTP_PT[pt]; - rtp_bridge_unlock(rtp); -} - -/*! \brief remove setting from payload type list if the rtpmap header indicates - an unknown media type */ -void ast_rtp_unset_m_type(struct ast_rtp* rtp, int pt) -{ - if (pt < 0 || pt > MAX_RTP_PT) - return; /* bogus payload type */ - - rtp_bridge_lock(rtp); - rtp->current_RTP_PT[pt].isAstFormat = 0; - rtp->current_RTP_PT[pt].code = 0; - rtp_bridge_unlock(rtp); -} - -/*! \brief Make a note of a RTP payload type (with MIME type) that was seen in - * an SDP "a=rtpmap:" line. - * \return 0 if the MIME type was found and set, -1 if it wasn't found - */ -int ast_rtp_set_rtpmap_type_rate(struct ast_rtp *rtp, int pt, - char *mimeType, char *mimeSubtype, - enum ast_rtp_options options, - unsigned int sample_rate) -{ - unsigned int i; - int found = 0; - - if (pt < 0 || pt > MAX_RTP_PT) - return -1; /* bogus payload type */ - - rtp_bridge_lock(rtp); - - for (i = 0; i < ARRAY_LEN(mimeTypes); ++i) { - const struct mimeType *t = &mimeTypes[i]; - - if (strcasecmp(mimeSubtype, t->subtype)) { - continue; - } - - if (strcasecmp(mimeType, t->type)) { - continue; - } - - /* if both sample rates have been supplied, and they don't match, - then this not a match; if one has not been supplied, then the - rates are not compared */ - if (sample_rate && t->sample_rate && - (sample_rate != t->sample_rate)) { - continue; - } - - found = 1; - rtp->current_RTP_PT[pt] = t->payloadType; - - if ((t->payloadType.code == AST_FORMAT_G726) && - t->payloadType.isAstFormat && - (options & AST_RTP_OPT_G726_NONSTANDARD)) { - rtp->current_RTP_PT[pt].code = AST_FORMAT_G726_AAL2; - } - - break; - } - - rtp_bridge_unlock(rtp); - - return (found ? 0 : -2); -} - -int ast_rtp_set_rtpmap_type(struct ast_rtp *rtp, int pt, - char *mimeType, char *mimeSubtype, - enum ast_rtp_options options) -{ - return ast_rtp_set_rtpmap_type_rate(rtp, pt, mimeType, mimeSubtype, options, 0); -} - -/*! \brief Return the union of all of the codecs that were set by rtp_set...() calls - * They're returned as two distinct sets: AST_FORMATs, and AST_RTPs */ -void ast_rtp_get_current_formats(struct ast_rtp* rtp, - int* astFormats, int* nonAstFormats) -{ - int pt; - - rtp_bridge_lock(rtp); - - *astFormats = *nonAstFormats = 0; - for (pt = 0; pt < MAX_RTP_PT; ++pt) { - if (rtp->current_RTP_PT[pt].isAstFormat) { - *astFormats |= rtp->current_RTP_PT[pt].code; - } else { - *nonAstFormats |= rtp->current_RTP_PT[pt].code; - } - } - - rtp_bridge_unlock(rtp); -} - -struct rtpPayloadType ast_rtp_lookup_pt(struct ast_rtp* rtp, int pt) -{ - struct rtpPayloadType result; - - result.isAstFormat = result.code = 0; - - if (pt < 0 || pt > MAX_RTP_PT) - return result; /* bogus payload type */ - - /* Start with negotiated codecs */ - rtp_bridge_lock(rtp); - result = rtp->current_RTP_PT[pt]; - rtp_bridge_unlock(rtp); - - /* If it doesn't exist, check our static RTP type list, just in case */ - if (!result.code) - result = static_RTP_PT[pt]; - - return result; -} - -/*! \brief Looks up an RTP code out of our *static* outbound list */ -int ast_rtp_lookup_code(struct ast_rtp* rtp, const int isAstFormat, const int code) -{ - int pt = 0; - - rtp_bridge_lock(rtp); - - if (isAstFormat == rtp->rtp_lookup_code_cache_isAstFormat && - code == rtp->rtp_lookup_code_cache_code) { - /* Use our cached mapping, to avoid the overhead of the loop below */ - pt = rtp->rtp_lookup_code_cache_result; - rtp_bridge_unlock(rtp); - return pt; - } - - /* Check the dynamic list first */ - for (pt = 0; pt < MAX_RTP_PT; ++pt) { - if (rtp->current_RTP_PT[pt].code == code && rtp->current_RTP_PT[pt].isAstFormat == isAstFormat) { - rtp->rtp_lookup_code_cache_isAstFormat = isAstFormat; - rtp->rtp_lookup_code_cache_code = code; - rtp->rtp_lookup_code_cache_result = pt; - rtp_bridge_unlock(rtp); - return pt; - } - } - - /* Then the static list */ - for (pt = 0; pt < MAX_RTP_PT; ++pt) { - if (static_RTP_PT[pt].code == code && static_RTP_PT[pt].isAstFormat == isAstFormat) { - rtp->rtp_lookup_code_cache_isAstFormat = isAstFormat; - rtp->rtp_lookup_code_cache_code = code; - rtp->rtp_lookup_code_cache_result = pt; - rtp_bridge_unlock(rtp); - return pt; - } - } - - rtp_bridge_unlock(rtp); - - return -1; -} - -const char *ast_rtp_lookup_mime_subtype(const int isAstFormat, const int code, - enum ast_rtp_options options) -{ - unsigned int i; - - for (i = 0; i < ARRAY_LEN(mimeTypes); ++i) { - if ((mimeTypes[i].payloadType.code == code) && (mimeTypes[i].payloadType.isAstFormat == isAstFormat)) { - if (isAstFormat && - (code == AST_FORMAT_G726_AAL2) && - (options & AST_RTP_OPT_G726_NONSTANDARD)) - return "G726-32"; - else - return mimeTypes[i].subtype; - } - } - - return ""; -} - -unsigned int ast_rtp_lookup_sample_rate(int isAstFormat, int code) -{ - unsigned int i; - - for (i = 0; i < ARRAY_LEN(mimeTypes); ++i) { - if ((mimeTypes[i].payloadType.code == code) && (mimeTypes[i].payloadType.isAstFormat == isAstFormat)) { - return mimeTypes[i].sample_rate; - } - } - - return 0; -} - -char *ast_rtp_lookup_mime_multiple(char *buf, size_t size, const int capability, - const int isAstFormat, enum ast_rtp_options options) -{ - int format; - unsigned len; - char *end = buf; - char *start = buf; - - if (!buf || !size) - return NULL; - - snprintf(end, size, "0x%x (", capability); - - len = strlen(end); - end += len; - size -= len; - start = end; - - for (format = 1; format < AST_RTP_MAX; format <<= 1) { - if (capability & format) { - const char *name = ast_rtp_lookup_mime_subtype(isAstFormat, format, options); - - snprintf(end, size, "%s|", name); - len = strlen(end); - end += len; - size -= len; - } - } - - if (start == end) - ast_copy_string(start, "nothing)", size); - else if (size > 1) - *(end -1) = ')'; - - return buf; -} - -/*! \brief Open RTP or RTCP socket for a session. - * Print a message on failure. - */ -static int rtp_socket(const char *type) -{ - int s = socket(AF_INET, SOCK_DGRAM, 0); - if (s < 0) { - if (type == NULL) - type = "RTP/RTCP"; - ast_log(LOG_WARNING, "Unable to allocate %s socket: %s\n", type, strerror(errno)); - } else { - long flags = fcntl(s, F_GETFL); - fcntl(s, F_SETFL, flags | O_NONBLOCK); -#ifdef SO_NO_CHECK - if (nochecksums) - setsockopt(s, SOL_SOCKET, SO_NO_CHECK, &nochecksums, sizeof(nochecksums)); -#endif - } - return s; -} - -/*! - * \brief Initialize a new RTCP session. - * - * \returns The newly initialized RTCP session. - */ -static struct ast_rtcp *ast_rtcp_new(void) -{ - struct ast_rtcp *rtcp; - - if (!(rtcp = ast_calloc(1, sizeof(*rtcp)))) - return NULL; - rtcp->s = rtp_socket("RTCP"); - rtcp->us.sin_family = AF_INET; - rtcp->them.sin_family = AF_INET; - rtcp->schedid = -1; - - if (rtcp->s < 0) { - ast_free(rtcp); - return NULL; - } - - return rtcp; -} - -/*! - * \brief Initialize a new RTP structure. - * - */ -void ast_rtp_new_init(struct ast_rtp *rtp) -{ -#ifdef P2P_INTENSE - ast_mutex_init(&rtp->bridge_lock); -#endif - - rtp->them.sin_family = AF_INET; - rtp->us.sin_family = AF_INET; - rtp->ssrc = ast_random(); - rtp->seqno = ast_random() & 0xffff; - ast_set_flag(rtp, FLAG_HAS_DTMF); - rtp->strict_rtp_state = (strictrtp ? STRICT_RTP_LEARN : STRICT_RTP_OPEN); -} - -struct ast_rtp *ast_rtp_new_with_bindaddr(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode, struct in_addr addr) -{ - struct ast_rtp *rtp; - int x; - int startplace; - - if (!(rtp = ast_calloc(1, sizeof(*rtp)))) - return NULL; - - ast_rtp_new_init(rtp); - - rtp->s = rtp_socket("RTP"); - if (rtp->s < 0) - goto fail; - if (sched && rtcpenable) { - rtp->sched = sched; - rtp->rtcp = ast_rtcp_new(); - } - - /* - * Try to bind the RTP port, x, and possibly the RTCP port, x+1 as well. - * Start from a random (even, by RTP spec) port number, and - * iterate until success or no ports are available. - * Note that the requirement of RTP port being even, or RTCP being the - * next one, cannot be enforced in presence of a NAT box because the - * mapping is not under our control. - */ - x = (rtpend == rtpstart) ? rtpstart : (ast_random() % (rtpend - rtpstart)) + rtpstart; - x = x & ~1; /* make it an even number */ - startplace = x; /* remember the starting point */ - /* this is constant across the loop */ - rtp->us.sin_addr = addr; - if (rtp->rtcp) - rtp->rtcp->us.sin_addr = addr; - for (;;) { - rtp->us.sin_port = htons(x); - if (!bind(rtp->s, (struct sockaddr *)&rtp->us, sizeof(rtp->us))) { - /* bind succeeded, if no rtcp then we are done */ - if (!rtp->rtcp) - break; - /* have rtcp, try to bind it */ - rtp->rtcp->us.sin_port = htons(x + 1); - if (!bind(rtp->rtcp->s, (struct sockaddr *)&rtp->rtcp->us, sizeof(rtp->rtcp->us))) - break; /* success again, we are really done */ - /* - * RTCP bind failed, so close and recreate the - * already bound RTP socket for the next round. - */ - close(rtp->s); - rtp->s = rtp_socket("RTP"); - if (rtp->s < 0) - goto fail; - } - /* - * If we get here, there was an error in one of the bind() - * calls, so make sure it is nothing unexpected. - */ - if (errno != EADDRINUSE) { - /* We got an error that wasn't expected, abort! */ - ast_log(LOG_ERROR, "Unexpected bind error: %s\n", strerror(errno)); - goto fail; - } - /* - * One of the ports is in use. For the next iteration, - * increment by two and handle wraparound. - * If we reach the starting point, then declare failure. - */ - x += 2; - if (x > rtpend) - x = (rtpstart + 1) & ~1; - if (x == startplace) { - ast_log(LOG_ERROR, "No RTP ports remaining. Can't setup media stream for this call.\n"); - goto fail; - } - } - rtp->sched = sched; - rtp->io = io; - if (callbackmode) { - rtp->ioid = ast_io_add(rtp->io, rtp->s, rtpread, AST_IO_IN, rtp); - ast_set_flag(rtp, FLAG_CALLBACK_MODE); - } - ast_rtp_pt_default(rtp); - return rtp; - -fail: - if (rtp->s >= 0) - close(rtp->s); - if (rtp->rtcp) { - close(rtp->rtcp->s); - ast_free(rtp->rtcp); - } - ast_free(rtp); - return NULL; -} - -struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode) -{ - struct in_addr ia; - - memset(&ia, 0, sizeof(ia)); - return ast_rtp_new_with_bindaddr(sched, io, rtcpenable, callbackmode, ia); -} - -int ast_rtp_setqos(struct ast_rtp *rtp, int type_of_service, int class_of_service, char *desc) -{ - return ast_netsock_set_qos(rtp->s, type_of_service, class_of_service, desc); -} - -void ast_rtp_new_source(struct ast_rtp *rtp) -{ - if (rtp) { - rtp->set_marker_bit = 1; - } - return; -} - -void ast_rtp_set_peer(struct ast_rtp *rtp, struct sockaddr_in *them) -{ - rtp->them.sin_port = them->sin_port; - rtp->them.sin_addr = them->sin_addr; - if (rtp->rtcp) { - int h = ntohs(them->sin_port); - rtp->rtcp->them.sin_port = htons(h + 1); - rtp->rtcp->them.sin_addr = them->sin_addr; - } - rtp->rxseqno = 0; - /* If strict RTP protection is enabled switch back to the learn state so we don't drop packets from above */ - if (strictrtp) - rtp->strict_rtp_state = STRICT_RTP_LEARN; -} - -int ast_rtp_get_peer(struct ast_rtp *rtp, struct sockaddr_in *them) -{ - if ((them->sin_family != AF_INET) || - (them->sin_port != rtp->them.sin_port) || - (them->sin_addr.s_addr != rtp->them.sin_addr.s_addr)) { - them->sin_family = AF_INET; - them->sin_port = rtp->them.sin_port; - them->sin_addr = rtp->them.sin_addr; - return 1; - } - return 0; -} - -void ast_rtp_get_us(struct ast_rtp *rtp, struct sockaddr_in *us) -{ - *us = rtp->us; -} - -struct ast_rtp *ast_rtp_get_bridged(struct ast_rtp *rtp) -{ - struct ast_rtp *bridged = NULL; - - rtp_bridge_lock(rtp); - bridged = rtp->bridged; - rtp_bridge_unlock(rtp); - - return bridged; -} - -void ast_rtp_stop(struct ast_rtp *rtp) -{ - if (rtp->rtcp) { - AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); - } - if (rtp->red) { - AST_SCHED_DEL(rtp->sched, rtp->red->schedid); - free(rtp->red); - rtp->red = NULL; - } - - memset(&rtp->them.sin_addr, 0, sizeof(rtp->them.sin_addr)); - memset(&rtp->them.sin_port, 0, sizeof(rtp->them.sin_port)); - if (rtp->rtcp) { - memset(&rtp->rtcp->them.sin_addr, 0, sizeof(rtp->rtcp->them.sin_addr)); - memset(&rtp->rtcp->them.sin_port, 0, sizeof(rtp->rtcp->them.sin_port)); - } - - ast_clear_flag(rtp, FLAG_P2P_SENT_MARK); -} - -void ast_rtp_reset(struct ast_rtp *rtp) -{ - memset(&rtp->rxcore, 0, sizeof(rtp->rxcore)); - memset(&rtp->txcore, 0, sizeof(rtp->txcore)); - memset(&rtp->dtmfmute, 0, sizeof(rtp->dtmfmute)); - rtp->lastts = 0; - rtp->lastdigitts = 0; - rtp->lastrxts = 0; - rtp->lastividtimestamp = 0; - rtp->lastovidtimestamp = 0; - rtp->lastitexttimestamp = 0; - rtp->lastotexttimestamp = 0; - rtp->lasteventseqn = 0; - rtp->lastevent = 0; - rtp->lasttxformat = 0; - rtp->lastrxformat = 0; - rtp->dtmfcount = 0; - rtp->dtmfsamples = 0; - rtp->seqno = 0; - rtp->rxseqno = 0; -} - -/*! Get QoS values from RTP and RTCP data (used in "sip show channelstats") */ -unsigned int ast_rtp_get_qosvalue(struct ast_rtp *rtp, enum ast_rtp_qos_vars value) -{ - if (rtp == NULL) { - if (option_debug > 1) - ast_log(LOG_DEBUG, "NO RTP Structure? Kidding me? \n"); - return 0; - } - if (option_debug > 1 && rtp->rtcp == NULL) { - ast_log(LOG_DEBUG, "NO RTCP structure. Maybe in RTP p2p bridging mode? \n"); - } - - switch (value) { - case AST_RTP_TXCOUNT: - return (unsigned int) rtp->txcount; - case AST_RTP_RXCOUNT: - return (unsigned int) rtp->rxcount; - case AST_RTP_TXJITTER: - return (unsigned int) (rtp->rxjitter * 100.0); - case AST_RTP_RXJITTER: - return (unsigned int) (rtp->rtcp ? (rtp->rtcp->reported_jitter / (unsigned int) 65536.0) : 0); - case AST_RTP_RXPLOSS: - return rtp->rtcp ? (rtp->rtcp->expected_prior - rtp->rtcp->received_prior) : 0; - case AST_RTP_TXPLOSS: - return rtp->rtcp ? rtp->rtcp->reported_lost : 0; - case AST_RTP_RTT: - return (unsigned int) (rtp->rtcp ? (rtp->rtcp->rtt * 100) : 0); - } - return 0; /* To make the compiler happy */ -} - -static double __ast_rtp_get_qos(struct ast_rtp *rtp, const char *qos, int *found) -{ - *found = 1; - - if (!strcasecmp(qos, "remote_maxjitter")) - return rtp->rtcp->reported_maxjitter * 1000.0; - if (!strcasecmp(qos, "remote_minjitter")) - return rtp->rtcp->reported_minjitter * 1000.0; - if (!strcasecmp(qos, "remote_normdevjitter")) - return rtp->rtcp->reported_normdev_jitter * 1000.0; - if (!strcasecmp(qos, "remote_stdevjitter")) - return sqrt(rtp->rtcp->reported_stdev_jitter) * 1000.0; - - if (!strcasecmp(qos, "local_maxjitter")) - return rtp->rtcp->maxrxjitter * 1000.0; - if (!strcasecmp(qos, "local_minjitter")) - return rtp->rtcp->minrxjitter * 1000.0; - if (!strcasecmp(qos, "local_normdevjitter")) - return rtp->rtcp->normdev_rxjitter * 1000.0; - if (!strcasecmp(qos, "local_stdevjitter")) - return sqrt(rtp->rtcp->stdev_rxjitter) * 1000.0; - - if (!strcasecmp(qos, "maxrtt")) - return rtp->rtcp->maxrtt * 1000.0; - if (!strcasecmp(qos, "minrtt")) - return rtp->rtcp->minrtt * 1000.0; - if (!strcasecmp(qos, "normdevrtt")) - return rtp->rtcp->normdevrtt * 1000.0; - if (!strcasecmp(qos, "stdevrtt")) - return sqrt(rtp->rtcp->stdevrtt) * 1000.0; - - *found = 0; - - return 0.0; -} - -int ast_rtp_get_qos(struct ast_rtp *rtp, const char *qos, char *buf, unsigned int buflen) -{ - double value; - int found; - - value = __ast_rtp_get_qos(rtp, qos, &found); - - if (!found) - return -1; - - snprintf(buf, buflen, "%.0lf", value); - - return 0; -} - -void ast_rtp_set_vars(struct ast_channel *chan, struct ast_rtp *rtp) { - char *audioqos; - char *audioqos_jitter; - char *audioqos_loss; - char *audioqos_rtt; - struct ast_channel *bridge; - - if (!rtp || !chan) - return; - - bridge = ast_bridged_channel(chan); - - audioqos = ast_rtp_get_quality(rtp, NULL, RTPQOS_SUMMARY); - audioqos_jitter = ast_rtp_get_quality(rtp, NULL, RTPQOS_JITTER); - audioqos_loss = ast_rtp_get_quality(rtp, NULL, RTPQOS_LOSS); - audioqos_rtt = ast_rtp_get_quality(rtp, NULL, RTPQOS_RTT); - - pbx_builtin_setvar_helper(chan, "RTPAUDIOQOS", audioqos); - pbx_builtin_setvar_helper(chan, "RTPAUDIOQOSJITTER", audioqos_jitter); - pbx_builtin_setvar_helper(chan, "RTPAUDIOQOSLOSS", audioqos_loss); - pbx_builtin_setvar_helper(chan, "RTPAUDIOQOSRTT", audioqos_rtt); - - if (!bridge) - return; - - pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSBRIDGED", audioqos); - pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSJITTERBRIDGED", audioqos_jitter); - pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSLOSSBRIDGED", audioqos_loss); - pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSRTTBRIDGED", audioqos_rtt); -} - -static char *__ast_rtp_get_quality_jitter(struct ast_rtp *rtp) -{ - /* - *ssrc our ssrc - *themssrc their ssrc - *lp lost packets - *rxjitter our calculated jitter(rx) - *rxcount no. received packets - *txjitter reported jitter of the other end - *txcount transmitted packets - *rlp remote lost packets - *rtt round trip time - */ -#define RTCP_JITTER_FORMAT1 \ - "minrxjitter=%f;" \ - "maxrxjitter=%f;" \ - "avgrxjitter=%f;" \ - "stdevrxjitter=%f;" \ - "reported_minjitter=%f;" \ - "reported_maxjitter=%f;" \ - "reported_avgjitter=%f;" \ - "reported_stdevjitter=%f;" - -#define RTCP_JITTER_FORMAT2 \ - "rxjitter=%f;" - - if (rtp->rtcp && rtp->rtcp->rtcp_info) { - snprintf(rtp->rtcp->quality_jitter, sizeof(rtp->rtcp->quality_jitter), RTCP_JITTER_FORMAT1, - rtp->rtcp->minrxjitter, - rtp->rtcp->maxrxjitter, - rtp->rtcp->normdev_rxjitter, - sqrt(rtp->rtcp->stdev_rxjitter), - rtp->rtcp->reported_minjitter, - rtp->rtcp->reported_maxjitter, - rtp->rtcp->reported_normdev_jitter, - sqrt(rtp->rtcp->reported_stdev_jitter) - ); - } else { - snprintf(rtp->rtcp->quality_jitter, sizeof(rtp->rtcp->quality_jitter), RTCP_JITTER_FORMAT2, - rtp->rxjitter - ); - } - - return rtp->rtcp->quality_jitter; - -#undef RTCP_JITTER_FORMAT1 -#undef RTCP_JITTER_FORMAT2 -} - -static char *__ast_rtp_get_quality_loss(struct ast_rtp *rtp) -{ - unsigned int lost; - unsigned int extended; - unsigned int expected; - int fraction; - -#define RTCP_LOSS_FORMAT1 \ - "minrxlost=%f;" \ - "maxrxlost=%f;" \ - "avgrxlostr=%f;" \ - "stdevrxlost=%f;" \ - "reported_minlost=%f;" \ - "reported_maxlost=%f;" \ - "reported_avglost=%f;" \ - "reported_stdevlost=%f;" - -#define RTCP_LOSS_FORMAT2 \ - "lost=%d;" \ - "expected=%d;" - - if (rtp->rtcp && rtp->rtcp->rtcp_info && rtp->rtcp->maxrxlost > 0) { - snprintf(rtp->rtcp->quality_loss, sizeof(rtp->rtcp->quality_loss), RTCP_LOSS_FORMAT1, - rtp->rtcp->minrxlost, - rtp->rtcp->maxrxlost, - rtp->rtcp->normdev_rxlost, - sqrt(rtp->rtcp->stdev_rxlost), - rtp->rtcp->reported_minlost, - rtp->rtcp->reported_maxlost, - rtp->rtcp->reported_normdev_lost, - sqrt(rtp->rtcp->reported_stdev_lost) - ); - } else { - extended = rtp->cycles + rtp->lastrxseqno; - expected = extended - rtp->seedrxseqno + 1; - if (rtp->rxcount > expected) - expected += rtp->rxcount - expected; - lost = expected - rtp->rxcount; - - if (!expected || lost <= 0) - fraction = 0; - else - fraction = (lost << 8) / expected; - - snprintf(rtp->rtcp->quality_loss, sizeof(rtp->rtcp->quality_loss), RTCP_LOSS_FORMAT2, - lost, - expected - ); - } - - return rtp->rtcp->quality_loss; - -#undef RTCP_LOSS_FORMAT1 -#undef RTCP_LOSS_FORMAT2 -} - -static char *__ast_rtp_get_quality_rtt(struct ast_rtp *rtp) -{ - if (rtp->rtcp && rtp->rtcp->rtcp_info) { - snprintf(rtp->rtcp->quality_rtt, sizeof(rtp->rtcp->quality_rtt), "minrtt=%f;maxrtt=%f;avgrtt=%f;stdevrtt=%f;", - rtp->rtcp->minrtt, - rtp->rtcp->maxrtt, - rtp->rtcp->normdevrtt, - sqrt(rtp->rtcp->stdevrtt) - ); - } else { - snprintf(rtp->rtcp->quality_rtt, sizeof(rtp->rtcp->quality_rtt), "Not available"); - } - - return rtp->rtcp->quality_rtt; -} - -static char *__ast_rtp_get_quality(struct ast_rtp *rtp) -{ - /* - *ssrc our ssrc - *themssrc their ssrc - *lp lost packets - *rxjitter our calculated jitter(rx) - *rxcount no. received packets - *txjitter reported jitter of the other end - *txcount transmitted packets - *rlp remote lost packets - *rtt round trip time - */ - - if (rtp->rtcp && rtp->rtcp->rtcp_info) { - snprintf(rtp->rtcp->quality, sizeof(rtp->rtcp->quality), - "ssrc=%u;themssrc=%u;lp=%u;rxjitter=%f;rxcount=%u;txjitter=%f;txcount=%u;rlp=%u;rtt=%f", - rtp->ssrc, - rtp->themssrc, - rtp->rtcp->expected_prior - rtp->rtcp->received_prior, - rtp->rxjitter, - rtp->rxcount, - (double)rtp->rtcp->reported_jitter / 65536.0, - rtp->txcount, - rtp->rtcp->reported_lost, - rtp->rtcp->rtt - ); - } else { - snprintf(rtp->rtcp->quality, sizeof(rtp->rtcp->quality), "ssrc=%u;themssrc=%u;rxjitter=%f;rxcount=%u;txcount=%u;", - rtp->ssrc, - rtp->themssrc, - rtp->rxjitter, - rtp->rxcount, - rtp->txcount - ); - } - - return rtp->rtcp->quality; -} - -char *ast_rtp_get_quality(struct ast_rtp *rtp, struct ast_rtp_quality *qual, enum ast_rtp_quality_type qtype) -{ - if (qual && rtp) { - qual->local_ssrc = rtp->ssrc; - qual->local_jitter = rtp->rxjitter; - qual->local_count = rtp->rxcount; - qual->remote_ssrc = rtp->themssrc; - qual->remote_count = rtp->txcount; - - if (rtp->rtcp) { - qual->local_lostpackets = rtp->rtcp->expected_prior - rtp->rtcp->received_prior; - qual->remote_lostpackets = rtp->rtcp->reported_lost; - qual->remote_jitter = rtp->rtcp->reported_jitter / 65536.0; - qual->rtt = rtp->rtcp->rtt; - } - } - - switch (qtype) { - case RTPQOS_SUMMARY: - return __ast_rtp_get_quality(rtp); - case RTPQOS_JITTER: - return __ast_rtp_get_quality_jitter(rtp); - case RTPQOS_LOSS: - return __ast_rtp_get_quality_loss(rtp); - case RTPQOS_RTT: - return __ast_rtp_get_quality_rtt(rtp); - } - - return NULL; -} - -void ast_rtp_destroy(struct ast_rtp *rtp) -{ - if (rtcp_debug_test_addr(&rtp->them) || rtcpstats) { - /*Print some info on the call here */ - ast_verbose(" RTP-stats\n"); - ast_verbose("* Our Receiver:\n"); - ast_verbose(" SSRC: %u\n", rtp->themssrc); - ast_verbose(" Received packets: %u\n", rtp->rxcount); - ast_verbose(" Lost packets: %u\n", rtp->rtcp ? (rtp->rtcp->expected_prior - rtp->rtcp->received_prior) : 0); - ast_verbose(" Jitter: %.4f\n", rtp->rxjitter); - ast_verbose(" Transit: %.4f\n", rtp->rxtransit); - ast_verbose(" RR-count: %u\n", rtp->rtcp ? rtp->rtcp->rr_count : 0); - ast_verbose("* Our Sender:\n"); - ast_verbose(" SSRC: %u\n", rtp->ssrc); - ast_verbose(" Sent packets: %u\n", rtp->txcount); - ast_verbose(" Lost packets: %u\n", rtp->rtcp ? rtp->rtcp->reported_lost : 0); - ast_verbose(" Jitter: %u\n", rtp->rtcp ? (rtp->rtcp->reported_jitter / (unsigned int)65536.0) : 0); - ast_verbose(" SR-count: %u\n", rtp->rtcp ? rtp->rtcp->sr_count : 0); - ast_verbose(" RTT: %f\n", rtp->rtcp ? rtp->rtcp->rtt : 0); - } - - manager_event(EVENT_FLAG_REPORTING, "RTPReceiverStat", "SSRC: %u\r\n" - "ReceivedPackets: %u\r\n" - "LostPackets: %u\r\n" - "Jitter: %.4f\r\n" - "Transit: %.4f\r\n" - "RRCount: %u\r\n", - rtp->themssrc, - rtp->rxcount, - rtp->rtcp ? (rtp->rtcp->expected_prior - rtp->rtcp->received_prior) : 0, - rtp->rxjitter, - rtp->rxtransit, - rtp->rtcp ? rtp->rtcp->rr_count : 0); - manager_event(EVENT_FLAG_REPORTING, "RTPSenderStat", "SSRC: %u\r\n" - "SentPackets: %u\r\n" - "LostPackets: %u\r\n" - "Jitter: %u\r\n" - "SRCount: %u\r\n" - "RTT: %f\r\n", - rtp->ssrc, - rtp->txcount, - rtp->rtcp ? rtp->rtcp->reported_lost : 0, - rtp->rtcp ? rtp->rtcp->reported_jitter : 0, - rtp->rtcp ? rtp->rtcp->sr_count : 0, - rtp->rtcp ? rtp->rtcp->rtt : 0); - if (rtp->smoother) - ast_smoother_free(rtp->smoother); - if (rtp->ioid) - ast_io_remove(rtp->io, rtp->ioid); - if (rtp->s > -1) - close(rtp->s); - if (rtp->rtcp) { - AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); - close(rtp->rtcp->s); - ast_free(rtp->rtcp); - rtp->rtcp=NULL; - } -#ifdef P2P_INTENSE - ast_mutex_destroy(&rtp->bridge_lock); -#endif - ast_free(rtp); -} - -static unsigned int calc_txstamp(struct ast_rtp *rtp, struct timeval *delivery) -{ - struct timeval t; - long ms; - if (ast_tvzero(rtp->txcore)) { - rtp->txcore = ast_tvnow(); - /* Round to 20ms for nice, pretty timestamps */ - rtp->txcore.tv_usec -= rtp->txcore.tv_usec % 20000; - } - /* Use previous txcore if available */ - t = (delivery && !ast_tvzero(*delivery)) ? *delivery : ast_tvnow(); - ms = ast_tvdiff_ms(t, rtp->txcore); - if (ms < 0) - ms = 0; - /* Use what we just got for next time */ - rtp->txcore = t; - return (unsigned int) ms; -} - -/*! \brief Send begin frames for DTMF */ -int ast_rtp_senddigit_begin(struct ast_rtp *rtp, char digit) -{ - unsigned int *rtpheader; - int hdrlen = 12, res = 0, i = 0, payload = 0; - char data[256]; - - if ((digit <= '9') && (digit >= '0')) - digit -= '0'; - else if (digit == '*') - digit = 10; - else if (digit == '#') - digit = 11; - else if ((digit >= 'A') && (digit <= 'D')) - digit = digit - 'A' + 12; - else if ((digit >= 'a') && (digit <= 'd')) - digit = digit - 'a' + 12; - else { - ast_log(LOG_WARNING, "Don't know how to represent '%c'\n", digit); - return 0; - } - - /* If we have no peer, return immediately */ - if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port) - return 0; - - payload = ast_rtp_lookup_code(rtp, 0, AST_RTP_DTMF); - - rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000)); - rtp->send_duration = 160; - rtp->lastdigitts = rtp->lastts + rtp->send_duration; - - /* Get a pointer to the header */ - rtpheader = (unsigned int *)data; - rtpheader[0] = htonl((2 << 30) | (1 << 23) | (payload << 16) | (rtp->seqno)); - rtpheader[1] = htonl(rtp->lastdigitts); - rtpheader[2] = htonl(rtp->ssrc); - - for (i = 0; i < 2; i++) { - rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (rtp->send_duration)); - res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them)); - if (res < 0) - ast_log(LOG_ERROR, "RTP Transmission error to %s:%u: %s\n", - ast_inet_ntoa(rtp->them.sin_addr), - ntohs(rtp->them.sin_port), strerror(errno)); - if (rtp_debug_test_addr(&rtp->them)) - ast_verbose("Sent RTP DTMF packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", - ast_inet_ntoa(rtp->them.sin_addr), - ntohs(rtp->them.sin_port), payload, rtp->seqno, rtp->lastdigitts, res - hdrlen); - /* Increment sequence number */ - rtp->seqno++; - /* Increment duration */ - rtp->send_duration += 160; - /* Clear marker bit and set seqno */ - rtpheader[0] = htonl((2 << 30) | (payload << 16) | (rtp->seqno)); - } - - /* Since we received a begin, we can safely store the digit and disable any compensation */ - rtp->sending_digit = 1; - rtp->send_digit = digit; - rtp->send_payload = payload; - - return 0; -} - -/*! \brief Send continuation frame for DTMF */ -static int ast_rtp_senddigit_continuation(struct ast_rtp *rtp) -{ - unsigned int *rtpheader; - int hdrlen = 12, res = 0; - char data[256]; - - if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port) - return 0; - - /* Setup packet to send */ - rtpheader = (unsigned int *)data; - rtpheader[0] = htonl((2 << 30) | (1 << 23) | (rtp->send_payload << 16) | (rtp->seqno)); - rtpheader[1] = htonl(rtp->lastdigitts); - rtpheader[2] = htonl(rtp->ssrc); - rtpheader[3] = htonl((rtp->send_digit << 24) | (0xa << 16) | (rtp->send_duration)); - rtpheader[0] = htonl((2 << 30) | (rtp->send_payload << 16) | (rtp->seqno)); - - /* Transmit */ - res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them)); - if (res < 0) - ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n", - ast_inet_ntoa(rtp->them.sin_addr), - ntohs(rtp->them.sin_port), strerror(errno)); - if (rtp_debug_test_addr(&rtp->them)) - ast_verbose("Sent RTP DTMF packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", - ast_inet_ntoa(rtp->them.sin_addr), - ntohs(rtp->them.sin_port), rtp->send_payload, rtp->seqno, rtp->lastdigitts, res - hdrlen); - - /* Increment sequence number */ - rtp->seqno++; - /* Increment duration */ - rtp->send_duration += 160; - - return 0; -} - -/*! \brief Send end packets for DTMF */ -int ast_rtp_senddigit_end(struct ast_rtp *rtp, char digit) -{ - unsigned int *rtpheader; - int hdrlen = 12, res = 0, i = 0; - char data[256]; - - /* If no address, then bail out */ - if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port) - return 0; - - if ((digit <= '9') && (digit >= '0')) - digit -= '0'; - else if (digit == '*') - digit = 10; - else if (digit == '#') - digit = 11; - else if ((digit >= 'A') && (digit <= 'D')) - digit = digit - 'A' + 12; - else if ((digit >= 'a') && (digit <= 'd')) - digit = digit - 'a' + 12; - else { - ast_log(LOG_WARNING, "Don't know how to represent '%c'\n", digit); - return 0; - } - - rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000)); - - rtpheader = (unsigned int *)data; - rtpheader[1] = htonl(rtp->lastdigitts); - rtpheader[2] = htonl(rtp->ssrc); - rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (rtp->send_duration)); - /* Set end bit */ - rtpheader[3] |= htonl((1 << 23)); - - /* Send 3 termination packets */ - for (i = 0; i < 3; i++) { - rtpheader[0] = htonl((2 << 30) | (rtp->send_payload << 16) | (rtp->seqno)); - res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them)); - rtp->seqno++; - if (res < 0) - ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n", - ast_inet_ntoa(rtp->them.sin_addr), - ntohs(rtp->them.sin_port), strerror(errno)); - if (rtp_debug_test_addr(&rtp->them)) - ast_verbose("Sent RTP DTMF packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", - ast_inet_ntoa(rtp->them.sin_addr), - ntohs(rtp->them.sin_port), rtp->send_payload, rtp->seqno, rtp->lastdigitts, res - hdrlen); - } - rtp->lastts += rtp->send_duration; - rtp->sending_digit = 0; - rtp->send_digit = 0; - - return res; -} - -/*! \brief Public function: Send an H.261 fast update request, some devices need this rather than SIP XML */ -int ast_rtcp_send_h261fur(void *data) -{ - struct ast_rtp *rtp = data; - int res; - - rtp->rtcp->sendfur = 1; - res = ast_rtcp_write(data); - - return res; -} - -/*! \brief Send RTCP sender's report */ -static int ast_rtcp_write_sr(const void *data) -{ - struct ast_rtp *rtp = (struct ast_rtp *)data; - int res; - int len = 0; - struct timeval now; - unsigned int now_lsw; - unsigned int now_msw; - unsigned int *rtcpheader; - unsigned int lost; - unsigned int extended; - unsigned int expected; - unsigned int expected_interval; - unsigned int received_interval; - int lost_interval; - int fraction; - struct timeval dlsr; - char bdata[512]; - - /* Commented condition is always not NULL if rtp->rtcp is not NULL */ - if (!rtp || !rtp->rtcp/* || (&rtp->rtcp->them.sin_addr == 0)*/) - return 0; - - if (!rtp->rtcp->them.sin_addr.s_addr) { /* This'll stop rtcp for this rtp session */ - ast_verbose("RTCP SR transmission error, rtcp halted\n"); - AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); - return 0; - } - - gettimeofday(&now, NULL); - timeval2ntp(now, &now_msw, &now_lsw); /* fill thses ones in from utils.c*/ - rtcpheader = (unsigned int *)bdata; - rtcpheader[1] = htonl(rtp->ssrc); /* Our SSRC */ - rtcpheader[2] = htonl(now_msw); /* now, MSW. gettimeofday() + SEC_BETWEEN_1900_AND_1970*/ - rtcpheader[3] = htonl(now_lsw); /* now, LSW */ - rtcpheader[4] = htonl(rtp->lastts); /* FIXME shouldn't be that, it should be now */ - rtcpheader[5] = htonl(rtp->txcount); /* No. packets sent */ - rtcpheader[6] = htonl(rtp->txoctetcount); /* No. bytes sent */ - len += 28; - - extended = rtp->cycles + rtp->lastrxseqno; - expected = extended - rtp->seedrxseqno + 1; - if (rtp->rxcount > expected) - expected += rtp->rxcount - expected; - lost = expected - rtp->rxcount; - expected_interval = expected - rtp->rtcp->expected_prior; - rtp->rtcp->expected_prior = expected; - received_interval = rtp->rxcount - rtp->rtcp->received_prior; - rtp->rtcp->received_prior = rtp->rxcount; - lost_interval = expected_interval - received_interval; - if (expected_interval == 0 || lost_interval <= 0) - fraction = 0; - else - fraction = (lost_interval << 8) / expected_interval; - timersub(&now, &rtp->rtcp->rxlsr, &dlsr); - rtcpheader[7] = htonl(rtp->themssrc); - rtcpheader[8] = htonl(((fraction & 0xff) << 24) | (lost & 0xffffff)); - rtcpheader[9] = htonl((rtp->cycles) | ((rtp->lastrxseqno & 0xffff))); - rtcpheader[10] = htonl((unsigned int)(rtp->rxjitter * 65536.)); - rtcpheader[11] = htonl(rtp->rtcp->themrxlsr); - rtcpheader[12] = htonl((((dlsr.tv_sec * 1000) + (dlsr.tv_usec / 1000)) * 65536) / 1000); - len += 24; - - rtcpheader[0] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SR << 16) | ((len/4)-1)); - - if (rtp->rtcp->sendfur) { - rtcpheader[13] = htonl((2 << 30) | (0 << 24) | (RTCP_PT_FUR << 16) | 1); - rtcpheader[14] = htonl(rtp->ssrc); /* Our SSRC */ - len += 8; - rtp->rtcp->sendfur = 0; - } - - /* Insert SDES here. Probably should make SDES text equal to mimetypes[code].type (not subtype 'cos */ - /* it can change mid call, and SDES can't) */ - rtcpheader[len/4] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2); - rtcpheader[(len/4)+1] = htonl(rtp->ssrc); /* Our SSRC */ - rtcpheader[(len/4)+2] = htonl(0x01 << 24); /* Empty for the moment */ - len += 12; - - res = sendto(rtp->rtcp->s, (unsigned int *)rtcpheader, len, 0, (struct sockaddr *)&rtp->rtcp->them, sizeof(rtp->rtcp->them)); - if (res < 0) { - ast_log(LOG_ERROR, "RTCP SR transmission error to %s:%d, rtcp halted %s\n",ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port), strerror(errno)); - AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); - return 0; - } - - /* FIXME Don't need to get a new one */ - gettimeofday(&rtp->rtcp->txlsr, NULL); - rtp->rtcp->sr_count++; - - rtp->rtcp->lastsrtxcount = rtp->txcount; - - if (rtcp_debug_test_addr(&rtp->rtcp->them)) { - ast_verbose("* Sent RTCP SR to %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); - ast_verbose(" Our SSRC: %u\n", rtp->ssrc); - ast_verbose(" Sent(NTP): %u.%010u\n", (unsigned int)now.tv_sec, (unsigned int)now.tv_usec*4096); - ast_verbose(" Sent(RTP): %u\n", rtp->lastts); - ast_verbose(" Sent packets: %u\n", rtp->txcount); - ast_verbose(" Sent octets: %u\n", rtp->txoctetcount); - ast_verbose(" Report block:\n"); - ast_verbose(" Fraction lost: %u\n", fraction); - ast_verbose(" Cumulative loss: %u\n", lost); - ast_verbose(" IA jitter: %.4f\n", rtp->rxjitter); - ast_verbose(" Their last SR: %u\n", rtp->rtcp->themrxlsr); - ast_verbose(" DLSR: %4.4f (sec)\n\n", (double)(ntohl(rtcpheader[12])/65536.0)); - } - manager_event(EVENT_FLAG_REPORTING, "RTCPSent", "To: %s:%d\r\n" - "OurSSRC: %u\r\n" - "SentNTP: %u.%010u\r\n" - "SentRTP: %u\r\n" - "SentPackets: %u\r\n" - "SentOctets: %u\r\n" - "ReportBlock:\r\n" - "FractionLost: %u\r\n" - "CumulativeLoss: %u\r\n" - "IAJitter: %.4f\r\n" - "TheirLastSR: %u\r\n" - "DLSR: %4.4f (sec)\r\n", - ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port), - rtp->ssrc, - (unsigned int)now.tv_sec, (unsigned int)now.tv_usec*4096, - rtp->lastts, - rtp->txcount, - rtp->txoctetcount, - fraction, - lost, - rtp->rxjitter, - rtp->rtcp->themrxlsr, - (double)(ntohl(rtcpheader[12])/65536.0)); - return res; -} - -/*! \brief Send RTCP recipient's report */ -static int ast_rtcp_write_rr(const void *data) -{ - struct ast_rtp *rtp = (struct ast_rtp *)data; - int res; - int len = 32; - unsigned int lost; - unsigned int extended; - unsigned int expected; - unsigned int expected_interval; - unsigned int received_interval; - int lost_interval; - struct timeval now; - unsigned int *rtcpheader; - char bdata[1024]; - struct timeval dlsr; - int fraction; - - double rxlost_current; - - if (!rtp || !rtp->rtcp || (&rtp->rtcp->them.sin_addr == 0)) - return 0; - - if (!rtp->rtcp->them.sin_addr.s_addr) { - ast_log(LOG_ERROR, "RTCP RR transmission error, rtcp halted\n"); - AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); - return 0; - } - - extended = rtp->cycles + rtp->lastrxseqno; - expected = extended - rtp->seedrxseqno + 1; - lost = expected - rtp->rxcount; - expected_interval = expected - rtp->rtcp->expected_prior; - rtp->rtcp->expected_prior = expected; - received_interval = rtp->rxcount - rtp->rtcp->received_prior; - rtp->rtcp->received_prior = rtp->rxcount; - lost_interval = expected_interval - received_interval; - - if (lost_interval <= 0) - rtp->rtcp->rxlost = 0; - else rtp->rtcp->rxlost = rtp->rtcp->rxlost; - if (rtp->rtcp->rxlost_count == 0) - rtp->rtcp->minrxlost = rtp->rtcp->rxlost; - if (lost_interval < rtp->rtcp->minrxlost) - rtp->rtcp->minrxlost = rtp->rtcp->rxlost; - if (lost_interval > rtp->rtcp->maxrxlost) - rtp->rtcp->maxrxlost = rtp->rtcp->rxlost; - - rxlost_current = normdev_compute(rtp->rtcp->normdev_rxlost, rtp->rtcp->rxlost, rtp->rtcp->rxlost_count); - rtp->rtcp->stdev_rxlost = stddev_compute(rtp->rtcp->stdev_rxlost, rtp->rtcp->rxlost, rtp->rtcp->normdev_rxlost, rxlost_current, rtp->rtcp->rxlost_count); - rtp->rtcp->normdev_rxlost = rxlost_current; - rtp->rtcp->rxlost_count++; - - if (expected_interval == 0 || lost_interval <= 0) - fraction = 0; - else - fraction = (lost_interval << 8) / expected_interval; - gettimeofday(&now, NULL); - timersub(&now, &rtp->rtcp->rxlsr, &dlsr); - rtcpheader = (unsigned int *)bdata; - rtcpheader[0] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_RR << 16) | ((len/4)-1)); - rtcpheader[1] = htonl(rtp->ssrc); - rtcpheader[2] = htonl(rtp->themssrc); - rtcpheader[3] = htonl(((fraction & 0xff) << 24) | (lost & 0xffffff)); - rtcpheader[4] = htonl((rtp->cycles) | ((rtp->lastrxseqno & 0xffff))); - rtcpheader[5] = htonl((unsigned int)(rtp->rxjitter * 65536.)); - rtcpheader[6] = htonl(rtp->rtcp->themrxlsr); - rtcpheader[7] = htonl((((dlsr.tv_sec * 1000) + (dlsr.tv_usec / 1000)) * 65536) / 1000); - - if (rtp->rtcp->sendfur) { - rtcpheader[8] = htonl((2 << 30) | (0 << 24) | (RTCP_PT_FUR << 16) | 1); /* Header from page 36 in RFC 3550 */ - rtcpheader[9] = htonl(rtp->ssrc); /* Our SSRC */ - len += 8; - rtp->rtcp->sendfur = 0; - } - - /*! \note Insert SDES here. Probably should make SDES text equal to mimetypes[code].type (not subtype 'cos - it can change mid call, and SDES can't) */ - rtcpheader[len/4] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2); - rtcpheader[(len/4)+1] = htonl(rtp->ssrc); /* Our SSRC */ - rtcpheader[(len/4)+2] = htonl(0x01 << 24); /* Empty for the moment */ - len += 12; - - res = sendto(rtp->rtcp->s, (unsigned int *)rtcpheader, len, 0, (struct sockaddr *)&rtp->rtcp->them, sizeof(rtp->rtcp->them)); - - if (res < 0) { - ast_log(LOG_ERROR, "RTCP RR transmission error, rtcp halted: %s\n",strerror(errno)); - /* Remove the scheduler */ - AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); - return 0; - } - - rtp->rtcp->rr_count++; - - if (rtcp_debug_test_addr(&rtp->rtcp->them)) { - ast_verbose("\n* Sending RTCP RR to %s:%d\n" - " Our SSRC: %u\nTheir SSRC: %u\niFraction lost: %d\nCumulative loss: %u\n" - " IA jitter: %.4f\n" - " Their last SR: %u\n" - " DLSR: %4.4f (sec)\n\n", - ast_inet_ntoa(rtp->rtcp->them.sin_addr), - ntohs(rtp->rtcp->them.sin_port), - rtp->ssrc, rtp->themssrc, fraction, lost, - rtp->rxjitter, - rtp->rtcp->themrxlsr, - (double)(ntohl(rtcpheader[7])/65536.0)); - } - - return res; -} - -/*! \brief Write and RTCP packet to the far end - * \note Decide if we are going to send an SR (with Reception Block) or RR - * RR is sent if we have not sent any rtp packets in the previous interval */ -static int ast_rtcp_write(const void *data) -{ - struct ast_rtp *rtp = (struct ast_rtp *)data; - int res; - - if (!rtp || !rtp->rtcp) - return 0; - - if (rtp->txcount > rtp->rtcp->lastsrtxcount) - res = ast_rtcp_write_sr(data); - else - res = ast_rtcp_write_rr(data); - - return res; -} - -/*! \brief generate comfort noice (CNG) */ -int ast_rtp_sendcng(struct ast_rtp *rtp, int level) -{ - unsigned int *rtpheader; - int hdrlen = 12; - int res; - int payload; - char data[256]; - level = 127 - (level & 0x7f); - payload = ast_rtp_lookup_code(rtp, 0, AST_RTP_CN); - - /* If we have no peer, return immediately */ - if (!rtp->them.sin_addr.s_addr) - return 0; - - rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000)); - - /* Get a pointer to the header */ - rtpheader = (unsigned int *)data; - rtpheader[0] = htonl((2 << 30) | (1 << 23) | (payload << 16) | (rtp->seqno++)); - rtpheader[1] = htonl(rtp->lastts); - rtpheader[2] = htonl(rtp->ssrc); - data[12] = level; - if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) { - res = sendto(rtp->s, (void *)rtpheader, hdrlen + 1, 0, (struct sockaddr *)&rtp->them, sizeof(rtp->them)); - if (res <0) - ast_log(LOG_ERROR, "RTP Comfort Noise Transmission error to %s:%d: %s\n", ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno)); - if (rtp_debug_test_addr(&rtp->them)) - ast_verbose("Sent Comfort Noise RTP packet to %s:%u (type %d, seq %u, ts %u, len %d)\n" - , ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), payload, rtp->seqno, rtp->lastts,res - hdrlen); - - } - return 0; -} - -/*! \brief Write RTP packet with audio or video media frames into UDP packet */ -static int ast_rtp_raw_write(struct ast_rtp *rtp, struct ast_frame *f, int codec) -{ - unsigned char *rtpheader; - int hdrlen = 12; - int res; - unsigned int ms; - int pred; - int mark = 0; - - if (rtp->sending_digit) { - return 0; - } - - ms = calc_txstamp(rtp, &f->delivery); - /* Default prediction */ - if (f->frametype == AST_FRAME_VOICE) { - pred = rtp->lastts + f->samples; - - /* Re-calculate last TS */ - rtp->lastts = rtp->lastts + ms * 8; - if (ast_tvzero(f->delivery)) { - /* If this isn't an absolute delivery time, Check if it is close to our prediction, - and if so, go with our prediction */ - if (abs(rtp->lastts - pred) < MAX_TIMESTAMP_SKEW) - rtp->lastts = pred; - else { - ast_debug(3, "Difference is %d, ms is %d\n", abs(rtp->lastts - pred), ms); - mark = 1; - } - } - } else if (f->frametype == AST_FRAME_VIDEO) { - mark = f->subclass & 0x1; - pred = rtp->lastovidtimestamp + f->samples; - /* Re-calculate last TS */ - rtp->lastts = rtp->lastts + ms * 90; - /* If it's close to our prediction, go for it */ - if (ast_tvzero(f->delivery)) { - if (abs(rtp->lastts - pred) < 7200) { - rtp->lastts = pred; - rtp->lastovidtimestamp += f->samples; - } else { - ast_debug(3, "Difference is %d, ms is %d (%d), pred/ts/samples %d/%d/%d\n", abs(rtp->lastts - pred), ms, ms * 90, rtp->lastts, pred, f->samples); - rtp->lastovidtimestamp = rtp->lastts; - } - } - } else { - pred = rtp->lastotexttimestamp + f->samples; - /* Re-calculate last TS */ - rtp->lastts = rtp->lastts + ms * 90; - /* If it's close to our prediction, go for it */ - if (ast_tvzero(f->delivery)) { - if (abs(rtp->lastts - pred) < 7200) { - rtp->lastts = pred; - rtp->lastotexttimestamp += f->samples; - } else { - ast_debug(3, "Difference is %d, ms is %d (%d), pred/ts/samples %d/%d/%d\n", abs(rtp->lastts - pred), ms, ms * 90, rtp->lastts, pred, f->samples); - rtp->lastotexttimestamp = rtp->lastts; - } - } - } - - /* If we have been explicitly told to set the marker bit do so */ - if (rtp->set_marker_bit) { - mark = 1; - rtp->set_marker_bit = 0; - } - - /* If the timestamp for non-digit packets has moved beyond the timestamp - for digits, update the digit timestamp. - */ - if (rtp->lastts > rtp->lastdigitts) - rtp->lastdigitts = rtp->lastts; - - if (ast_test_flag(f, AST_FRFLAG_HAS_TIMING_INFO)) - rtp->lastts = f->ts * 8; - - /* Get a pointer to the header */ - rtpheader = (unsigned char *)(f->data.ptr - hdrlen); - - put_unaligned_uint32(rtpheader, htonl((2 << 30) | (codec << 16) | (rtp->seqno) | (mark << 23))); - put_unaligned_uint32(rtpheader + 4, htonl(rtp->lastts)); - put_unaligned_uint32(rtpheader + 8, htonl(rtp->ssrc)); - - if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) { - res = sendto(rtp->s, (void *)rtpheader, f->datalen + hdrlen, 0, (struct sockaddr *)&rtp->them, sizeof(rtp->them)); - if (res < 0) { - if (!rtp->nat || (rtp->nat && (ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) { - ast_debug(1, "RTP Transmission error of packet %d to %s:%d: %s\n", rtp->seqno, ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno)); - } else if (((ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug) && !ast_test_flag(rtp, FLAG_NAT_INACTIVE_NOWARN)) { - /* Only give this error message once if we are not RTP debugging */ - if (option_debug || rtpdebug) - ast_debug(0, "RTP NAT: Can't write RTP to private address %s:%d, waiting for other end to send audio...\n", ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port)); - ast_set_flag(rtp, FLAG_NAT_INACTIVE_NOWARN); - } - } else { - rtp->txcount++; - rtp->txoctetcount +=(res - hdrlen); - - /* Do not schedule RR if RTCP isn't run */ - if (rtp->rtcp && rtp->rtcp->them.sin_addr.s_addr && rtp->rtcp->schedid < 1) { - rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, rtp); - } - } - - if (rtp_debug_test_addr(&rtp->them)) - ast_verbose("Sent RTP packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", - ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), codec, rtp->seqno, rtp->lastts,res - hdrlen); - } - - rtp->seqno++; - - return 0; -} - -void ast_rtp_codec_setpref(struct ast_rtp *rtp, struct ast_codec_pref *prefs) -{ - struct ast_format_list current_format_old, current_format_new; - - /* if no packets have been sent through this session yet, then - * changing preferences does not require any extra work - */ - if (rtp->lasttxformat == 0) { - rtp->pref = *prefs; - return; - } - - current_format_old = ast_codec_pref_getsize(&rtp->pref, rtp->lasttxformat); - - rtp->pref = *prefs; - - current_format_new = ast_codec_pref_getsize(&rtp->pref, rtp->lasttxformat); - - /* if the framing desired for the current format has changed, we may have to create - * or adjust the smoother for this session - */ - if ((current_format_new.inc_ms != 0) && - (current_format_new.cur_ms != current_format_old.cur_ms)) { - int new_size = (current_format_new.cur_ms * current_format_new.fr_len) / current_format_new.inc_ms; - - if (rtp->smoother) { - ast_smoother_reconfigure(rtp->smoother, new_size); - if (option_debug) { - ast_log(LOG_DEBUG, "Adjusted smoother to %d ms and %d bytes\n", current_format_new.cur_ms, new_size); - } - } else { - if (!(rtp->smoother = ast_smoother_new(new_size))) { - ast_log(LOG_WARNING, "Unable to create smoother: format: %d ms: %d len: %d\n", rtp->lasttxformat, current_format_new.cur_ms, new_size); - return; - } - if (current_format_new.flags) { - ast_smoother_set_flags(rtp->smoother, current_format_new.flags); - } - if (option_debug) { - ast_log(LOG_DEBUG, "Created smoother: format: %d ms: %d len: %d\n", rtp->lasttxformat, current_format_new.cur_ms, new_size); - } - } - } - -} - -struct ast_codec_pref *ast_rtp_codec_getpref(struct ast_rtp *rtp) -{ - return &rtp->pref; -} - -int ast_rtp_codec_getformat(int pt) -{ - if (pt < 0 || pt > MAX_RTP_PT) - return 0; /* bogus payload type */ - - if (static_RTP_PT[pt].isAstFormat) - return static_RTP_PT[pt].code; - else - return 0; -} - -int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *_f) -{ - struct ast_frame *f; - int codec; - int hdrlen = 12; - int subclass; - - - /* If we have no peer, return immediately */ - if (!rtp->them.sin_addr.s_addr) - return 0; - - /* If there is no data length, return immediately */ - if (!_f->datalen && !rtp->red) - return 0; - - /* Make sure we have enough space for RTP header */ - if ((_f->frametype != AST_FRAME_VOICE) && (_f->frametype != AST_FRAME_VIDEO) && (_f->frametype != AST_FRAME_TEXT)) { - ast_log(LOG_WARNING, "RTP can only send voice, video and text\n"); - return -1; - } - - if (rtp->red) { - /* return 0; */ - /* no primary data or generations to send */ - if ((_f = red_t140_to_red(rtp->red)) == NULL) - return 0; - } - - /* The bottom bit of a video subclass contains the marker bit */ - subclass = _f->subclass; - if (_f->frametype == AST_FRAME_VIDEO) - subclass &= ~0x1; - - codec = ast_rtp_lookup_code(rtp, 1, subclass); - if (codec < 0) { - ast_log(LOG_WARNING, "Don't know how to send format %s packets with RTP\n", ast_getformatname(_f->subclass)); - return -1; - } - - if (rtp->lasttxformat != subclass) { - /* New format, reset the smoother */ - ast_debug(1, "Ooh, format changed from %s to %s\n", ast_getformatname(rtp->lasttxformat), ast_getformatname(subclass)); - rtp->lasttxformat = subclass; - if (rtp->smoother) - ast_smoother_free(rtp->smoother); - rtp->smoother = NULL; - } - - if (!rtp->smoother) { - struct ast_format_list fmt = ast_codec_pref_getsize(&rtp->pref, subclass); - - switch (subclass) { - case AST_FORMAT_SPEEX: - case AST_FORMAT_G723_1: - case AST_FORMAT_SIREN7: - case AST_FORMAT_SIREN14: - /* these are all frame-based codecs and cannot be safely run through - a smoother */ - break; - default: - if (fmt.inc_ms) { /* if codec parameters is set / avoid division by zero */ - if (!(rtp->smoother = ast_smoother_new((fmt.cur_ms * fmt.fr_len) / fmt.inc_ms))) { - ast_log(LOG_WARNING, "Unable to create smoother: format: %d ms: %d len: %d\n", subclass, fmt.cur_ms, ((fmt.cur_ms * fmt.fr_len) / fmt.inc_ms)); - return -1; - } - if (fmt.flags) - ast_smoother_set_flags(rtp->smoother, fmt.flags); - ast_debug(1, "Created smoother: format: %d ms: %d len: %d\n", subclass, fmt.cur_ms, ((fmt.cur_ms * fmt.fr_len) / fmt.inc_ms)); - } - } - } - if (rtp->smoother) { - if (ast_smoother_test_flag(rtp->smoother, AST_SMOOTHER_FLAG_BE)) { - ast_smoother_feed_be(rtp->smoother, _f); - } else { - ast_smoother_feed(rtp->smoother, _f); - } - - while ((f = ast_smoother_read(rtp->smoother)) && (f->data.ptr)) { - if (f->subclass == AST_FORMAT_G722) { - /* G.722 is silllllllllllllly */ - f->samples /= 2; - } - - ast_rtp_raw_write(rtp, f, codec); - } - } else { - /* Don't buffer outgoing frames; send them one-per-packet: */ - if (_f->offset < hdrlen) - f = ast_frdup(_f); /*! \bug XXX this might never be free'd. Why do we do this? */ - else - f = _f; - if (f->data.ptr) - ast_rtp_raw_write(rtp, f, codec); - if (f != _f) - ast_frfree(f); - } - - return 0; -} - -/*! \brief Unregister interface to channel driver */ -void ast_rtp_proto_unregister(struct ast_rtp_protocol *proto) -{ - AST_RWLIST_WRLOCK(&protos); - AST_RWLIST_REMOVE(&protos, proto, list); - AST_RWLIST_UNLOCK(&protos); -} - -/*! \brief Register interface to channel driver */ -int ast_rtp_proto_register(struct ast_rtp_protocol *proto) -{ - struct ast_rtp_protocol *cur; - - AST_RWLIST_WRLOCK(&protos); - AST_RWLIST_TRAVERSE(&protos, cur, list) { - if (!strcmp(cur->type, proto->type)) { - ast_log(LOG_WARNING, "Tried to register same protocol '%s' twice\n", cur->type); - AST_RWLIST_UNLOCK(&protos); - return -1; - } - } - AST_RWLIST_INSERT_HEAD(&protos, proto, list); - AST_RWLIST_UNLOCK(&protos); - - return 0; -} - -/*! \brief Bridge loop for true native bridge (reinvite) */ -static enum ast_bridge_result bridge_native_loop(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp *p0, struct ast_rtp *p1, struct ast_rtp *vp0, struct ast_rtp *vp1, struct ast_rtp *tp0, struct ast_rtp *tp1, struct ast_rtp_protocol *pr0, struct ast_rtp_protocol *pr1, int codec0, int codec1, int timeoutms, int flags, struct ast_frame **fo, struct ast_channel **rc, void *pvt0, void *pvt1) -{ - struct ast_frame *fr = NULL; - struct ast_channel *who = NULL, *other = NULL, *cs[3] = {NULL, }; - int oldcodec0 = codec0, oldcodec1 = codec1; - struct sockaddr_in ac1 = {0,}, vac1 = {0,}, tac1 = {0,}, ac0 = {0,}, vac0 = {0,}, tac0 = {0,}; - struct sockaddr_in t1 = {0,}, vt1 = {0,}, tt1 = {0,}, t0 = {0,}, vt0 = {0,}, tt0 = {0,}; - - /* Set it up so audio goes directly between the two endpoints */ - - /* Test the first channel */ - if (!(pr0->set_rtp_peer(c0, p1, vp1, tp1, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE)))) { - ast_rtp_get_peer(p1, &ac1); - if (vp1) - ast_rtp_get_peer(vp1, &vac1); - if (tp1) - ast_rtp_get_peer(tp1, &tac1); - } else - ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c0->name, c1->name); - - /* Test the second channel */ - if (!(pr1->set_rtp_peer(c1, p0, vp0, tp0, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE)))) { - ast_rtp_get_peer(p0, &ac0); - if (vp0) - ast_rtp_get_peer(vp0, &vac0); - if (tp0) - ast_rtp_get_peer(tp0, &tac0); - } else - ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c1->name, c0->name); - - /* Now we can unlock and move into our loop */ - ast_channel_unlock(c0); - ast_channel_unlock(c1); - - ast_poll_channel_add(c0, c1); - - /* Throw our channels into the structure and enter the loop */ - cs[0] = c0; - cs[1] = c1; - cs[2] = NULL; - for (;;) { - /* Check if anything changed */ - if ((c0->tech_pvt != pvt0) || - (c1->tech_pvt != pvt1) || - (c0->masq || c0->masqr || c1->masq || c1->masqr) || - (c0->monitor || c0->audiohooks || c1->monitor || c1->audiohooks)) { - ast_debug(1, "Oooh, something is weird, backing out\n"); - if (c0->tech_pvt == pvt0) - if (pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0)) - ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name); - if (c1->tech_pvt == pvt1) - if (pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0)) - ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name); - ast_poll_channel_del(c0, c1); - return AST_BRIDGE_RETRY; - } - - /* Check if they have changed their address */ - ast_rtp_get_peer(p1, &t1); - if (vp1) - ast_rtp_get_peer(vp1, &vt1); - if (tp1) - ast_rtp_get_peer(tp1, &tt1); - if (pr1->get_codec) - codec1 = pr1->get_codec(c1); - ast_rtp_get_peer(p0, &t0); - if (vp0) - ast_rtp_get_peer(vp0, &vt0); - if (tp0) - ast_rtp_get_peer(tp0, &tt0); - if (pr0->get_codec) - codec0 = pr0->get_codec(c0); - if ((inaddrcmp(&t1, &ac1)) || - (vp1 && inaddrcmp(&vt1, &vac1)) || - (tp1 && inaddrcmp(&tt1, &tac1)) || - (codec1 != oldcodec1)) { - ast_debug(2, "Oooh, '%s' changed end address to %s:%d (format %d)\n", - c1->name, ast_inet_ntoa(t1.sin_addr), ntohs(t1.sin_port), codec1); - ast_debug(2, "Oooh, '%s' changed end vaddress to %s:%d (format %d)\n", - c1->name, ast_inet_ntoa(vt1.sin_addr), ntohs(vt1.sin_port), codec1); - ast_debug(2, "Oooh, '%s' changed end taddress to %s:%d (format %d)\n", - c1->name, ast_inet_ntoa(tt1.sin_addr), ntohs(tt1.sin_port), codec1); - ast_debug(2, "Oooh, '%s' was %s:%d/(format %d)\n", - c1->name, ast_inet_ntoa(ac1.sin_addr), ntohs(ac1.sin_port), oldcodec1); - ast_debug(2, "Oooh, '%s' was %s:%d/(format %d)\n", - c1->name, ast_inet_ntoa(vac1.sin_addr), ntohs(vac1.sin_port), oldcodec1); - ast_debug(2, "Oooh, '%s' was %s:%d/(format %d)\n", - c1->name, ast_inet_ntoa(tac1.sin_addr), ntohs(tac1.sin_port), oldcodec1); - if (pr0->set_rtp_peer(c0, t1.sin_addr.s_addr ? p1 : NULL, vt1.sin_addr.s_addr ? vp1 : NULL, tt1.sin_addr.s_addr ? tp1 : NULL, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE))) - ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c0->name, c1->name); - memcpy(&ac1, &t1, sizeof(ac1)); - memcpy(&vac1, &vt1, sizeof(vac1)); - memcpy(&tac1, &tt1, sizeof(tac1)); - oldcodec1 = codec1; - } - if ((inaddrcmp(&t0, &ac0)) || - (vp0 && inaddrcmp(&vt0, &vac0)) || - (tp0 && inaddrcmp(&tt0, &tac0))) { - ast_debug(2, "Oooh, '%s' changed end address to %s:%d (format %d)\n", - c0->name, ast_inet_ntoa(t0.sin_addr), ntohs(t0.sin_port), codec0); - ast_debug(2, "Oooh, '%s' was %s:%d/(format %d)\n", - c0->name, ast_inet_ntoa(ac0.sin_addr), ntohs(ac0.sin_port), oldcodec0); - if (pr1->set_rtp_peer(c1, t0.sin_addr.s_addr ? p0 : NULL, vt0.sin_addr.s_addr ? vp0 : NULL, tt0.sin_addr.s_addr ? tp0 : NULL, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE))) - ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c1->name, c0->name); - memcpy(&ac0, &t0, sizeof(ac0)); - memcpy(&vac0, &vt0, sizeof(vac0)); - memcpy(&tac0, &tt0, sizeof(tac0)); - oldcodec0 = codec0; - } - - /* Wait for frame to come in on the channels */ - if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) { - if (!timeoutms) { - if (pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0)) - ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name); - if (pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0)) - ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name); - return AST_BRIDGE_RETRY; - } - ast_debug(1, "Ooh, empty read...\n"); - if (ast_check_hangup(c0) || ast_check_hangup(c1)) - break; - continue; - } - fr = ast_read(who); - other = (who == c0) ? c1 : c0; - if (!fr || ((fr->frametype == AST_FRAME_DTMF_BEGIN || fr->frametype == AST_FRAME_DTMF_END) && - (((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) || - ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1))))) { - /* Break out of bridge */ - *fo = fr; - *rc = who; - ast_debug(1, "Oooh, got a %s\n", fr ? "digit" : "hangup"); - if (c0->tech_pvt == pvt0) - if (pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0)) - ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name); - if (c1->tech_pvt == pvt1) - if (pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0)) - ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name); - ast_poll_channel_del(c0, c1); - return AST_BRIDGE_COMPLETE; - } else if ((fr->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) { - if ((fr->subclass == AST_CONTROL_HOLD) || - (fr->subclass == AST_CONTROL_UNHOLD) || - (fr->subclass == AST_CONTROL_VIDUPDATE) || - (fr->subclass == AST_CONTROL_T38) || - (fr->subclass == AST_CONTROL_SRCUPDATE)) { - if (fr->subclass == AST_CONTROL_HOLD) { - /* If we someone went on hold we want the other side to reinvite back to us */ - if (who == c0) - pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0); - else - pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0); - } else if (fr->subclass == AST_CONTROL_UNHOLD) { - /* If they went off hold they should go back to being direct */ - if (who == c0) - pr1->set_rtp_peer(c1, p0, vp0, tp0, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE)); - else - pr0->set_rtp_peer(c0, p1, vp1, tp1, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE)); - } - /* Update local address information */ - ast_rtp_get_peer(p0, &t0); - memcpy(&ac0, &t0, sizeof(ac0)); - ast_rtp_get_peer(p1, &t1); - memcpy(&ac1, &t1, sizeof(ac1)); - /* Update codec information */ - if (pr0->get_codec && c0->tech_pvt) - oldcodec0 = codec0 = pr0->get_codec(c0); - if (pr1->get_codec && c1->tech_pvt) - oldcodec1 = codec1 = pr1->get_codec(c1); - ast_indicate_data(other, fr->subclass, fr->data.ptr, fr->datalen); - ast_frfree(fr); - } else { - *fo = fr; - *rc = who; - ast_debug(1, "Got a FRAME_CONTROL (%d) frame on channel %s\n", fr->subclass, who->name); - return AST_BRIDGE_COMPLETE; - } - } else { - if ((fr->frametype == AST_FRAME_DTMF_BEGIN) || - (fr->frametype == AST_FRAME_DTMF_END) || - (fr->frametype == AST_FRAME_VOICE) || - (fr->frametype == AST_FRAME_VIDEO) || - (fr->frametype == AST_FRAME_IMAGE) || - (fr->frametype == AST_FRAME_HTML) || - (fr->frametype == AST_FRAME_MODEM) || - (fr->frametype == AST_FRAME_TEXT)) { - ast_write(other, fr); - } - ast_frfree(fr); - } - /* Swap priority */ -#ifndef HAVE_EPOLL - cs[2] = cs[0]; - cs[0] = cs[1]; - cs[1] = cs[2]; -#endif - } - - ast_poll_channel_del(c0, c1); - - if (pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0)) - ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name); - if (pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0)) - ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name); - - return AST_BRIDGE_FAILED; -} - -/*! \brief P2P RTP Callback */ -#ifdef P2P_INTENSE -static int p2p_rtp_callback(int *id, int fd, short events, void *cbdata) -{ - int res = 0, hdrlen = 12; - struct sockaddr_in sin; - socklen_t len; - unsigned int *header; - struct ast_rtp *rtp = cbdata, *bridged = NULL; - - if (!rtp) - return 1; - - len = sizeof(sin); - if ((res = recvfrom(fd, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET, 0, (struct sockaddr *)&sin, &len)) < 0) - return 1; - - header = (unsigned int *)(rtp->rawdata + AST_FRIENDLY_OFFSET); - - /* If NAT support is turned on, then see if we need to change their address */ - if ((rtp->nat) && - ((rtp->them.sin_addr.s_addr != sin.sin_addr.s_addr) || - (rtp->them.sin_port != sin.sin_port))) { - rtp->them = sin; - rtp->rxseqno = 0; - ast_set_flag(rtp, FLAG_NAT_ACTIVE); - if (option_debug || rtpdebug) - ast_debug(0, "P2P RTP NAT: Got audio from other end. Now sending to address %s:%d\n", ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port)); - } - - /* Write directly out to other RTP stream if bridged */ - if ((bridged = ast_rtp_get_bridged(rtp))) - bridge_p2p_rtp_write(rtp, bridged, header, res, hdrlen); - - return 1; -} - -/*! \brief Helper function to switch a channel and RTP stream into callback mode */ -static int p2p_callback_enable(struct ast_channel *chan, struct ast_rtp *rtp, int **iod) -{ - /* If we need DTMF, are looking for STUN, or we have no IO structure then we can't do direct callback */ - if (ast_test_flag(rtp, FLAG_P2P_NEED_DTMF) || ast_test_flag(rtp, FLAG_HAS_STUN) || !rtp->io) - return 0; - - /* If the RTP structure is already in callback mode, remove it temporarily */ - if (rtp->ioid) { - ast_io_remove(rtp->io, rtp->ioid); - rtp->ioid = NULL; - } - - /* Steal the file descriptors from the channel */ - chan->fds[0] = -1; - - /* Now, fire up callback mode */ - iod[0] = ast_io_add(rtp->io, ast_rtp_fd(rtp), p2p_rtp_callback, AST_IO_IN, rtp); - - return 1; -} -#else -static int p2p_callback_enable(struct ast_channel *chan, struct ast_rtp *rtp, int **iod) -{ - return 0; -} -#endif - -/*! \brief Helper function to switch a channel and RTP stream out of callback mode */ -static int p2p_callback_disable(struct ast_channel *chan, struct ast_rtp *rtp, int **iod) -{ - ast_channel_lock(chan); - - /* Remove the callback from the IO context */ - ast_io_remove(rtp->io, iod[0]); - - /* Restore file descriptors */ - chan->fds[0] = ast_rtp_fd(rtp); - ast_channel_unlock(chan); - - /* Restore callback mode if previously used */ - if (ast_test_flag(rtp, FLAG_CALLBACK_MODE)) - rtp->ioid = ast_io_add(rtp->io, ast_rtp_fd(rtp), rtpread, AST_IO_IN, rtp); - - return 0; -} - -/*! \brief Helper function that sets what an RTP structure is bridged to */ -static void p2p_set_bridge(struct ast_rtp *rtp0, struct ast_rtp *rtp1) -{ - rtp_bridge_lock(rtp0); - rtp0->bridged = rtp1; - rtp_bridge_unlock(rtp0); -} - -/*! \brief Bridge loop for partial native bridge (packet2packet) - - In p2p mode, Asterisk is a very basic RTP proxy, just forwarding whatever - rtp/rtcp we get in to the channel. - \note this currently only works for Audio -*/ -static enum ast_bridge_result bridge_p2p_loop(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp *p0, struct ast_rtp *p1, int timeoutms, int flags, struct ast_frame **fo, struct ast_channel **rc, void *pvt0, void *pvt1) -{ - struct ast_frame *fr = NULL; - struct ast_channel *who = NULL, *other = NULL, *cs[3] = {NULL, }; - int *p0_iod[2] = {NULL, NULL}, *p1_iod[2] = {NULL, NULL}; - int p0_callback = 0, p1_callback = 0; - enum ast_bridge_result res = AST_BRIDGE_FAILED; - - /* Okay, setup each RTP structure to do P2P forwarding */ - ast_clear_flag(p0, FLAG_P2P_SENT_MARK); - p2p_set_bridge(p0, p1); - ast_clear_flag(p1, FLAG_P2P_SENT_MARK); - p2p_set_bridge(p1, p0); - - /* Activate callback modes if possible */ - p0_callback = p2p_callback_enable(c0, p0, &p0_iod[0]); - p1_callback = p2p_callback_enable(c1, p1, &p1_iod[0]); - - /* Now let go of the channel locks and be on our way */ - ast_channel_unlock(c0); - ast_channel_unlock(c1); - - ast_poll_channel_add(c0, c1); - - /* Go into a loop forwarding frames until we don't need to anymore */ - cs[0] = c0; - cs[1] = c1; - cs[2] = NULL; - for (;;) { - /* If the underlying formats have changed force this bridge to break */ - if ((c0->rawreadformat != c1->rawwriteformat) || (c1->rawreadformat != c0->rawwriteformat)) { - ast_debug(3, "p2p-rtp-bridge: Oooh, formats changed, backing out\n"); - res = AST_BRIDGE_FAILED_NOWARN; - break; - } - /* Check if anything changed */ - if ((c0->tech_pvt != pvt0) || - (c1->tech_pvt != pvt1) || - (c0->masq || c0->masqr || c1->masq || c1->masqr) || - (c0->monitor || c0->audiohooks || c1->monitor || c1->audiohooks)) { - ast_debug(3, "p2p-rtp-bridge: Oooh, something is weird, backing out\n"); - /* If a masquerade needs to happen we have to try to read in a frame so that it actually happens. Without this we risk being called again and going into a loop */ - if ((c0->masq || c0->masqr) && (fr = ast_read(c0))) - ast_frfree(fr); - if ((c1->masq || c1->masqr) && (fr = ast_read(c1))) - ast_frfree(fr); - res = AST_BRIDGE_RETRY; - break; - } - /* Wait on a channel to feed us a frame */ - if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) { - if (!timeoutms) { - res = AST_BRIDGE_RETRY; - break; - } - if (option_debug > 2) - ast_log(LOG_NOTICE, "p2p-rtp-bridge: Ooh, empty read...\n"); - if (ast_check_hangup(c0) || ast_check_hangup(c1)) - break; - continue; - } - /* Read in frame from channel */ - fr = ast_read(who); - other = (who == c0) ? c1 : c0; - /* Depending on the frame we may need to break out of our bridge */ - if (!fr || ((fr->frametype == AST_FRAME_DTMF_BEGIN || fr->frametype == AST_FRAME_DTMF_END) && - ((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) | - ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1)))) { - /* Record received frame and who */ - *fo = fr; - *rc = who; - ast_debug(3, "p2p-rtp-bridge: Ooh, got a %s\n", fr ? "digit" : "hangup"); - res = AST_BRIDGE_COMPLETE; - break; - } else if ((fr->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) { - if ((fr->subclass == AST_CONTROL_HOLD) || - (fr->subclass == AST_CONTROL_UNHOLD) || - (fr->subclass == AST_CONTROL_VIDUPDATE) || - (fr->subclass == AST_CONTROL_T38) || - (fr->subclass == AST_CONTROL_SRCUPDATE)) { - /* If we are going on hold, then break callback mode and P2P bridging */ - if (fr->subclass == AST_CONTROL_HOLD) { - if (p0_callback) - p0_callback = p2p_callback_disable(c0, p0, &p0_iod[0]); - if (p1_callback) - p1_callback = p2p_callback_disable(c1, p1, &p1_iod[0]); - p2p_set_bridge(p0, NULL); - p2p_set_bridge(p1, NULL); - } else if (fr->subclass == AST_CONTROL_UNHOLD) { - /* If we are off hold, then go back to callback mode and P2P bridging */ - ast_clear_flag(p0, FLAG_P2P_SENT_MARK); - p2p_set_bridge(p0, p1); - ast_clear_flag(p1, FLAG_P2P_SENT_MARK); - p2p_set_bridge(p1, p0); - p0_callback = p2p_callback_enable(c0, p0, &p0_iod[0]); - p1_callback = p2p_callback_enable(c1, p1, &p1_iod[0]); - } - ast_indicate_data(other, fr->subclass, fr->data.ptr, fr->datalen); - ast_frfree(fr); - } else { - *fo = fr; - *rc = who; - ast_debug(3, "p2p-rtp-bridge: Got a FRAME_CONTROL (%d) frame on channel %s\n", fr->subclass, who->name); - res = AST_BRIDGE_COMPLETE; - break; - } - } else { - if ((fr->frametype == AST_FRAME_DTMF_BEGIN) || - (fr->frametype == AST_FRAME_DTMF_END) || - (fr->frametype == AST_FRAME_VOICE) || - (fr->frametype == AST_FRAME_VIDEO) || - (fr->frametype == AST_FRAME_IMAGE) || - (fr->frametype == AST_FRAME_HTML) || - (fr->frametype == AST_FRAME_MODEM) || - (fr->frametype == AST_FRAME_TEXT)) { - ast_write(other, fr); - } - - ast_frfree(fr); - } - /* Swap priority */ -#ifndef HAVE_EPOLL - cs[2] = cs[0]; - cs[0] = cs[1]; - cs[1] = cs[2]; -#endif - } - - /* If we are totally avoiding the core, then restore our link to it */ - if (p0_callback) - p0_callback = p2p_callback_disable(c0, p0, &p0_iod[0]); - if (p1_callback) - p1_callback = p2p_callback_disable(c1, p1, &p1_iod[0]); - - /* Break out of the direct bridge */ - p2p_set_bridge(p0, NULL); - p2p_set_bridge(p1, NULL); - - ast_poll_channel_del(c0, c1); - - return res; -} - -/*! \page AstRTPbridge The Asterisk RTP bridge - The RTP bridge is called from the channel drivers that are using the RTP - subsystem in Asterisk - like SIP, H.323 and Jingle/Google Talk. - - This bridge aims to offload the Asterisk server by setting up - the media stream directly between the endpoints, keeping the - signalling in Asterisk. - - It checks with the channel driver, using a callback function, if - there are possibilities for a remote bridge. - - If this fails, the bridge hands off to the core bridge. Reasons - can be NAT support needed, DTMF features in audio needed by - the PBX for transfers or spying/monitoring on channels. - - If transcoding is needed - we can't do a remote bridge. - If only NAT support is needed, we're using Asterisk in - RTP proxy mode with the p2p RTP bridge, basically - forwarding incoming audio packets to the outbound - stream on a network level. - - References: - - ast_rtp_bridge() - - ast_channel_early_bridge() - - ast_channel_bridge() - - rtp.c - - rtp.h -*/ -/*! \brief Bridge calls. If possible and allowed, initiate - re-invite so the peers exchange media directly outside - of Asterisk. -*/ -enum ast_bridge_result ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms) -{ - struct ast_rtp *p0 = NULL, *p1 = NULL; /* Audio RTP Channels */ - struct ast_rtp *vp0 = NULL, *vp1 = NULL; /* Video RTP channels */ - struct ast_rtp *tp0 = NULL, *tp1 = NULL; /* Text RTP channels */ - struct ast_rtp_protocol *pr0 = NULL, *pr1 = NULL; - enum ast_rtp_get_result audio_p0_res = AST_RTP_GET_FAILED, video_p0_res = AST_RTP_GET_FAILED, text_p0_res = AST_RTP_GET_FAILED; - enum ast_rtp_get_result audio_p1_res = AST_RTP_GET_FAILED, video_p1_res = AST_RTP_GET_FAILED, text_p1_res = AST_RTP_GET_FAILED; - enum ast_bridge_result res = AST_BRIDGE_FAILED; - int codec0 = 0, codec1 = 0; - void *pvt0 = NULL, *pvt1 = NULL; - - /* Lock channels */ - ast_channel_lock(c0); - while (ast_channel_trylock(c1)) { - ast_channel_unlock(c0); - usleep(1); - ast_channel_lock(c0); - } - - /* Ensure neither channel got hungup during lock avoidance */ - if (ast_check_hangup(c0) || ast_check_hangup(c1)) { - ast_log(LOG_WARNING, "Got hangup while attempting to bridge '%s' and '%s'\n", c0->name, c1->name); - ast_channel_unlock(c0); - ast_channel_unlock(c1); - return AST_BRIDGE_FAILED; - } - - /* Find channel driver interfaces */ - if (!(pr0 = get_proto(c0))) { - ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c0->name); - ast_channel_unlock(c0); - ast_channel_unlock(c1); - return AST_BRIDGE_FAILED; - } - if (!(pr1 = get_proto(c1))) { - ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c1->name); - ast_channel_unlock(c0); - ast_channel_unlock(c1); - return AST_BRIDGE_FAILED; - } - - /* Get channel specific interface structures */ - pvt0 = c0->tech_pvt; - pvt1 = c1->tech_pvt; - - /* Get audio and video interface (if native bridge is possible) */ - audio_p0_res = pr0->get_rtp_info(c0, &p0); - video_p0_res = pr0->get_vrtp_info ? pr0->get_vrtp_info(c0, &vp0) : AST_RTP_GET_FAILED; - text_p0_res = pr0->get_trtp_info ? pr0->get_trtp_info(c0, &vp0) : AST_RTP_GET_FAILED; - audio_p1_res = pr1->get_rtp_info(c1, &p1); - video_p1_res = pr1->get_vrtp_info ? pr1->get_vrtp_info(c1, &vp1) : AST_RTP_GET_FAILED; - text_p1_res = pr1->get_trtp_info ? pr1->get_trtp_info(c1, &vp1) : AST_RTP_GET_FAILED; - - /* If we are carrying video, and both sides are not reinviting... then fail the native bridge */ - if (video_p0_res != AST_RTP_GET_FAILED && (audio_p0_res != AST_RTP_TRY_NATIVE || video_p0_res != AST_RTP_TRY_NATIVE)) - audio_p0_res = AST_RTP_GET_FAILED; - if (video_p1_res != AST_RTP_GET_FAILED && (audio_p1_res != AST_RTP_TRY_NATIVE || video_p1_res != AST_RTP_TRY_NATIVE)) - audio_p1_res = AST_RTP_GET_FAILED; - - /* Check if a bridge is possible (partial/native) */ - if (audio_p0_res == AST_RTP_GET_FAILED || audio_p1_res == AST_RTP_GET_FAILED) { - /* Somebody doesn't want to play... */ - ast_channel_unlock(c0); - ast_channel_unlock(c1); - return AST_BRIDGE_FAILED_NOWARN; - } - - /* If we need to feed DTMF frames into the core then only do a partial native bridge */ - if (ast_test_flag(p0, FLAG_HAS_DTMF) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) { - ast_set_flag(p0, FLAG_P2P_NEED_DTMF); - audio_p0_res = AST_RTP_TRY_PARTIAL; - } - - if (ast_test_flag(p1, FLAG_HAS_DTMF) && (flags & AST_BRIDGE_DTMF_CHANNEL_1)) { - ast_set_flag(p1, FLAG_P2P_NEED_DTMF); - audio_p1_res = AST_RTP_TRY_PARTIAL; - } - - /* If both sides are not using the same method of DTMF transmission - * (ie: one is RFC2833, other is INFO... then we can not do direct media. - * -------------------------------------------------- - * | DTMF Mode | HAS_DTMF | Accepts Begin Frames | - * |-----------|------------|-----------------------| - * | Inband | False | True | - * | RFC2833 | True | True | - * | SIP INFO | False | False | - * -------------------------------------------------- - * However, if DTMF from both channels is being monitored by the core, then - * we can still do packet-to-packet bridging, because passing through the - * core will handle DTMF mode translation. - */ - if ((ast_test_flag(p0, FLAG_HAS_DTMF) != ast_test_flag(p1, FLAG_HAS_DTMF)) || - (!c0->tech->send_digit_begin != !c1->tech->send_digit_begin)) { - if (!ast_test_flag(p0, FLAG_P2P_NEED_DTMF) || !ast_test_flag(p1, FLAG_P2P_NEED_DTMF)) { - ast_channel_unlock(c0); - ast_channel_unlock(c1); - return AST_BRIDGE_FAILED_NOWARN; - } - audio_p0_res = AST_RTP_TRY_PARTIAL; - audio_p1_res = AST_RTP_TRY_PARTIAL; - } - - /* If we need to feed frames into the core don't do a P2P bridge */ - if ((audio_p0_res == AST_RTP_TRY_PARTIAL && ast_test_flag(p0, FLAG_P2P_NEED_DTMF)) || - (audio_p1_res == AST_RTP_TRY_PARTIAL && ast_test_flag(p1, FLAG_P2P_NEED_DTMF))) { - ast_channel_unlock(c0); - ast_channel_unlock(c1); - return AST_BRIDGE_FAILED_NOWARN; - } - - /* Get codecs from both sides */ - codec0 = pr0->get_codec ? pr0->get_codec(c0) : 0; - codec1 = pr1->get_codec ? pr1->get_codec(c1) : 0; - if (codec0 && codec1 && !(codec0 & codec1)) { - /* Hey, we can't do native bridging if both parties speak different codecs */ - ast_debug(3, "Channel codec0 = %d is not codec1 = %d, cannot native bridge in RTP.\n", codec0, codec1); - ast_channel_unlock(c0); - ast_channel_unlock(c1); - return AST_BRIDGE_FAILED_NOWARN; - } - - /* If either side can only do a partial bridge, then don't try for a true native bridge */ - if (audio_p0_res == AST_RTP_TRY_PARTIAL || audio_p1_res == AST_RTP_TRY_PARTIAL) { - struct ast_format_list fmt0, fmt1; - - /* In order to do Packet2Packet bridging both sides must be in the same rawread/rawwrite */ - if (c0->rawreadformat != c1->rawwriteformat || c1->rawreadformat != c0->rawwriteformat) { - ast_debug(1, "Cannot packet2packet bridge - raw formats are incompatible\n"); - ast_channel_unlock(c0); - ast_channel_unlock(c1); - return AST_BRIDGE_FAILED_NOWARN; - } - /* They must also be using the same packetization */ - fmt0 = ast_codec_pref_getsize(&p0->pref, c0->rawreadformat); - fmt1 = ast_codec_pref_getsize(&p1->pref, c1->rawreadformat); - if (fmt0.cur_ms != fmt1.cur_ms) { - ast_debug(1, "Cannot packet2packet bridge - packetization settings prevent it\n"); - ast_channel_unlock(c0); - ast_channel_unlock(c1); - return AST_BRIDGE_FAILED_NOWARN; - } - - ast_verb(3, "Packet2Packet bridging %s and %s\n", c0->name, c1->name); - res = bridge_p2p_loop(c0, c1, p0, p1, timeoutms, flags, fo, rc, pvt0, pvt1); - } else { - ast_verb(3, "Native bridging %s and %s\n", c0->name, c1->name); - res = bridge_native_loop(c0, c1, p0, p1, vp0, vp1, tp0, tp1, pr0, pr1, codec0, codec1, timeoutms, flags, fo, rc, pvt0, pvt1); - } - - return res; -} - -static char *rtp_do_debug_ip(struct ast_cli_args *a) -{ - struct hostent *hp; - struct ast_hostent ahp; - int port = 0; - char *p, *arg; - - arg = a->argv[3]; - p = strstr(arg, ":"); - if (p) { - *p = '\0'; - p++; - port = atoi(p); - } - hp = ast_gethostbyname(arg, &ahp); - if (hp == NULL) { - ast_cli(a->fd, "Lookup failed for '%s'\n", arg); - return CLI_FAILURE; - } - rtpdebugaddr.sin_family = AF_INET; - memcpy(&rtpdebugaddr.sin_addr, hp->h_addr, sizeof(rtpdebugaddr.sin_addr)); - rtpdebugaddr.sin_port = htons(port); - if (port == 0) - ast_cli(a->fd, "RTP Debugging Enabled for IP: %s\n", ast_inet_ntoa(rtpdebugaddr.sin_addr)); - else - ast_cli(a->fd, "RTP Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(rtpdebugaddr.sin_addr), port); - rtpdebug = 1; - return CLI_SUCCESS; -} - -static char *rtcp_do_debug_ip(struct ast_cli_args *a) -{ - struct hostent *hp; - struct ast_hostent ahp; - int port = 0; - char *p, *arg; - - arg = a->argv[3]; - p = strstr(arg, ":"); - if (p) { - *p = '\0'; - p++; - port = atoi(p); - } - hp = ast_gethostbyname(arg, &ahp); - if (hp == NULL) { - ast_cli(a->fd, "Lookup failed for '%s'\n", arg); - return CLI_FAILURE; - } - rtcpdebugaddr.sin_family = AF_INET; - memcpy(&rtcpdebugaddr.sin_addr, hp->h_addr, sizeof(rtcpdebugaddr.sin_addr)); - rtcpdebugaddr.sin_port = htons(port); - if (port == 0) - ast_cli(a->fd, "RTCP Debugging Enabled for IP: %s\n", ast_inet_ntoa(rtcpdebugaddr.sin_addr)); - else - ast_cli(a->fd, "RTCP Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(rtcpdebugaddr.sin_addr), port); - rtcpdebug = 1; - return CLI_SUCCESS; -} - -static char *handle_cli_rtp_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) -{ - switch (cmd) { - case CLI_INIT: - e->command = "rtp set debug {on|off|ip}"; - e->usage = - "Usage: rtp set debug {on|off|ip host[:port]}\n" - " Enable/Disable dumping of all RTP packets. If 'ip' is\n" - " specified, limit the dumped packets to those to and from\n" - " the specified 'host' with optional port.\n"; - return NULL; - case CLI_GENERATE: - return NULL; - } - - if (a->argc == e->args) { /* set on or off */ - if (!strncasecmp(a->argv[e->args-1], "on", 2)) { - rtpdebug = 1; - memset(&rtpdebugaddr, 0, sizeof(rtpdebugaddr)); - ast_cli(a->fd, "RTP Debugging Enabled\n"); - return CLI_SUCCESS; - } else if (!strncasecmp(a->argv[e->args-1], "off", 3)) { - rtpdebug = 0; - ast_cli(a->fd, "RTP Debugging Disabled\n"); - return CLI_SUCCESS; - } - } else if (a->argc == e->args +1) { /* ip */ - return rtp_do_debug_ip(a); - } - - return CLI_SHOWUSAGE; /* default, failure */ -} - -static char *handle_cli_rtcp_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) -{ - switch (cmd) { - case CLI_INIT: - e->command = "rtcp set debug {on|off|ip}"; - e->usage = - "Usage: rtcp set debug {on|off|ip host[:port]}\n" - " Enable/Disable dumping of all RTCP packets. If 'ip' is\n" - " specified, limit the dumped packets to those to and from\n" - " the specified 'host' with optional port.\n"; - return NULL; - case CLI_GENERATE: - return NULL; - } - - if (a->argc == e->args) { /* set on or off */ - if (!strncasecmp(a->argv[e->args-1], "on", 2)) { - rtcpdebug = 1; - memset(&rtcpdebugaddr, 0, sizeof(rtcpdebugaddr)); - ast_cli(a->fd, "RTCP Debugging Enabled\n"); - return CLI_SUCCESS; - } else if (!strncasecmp(a->argv[e->args-1], "off", 3)) { - rtcpdebug = 0; - ast_cli(a->fd, "RTCP Debugging Disabled\n"); - return CLI_SUCCESS; - } - } else if (a->argc == e->args +1) { /* ip */ - return rtcp_do_debug_ip(a); - } - - return CLI_SHOWUSAGE; /* default, failure */ -} - -static char *handle_cli_rtcp_set_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) -{ - switch (cmd) { - case CLI_INIT: - e->command = "rtcp set stats {on|off}"; - e->usage = - "Usage: rtcp set stats {on|off}\n" - " Enable/Disable dumping of RTCP stats.\n"; - return NULL; - case CLI_GENERATE: - return NULL; - } - - if (a->argc != e->args) - return CLI_SHOWUSAGE; - - if (!strncasecmp(a->argv[e->args-1], "on", 2)) - rtcpstats = 1; - else if (!strncasecmp(a->argv[e->args-1], "off", 3)) - rtcpstats = 0; - else - return CLI_SHOWUSAGE; - - ast_cli(a->fd, "RTCP Stats %s\n", rtcpstats ? "Enabled" : "Disabled"); - return CLI_SUCCESS; -} - -static char *handle_cli_stun_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) -{ - switch (cmd) { - case CLI_INIT: - e->command = "stun set debug {on|off}"; - e->usage = - "Usage: stun set debug {on|off}\n" - " Enable/Disable STUN (Simple Traversal of UDP through NATs)\n" - " debugging\n"; - return NULL; - case CLI_GENERATE: - return NULL; - } - - if (a->argc != e->args) - return CLI_SHOWUSAGE; - - if (!strncasecmp(a->argv[e->args-1], "on", 2)) - stundebug = 1; - else if (!strncasecmp(a->argv[e->args-1], "off", 3)) - stundebug = 0; - else - return CLI_SHOWUSAGE; - - ast_cli(a->fd, "STUN Debugging %s\n", stundebug ? "Enabled" : "Disabled"); - return CLI_SUCCESS; -} - -static struct ast_cli_entry cli_rtp[] = { - AST_CLI_DEFINE(handle_cli_rtp_set_debug, "Enable/Disable RTP debugging"), - AST_CLI_DEFINE(handle_cli_rtcp_set_debug, "Enable/Disable RTCP debugging"), - AST_CLI_DEFINE(handle_cli_rtcp_set_stats, "Enable/Disable RTCP stats"), - AST_CLI_DEFINE(handle_cli_stun_set_debug, "Enable/Disable STUN debugging"), -}; - -static int __ast_rtp_reload(int reload) -{ - struct ast_config *cfg; - const char *s; - struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; - - cfg = ast_config_load2("rtp.conf", "rtp", config_flags); - if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) { - return 0; - } - - rtpstart = 5000; - rtpend = 31000; - dtmftimeout = DEFAULT_DTMF_TIMEOUT; - strictrtp = STRICT_RTP_OPEN; - if (cfg) { - if ((s = ast_variable_retrieve(cfg, "general", "rtpstart"))) { - rtpstart = atoi(s); - if (rtpstart < 1024) - rtpstart = 1024; - if (rtpstart > 65535) - rtpstart = 65535; - } - if ((s = ast_variable_retrieve(cfg, "general", "rtpend"))) { - rtpend = atoi(s); - if (rtpend < 1024) - rtpend = 1024; - if (rtpend > 65535) - rtpend = 65535; - } - if ((s = ast_variable_retrieve(cfg, "general", "rtcpinterval"))) { - rtcpinterval = atoi(s); - if (rtcpinterval == 0) - rtcpinterval = 0; /* Just so we're clear... it's zero */ - if (rtcpinterval < RTCP_MIN_INTERVALMS) - rtcpinterval = RTCP_MIN_INTERVALMS; /* This catches negative numbers too */ - if (rtcpinterval > RTCP_MAX_INTERVALMS) - rtcpinterval = RTCP_MAX_INTERVALMS; - } - if ((s = ast_variable_retrieve(cfg, "general", "rtpchecksums"))) { -#ifdef SO_NO_CHECK - if (ast_false(s)) - nochecksums = 1; - else - nochecksums = 0; -#else - if (ast_false(s)) - ast_log(LOG_WARNING, "Disabling RTP checksums is not supported on this operating system!\n"); -#endif - } - if ((s = ast_variable_retrieve(cfg, "general", "dtmftimeout"))) { - dtmftimeout = atoi(s); - if ((dtmftimeout < 0) || (dtmftimeout > 20000)) { - ast_log(LOG_WARNING, "DTMF timeout of '%d' outside range, using default of '%d' instead\n", - dtmftimeout, DEFAULT_DTMF_TIMEOUT); - dtmftimeout = DEFAULT_DTMF_TIMEOUT; - }; - } - if ((s = ast_variable_retrieve(cfg, "general", "strictrtp"))) { - strictrtp = ast_true(s); - } - ast_config_destroy(cfg); - } - if (rtpstart >= rtpend) { - ast_log(LOG_WARNING, "Unreasonable values for RTP start/end port in rtp.conf\n"); - rtpstart = 5000; - rtpend = 31000; - } - ast_verb(2, "RTP Allocating from port range %d -> %d\n", rtpstart, rtpend); - return 0; -} - -int ast_rtp_reload(void) -{ - return __ast_rtp_reload(1); -} - -/*! \brief Initialize the RTP system in Asterisk */ -void ast_rtp_init(void) -{ - ast_cli_register_multiple(cli_rtp, sizeof(cli_rtp) / sizeof(struct ast_cli_entry)); - __ast_rtp_reload(0); -} - -/*! \brief Write t140 redundacy frame - * \param data primary data to be buffered - */ -static int red_write(const void *data) -{ - struct ast_rtp *rtp = (struct ast_rtp*) data; - - ast_rtp_write(rtp, &rtp->red->t140); - - return 1; -} - -/*! \brief Construct a redundant frame - * \param red redundant data structure - */ -static struct ast_frame *red_t140_to_red(struct rtp_red *red) { - unsigned char *data = red->t140red.data.ptr; - int len = 0; - int i; - - /* replace most aged generation */ - if (red->len[0]) { - for (i = 1; i < red->num_gen+1; i++) - len += red->len[i]; - - memmove(&data[red->hdrlen], &data[red->hdrlen+red->len[0]], len); - } - - /* Store length of each generation and primary data length*/ - for (i = 0; i < red->num_gen; i++) - red->len[i] = red->len[i+1]; - red->len[i] = red->t140.datalen; - - /* write each generation length in red header */ - len = red->hdrlen; - for (i = 0; i < red->num_gen; i++) - len += data[i*4+3] = red->len[i]; - - /* add primary data to buffer */ - memcpy(&data[len], red->t140.data.ptr, red->t140.datalen); - red->t140red.datalen = len + red->t140.datalen; - - /* no primary data and no generations to send */ - if (len == red->hdrlen && !red->t140.datalen) - return NULL; - - /* reset t.140 buffer */ - red->t140.datalen = 0; - - return &red->t140red; -} - -/*! \brief Initialize t140 redundancy - * \param rtp - * \param ti buffer t140 for ti (msecs) before sending redundant frame - * \param red_data_pt Payloadtypes for primary- and generation-data - * \param num_gen numbers of generations (primary generation not encounted) - * -*/ -int ast_rtp_red_init(struct ast_rtp *rtp, int ti, int *red_data_pt, int num_gen) -{ - struct rtp_red *r; - int x; - - if (!(r = ast_calloc(1, sizeof(struct rtp_red)))) - return -1; - - r->t140.frametype = AST_FRAME_TEXT; - r->t140.subclass = AST_FORMAT_T140RED; - r->t140.data.ptr = &r->buf_data; - - r->t140.ts = 0; - r->t140red = r->t140; - r->t140red.data.ptr = &r->t140red_data; - r->t140red.datalen = 0; - r->ti = ti; - r->num_gen = num_gen; - r->hdrlen = num_gen * 4 + 1; - r->prev_ts = 0; - - for (x = 0; x < num_gen; x++) { - r->pt[x] = red_data_pt[x]; - r->pt[x] |= 1 << 7; /* mark redundant generations pt */ - r->t140red_data[x*4] = r->pt[x]; - } - r->t140red_data[x*4] = r->pt[x] = red_data_pt[x]; /* primary pt */ - r->schedid = ast_sched_add(rtp->sched, ti, red_write, rtp); - rtp->red = r; - - r->t140.datalen = 0; - - return 0; -} - -/*! \brief Buffer t140 from chan_sip - * \param rtp - * \param f frame - */ -void ast_red_buffer_t140(struct ast_rtp *rtp, struct ast_frame *f) -{ - if (f->datalen > -1) { - struct rtp_red *red = rtp->red; - memcpy(&red->buf_data[red->t140.datalen], f->data.ptr, f->datalen); - red->t140.datalen += f->datalen; - red->t140.ts = f->ts; - } -} diff --git a/main/rtp_engine.c b/main/rtp_engine.c new file mode 100644 index 0000000000..fd448b849b --- /dev/null +++ b/main/rtp_engine.c @@ -0,0 +1,1572 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2008, Digium, Inc. + * + * Joshua Colp <jcolp@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Pluggable RTP Architecture + * + * \author Joshua Colp <jcolp@digium.com> + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include <math.h> + +#include "asterisk/channel.h" +#include "asterisk/frame.h" +#include "asterisk/module.h" +#include "asterisk/rtp_engine.h" +#include "asterisk/manager.h" +#include "asterisk/options.h" +#include "asterisk/astobj2.h" +#include "asterisk/pbx.h" + +/*! Structure that represents an RTP session (instance) */ +struct ast_rtp_instance { + /*! Engine that is handling this RTP instance */ + struct ast_rtp_engine *engine; + /*! Data unique to the RTP engine */ + void *data; + /*! RTP properties that have been set and their value */ + int properties[AST_RTP_PROPERTY_MAX]; + /*! Address that we are expecting RTP to come in to */ + struct sockaddr_in local_address; + /*! Address that we are sending RTP to */ + struct sockaddr_in remote_address; + /*! Instance that we are bridged to if doing remote or local bridging */ + struct ast_rtp_instance *bridged; + /*! Payload and packetization information */ + struct ast_rtp_codecs codecs; + /*! RTP timeout time (negative or zero means disabled, negative value means temporarily disabled) */ + int timeout; + /*! RTP timeout when on hold (negative or zero means disabled, negative value means temporarily disabled). */ + int holdtimeout; + /*! DTMF mode in use */ + enum ast_rtp_dtmf_mode dtmf_mode; +}; + +/*! List of RTP engines that are currently registered */ +static AST_RWLIST_HEAD_STATIC(engines, ast_rtp_engine); + +/*! List of RTP glues */ +static AST_RWLIST_HEAD_STATIC(glues, ast_rtp_glue); + +/*! The following array defines the MIME Media type (and subtype) for each + of our codecs, or RTP-specific data type. */ +static const struct ast_rtp_mime_type { + struct ast_rtp_payload_type payload_type; + char *type; + char *subtype; + unsigned int sample_rate; +} ast_rtp_mime_types[] = { + {{1, AST_FORMAT_G723_1}, "audio", "G723", 8000}, + {{1, AST_FORMAT_GSM}, "audio", "GSM", 8000}, + {{1, AST_FORMAT_ULAW}, "audio", "PCMU", 8000}, + {{1, AST_FORMAT_ULAW}, "audio", "G711U", 8000}, + {{1, AST_FORMAT_ALAW}, "audio", "PCMA", 8000}, + {{1, AST_FORMAT_ALAW}, "audio", "G711A", 8000}, + {{1, AST_FORMAT_G726}, "audio", "G726-32", 8000}, + {{1, AST_FORMAT_ADPCM}, "audio", "DVI4", 8000}, + {{1, AST_FORMAT_SLINEAR}, "audio", "L16", 8000}, + {{1, AST_FORMAT_LPC10}, "audio", "LPC", 8000}, + {{1, AST_FORMAT_G729A}, "audio", "G729", 8000}, + {{1, AST_FORMAT_G729A}, "audio", "G729A", 8000}, + {{1, AST_FORMAT_G729A}, "audio", "G.729", 8000}, + {{1, AST_FORMAT_SPEEX}, "audio", "speex", 8000}, + {{1, AST_FORMAT_ILBC}, "audio", "iLBC", 8000}, + /* this is the sample rate listed in the RTP profile for the G.722 + codec, *NOT* the actual sample rate of the media stream + */ + {{1, AST_FORMAT_G722}, "audio", "G722", 8000}, + {{1, AST_FORMAT_G726_AAL2}, "audio", "AAL2-G726-32", 8000}, + {{0, AST_RTP_DTMF}, "audio", "telephone-event", 8000}, + {{0, AST_RTP_CISCO_DTMF}, "audio", "cisco-telephone-event", 8000}, + {{0, AST_RTP_CN}, "audio", "CN", 8000}, + {{1, AST_FORMAT_JPEG}, "video", "JPEG", 90000}, + {{1, AST_FORMAT_PNG}, "video", "PNG", 90000}, + {{1, AST_FORMAT_H261}, "video", "H261", 90000}, + {{1, AST_FORMAT_H263}, "video", "H263", 90000}, + {{1, AST_FORMAT_H263_PLUS}, "video", "h263-1998", 90000}, + {{1, AST_FORMAT_H264}, "video", "H264", 90000}, + {{1, AST_FORMAT_MP4_VIDEO}, "video", "MP4V-ES", 90000}, + {{1, AST_FORMAT_T140RED}, "text", "RED", 1000}, + {{1, AST_FORMAT_T140}, "text", "T140", 1000}, + {{1, AST_FORMAT_SIREN7}, "audio", "G7221", 16000}, + {{1, AST_FORMAT_SIREN14}, "audio", "G7221", 32000}, +}; + +/*! + * \brief Mapping between Asterisk codecs and rtp payload types + * + * Static (i.e., well-known) RTP payload types for our "AST_FORMAT..."s: + * also, our own choices for dynamic payload types. This is our master + * table for transmission + * + * See http://www.iana.org/assignments/rtp-parameters for a list of + * assigned values + */ +static const struct ast_rtp_payload_type static_RTP_PT[AST_RTP_MAX_PT] = { + [0] = {1, AST_FORMAT_ULAW}, + #ifdef USE_DEPRECATED_G726 + [2] = {1, AST_FORMAT_G726}, /* Technically this is G.721, but if Cisco can do it, so can we... */ + #endif + [3] = {1, AST_FORMAT_GSM}, + [4] = {1, AST_FORMAT_G723_1}, + [5] = {1, AST_FORMAT_ADPCM}, /* 8 kHz */ + [6] = {1, AST_FORMAT_ADPCM}, /* 16 kHz */ + [7] = {1, AST_FORMAT_LPC10}, + [8] = {1, AST_FORMAT_ALAW}, + [9] = {1, AST_FORMAT_G722}, + [10] = {1, AST_FORMAT_SLINEAR}, /* 2 channels */ + [11] = {1, AST_FORMAT_SLINEAR}, /* 1 channel */ + [13] = {0, AST_RTP_CN}, + [16] = {1, AST_FORMAT_ADPCM}, /* 11.025 kHz */ + [17] = {1, AST_FORMAT_ADPCM}, /* 22.050 kHz */ + [18] = {1, AST_FORMAT_G729A}, + [19] = {0, AST_RTP_CN}, /* Also used for CN */ + [26] = {1, AST_FORMAT_JPEG}, + [31] = {1, AST_FORMAT_H261}, + [34] = {1, AST_FORMAT_H263}, + [97] = {1, AST_FORMAT_ILBC}, + [98] = {1, AST_FORMAT_H263_PLUS}, + [99] = {1, AST_FORMAT_H264}, + [101] = {0, AST_RTP_DTMF}, + [102] = {1, AST_FORMAT_SIREN7}, + [103] = {1, AST_FORMAT_H263_PLUS}, + [104] = {1, AST_FORMAT_MP4_VIDEO}, + [105] = {1, AST_FORMAT_T140RED}, /* Real time text chat (with redundancy encoding) */ + [106] = {1, AST_FORMAT_T140}, /* Real time text chat */ + [110] = {1, AST_FORMAT_SPEEX}, + [111] = {1, AST_FORMAT_G726}, + [112] = {1, AST_FORMAT_G726_AAL2}, + [115] = {1, AST_FORMAT_SIREN14}, + [121] = {0, AST_RTP_CISCO_DTMF}, /* Must be type 121 */ +}; + +int ast_rtp_engine_register2(struct ast_rtp_engine *engine, struct ast_module *module) +{ + struct ast_rtp_engine *current_engine; + + /* Perform a sanity check on the engine structure to make sure it has the basics */ + if (ast_strlen_zero(engine->name) || !engine->new || !engine->destroy || !engine->write || !engine->read) { + ast_log(LOG_WARNING, "RTP Engine '%s' failed sanity check so it was not registered.\n", !ast_strlen_zero(engine->name) ? engine->name : "Unknown"); + return -1; + } + + /* Link owner module to the RTP engine for reference counting purposes */ + engine->mod = module; + + AST_RWLIST_WRLOCK(&engines); + + /* Ensure that no two modules with the same name are registered at the same time */ + AST_RWLIST_TRAVERSE(&engines, current_engine, entry) { + if (!strcmp(current_engine->name, engine->name)) { + ast_log(LOG_WARNING, "An RTP engine with the name '%s' has already been registered.\n", engine->name); + AST_RWLIST_UNLOCK(&engines); + return -1; + } + } + + /* The engine survived our critique. Off to the list it goes to be used */ + AST_RWLIST_INSERT_TAIL(&engines, engine, entry); + + AST_RWLIST_UNLOCK(&engines); + + ast_verb(2, "Registered RTP engine '%s'\n", engine->name); + + return 0; +} + +int ast_rtp_engine_unregister(struct ast_rtp_engine *engine) +{ + struct ast_rtp_engine *current_engine = NULL; + + AST_RWLIST_WRLOCK(&engines); + + if ((current_engine = AST_RWLIST_REMOVE(&engines, engine, entry))) { + ast_verb(2, "Unregistered RTP engine '%s'\n", engine->name); + } + + AST_RWLIST_UNLOCK(&engines); + + return current_engine ? 0 : -1; +} + +int ast_rtp_glue_register2(struct ast_rtp_glue *glue, struct ast_module *module) +{ + struct ast_rtp_glue *current_glue = NULL; + + if (ast_strlen_zero(glue->type)) { + return -1; + } + + glue->mod = module; + + AST_RWLIST_WRLOCK(&glues); + + AST_RWLIST_TRAVERSE(&glues, current_glue, entry) { + if (!strcasecmp(current_glue->type, glue->type)) { + ast_log(LOG_WARNING, "RTP glue with the name '%s' has already been registered.\n", glue->type); + AST_RWLIST_UNLOCK(&glues); + return -1; + } + } + + AST_RWLIST_INSERT_TAIL(&glues, glue, entry); + + AST_RWLIST_UNLOCK(&glues); + + ast_verb(2, "Registered RTP glue '%s'\n", glue->type); + + return 0; +} + +int ast_rtp_glue_unregister(struct ast_rtp_glue *glue) +{ + struct ast_rtp_glue *current_glue = NULL; + + AST_RWLIST_WRLOCK(&glues); + + if ((current_glue = AST_RWLIST_REMOVE(&glues, glue, entry))) { + ast_verb(2, "Unregistered RTP glue '%s'\n", glue->type); + } + + AST_RWLIST_UNLOCK(&glues); + + return current_glue ? 0 : -1; +} + +static void instance_destructor(void *obj) +{ + struct ast_rtp_instance *instance = obj; + + /* Pass us off to the engine to destroy */ + if (instance->data && instance->engine->destroy(instance)) { + ast_debug(1, "Engine '%s' failed to destroy RTP instance '%p'\n", instance->engine->name, instance); + return; + } + + /* Drop our engine reference */ + ast_module_unref(instance->engine->mod); + + ast_debug(1, "Destroyed RTP instance '%p'\n", instance); +} + +int ast_rtp_instance_destroy(struct ast_rtp_instance *instance) +{ + ao2_ref(instance, -1); + + return 0; +} + +struct ast_rtp_instance *ast_rtp_instance_new(const char *engine_name, struct sched_context *sched, struct sockaddr_in *sin, void *data) +{ + struct ast_rtp_instance *instance = NULL; + struct ast_rtp_engine *engine = NULL; + + AST_RWLIST_RDLOCK(&engines); + + /* If an engine name was specified try to use it or otherwise use the first one registered */ + if (!ast_strlen_zero(engine_name)) { + AST_RWLIST_TRAVERSE(&engines, engine, entry) { + if (!strcmp(engine->name, engine_name)) { + break; + } + } + } else { + engine = AST_RWLIST_FIRST(&engines); + } + + /* If no engine was actually found bail out now */ + if (!engine) { + ast_log(LOG_ERROR, "No RTP engine was found. Do you have one loaded?\n"); + AST_RWLIST_UNLOCK(&engines); + return NULL; + } + + /* Bump up the reference count before we return so the module can not be unloaded */ + ast_module_ref(engine->mod); + + AST_RWLIST_UNLOCK(&engines); + + /* Allocate a new RTP instance */ + if (!(instance = ao2_alloc(sizeof(*instance), instance_destructor))) { + ast_module_unref(engine->mod); + return NULL; + } + instance->engine = engine; + memcpy(&instance->local_address, sin, sizeof(instance->local_address)); + + ast_debug(1, "Using engine '%s' for RTP instance '%p'\n", engine->name, instance); + + /* And pass it off to the engine to setup */ + if (instance->engine->new(instance, sched, sin, data)) { + ast_debug(1, "Engine '%s' failed to setup RTP instance '%p'\n", engine->name, instance); + ao2_ref(instance, -1); + return NULL; + } + + ast_debug(1, "RTP instance '%p' is setup and ready to go\n", instance); + + return instance; +} + +void ast_rtp_instance_set_data(struct ast_rtp_instance *instance, void *data) +{ + instance->data = data; +} + +void *ast_rtp_instance_get_data(struct ast_rtp_instance *instance) +{ + return instance->data; +} + +int ast_rtp_instance_write(struct ast_rtp_instance *instance, struct ast_frame *frame) +{ + return instance->engine->write(instance, frame); +} + +struct ast_frame *ast_rtp_instance_read(struct ast_rtp_instance *instance, int rtcp) +{ + return instance->engine->read(instance, rtcp); +} + +int ast_rtp_instance_set_local_address(struct ast_rtp_instance *instance, struct sockaddr_in *address) +{ + memcpy(&instance->local_address, address, sizeof(instance->local_address)); + return 0; +} + +int ast_rtp_instance_set_remote_address(struct ast_rtp_instance *instance, struct sockaddr_in *address) +{ + if (&instance->remote_address != address) { + memcpy(&instance->remote_address, address, sizeof(instance->remote_address)); + } + + /* moo */ + + if (instance->engine->remote_address_set) { + instance->engine->remote_address_set(instance, address); + } + + return 0; +} + +int ast_rtp_instance_get_local_address(struct ast_rtp_instance *instance, struct sockaddr_in *address) +{ + if ((address->sin_family != AF_INET) || + (address->sin_port != instance->local_address.sin_port) || + (address->sin_addr.s_addr != instance->local_address.sin_addr.s_addr)) { + memcpy(address, &instance->local_address, sizeof(address)); + return 1; + } + + return 0; +} + +int ast_rtp_instance_get_remote_address(struct ast_rtp_instance *instance, struct sockaddr_in *address) +{ + if ((address->sin_family != AF_INET) || + (address->sin_port != instance->remote_address.sin_port) || + (address->sin_addr.s_addr != instance->remote_address.sin_addr.s_addr)) { + memcpy(address, &instance->remote_address, sizeof(address)); + return 1; + } + + return 0; +} + +void ast_rtp_instance_set_extended_prop(struct ast_rtp_instance *instance, int property, void *value) +{ + if (instance->engine->extended_prop_set) { + instance->engine->extended_prop_set(instance, property, value); + } +} + +void *ast_rtp_instance_get_extended_prop(struct ast_rtp_instance *instance, int property) +{ + if (instance->engine->extended_prop_get) { + return instance->engine->extended_prop_get(instance, property); + } + + return NULL; +} + +void ast_rtp_instance_set_prop(struct ast_rtp_instance *instance, enum ast_rtp_property property, int value) +{ + instance->properties[property] = value; + + if (instance->engine->prop_set) { + instance->engine->prop_set(instance, property, value); + } +} + +int ast_rtp_instance_get_prop(struct ast_rtp_instance *instance, enum ast_rtp_property property) +{ + return instance->properties[property]; +} + +struct ast_rtp_codecs *ast_rtp_instance_get_codecs(struct ast_rtp_instance *instance) +{ + return &instance->codecs; +} + +void ast_rtp_codecs_payloads_clear(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance) +{ + int i; + + for (i = 0; i < AST_RTP_MAX_PT; i++) { + ast_debug(2, "Clearing payload %d on %p\n", i, codecs); + codecs->payloads[i].asterisk_format = 0; + codecs->payloads[i].code = 0; + if (instance && instance->engine && instance->engine->payload_set) { + instance->engine->payload_set(instance, i, 0, 0); + } + } +} + +void ast_rtp_codecs_payloads_default(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance) +{ + int i; + + for (i = 0; i < AST_RTP_MAX_PT; i++) { + if (static_RTP_PT[i].code) { + ast_debug(2, "Set default payload %d on %p\n", i, codecs); + codecs->payloads[i].asterisk_format = static_RTP_PT[i].asterisk_format; + codecs->payloads[i].code = static_RTP_PT[i].code; + if (instance && instance->engine && instance->engine->payload_set) { + instance->engine->payload_set(instance, i, codecs->payloads[i].asterisk_format, codecs->payloads[i].code); + } + } + } +} + +void ast_rtp_codecs_payloads_copy(struct ast_rtp_codecs *src, struct ast_rtp_codecs *dest, struct ast_rtp_instance *instance) +{ + int i; + + for (i = 0; i < AST_RTP_MAX_PT; i++) { + if (src->payloads[i].code) { + ast_debug(2, "Copying payload %d from %p to %p\n", i, src, dest); + dest->payloads[i].asterisk_format = src->payloads[i].asterisk_format; + dest->payloads[i].code = src->payloads[i].code; + if (instance && instance->engine && instance->engine->payload_set) { + instance->engine->payload_set(instance, i, dest->payloads[i].asterisk_format, dest->payloads[i].code); + } + } + } +} + +void ast_rtp_codecs_payloads_set_m_type(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload) +{ + if (payload < 0 || payload > AST_RTP_MAX_PT || !static_RTP_PT[payload].code) { + return; + } + + codecs->payloads[payload].asterisk_format = static_RTP_PT[payload].asterisk_format; + codecs->payloads[payload].code = static_RTP_PT[payload].code; + + ast_debug(1, "Setting payload %d based on m type on %p\n", payload, codecs); + + if (instance && instance->engine && instance->engine->payload_set) { + instance->engine->payload_set(instance, payload, codecs->payloads[payload].asterisk_format, codecs->payloads[payload].code); + } +} + +int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int pt, + char *mimetype, char *mimesubtype, + enum ast_rtp_options options, + unsigned int sample_rate) +{ + unsigned int i; + int found = 0; + + if (pt < 0 || pt > AST_RTP_MAX_PT) + return -1; /* bogus payload type */ + + for (i = 0; i < ARRAY_LEN(ast_rtp_mime_types); ++i) { + const struct ast_rtp_mime_type *t = &ast_rtp_mime_types[i]; + + if (strcasecmp(mimesubtype, t->subtype)) { + continue; + } + + if (strcasecmp(mimetype, t->type)) { + continue; + } + + /* if both sample rates have been supplied, and they don't match, + then this not a match; if one has not been supplied, then the + rates are not compared */ + if (sample_rate && t->sample_rate && + (sample_rate != t->sample_rate)) { + continue; + } + + found = 1; + codecs->payloads[pt] = t->payload_type; + + if ((t->payload_type.code == AST_FORMAT_G726) && + t->payload_type.asterisk_format && + (options & AST_RTP_OPT_G726_NONSTANDARD)) { + codecs->payloads[pt].code = AST_FORMAT_G726_AAL2; + } + + if (instance && instance->engine && instance->engine->payload_set) { + instance->engine->payload_set(instance, pt, codecs->payloads[i].asterisk_format, codecs->payloads[i].code); + } + + break; + } + + return (found ? 0 : -2); +} + +int ast_rtp_codecs_payloads_set_rtpmap_type(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload, char *mimetype, char *mimesubtype, enum ast_rtp_options options) +{ + return ast_rtp_codecs_payloads_set_rtpmap_type_rate(codecs, instance, payload, mimetype, mimesubtype, options, 0); +} + +void ast_rtp_codecs_payloads_unset(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload) +{ + if (payload < 0 || payload > AST_RTP_MAX_PT) { + return; + } + + ast_debug(2, "Unsetting payload %d on %p\n", payload, codecs); + + codecs->payloads[payload].asterisk_format = 0; + codecs->payloads[payload].code = 0; + + if (instance && instance->engine && instance->engine->payload_set) { + instance->engine->payload_set(instance, payload, 0, 0); + } +} + +struct ast_rtp_payload_type ast_rtp_codecs_payload_lookup(struct ast_rtp_codecs *codecs, int payload) +{ + struct ast_rtp_payload_type result = { .asterisk_format = 0, }; + + if (payload < 0 || payload > AST_RTP_MAX_PT) { + return result; + } + + result.asterisk_format = codecs->payloads[payload].asterisk_format; + result.code = codecs->payloads[payload].code; + + if (!result.code) { + result = static_RTP_PT[payload]; + } + + return result; +} + +void ast_rtp_codecs_payload_formats(struct ast_rtp_codecs *codecs, int *astformats, int *nonastformats) +{ + int i; + + *astformats = *nonastformats = 0; + + for (i = 0; i < AST_RTP_MAX_PT; i++) { + if (codecs->payloads[i].code) { + ast_debug(1, "Incorporating payload %d on %p\n", i, codecs); + } + if (codecs->payloads[i].asterisk_format) { + *astformats |= codecs->payloads[i].code; + } else { + *nonastformats |= codecs->payloads[i].code; + } + } +} + +int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, const int asterisk_format, const int code) +{ + int i; + + for (i = 0; i < AST_RTP_MAX_PT; i++) { + if (codecs->payloads[i].asterisk_format == asterisk_format && codecs->payloads[i].code == code) { + ast_debug(2, "Found code %d at payload %d on %p\n", code, i, codecs); + return i; + } + } + + for (i = 0; i < AST_RTP_MAX_PT; i++) { + if (static_RTP_PT[i].asterisk_format == asterisk_format && static_RTP_PT[i].code == code) { + return i; + } + } + + return -1; +} + +const char *ast_rtp_lookup_mime_subtype2(const int asterisk_format, const int code, enum ast_rtp_options options) +{ + int i; + + for (i = 0; i < ARRAY_LEN(ast_rtp_mime_types); i++) { + if (ast_rtp_mime_types[i].payload_type.code == code && ast_rtp_mime_types[i].payload_type.asterisk_format == asterisk_format) { + if (asterisk_format && (code == AST_FORMAT_G726_AAL2) && (options & AST_RTP_OPT_G726_NONSTANDARD)) { + return "G726-32"; + } else { + return ast_rtp_mime_types[i].subtype; + } + } + } + + return ""; +} + +unsigned int ast_rtp_lookup_sample_rate2(int asterisk_format, int code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_LEN(ast_rtp_mime_types); ++i) { + if ((ast_rtp_mime_types[i].payload_type.code == code) && (ast_rtp_mime_types[i].payload_type.asterisk_format == asterisk_format)) { + return ast_rtp_mime_types[i].sample_rate; + } + } + + return 0; +} + +char *ast_rtp_lookup_mime_multiple2(struct ast_str *buf, const int capability, const int asterisk_format, enum ast_rtp_options options) +{ + int format, found = 0; + + if (!buf) { + return NULL; + } + + ast_str_append(&buf, 0, "0x%x (", capability); + + for (format = 1; format < AST_RTP_MAX; format <<= 1) { + if (capability & format) { + const char *name = ast_rtp_lookup_mime_subtype2(asterisk_format, format, options); + ast_str_append(&buf, 0, "%s|", name); + found = 1; + } + } + + ast_str_append(&buf, 0, "%s", found ? ")" : "nothing)"); + + return ast_str_buffer(buf); +} + +void ast_rtp_codecs_packetization_set(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, struct ast_codec_pref *prefs) +{ + codecs->pref = *prefs; + + if (instance && instance->engine->packetization_set) { + instance->engine->packetization_set(instance, &instance->codecs.pref); + } +} + +int ast_rtp_instance_dtmf_begin(struct ast_rtp_instance *instance, char digit) +{ + return instance->engine->dtmf_begin ? instance->engine->dtmf_begin(instance, digit) : -1; +} + +int ast_rtp_instance_dtmf_end(struct ast_rtp_instance *instance, char digit) +{ + return instance->engine->dtmf_end ? instance->engine->dtmf_end(instance, digit) : -1; +} + +int ast_rtp_instance_dtmf_mode_set(struct ast_rtp_instance *instance, enum ast_rtp_dtmf_mode dtmf_mode) +{ + if (!instance->engine->dtmf_mode_set || instance->engine->dtmf_mode_set(instance, dtmf_mode)) { + return -1; + } + + instance->dtmf_mode = dtmf_mode; + + return 0; +} + +enum ast_rtp_dtmf_mode ast_rtp_instance_dtmf_mode_get(struct ast_rtp_instance *instance) +{ + return instance->dtmf_mode; +} + +void ast_rtp_instance_new_source(struct ast_rtp_instance *instance) +{ + if (instance->engine->new_source) { + instance->engine->new_source(instance); + } +} + +int ast_rtp_instance_set_qos(struct ast_rtp_instance *instance, int tos, int cos, const char *desc) +{ + return instance->engine->qos ? instance->engine->qos(instance, tos, cos, desc) : -1; +} + +void ast_rtp_instance_stop(struct ast_rtp_instance *instance) +{ + if (instance->engine->stop) { + instance->engine->stop(instance); + } +} + +int ast_rtp_instance_fd(struct ast_rtp_instance *instance, int rtcp) +{ + return instance->engine->fd ? instance->engine->fd(instance, rtcp) : -1; +} + +struct ast_rtp_glue *ast_rtp_instance_get_glue(const char *type) +{ + struct ast_rtp_glue *glue = NULL; + + AST_RWLIST_RDLOCK(&glues); + + AST_RWLIST_TRAVERSE(&glues, glue, entry) { + if (!strcasecmp(glue->type, type)) { + break; + } + } + + AST_RWLIST_UNLOCK(&glues); + + return glue; +} + +static enum ast_bridge_result local_bridge_loop(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp_instance *instance0, struct ast_rtp_instance *instance1, int timeoutms, int flags, struct ast_frame **fo, struct ast_channel **rc, void *pvt0, void *pvt1) +{ + enum ast_bridge_result res = AST_BRIDGE_FAILED; + struct ast_channel *who = NULL, *other = NULL, *cs[3] = { NULL, }; + struct ast_frame *fr = NULL; + + /* Start locally bridging both instances */ + if (instance0->engine->local_bridge && instance0->engine->local_bridge(instance0, instance1)) { + ast_debug(1, "Failed to locally bridge %s to %s, backing out.\n", c0->name, c1->name); + ast_channel_unlock(c0); + ast_channel_unlock(c1); + return AST_BRIDGE_FAILED_NOWARN; + } + if (instance1->engine->local_bridge && instance1->engine->local_bridge(instance1, instance0)) { + ast_debug(1, "Failed to locally bridge %s to %s, backing out.\n", c1->name, c0->name); + if (instance0->engine->local_bridge) { + instance0->engine->local_bridge(instance0, NULL); + } + ast_channel_unlock(c0); + ast_channel_unlock(c1); + return AST_BRIDGE_FAILED_NOWARN; + } + + ast_channel_unlock(c0); + ast_channel_unlock(c1); + + instance0->bridged = instance1; + instance1->bridged = instance0; + + ast_poll_channel_add(c0, c1); + + /* Hop into a loop waiting for a frame from either channel */ + cs[0] = c0; + cs[1] = c1; + cs[2] = NULL; + for (;;) { + /* If the underlying formats have changed force this bridge to break */ + if ((c0->rawreadformat != c1->rawwriteformat) || (c1->rawreadformat != c0->rawwriteformat)) { + ast_debug(1, "rtp-engine-local-bridge: Oooh, formats changed, backing out\n"); + res = AST_BRIDGE_FAILED_NOWARN; + break; + } + /* Check if anything changed */ + if ((c0->tech_pvt != pvt0) || + (c1->tech_pvt != pvt1) || + (c0->masq || c0->masqr || c1->masq || c1->masqr) || + (c0->monitor || c0->audiohooks || c1->monitor || c1->audiohooks)) { + ast_debug(1, "rtp-engine-local-bridge: Oooh, something is weird, backing out\n"); + /* If a masquerade needs to happen we have to try to read in a frame so that it actually happens. Without this we risk being called again and going into a loop */ + if ((c0->masq || c0->masqr) && (fr = ast_read(c0))) { + ast_frfree(fr); + } + if ((c1->masq || c1->masqr) && (fr = ast_read(c1))) { + ast_frfree(fr); + } + res = AST_BRIDGE_RETRY; + break; + } + /* Wait on a channel to feed us a frame */ + if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) { + if (!timeoutms) { + res = AST_BRIDGE_RETRY; + break; + } + ast_debug(2, "rtp-engine-local-bridge: Ooh, empty read...\n"); + if (ast_check_hangup(c0) || ast_check_hangup(c1)) { + break; + } + continue; + } + /* Read in frame from channel */ + fr = ast_read(who); + other = (who == c0) ? c1 : c0; + /* Depending on the frame we may need to break out of our bridge */ + if (!fr || ((fr->frametype == AST_FRAME_DTMF_BEGIN || fr->frametype == AST_FRAME_DTMF_END) && + ((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) | + ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1)))) { + /* Record received frame and who */ + *fo = fr; + *rc = who; + ast_debug(1, "rtp-engine-local-bridge: Ooh, got a %s\n", fr ? "digit" : "hangup"); + res = AST_BRIDGE_COMPLETE; + break; + } else if ((fr->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) { + if ((fr->subclass == AST_CONTROL_HOLD) || + (fr->subclass == AST_CONTROL_UNHOLD) || + (fr->subclass == AST_CONTROL_VIDUPDATE) || + (fr->subclass == AST_CONTROL_T38) || + (fr->subclass == AST_CONTROL_SRCUPDATE)) { + /* If we are going on hold, then break callback mode and P2P bridging */ + if (fr->subclass == AST_CONTROL_HOLD) { + if (instance0->engine->local_bridge) { + instance0->engine->local_bridge(instance0, NULL); + } + if (instance1->engine->local_bridge) { + instance1->engine->local_bridge(instance1, NULL); + } + instance0->bridged = NULL; + instance1->bridged = NULL; + } else if (fr->subclass == AST_CONTROL_UNHOLD) { + if (instance0->engine->local_bridge) { + instance0->engine->local_bridge(instance0, instance1); + } + if (instance1->engine->local_bridge) { + instance1->engine->local_bridge(instance1, instance0); + } + instance0->bridged = instance1; + instance1->bridged = instance0; + } + ast_indicate_data(other, fr->subclass, fr->data.ptr, fr->datalen); + ast_frfree(fr); + } else { + *fo = fr; + *rc = who; + ast_debug(1, "rtp-engine-local-bridge: Got a FRAME_CONTROL (%d) frame on channel %s\n", fr->subclass, who->name); + res = AST_BRIDGE_COMPLETE; + break; + } + } else { + if ((fr->frametype == AST_FRAME_DTMF_BEGIN) || + (fr->frametype == AST_FRAME_DTMF_END) || + (fr->frametype == AST_FRAME_VOICE) || + (fr->frametype == AST_FRAME_VIDEO) || + (fr->frametype == AST_FRAME_IMAGE) || + (fr->frametype == AST_FRAME_HTML) || + (fr->frametype == AST_FRAME_MODEM) || + (fr->frametype == AST_FRAME_TEXT)) { + ast_write(other, fr); + } + + ast_frfree(fr); + } + /* Swap priority */ + cs[2] = cs[0]; + cs[0] = cs[1]; + cs[1] = cs[2]; + } + + /* Stop locally bridging both instances */ + if (instance0->engine->local_bridge) { + instance0->engine->local_bridge(instance0, NULL); + } + if (instance1->engine->local_bridge) { + instance1->engine->local_bridge(instance1, NULL); + } + + instance0->bridged = NULL; + instance1->bridged = NULL; + + ast_poll_channel_del(c0, c1); + + return res; +} + +static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp_instance *instance0, struct ast_rtp_instance *instance1, + struct ast_rtp_instance *vinstance0, struct ast_rtp_instance *vinstance1, struct ast_rtp_instance *tinstance0, + struct ast_rtp_instance *tinstance1, struct ast_rtp_glue *glue0, struct ast_rtp_glue *glue1, int codec0, int codec1, int timeoutms, + int flags, struct ast_frame **fo, struct ast_channel **rc, void *pvt0, void *pvt1) +{ + enum ast_bridge_result res = AST_BRIDGE_FAILED; + struct ast_channel *who = NULL, *other = NULL, *cs[3] = { NULL, }; + int oldcodec0 = codec0, oldcodec1 = codec1; + struct sockaddr_in ac1 = {0,}, vac1 = {0,}, tac1 = {0,}, ac0 = {0,}, vac0 = {0,}, tac0 = {0,}; + struct sockaddr_in t1 = {0,}, vt1 = {0,}, tt1 = {0,}, t0 = {0,}, vt0 = {0,}, tt0 = {0,}; + struct ast_frame *fr = NULL; + + /* Test the first channel */ + if (!(glue0->update_peer(c0, instance1, vinstance1, tinstance1, codec1, 0))) { + ast_rtp_instance_get_remote_address(instance1, &ac1); + if (vinstance1) { + ast_rtp_instance_get_remote_address(vinstance1, &vac1); + } + if (tinstance1) { + ast_rtp_instance_get_remote_address(tinstance1, &tac1); + } + } else { + ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c0->name, c1->name); + } + + /* Test the second channel */ + if (!(glue1->update_peer(c1, instance0, vinstance0, tinstance0, codec0, 0))) { + ast_rtp_instance_get_remote_address(instance0, &ac0); + if (vinstance0) { + ast_rtp_instance_get_remote_address(instance0, &vac0); + } + if (tinstance0) { + ast_rtp_instance_get_remote_address(instance0, &tac0); + } + } else { + ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c1->name, c0->name); + } + + ast_channel_unlock(c0); + ast_channel_unlock(c1); + + instance0->bridged = instance1; + instance1->bridged = instance0; + + ast_poll_channel_add(c0, c1); + + /* Go into a loop handling any stray frames that may come in */ + cs[0] = c0; + cs[1] = c1; + cs[2] = NULL; + for (;;) { + /* Check if anything changed */ + if ((c0->tech_pvt != pvt0) || + (c1->tech_pvt != pvt1) || + (c0->masq || c0->masqr || c1->masq || c1->masqr) || + (c0->monitor || c0->audiohooks || c1->monitor || c1->audiohooks)) { + ast_debug(1, "Oooh, something is weird, backing out\n"); + res = AST_BRIDGE_RETRY; + break; + } + + /* Check if they have changed their address */ + ast_rtp_instance_get_remote_address(instance1, &t1); + if (vinstance1) { + ast_rtp_instance_get_remote_address(vinstance1, &vt1); + } + if (tinstance1) { + ast_rtp_instance_get_remote_address(tinstance1, &tt1); + } + if (glue1->get_codec) { + codec1 = glue1->get_codec(c1); + } + + ast_rtp_instance_get_remote_address(instance0, &t0); + if (vinstance0) { + ast_rtp_instance_get_remote_address(vinstance0, &vt0); + } + if (tinstance0) { + ast_rtp_instance_get_remote_address(tinstance0, &tt0); + } + if (glue0->get_codec) { + codec0 = glue0->get_codec(c0); + } + + if ((inaddrcmp(&t1, &ac1)) || + (vinstance1 && inaddrcmp(&vt1, &vac1)) || + (tinstance1 && inaddrcmp(&tt1, &tac1)) || + (codec1 != oldcodec1)) { + ast_debug(1, "Oooh, '%s' changed end address to %s:%d (format %d)\n", + c1->name, ast_inet_ntoa(t1.sin_addr), ntohs(t1.sin_port), codec1); + ast_debug(1, "Oooh, '%s' changed end vaddress to %s:%d (format %d)\n", + c1->name, ast_inet_ntoa(vt1.sin_addr), ntohs(vt1.sin_port), codec1); + ast_debug(1, "Oooh, '%s' changed end taddress to %s:%d (format %d)\n", + c1->name, ast_inet_ntoa(tt1.sin_addr), ntohs(tt1.sin_port), codec1); + ast_debug(1, "Oooh, '%s' was %s:%d/(format %d)\n", + c1->name, ast_inet_ntoa(ac1.sin_addr), ntohs(ac1.sin_port), oldcodec1); + ast_debug(1, "Oooh, '%s' was %s:%d/(format %d)\n", + c1->name, ast_inet_ntoa(vac1.sin_addr), ntohs(vac1.sin_port), oldcodec1); + ast_debug(1, "Oooh, '%s' was %s:%d/(format %d)\n", + c1->name, ast_inet_ntoa(tac1.sin_addr), ntohs(tac1.sin_port), oldcodec1); + if (glue0->update_peer(c0, t1.sin_addr.s_addr ? instance1 : NULL, vt1.sin_addr.s_addr ? vinstance1 : NULL, tt1.sin_addr.s_addr ? tinstance1 : NULL, codec1, 0)) { + ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c0->name, c1->name); + } + memcpy(&ac1, &t1, sizeof(ac1)); + memcpy(&vac1, &vt1, sizeof(vac1)); + memcpy(&tac1, &tt1, sizeof(tac1)); + oldcodec1 = codec1; + } + if ((inaddrcmp(&t0, &ac0)) || + (vinstance0 && inaddrcmp(&vt0, &vac0)) || + (tinstance0 && inaddrcmp(&tt0, &tac0))) { + ast_debug(1, "Oooh, '%s' changed end address to %s:%d (format %d)\n", + c0->name, ast_inet_ntoa(t0.sin_addr), ntohs(t0.sin_port), codec0); + ast_debug(1, "Oooh, '%s' was %s:%d/(format %d)\n", + c0->name, ast_inet_ntoa(ac0.sin_addr), ntohs(ac0.sin_port), oldcodec0); + if (glue1->update_peer(c1, t0.sin_addr.s_addr ? instance0 : NULL, vt0.sin_addr.s_addr ? vinstance0 : NULL, tt0.sin_addr.s_addr ? tinstance0 : NULL, codec0, 0)) { + ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c1->name, c0->name); + } + memcpy(&ac0, &t0, sizeof(ac0)); + memcpy(&vac0, &vt0, sizeof(vac0)); + memcpy(&tac0, &tt0, sizeof(tac0)); + oldcodec0 = codec0; + } + + /* Wait for frame to come in on the channels */ + if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) { + if (!timeoutms) { + res = AST_BRIDGE_RETRY; + break; + } + ast_debug(1, "Ooh, empty read...\n"); + if (ast_check_hangup(c0) || ast_check_hangup(c1)) { + break; + } + continue; + } + fr = ast_read(who); + other = (who == c0) ? c1 : c0; + if (!fr || ((fr->frametype == AST_FRAME_DTMF_BEGIN || fr->frametype == AST_FRAME_DTMF_END) && + (((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) || + ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1))))) { + /* Break out of bridge */ + *fo = fr; + *rc = who; + ast_debug(1, "Oooh, got a %s\n", fr ? "digit" : "hangup"); + res = AST_BRIDGE_COMPLETE; + break; + } else if ((fr->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) { + if ((fr->subclass == AST_CONTROL_HOLD) || + (fr->subclass == AST_CONTROL_UNHOLD) || + (fr->subclass == AST_CONTROL_VIDUPDATE) || + (fr->subclass == AST_CONTROL_T38) || + (fr->subclass == AST_CONTROL_SRCUPDATE)) { + if (fr->subclass == AST_CONTROL_HOLD) { + /* If we someone went on hold we want the other side to reinvite back to us */ + if (who == c0) { + glue1->update_peer(c1, NULL, NULL, NULL, 0, 0); + } else { + glue0->update_peer(c0, NULL, NULL, NULL, 0, 0); + } + } else if (fr->subclass == AST_CONTROL_UNHOLD) { + /* If they went off hold they should go back to being direct */ + if (who == c0) { + glue1->update_peer(c1, instance0, vinstance0, tinstance0, codec0, 0); + } else { + glue0->update_peer(c0, instance1, vinstance1, tinstance1, codec1, 0); + } + } + /* Update local address information */ + ast_rtp_instance_get_remote_address(instance0, &t0); + memcpy(&ac0, &t0, sizeof(ac0)); + ast_rtp_instance_get_remote_address(instance1, &t1); + memcpy(&ac1, &t1, sizeof(ac1)); + /* Update codec information */ + if (glue0->get_codec && c0->tech_pvt) { + oldcodec0 = codec0 = glue0->get_codec(c0); + } + if (glue1->get_codec && c1->tech_pvt) { + oldcodec1 = codec1 = glue1->get_codec(c1); + } + ast_indicate_data(other, fr->subclass, fr->data.ptr, fr->datalen); + ast_frfree(fr); + } else { + *fo = fr; + *rc = who; + ast_debug(1, "Got a FRAME_CONTROL (%d) frame on channel %s\n", fr->subclass, who->name); + return AST_BRIDGE_COMPLETE; + } + } else { + if ((fr->frametype == AST_FRAME_DTMF_BEGIN) || + (fr->frametype == AST_FRAME_DTMF_END) || + (fr->frametype == AST_FRAME_VOICE) || + (fr->frametype == AST_FRAME_VIDEO) || + (fr->frametype == AST_FRAME_IMAGE) || + (fr->frametype == AST_FRAME_HTML) || + (fr->frametype == AST_FRAME_MODEM) || + (fr->frametype == AST_FRAME_TEXT)) { + ast_write(other, fr); + } + ast_frfree(fr); + } + /* Swap priority */ + cs[2] = cs[0]; + cs[0] = cs[1]; + cs[1] = cs[2]; + } + + if (glue0->update_peer(c0, NULL, NULL, NULL, 0, 0)) { + ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name); + } + if (glue1->update_peer(c1, NULL, NULL, NULL, 0, 0)) { + ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name); + } + + instance0->bridged = NULL; + instance1->bridged = NULL; + + ast_poll_channel_del(c0, c1); + + return res; +} + +/*! + * \brief Conditionally unref an rtp instance + */ +static void unref_instance_cond(struct ast_rtp_instance **instance) +{ + if (*instance) { + ao2_ref(*instance, -1); + *instance = NULL; + } +} + +enum ast_bridge_result ast_rtp_instance_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms) +{ + struct ast_rtp_instance *instance0 = NULL, *instance1 = NULL, + *vinstance0 = NULL, *vinstance1 = NULL, + *tinstance0 = NULL, *tinstance1 = NULL; + struct ast_rtp_glue *glue0, *glue1; + enum ast_rtp_glue_result audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID, video_glue0_res = AST_RTP_GLUE_RESULT_FORBID, text_glue0_res = AST_RTP_GLUE_RESULT_FORBID; + enum ast_rtp_glue_result audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID, video_glue1_res = AST_RTP_GLUE_RESULT_FORBID, text_glue1_res = AST_RTP_GLUE_RESULT_FORBID; + enum ast_bridge_result res = AST_BRIDGE_FAILED; + int codec0 = 0, codec1 = 0; + int unlock_chans = 1; + + /* Lock both channels so we can look for the glue that binds them together */ + ast_channel_lock(c0); + while (ast_channel_trylock(c1)) { + ast_channel_unlock(c0); + usleep(1); + ast_channel_lock(c0); + } + + /* Ensure neither channel got hungup during lock avoidance */ + if (ast_check_hangup(c0) || ast_check_hangup(c1)) { + ast_log(LOG_WARNING, "Got hangup while attempting to bridge '%s' and '%s'\n", c0->name, c1->name); + goto done; + } + + /* Grab glue that binds each channel to something using the RTP engine */ + if (!(glue0 = ast_rtp_instance_get_glue(c0->tech->type)) || !(glue1 = ast_rtp_instance_get_glue(c1->tech->type))) { + ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", glue0 ? c1->name : c0->name); + goto done; + } + + audio_glue0_res = glue0->get_rtp_info(c0, &instance0); + video_glue0_res = glue0->get_vrtp_info ? glue0->get_vrtp_info(c0, &vinstance0) : AST_RTP_GLUE_RESULT_FORBID; + text_glue0_res = glue0->get_trtp_info ? glue0->get_trtp_info(c0, &tinstance0) : AST_RTP_GLUE_RESULT_FORBID; + + audio_glue1_res = glue1->get_rtp_info(c1, &instance1); + video_glue1_res = glue1->get_vrtp_info ? glue1->get_vrtp_info(c1, &vinstance1) : AST_RTP_GLUE_RESULT_FORBID; + text_glue1_res = glue1->get_trtp_info ? glue1->get_trtp_info(c1, &tinstance1) : AST_RTP_GLUE_RESULT_FORBID; + + /* If we are carrying video, and both sides are not going to remotely bridge... fail the native bridge */ + if (video_glue0_res != AST_RTP_GLUE_RESULT_FORBID && (audio_glue0_res != AST_RTP_GLUE_RESULT_REMOTE || video_glue0_res != AST_RTP_GLUE_RESULT_REMOTE)) { + audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID; + } + if (video_glue1_res != AST_RTP_GLUE_RESULT_FORBID && (audio_glue1_res != AST_RTP_GLUE_RESULT_REMOTE || video_glue1_res != AST_RTP_GLUE_RESULT_REMOTE)) { + audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID; + } + + /* If any sort of bridge is forbidden just completely bail out and go back to generic bridging */ + if (audio_glue0_res == AST_RTP_GLUE_RESULT_FORBID || audio_glue1_res == AST_RTP_GLUE_RESULT_FORBID) { + res = AST_BRIDGE_FAILED_NOWARN; + goto done; + } + + /* If we have gotten to a local bridge make sure that both sides have the same local bridge callback and that they are DTMF compatible */ + if ((audio_glue0_res == AST_RTP_GLUE_RESULT_LOCAL || audio_glue1_res == AST_RTP_GLUE_RESULT_LOCAL) && ((instance0->engine->local_bridge != instance1->engine->local_bridge) || (instance0->engine->dtmf_compatible && !instance0->engine->dtmf_compatible(c0, instance0, c1, instance1)))) { + res = AST_BRIDGE_FAILED_NOWARN; + goto done; + } + + /* Make sure that codecs match */ + codec0 = glue0->get_codec ? glue0->get_codec(c0) : 0; + codec1 = glue1->get_codec ? glue1->get_codec(c1) : 0; + if (codec0 && codec1 && !(codec0 & codec1)) { + ast_debug(1, "Channel codec0 = %d is not codec1 = %d, cannot native bridge in RTP.\n", codec0, codec1); + res = AST_BRIDGE_FAILED_NOWARN; + goto done; + } + + /* Depending on the end result for bridging either do a local bridge or remote bridge */ + if (audio_glue0_res == AST_RTP_GLUE_RESULT_LOCAL || audio_glue1_res == AST_RTP_GLUE_RESULT_LOCAL) { + ast_verbose(VERBOSE_PREFIX_3 "Locally bridging %s and %s\n", c0->name, c1->name); + res = local_bridge_loop(c0, c1, instance0, instance1, timeoutms, flags, fo, rc, c0->tech_pvt, c1->tech_pvt); + } else { + ast_verbose(VERBOSE_PREFIX_3 "Remotely bridging %s and %s\n", c0->name, c1->name); + res = remote_bridge_loop(c0, c1, instance0, instance1, vinstance0, vinstance1, + tinstance0, tinstance1, glue0, glue1, codec0, codec1, timeoutms, flags, + fo, rc, c0->tech_pvt, c1->tech_pvt); + } + + unlock_chans = 0; + +done: + if (unlock_chans) { + ast_channel_unlock(c0); + ast_channel_unlock(c1); + } + + unref_instance_cond(&instance0); + unref_instance_cond(&instance1); + unref_instance_cond(&vinstance0); + unref_instance_cond(&vinstance1); + unref_instance_cond(&tinstance0); + unref_instance_cond(&tinstance1); + + return res; +} + +struct ast_rtp_instance *ast_rtp_instance_get_bridged(struct ast_rtp_instance *instance) +{ + return instance->bridged; +} + +void ast_rtp_instance_early_bridge_make_compatible(struct ast_channel *c0, struct ast_channel *c1) +{ + struct ast_rtp_instance *instance0 = NULL, *instance1 = NULL, + *vinstance0 = NULL, *vinstance1 = NULL, + *tinstance0 = NULL, *tinstance1 = NULL; + struct ast_rtp_glue *glue0, *glue1; + enum ast_rtp_glue_result audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID, video_glue0_res = AST_RTP_GLUE_RESULT_FORBID, text_glue0_res = AST_RTP_GLUE_RESULT_FORBID; + enum ast_rtp_glue_result audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID, video_glue1_res = AST_RTP_GLUE_RESULT_FORBID, text_glue1_res = AST_RTP_GLUE_RESULT_FORBID; + int codec0 = 0, codec1 = 0; + int res = 0; + + /* Lock both channels so we can look for the glue that binds them together */ + ast_channel_lock(c0); + while (ast_channel_trylock(c1)) { + ast_channel_unlock(c0); + usleep(1); + ast_channel_lock(c0); + } + + /* Grab glue that binds each channel to something using the RTP engine */ + if (!(glue0 = ast_rtp_instance_get_glue(c0->tech->type)) || !(glue1 = ast_rtp_instance_get_glue(c1->tech->type))) { + ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", glue0 ? c1->name : c0->name); + goto done; + } + + audio_glue0_res = glue0->get_rtp_info(c0, &instance0); + video_glue0_res = glue0->get_vrtp_info ? glue0->get_vrtp_info(c0, &vinstance0) : AST_RTP_GLUE_RESULT_FORBID; + text_glue0_res = glue0->get_trtp_info ? glue0->get_trtp_info(c0, &tinstance0) : AST_RTP_GLUE_RESULT_FORBID; + + audio_glue1_res = glue1->get_rtp_info(c1, &instance1); + video_glue1_res = glue1->get_vrtp_info ? glue1->get_vrtp_info(c1, &vinstance1) : AST_RTP_GLUE_RESULT_FORBID; + text_glue1_res = glue1->get_trtp_info ? glue1->get_trtp_info(c1, &tinstance1) : AST_RTP_GLUE_RESULT_FORBID; + + /* If we are carrying video, and both sides are not going to remotely bridge... fail the native bridge */ + if (video_glue0_res != AST_RTP_GLUE_RESULT_FORBID && (audio_glue0_res != AST_RTP_GLUE_RESULT_REMOTE || video_glue0_res != AST_RTP_GLUE_RESULT_REMOTE)) { + audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID; + } + if (video_glue1_res != AST_RTP_GLUE_RESULT_FORBID && (audio_glue1_res != AST_RTP_GLUE_RESULT_REMOTE || video_glue1_res != AST_RTP_GLUE_RESULT_REMOTE)) { + audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID; + } + if (audio_glue0_res == AST_RTP_GLUE_RESULT_REMOTE && (video_glue0_res == AST_RTP_GLUE_RESULT_FORBID || video_glue0_res == AST_RTP_GLUE_RESULT_REMOTE) && glue0->get_codec(c0)) { + codec0 = glue0->get_codec(c0); + } + if (audio_glue1_res == AST_RTP_GLUE_RESULT_REMOTE && (video_glue1_res == AST_RTP_GLUE_RESULT_FORBID || video_glue1_res == AST_RTP_GLUE_RESULT_REMOTE) && glue1->get_codec(c1)) { + codec1 = glue1->get_codec(c1); + } + + /* If any sort of bridge is forbidden just completely bail out and go back to generic bridging */ + if (audio_glue0_res != AST_RTP_GLUE_RESULT_REMOTE || audio_glue1_res != AST_RTP_GLUE_RESULT_REMOTE) { + goto done; + } + + /* Make sure we have matching codecs */ + if (!(codec0 & codec1)) { + goto done; + } + + ast_rtp_codecs_payloads_copy(&instance0->codecs, &instance1->codecs, instance1); + + if (vinstance0 && vinstance1) { + ast_rtp_codecs_payloads_copy(&vinstance0->codecs, &vinstance1->codecs, vinstance1); + } + if (tinstance0 && tinstance1) { + ast_rtp_codecs_payloads_copy(&tinstance0->codecs, &tinstance1->codecs, tinstance1); + } + + res = 0; + +done: + ast_channel_unlock(c0); + ast_channel_unlock(c1); + + unref_instance_cond(&instance0); + unref_instance_cond(&instance1); + unref_instance_cond(&vinstance0); + unref_instance_cond(&vinstance1); + unref_instance_cond(&tinstance0); + unref_instance_cond(&tinstance1); + + if (!res) { + ast_debug(1, "Seeded SDP of '%s' with that of '%s'\n", c0->name, c1 ? c1->name : "<unspecified>"); + } +} + +int ast_rtp_instance_early_bridge(struct ast_channel *c0, struct ast_channel *c1) +{ + struct ast_rtp_instance *instance0 = NULL, *instance1 = NULL, + *vinstance0 = NULL, *vinstance1 = NULL, + *tinstance0 = NULL, *tinstance1 = NULL; + struct ast_rtp_glue *glue0, *glue1; + enum ast_rtp_glue_result audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID, video_glue0_res = AST_RTP_GLUE_RESULT_FORBID, text_glue0_res = AST_RTP_GLUE_RESULT_FORBID; + enum ast_rtp_glue_result audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID, video_glue1_res = AST_RTP_GLUE_RESULT_FORBID, text_glue1_res = AST_RTP_GLUE_RESULT_FORBID; + int codec0 = 0, codec1 = 0; + int res = 0; + + /* If there is no second channel just immediately bail out, we are of no use in that scenario */ + if (!c1) { + return -1; + } + + /* Lock both channels so we can look for the glue that binds them together */ + ast_channel_lock(c0); + while (ast_channel_trylock(c1)) { + ast_channel_unlock(c0); + usleep(1); + ast_channel_lock(c0); + } + + /* Grab glue that binds each channel to something using the RTP engine */ + if (!(glue0 = ast_rtp_instance_get_glue(c0->tech->type)) || !(glue1 = ast_rtp_instance_get_glue(c1->tech->type))) { + ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", glue0 ? c1->name : c0->name); + goto done; + } + + audio_glue0_res = glue0->get_rtp_info(c0, &instance0); + video_glue0_res = glue0->get_vrtp_info ? glue0->get_vrtp_info(c0, &vinstance0) : AST_RTP_GLUE_RESULT_FORBID; + text_glue0_res = glue0->get_trtp_info ? glue0->get_trtp_info(c0, &tinstance0) : AST_RTP_GLUE_RESULT_FORBID; + + audio_glue1_res = glue1->get_rtp_info(c1, &instance1); + video_glue1_res = glue1->get_vrtp_info ? glue1->get_vrtp_info(c1, &vinstance1) : AST_RTP_GLUE_RESULT_FORBID; + text_glue1_res = glue1->get_trtp_info ? glue1->get_trtp_info(c1, &tinstance1) : AST_RTP_GLUE_RESULT_FORBID; + + /* If we are carrying video, and both sides are not going to remotely bridge... fail the native bridge */ + if (video_glue0_res != AST_RTP_GLUE_RESULT_FORBID && (audio_glue0_res != AST_RTP_GLUE_RESULT_REMOTE || video_glue0_res != AST_RTP_GLUE_RESULT_REMOTE)) { + audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID; + } + if (video_glue1_res != AST_RTP_GLUE_RESULT_FORBID && (audio_glue1_res != AST_RTP_GLUE_RESULT_REMOTE || video_glue1_res != AST_RTP_GLUE_RESULT_REMOTE)) { + audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID; + } + if (audio_glue0_res == AST_RTP_GLUE_RESULT_REMOTE && (video_glue0_res == AST_RTP_GLUE_RESULT_FORBID || video_glue0_res == AST_RTP_GLUE_RESULT_REMOTE) && glue0->get_codec(c0)) { + codec0 = glue0->get_codec(c0); + } + if (audio_glue1_res == AST_RTP_GLUE_RESULT_REMOTE && (video_glue1_res == AST_RTP_GLUE_RESULT_FORBID || video_glue1_res == AST_RTP_GLUE_RESULT_REMOTE) && glue1->get_codec(c1)) { + codec1 = glue1->get_codec(c1); + } + + /* If any sort of bridge is forbidden just completely bail out and go back to generic bridging */ + if (audio_glue0_res != AST_RTP_GLUE_RESULT_REMOTE || audio_glue1_res != AST_RTP_GLUE_RESULT_REMOTE) { + goto done; + } + + /* Make sure we have matching codecs */ + if (!(codec0 & codec1)) { + goto done; + } + + /* Bridge media early */ + if (glue0->update_peer(c0, instance1, vinstance1, tinstance1, codec1, 0)) { + ast_log(LOG_WARNING, "Channel '%s' failed to setup early bridge to '%s'\n", c0->name, c1 ? c1->name : "<unspecified>"); + } + + res = 0; + +done: + ast_channel_unlock(c0); + ast_channel_unlock(c1); + + unref_instance_cond(&instance0); + unref_instance_cond(&instance1); + unref_instance_cond(&vinstance0); + unref_instance_cond(&vinstance1); + unref_instance_cond(&tinstance0); + unref_instance_cond(&tinstance1); + + if (!res) { + ast_debug(1, "Setting early bridge SDP of '%s' with that of '%s'\n", c0->name, c1 ? c1->name : "<unspecified>"); + } + + return res; +} + +int ast_rtp_red_init(struct ast_rtp_instance *instance, int buffer_time, int *payloads, int generations) +{ + return instance->engine->red_init ? instance->engine->red_init(instance, buffer_time, payloads, generations) : -1; +} + +int ast_rtp_red_buffer(struct ast_rtp_instance *instance, struct ast_frame *frame) +{ + return instance->engine->red_buffer ? instance->engine->red_buffer(instance, frame) : -1; +} + +int ast_rtp_instance_get_stats(struct ast_rtp_instance *instance, struct ast_rtp_instance_stats *stats, enum ast_rtp_instance_stat stat) +{ + return instance->engine->get_stat ? instance->engine->get_stat(instance, stats, stat) : -1; +} + +char *ast_rtp_instance_get_quality(struct ast_rtp_instance *instance, enum ast_rtp_instance_stat_field field, char *buf, size_t size) +{ + struct ast_rtp_instance_stats stats; + enum ast_rtp_instance_stat stat; + + /* Determine what statistics we will need to retrieve based on field passed in */ + if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY) { + stat = AST_RTP_INSTANCE_STAT_ALL; + } else if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_JITTER) { + stat = AST_RTP_INSTANCE_STAT_COMBINED_JITTER; + } else if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS) { + stat = AST_RTP_INSTANCE_STAT_COMBINED_LOSS; + } else if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT) { + stat = AST_RTP_INSTANCE_STAT_COMBINED_RTT; + } else { + return NULL; + } + + /* Attempt to actually retrieve the statistics we need to generate the quality string */ + if (ast_rtp_instance_get_stats(instance, &stats, stat)) { + return NULL; + } + + /* Now actually fill the buffer with the good information */ + if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY) { + snprintf(buf, size, "ssrc=%i;themssrc=%u;lp=%u;rxjitter=%u;rxcount=%u;txjitter=%u;txcount=%u;rlp=%u;rtt=%u", + stats.local_ssrc, stats.remote_ssrc, stats.rxploss, stats.txjitter, stats.rxcount, stats.rxjitter, stats.txcount, stats.txploss, stats.rtt); + } else if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_JITTER) { + snprintf(buf, size, "minrxjitter=%f;maxrxjitter=%f;avgrxjitter=%f;stdevrxjitter=%f;reported_minjitter=%f;reported_maxjitter=%f;reported_avgjitter=%f;reported_stdevjitter=%f;", + stats.local_minjitter, stats.local_maxjitter, stats.local_normdevjitter, sqrt(stats.local_stdevjitter), stats.remote_minjitter, stats.remote_maxjitter, stats.remote_normdevjitter, sqrt(stats.remote_stdevjitter)); + } else if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS) { + snprintf(buf, size, "minrxlost=%f;maxrxlost=%f;avgrxlost=%f;stdevrxlost=%f;reported_minlost=%f;reported_maxlost=%f;reported_avglost=%f;reported_stdevlost=%f;", + stats.local_minrxploss, stats.local_maxrxploss, stats.local_normdevrxploss, sqrt(stats.local_stdevrxploss), stats.remote_minrxploss, stats.remote_maxrxploss, stats.remote_normdevrxploss, sqrt(stats.remote_stdevrxploss)); + } else if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT) { + snprintf(buf, size, "minrtt=%f;maxrtt=%f;avgrtt=%f;stdevrtt=%f;", stats.minrtt, stats.maxrtt, stats.normdevrtt, stats.stdevrtt); + } + + return buf; +} + +void ast_rtp_instance_set_stats_vars(struct ast_channel *chan, struct ast_rtp_instance *instance) +{ + char quality_buf[AST_MAX_USER_FIELD], *quality; + struct ast_channel *bridge = ast_bridged_channel(chan); + + if ((quality = ast_rtp_instance_get_quality(instance, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) { + pbx_builtin_setvar_helper(chan, "RTPAUDIOQOS", quality); + if (bridge) { + pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSBRIDGED", quality); + } + } + + if ((quality = ast_rtp_instance_get_quality(instance, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_JITTER, quality_buf, sizeof(quality_buf)))) { + pbx_builtin_setvar_helper(chan, "RTPAUDIOQOSJITTER", quality); + if (bridge) { + pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSJITTERBRIDGED", quality); + } + } + + if ((quality = ast_rtp_instance_get_quality(instance, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS, quality_buf, sizeof(quality_buf)))) { + pbx_builtin_setvar_helper(chan, "RTPAUDIOQOSLOSS", quality); + if (bridge) { + pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSLOSSBRIDGED", quality); + } + } + + if ((quality = ast_rtp_instance_get_quality(instance, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT, quality_buf, sizeof(quality_buf)))) { + pbx_builtin_setvar_helper(chan, "RTPAUDIOQOSRTT", quality); + if (bridge) { + pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSRTTBRIDGED", quality); + } + } +} + +int ast_rtp_instance_set_read_format(struct ast_rtp_instance *instance, int format) +{ + return instance->engine->set_read_format ? instance->engine->set_read_format(instance, format) : -1; +} + +int ast_rtp_instance_set_write_format(struct ast_rtp_instance *instance, int format) +{ + return instance->engine->set_write_format ? instance->engine->set_write_format(instance, format) : -1; +} + +int ast_rtp_instance_make_compatible(struct ast_channel *chan, struct ast_rtp_instance *instance, struct ast_channel *peer) +{ + struct ast_rtp_glue *glue; + struct ast_rtp_instance *peer_instance = NULL; + int res = -1; + + if (!instance->engine->make_compatible) { + return -1; + } + + ast_channel_lock(peer); + + if (!(glue = ast_rtp_instance_get_glue(peer->tech->type))) { + ast_channel_unlock(peer); + return -1; + } + + glue->get_rtp_info(peer, &peer_instance); + + if (!peer_instance || peer_instance->engine != instance->engine) { + ast_channel_unlock(peer); + peer_instance = (ao2_ref(peer_instance, -1), NULL); + return -1; + } + + res = instance->engine->make_compatible(chan, instance, peer, peer_instance); + + ast_channel_unlock(peer); + + peer_instance = (ao2_ref(peer_instance, -1), NULL); + + return res; +} + +int ast_rtp_instance_activate(struct ast_rtp_instance *instance) +{ + return instance->engine->activate ? instance->engine->activate(instance) : 0; +} + +void ast_rtp_instance_stun_request(struct ast_rtp_instance *instance, struct sockaddr_in *suggestion, const char *username) +{ + if (instance->engine->stun_request) { + instance->engine->stun_request(instance, suggestion, username); + } +} + +void ast_rtp_instance_set_timeout(struct ast_rtp_instance *instance, int timeout) +{ + instance->timeout = timeout; +} + +void ast_rtp_instance_set_hold_timeout(struct ast_rtp_instance *instance, int timeout) +{ + instance->holdtimeout = timeout; +} + +int ast_rtp_instance_get_timeout(struct ast_rtp_instance *instance) +{ + return instance->timeout; +} + +int ast_rtp_instance_get_hold_timeout(struct ast_rtp_instance *instance) +{ + return instance->holdtimeout; +} diff --git a/main/stun.c b/main/stun.c new file mode 100644 index 0000000000..264430718b --- /dev/null +++ b/main/stun.c @@ -0,0 +1,475 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2008, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * + * \brief STUN Support + * + * \author Mark Spencer <markster@digium.com> + * + * \note STUN is defined in RFC 3489. + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 124370 $") + +#include "asterisk/_private.h" +#include "asterisk/stun.h" +#include "asterisk/cli.h" +#include "asterisk/utils.h" +#include "asterisk/channel.h" + +static int stundebug; /*!< Are we debugging stun? */ + +/*! + * \brief STUN support code + * + * This code provides some support for doing STUN transactions. + * Eventually it should be moved elsewhere as other protocols + * than RTP can benefit from it - e.g. SIP. + * STUN is described in RFC3489 and it is based on the exchange + * of UDP packets between a client and one or more servers to + * determine the externally visible address (and port) of the client + * once it has gone through the NAT boxes that connect it to the + * outside. + * The simplest request packet is just the header defined in + * struct stun_header, and from the response we may just look at + * one attribute, STUN_MAPPED_ADDRESS, that we find in the response. + * By doing more transactions with different server addresses we + * may determine more about the behaviour of the NAT boxes, of + * course - the details are in the RFC. + * + * All STUN packets start with a simple header made of a type, + * length (excluding the header) and a 16-byte random transaction id. + * Following the header we may have zero or more attributes, each + * structured as a type, length and a value (whose format depends + * on the type, but often contains addresses). + * Of course all fields are in network format. + */ + +typedef struct { unsigned int id[4]; } __attribute__((packed)) stun_trans_id; + +struct stun_header { + unsigned short msgtype; + unsigned short msglen; + stun_trans_id id; + unsigned char ies[0]; +} __attribute__((packed)); + +struct stun_attr { + unsigned short attr; + unsigned short len; + unsigned char value[0]; +} __attribute__((packed)); + +/* + * The format normally used for addresses carried by STUN messages. + */ +struct stun_addr { + unsigned char unused; + unsigned char family; + unsigned short port; + unsigned int addr; +} __attribute__((packed)); + +/*! \brief STUN message types + * 'BIND' refers to transactions used to determine the externally + * visible addresses. 'SEC' refers to transactions used to establish + * a session key for subsequent requests. + * 'SEC' functionality is not supported here. + */ + +#define STUN_BINDREQ 0x0001 +#define STUN_BINDRESP 0x0101 +#define STUN_BINDERR 0x0111 +#define STUN_SECREQ 0x0002 +#define STUN_SECRESP 0x0102 +#define STUN_SECERR 0x0112 + +/*! \brief Basic attribute types in stun messages. + * Messages can also contain custom attributes (codes above 0x7fff) + */ +#define STUN_MAPPED_ADDRESS 0x0001 +#define STUN_RESPONSE_ADDRESS 0x0002 +#define STUN_CHANGE_REQUEST 0x0003 +#define STUN_SOURCE_ADDRESS 0x0004 +#define STUN_CHANGED_ADDRESS 0x0005 +#define STUN_USERNAME 0x0006 +#define STUN_PASSWORD 0x0007 +#define STUN_MESSAGE_INTEGRITY 0x0008 +#define STUN_ERROR_CODE 0x0009 +#define STUN_UNKNOWN_ATTRIBUTES 0x000a +#define STUN_REFLECTED_FROM 0x000b + +/*! \brief helper function to print message names */ +static const char *stun_msg2str(int msg) +{ + switch (msg) { + case STUN_BINDREQ: + return "Binding Request"; + case STUN_BINDRESP: + return "Binding Response"; + case STUN_BINDERR: + return "Binding Error Response"; + case STUN_SECREQ: + return "Shared Secret Request"; + case STUN_SECRESP: + return "Shared Secret Response"; + case STUN_SECERR: + return "Shared Secret Error Response"; + } + return "Non-RFC3489 Message"; +} + +/*! \brief helper function to print attribute names */ +static const char *stun_attr2str(int msg) +{ + switch (msg) { + case STUN_MAPPED_ADDRESS: + return "Mapped Address"; + case STUN_RESPONSE_ADDRESS: + return "Response Address"; + case STUN_CHANGE_REQUEST: + return "Change Request"; + case STUN_SOURCE_ADDRESS: + return "Source Address"; + case STUN_CHANGED_ADDRESS: + return "Changed Address"; + case STUN_USERNAME: + return "Username"; + case STUN_PASSWORD: + return "Password"; + case STUN_MESSAGE_INTEGRITY: + return "Message Integrity"; + case STUN_ERROR_CODE: + return "Error Code"; + case STUN_UNKNOWN_ATTRIBUTES: + return "Unknown Attributes"; + case STUN_REFLECTED_FROM: + return "Reflected From"; + } + return "Non-RFC3489 Attribute"; +} + +/*! \brief here we store credentials extracted from a message */ +struct stun_state { + const char *username; + const char *password; +}; + +static int stun_process_attr(struct stun_state *state, struct stun_attr *attr) +{ + if (stundebug) + ast_verbose("Found STUN Attribute %s (%04x), length %d\n", + stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len)); + switch (ntohs(attr->attr)) { + case STUN_USERNAME: + state->username = (const char *) (attr->value); + break; + case STUN_PASSWORD: + state->password = (const char *) (attr->value); + break; + default: + if (stundebug) + ast_verbose("Ignoring STUN attribute %s (%04x), length %d\n", + stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len)); + } + return 0; +} + +/*! \brief append a string to an STUN message */ +static void append_attr_string(struct stun_attr **attr, int attrval, const char *s, int *len, int *left) +{ + int size = sizeof(**attr) + strlen(s); + if (*left > size) { + (*attr)->attr = htons(attrval); + (*attr)->len = htons(strlen(s)); + memcpy((*attr)->value, s, strlen(s)); + (*attr) = (struct stun_attr *)((*attr)->value + strlen(s)); + *len += size; + *left -= size; + } +} + +/*! \brief append an address to an STUN message */ +static void append_attr_address(struct stun_attr **attr, int attrval, struct sockaddr_in *sin, int *len, int *left) +{ + int size = sizeof(**attr) + 8; + struct stun_addr *addr; + if (*left > size) { + (*attr)->attr = htons(attrval); + (*attr)->len = htons(8); + addr = (struct stun_addr *)((*attr)->value); + addr->unused = 0; + addr->family = 0x01; + addr->port = sin->sin_port; + addr->addr = sin->sin_addr.s_addr; + (*attr) = (struct stun_attr *)((*attr)->value + 8); + *len += size; + *left -= size; + } +} + +/*! \brief wrapper to send an STUN message */ +static int stun_send(int s, struct sockaddr_in *dst, struct stun_header *resp) +{ + return sendto(s, resp, ntohs(resp->msglen) + sizeof(*resp), 0, + (struct sockaddr *)dst, sizeof(*dst)); +} + +/*! \brief helper function to generate a random request id */ +static void stun_req_id(struct stun_header *req) +{ + int x; + for (x = 0; x < 4; x++) + req->id.id[x] = ast_random(); +} + +/*! \brief handle an incoming STUN message. + * + * Do some basic sanity checks on packet size and content, + * try to extract a bit of information, and possibly reply. + * At the moment this only processes BIND requests, and returns + * the externally visible address of the request. + * If a callback is specified, invoke it with the attribute. + */ +int ast_stun_handle_packet(int s, struct sockaddr_in *src, unsigned char *data, size_t len, stun_cb_f *stun_cb, void *arg) +{ + struct stun_header *hdr = (struct stun_header *)data; + struct stun_attr *attr; + struct stun_state st; + int ret = AST_STUN_IGNORE; + int x; + + /* On entry, 'len' is the length of the udp payload. After the + * initial checks it becomes the size of unprocessed options, + * while 'data' is advanced accordingly. + */ + if (len < sizeof(struct stun_header)) { + ast_debug(1, "Runt STUN packet (only %d, wanting at least %d)\n", (int) len, (int) sizeof(struct stun_header)); + return -1; + } + len -= sizeof(struct stun_header); + data += sizeof(struct stun_header); + x = ntohs(hdr->msglen); /* len as advertised in the message */ + if (stundebug) + ast_verbose("STUN Packet, msg %s (%04x), length: %d\n", stun_msg2str(ntohs(hdr->msgtype)), ntohs(hdr->msgtype), x); + if (x > len) { + ast_debug(1, "Scrambled STUN packet length (got %d, expecting %d)\n", x, (int)len); + } else + len = x; + memset(&st, 0, sizeof(st)); + while (len) { + if (len < sizeof(struct stun_attr)) { + ast_debug(1, "Runt Attribute (got %d, expecting %d)\n", (int)len, (int) sizeof(struct stun_attr)); + break; + } + attr = (struct stun_attr *)data; + /* compute total attribute length */ + x = ntohs(attr->len) + sizeof(struct stun_attr); + if (x > len) { + ast_debug(1, "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n", x, (int)len); + break; + } + if (stun_cb) + stun_cb(attr, arg); + if (stun_process_attr(&st, attr)) { + ast_debug(1, "Failed to handle attribute %s (%04x)\n", stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr)); + break; + } + /* Clear attribute id: in case previous entry was a string, + * this will act as the terminator for the string. + */ + attr->attr = 0; + data += x; + len -= x; + } + /* Null terminate any string. + * XXX NOTE, we write past the size of the buffer passed by the + * caller, so this is potentially dangerous. The only thing that + * saves us is that usually we read the incoming message in a + * much larger buffer in the struct ast_rtp + */ + *data = '\0'; + + /* Now prepare to generate a reply, which at the moment is done + * only for properly formed (len == 0) STUN_BINDREQ messages. + */ + if (len == 0) { + unsigned char respdata[1024]; + struct stun_header *resp = (struct stun_header *)respdata; + int resplen = 0; /* len excluding header */ + int respleft = sizeof(respdata) - sizeof(struct stun_header); + + resp->id = hdr->id; + resp->msgtype = 0; + resp->msglen = 0; + attr = (struct stun_attr *)resp->ies; + switch (ntohs(hdr->msgtype)) { + case STUN_BINDREQ: + if (stundebug) + ast_verbose("STUN Bind Request, username: %s\n", + st.username ? st.username : "<none>"); + if (st.username) + append_attr_string(&attr, STUN_USERNAME, st.username, &resplen, &respleft); + append_attr_address(&attr, STUN_MAPPED_ADDRESS, src, &resplen, &respleft); + resp->msglen = htons(resplen); + resp->msgtype = htons(STUN_BINDRESP); + stun_send(s, src, resp); + ret = AST_STUN_ACCEPT; + break; + default: + if (stundebug) + ast_verbose("Dunno what to do with STUN message %04x (%s)\n", ntohs(hdr->msgtype), stun_msg2str(ntohs(hdr->msgtype))); + } + } + return ret; +} + +/*! \brief Extract the STUN_MAPPED_ADDRESS from the stun response. + * This is used as a callback for stun_handle_response + * when called from ast_stun_request. + */ +static int stun_get_mapped(struct stun_attr *attr, void *arg) +{ + struct stun_addr *addr = (struct stun_addr *)(attr + 1); + struct sockaddr_in *sa = (struct sockaddr_in *)arg; + + if (ntohs(attr->attr) != STUN_MAPPED_ADDRESS || ntohs(attr->len) != 8) + return 1; /* not us. */ + sa->sin_port = addr->port; + sa->sin_addr.s_addr = addr->addr; + return 0; +} + +/*! \brief Generic STUN request + * Send a generic stun request to the server specified, + * possibly waiting for a reply and filling the 'reply' field with + * the externally visible address. Note that in this case the request + * will be blocking. + * (Note, the interface may change slightly in the future). + * + * \param s the socket used to send the request + * \param dst the address of the STUN server + * \param username if non null, add the username in the request + * \param answer if non null, the function waits for a response and + * puts here the externally visible address. + * \return 0 on success, other values on error. + */ +int ast_stun_request(int s, struct sockaddr_in *dst, + const char *username, struct sockaddr_in *answer) +{ + struct stun_header *req; + unsigned char reqdata[1024]; + int reqlen, reqleft; + struct stun_attr *attr; + int res = 0; + int retry; + + req = (struct stun_header *)reqdata; + stun_req_id(req); + reqlen = 0; + reqleft = sizeof(reqdata) - sizeof(struct stun_header); + req->msgtype = 0; + req->msglen = 0; + attr = (struct stun_attr *)req->ies; + if (username) + append_attr_string(&attr, STUN_USERNAME, username, &reqlen, &reqleft); + req->msglen = htons(reqlen); + req->msgtype = htons(STUN_BINDREQ); + for (retry = 0; retry < 3; retry++) { /* XXX make retries configurable */ + /* send request, possibly wait for reply */ + unsigned char reply_buf[1024]; + fd_set rfds; + struct timeval to = { 3, 0 }; /* timeout, make it configurable */ + struct sockaddr_in src; + socklen_t srclen; + + res = stun_send(s, dst, req); + if (res < 0) { + ast_log(LOG_WARNING, "ast_stun_request send #%d failed error %d, retry\n", + retry, res); + continue; + } + if (answer == NULL) + break; + FD_ZERO(&rfds); + FD_SET(s, &rfds); + res = ast_select(s + 1, &rfds, NULL, NULL, &to); + if (res <= 0) /* timeout or error */ + continue; + memset(&src, 0, sizeof(src)); + srclen = sizeof(src); + /* XXX pass -1 in the size, because stun_handle_packet might + * write past the end of the buffer. + */ + res = recvfrom(s, reply_buf, sizeof(reply_buf) - 1, + 0, (struct sockaddr *)&src, &srclen); + if (res < 0) { + ast_log(LOG_WARNING, "ast_stun_request recvfrom #%d failed error %d, retry\n", + retry, res); + continue; + } + memset(answer, 0, sizeof(struct sockaddr_in)); + ast_stun_handle_packet(s, &src, reply_buf, res, + stun_get_mapped, answer); + res = 0; /* signal regular exit */ + break; + } + return res; +} + +static char *handle_cli_stun_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "stun set debug {on|off}"; + e->usage = + "Usage: stun set debug {on|off}\n" + " Enable/Disable STUN (Simple Traversal of UDP through NATs)\n" + " debugging\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc != e->args) + return CLI_SHOWUSAGE; + + if (!strncasecmp(a->argv[e->args-1], "on", 2)) + stundebug = 1; + else if (!strncasecmp(a->argv[e->args-1], "off", 3)) + stundebug = 0; + else + return CLI_SHOWUSAGE; + + ast_cli(a->fd, "STUN Debugging %s\n", stundebug ? "Enabled" : "Disabled"); + return CLI_SUCCESS; +} + +static struct ast_cli_entry cli_stun[] = { + AST_CLI_DEFINE(handle_cli_stun_set_debug, "Enable/Disable STUN debugging"), +}; + +/*! \brief Initialize the STUN system in Asterisk */ +void ast_stun_init(void) +{ + ast_cli_register_multiple(cli_stun, sizeof(cli_stun) / sizeof(struct ast_cli_entry)); +} diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c new file mode 100644 index 0000000000..e16088d6e8 --- /dev/null +++ b/res/res_rtp_asterisk.c @@ -0,0 +1,2579 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2008, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * + * \brief Supports RTP and RTCP with Symmetric RTP support for NAT traversal. + * + * \author Mark Spencer <markster@digium.com> + * + * \note RTP is defined in RFC 3550. + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 138083 $") + +#include <sys/time.h> +#include <signal.h> +#include <fcntl.h> +#include <math.h> + +#include "asterisk/stun.h" +#include "asterisk/pbx.h" +#include "asterisk/frame.h" +#include "asterisk/channel.h" +#include "asterisk/acl.h" +#include "asterisk/config.h" +#include "asterisk/lock.h" +#include "asterisk/utils.h" +#include "asterisk/netsock.h" +#include "asterisk/cli.h" +#include "asterisk/manager.h" +#include "asterisk/unaligned.h" +#include "asterisk/module.h" +#include "asterisk/rtp_engine.h" + +#define MAX_TIMESTAMP_SKEW 640 + +#define RTP_SEQ_MOD (1<<16) /*!< A sequence number can't be more than 16 bits */ +#define RTCP_DEFAULT_INTERVALMS 5000 /*!< Default milli-seconds between RTCP reports we send */ +#define RTCP_MIN_INTERVALMS 500 /*!< Min milli-seconds between RTCP reports we send */ +#define RTCP_MAX_INTERVALMS 60000 /*!< Max milli-seconds between RTCP reports we send */ + +#define DEFAULT_RTP_START 5000 /*!< Default port number to start allocating RTP ports from */ +#define DEFAULT_RTP_END 31000 /*!< Default maximum port number to end allocating RTP ports at */ + +#define MINIMUM_RTP_PORT 1024 /*!< Minimum port number to accept */ +#define MAXIMUM_RTP_PORT 65535 /*!< Maximum port number to accept */ + +#define RTCP_PT_FUR 192 +#define RTCP_PT_SR 200 +#define RTCP_PT_RR 201 +#define RTCP_PT_SDES 202 +#define RTCP_PT_BYE 203 +#define RTCP_PT_APP 204 + +#define RTP_MTU 1200 + +#define DEFAULT_DTMF_TIMEOUT 3000 /*!< samples */ + +#define ZFONE_PROFILE_ID 0x505a + +static int dtmftimeout = DEFAULT_DTMF_TIMEOUT; + +static int rtpstart = DEFAULT_RTP_START; /*!< First port for RTP sessions (set in rtp.conf) */ +static int rtpend = DEFAULT_RTP_END; /*!< Last port for RTP sessions (set in rtp.conf) */ +static int rtpdebug; /*!< Are we debugging? */ +static int rtcpdebug; /*!< Are we debugging RTCP? */ +static int rtcpstats; /*!< Are we debugging RTCP? */ +static int rtcpinterval = RTCP_DEFAULT_INTERVALMS; /*!< Time between rtcp reports in millisecs */ +static struct sockaddr_in rtpdebugaddr; /*!< Debug packets to/from this host */ +static struct sockaddr_in rtcpdebugaddr; /*!< Debug RTCP packets to/from this host */ +#ifdef SO_NO_CHECK +static int nochecksums; +#endif +static int strictrtp; + +enum strict_rtp_state { + STRICT_RTP_OPEN = 0, /*! No RTP packets should be dropped, all sources accepted */ + STRICT_RTP_LEARN, /*! Accept next packet as source */ + STRICT_RTP_CLOSED, /*! Drop all RTP packets not coming from source that was learned */ +}; + +#define FLAG_3389_WARNING (1 << 0) +#define FLAG_NAT_ACTIVE (3 << 1) +#define FLAG_NAT_INACTIVE (0 << 1) +#define FLAG_NAT_INACTIVE_NOWARN (1 << 1) +#define FLAG_NEED_MARKER_BIT (1 << 3) +#define FLAG_DTMF_COMPENSATE (1 << 4) + +/*! \brief RTP session description */ +struct ast_rtp { + int s; + struct ast_frame f; + unsigned char rawdata[8192 + AST_FRIENDLY_OFFSET]; + unsigned int ssrc; /*!< Synchronization source, RFC 3550, page 10. */ + unsigned int themssrc; /*!< Their SSRC */ + unsigned int rxssrc; + unsigned int lastts; + unsigned int lastrxts; + unsigned int lastividtimestamp; + unsigned int lastovidtimestamp; + unsigned int lastitexttimestamp; + unsigned int lastotexttimestamp; + unsigned int lasteventseqn; + int lastrxseqno; /*!< Last received sequence number */ + unsigned short seedrxseqno; /*!< What sequence number did they start with?*/ + unsigned int seedrxts; /*!< What RTP timestamp did they start with? */ + unsigned int rxcount; /*!< How many packets have we received? */ + unsigned int rxoctetcount; /*!< How many octets have we received? should be rxcount *160*/ + unsigned int txcount; /*!< How many packets have we sent? */ + unsigned int txoctetcount; /*!< How many octets have we sent? (txcount*160)*/ + unsigned int cycles; /*!< Shifted count of sequence number cycles */ + double rxjitter; /*!< Interarrival jitter at the moment */ + double rxtransit; /*!< Relative transit time for previous packet */ + int lasttxformat; + int lastrxformat; + + int rtptimeout; /*!< RTP timeout time (negative or zero means disabled, negative value means temporarily disabled) */ + int rtpholdtimeout; /*!< RTP timeout when on hold (negative or zero means disabled, negative value means temporarily disabled). */ + int rtpkeepalive; /*!< Send RTP comfort noice packets for keepalive */ + + /* DTMF Reception Variables */ + char resp; + unsigned int lastevent; + int dtmfcount; + unsigned int dtmfsamples; + /* DTMF Transmission Variables */ + unsigned int lastdigitts; + char sending_digit; /*!< boolean - are we sending digits */ + char send_digit; /*!< digit we are sending */ + int send_payload; + int send_duration; + unsigned int flags; + struct timeval rxcore; + struct timeval txcore; + double drxcore; /*!< The double representation of the first received packet */ + struct timeval lastrx; /*!< timeval when we last received a packet */ + struct timeval dtmfmute; + struct ast_smoother *smoother; + int *ioid; + unsigned short seqno; /*!< Sequence number, RFC 3550, page 13. */ + unsigned short rxseqno; + struct sched_context *sched; + struct io_context *io; + void *data; + struct ast_rtcp *rtcp; + struct ast_rtp *bridged; /*!< Who we are Packet bridged to */ + + enum strict_rtp_state strict_rtp_state; /*!< Current state that strict RTP protection is in */ + struct sockaddr_in strict_rtp_address; /*!< Remote address information for strict RTP purposes */ + + struct rtp_red *red; +}; + +/*! + * \brief Structure defining an RTCP session. + * + * The concept "RTCP session" is not defined in RFC 3550, but since + * this structure is analogous to ast_rtp, which tracks a RTP session, + * it is logical to think of this as a RTCP session. + * + * RTCP packet is defined on page 9 of RFC 3550. + * + */ +struct ast_rtcp { + int rtcp_info; + int s; /*!< Socket */ + struct sockaddr_in us; /*!< Socket representation of the local endpoint. */ + struct sockaddr_in them; /*!< Socket representation of the remote endpoint. */ + unsigned int soc; /*!< What they told us */ + unsigned int spc; /*!< What they told us */ + unsigned int themrxlsr; /*!< The middle 32 bits of the NTP timestamp in the last received SR*/ + struct timeval rxlsr; /*!< Time when we got their last SR */ + struct timeval txlsr; /*!< Time when we sent or last SR*/ + unsigned int expected_prior; /*!< no. packets in previous interval */ + unsigned int received_prior; /*!< no. packets received in previous interval */ + int schedid; /*!< Schedid returned from ast_sched_add() to schedule RTCP-transmissions*/ + unsigned int rr_count; /*!< number of RRs we've sent, not including report blocks in SR's */ + unsigned int sr_count; /*!< number of SRs we've sent */ + unsigned int lastsrtxcount; /*!< Transmit packet count when last SR sent */ + double accumulated_transit; /*!< accumulated a-dlsr-lsr */ + double rtt; /*!< Last reported rtt */ + unsigned int reported_jitter; /*!< The contents of their last jitter entry in the RR */ + unsigned int reported_lost; /*!< Reported lost packets in their RR */ + + double reported_maxjitter; + double reported_minjitter; + double reported_normdev_jitter; + double reported_stdev_jitter; + unsigned int reported_jitter_count; + + double reported_maxlost; + double reported_minlost; + double reported_normdev_lost; + double reported_stdev_lost; + + double rxlost; + double maxrxlost; + double minrxlost; + double normdev_rxlost; + double stdev_rxlost; + unsigned int rxlost_count; + + double maxrxjitter; + double minrxjitter; + double normdev_rxjitter; + double stdev_rxjitter; + unsigned int rxjitter_count; + double maxrtt; + double minrtt; + double normdevrtt; + double stdevrtt; + unsigned int rtt_count; +}; + +struct rtp_red { + struct ast_frame t140; /*!< Primary data */ + struct ast_frame t140red; /*!< Redundant t140*/ + unsigned char pt[AST_RED_MAX_GENERATION]; /*!< Payload types for redundancy data */ + unsigned char ts[AST_RED_MAX_GENERATION]; /*!< Time stamps */ + unsigned char len[AST_RED_MAX_GENERATION]; /*!< length of each generation */ + int num_gen; /*!< Number of generations */ + int schedid; /*!< Timer id */ + int ti; /*!< How long to buffer data before send */ + unsigned char t140red_data[64000]; + unsigned char buf_data[64000]; /*!< buffered primary data */ + int hdrlen; + long int prev_ts; +}; + +/* Forward Declarations */ +static int ast_rtp_new(struct ast_rtp_instance *instance, struct sched_context *sched, struct sockaddr_in *sin, void *data); +static int ast_rtp_destroy(struct ast_rtp_instance *instance); +static int ast_rtp_dtmf_begin(struct ast_rtp_instance *instance, char digit); +static int ast_rtp_dtmf_end(struct ast_rtp_instance *instance, char digit); +static void ast_rtp_new_source(struct ast_rtp_instance *instance); +static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *frame); +static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtcp); +static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_property property, int value); +static int ast_rtp_fd(struct ast_rtp_instance *instance, int rtcp); +static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct sockaddr_in *sin); +static int rtp_red_init(struct ast_rtp_instance *instance, int buffer_time, int *payloads, int generations); +static int rtp_red_buffer(struct ast_rtp_instance *instance, struct ast_frame *frame); +static int ast_rtp_local_bridge(struct ast_rtp_instance *instance0, struct ast_rtp_instance *instance1); +static int ast_rtp_get_stat(struct ast_rtp_instance *instance, struct ast_rtp_instance_stats *stats, enum ast_rtp_instance_stat stat); +static int ast_rtp_dtmf_compatible(struct ast_channel *chan0, struct ast_rtp_instance *instance0, struct ast_channel *chan1, struct ast_rtp_instance *instance1); +static void ast_rtp_stun_request(struct ast_rtp_instance *instance, struct sockaddr_in *suggestion, const char *username); +static void ast_rtp_stop(struct ast_rtp_instance *instance); + +/* RTP Engine Declaration */ +static struct ast_rtp_engine asterisk_rtp_engine = { + .name = "asterisk", + .new = ast_rtp_new, + .destroy = ast_rtp_destroy, + .dtmf_begin = ast_rtp_dtmf_begin, + .dtmf_end = ast_rtp_dtmf_end, + .new_source = ast_rtp_new_source, + .write = ast_rtp_write, + .read = ast_rtp_read, + .prop_set = ast_rtp_prop_set, + .fd = ast_rtp_fd, + .remote_address_set = ast_rtp_remote_address_set, + .red_init = rtp_red_init, + .red_buffer = rtp_red_buffer, + .local_bridge = ast_rtp_local_bridge, + .get_stat = ast_rtp_get_stat, + .dtmf_compatible = ast_rtp_dtmf_compatible, + .stun_request = ast_rtp_stun_request, + .stop = ast_rtp_stop, +}; + +static inline int rtp_debug_test_addr(struct sockaddr_in *addr) +{ + if (!rtpdebug) { + return 0; + } + + if (rtpdebugaddr.sin_addr.s_addr) { + if (((ntohs(rtpdebugaddr.sin_port) != 0) + && (rtpdebugaddr.sin_port != addr->sin_port)) + || (rtpdebugaddr.sin_addr.s_addr != addr->sin_addr.s_addr)) + return 0; + } + + return 1; +} + +static inline int rtcp_debug_test_addr(struct sockaddr_in *addr) +{ + if (!rtcpdebug) { + return 0; + } + + if (rtcpdebugaddr.sin_addr.s_addr) { + if (((ntohs(rtcpdebugaddr.sin_port) != 0) + && (rtcpdebugaddr.sin_port != addr->sin_port)) + || (rtcpdebugaddr.sin_addr.s_addr != addr->sin_addr.s_addr)) + return 0; + } + + return 1; +} + +static unsigned int ast_rtcp_calc_interval(struct ast_rtp *rtp) +{ + unsigned int interval; + /*! \todo XXX Do a more reasonable calculation on this one + * Look in RFC 3550 Section A.7 for an example*/ + interval = rtcpinterval; + return interval; +} + +/*! \brief Calculate normal deviation */ +static double normdev_compute(double normdev, double sample, unsigned int sample_count) +{ + normdev = normdev * sample_count + sample; + sample_count++; + + return normdev / sample_count; +} + +static double stddev_compute(double stddev, double sample, double normdev, double normdev_curent, unsigned int sample_count) +{ +/* + for the formula check http://www.cs.umd.edu/~austinjp/constSD.pdf + return sqrt( (sample_count*pow(stddev,2) + sample_count*pow((sample-normdev)/(sample_count+1),2) + pow(sample-normdev_curent,2)) / (sample_count+1)); + we can compute the sigma^2 and that way we would have to do the sqrt only 1 time at the end and would save another pow 2 compute + optimized formula +*/ +#define SQUARE(x) ((x) * (x)) + + stddev = sample_count * stddev; + sample_count++; + + return stddev + + ( sample_count * SQUARE( (sample - normdev) / sample_count ) ) + + ( SQUARE(sample - normdev_curent) / sample_count ); + +#undef SQUARE +} + +static int create_new_socket(const char *type) +{ + int sock = socket(AF_INET, SOCK_DGRAM, 0); + + if (sock < 0) { + if (!type) { + type = "RTP/RTCP"; + } + ast_log(LOG_WARNING, "Unable to allocate %s socket: %s\n", type, strerror(errno)); + } else { + long flags = fcntl(sock, F_GETFL); + fcntl(sock, F_SETFL, flags | O_NONBLOCK); +#ifdef SO_NO_CHECK + if (nochecksums) { + setsockopt(sock, SOL_SOCKET, SO_NO_CHECK, &nochecksums, sizeof(nochecksums)); + } +#endif + } + + return sock; +} + +static int ast_rtp_new(struct ast_rtp_instance *instance, struct sched_context *sched, struct sockaddr_in *sin, void *data) +{ + struct ast_rtp *rtp = NULL; + int x, startplace; + + /* Create a new RTP structure to hold all of our data */ + if (!(rtp = ast_calloc(1, sizeof(*rtp)))) { + return -1; + } + + /* Set default parameters on the newly created RTP structure */ + rtp->ssrc = ast_random(); + rtp->seqno = ast_random() & 0xffff; + rtp->strict_rtp_state = (strictrtp ? STRICT_RTP_LEARN : STRICT_RTP_OPEN); + + /* Create a new socket for us to listen on and use */ + if ((rtp->s = create_new_socket("RTP")) < 0) { + ast_debug(1, "Failed to create a new socket for RTP instance '%p'\n", instance); + ast_free(rtp); + return -1; + } + + /* Now actually find a free RTP port to use */ + x = (rtpend == rtpstart) ? rtpstart : (ast_random() % (rtpend - rtpstart)) + rtpstart; + x = x & ~1; + startplace = x; + + for (;;) { + struct sockaddr_in local_address = { 0, }; + + local_address.sin_port = htons(x); + /* Try to bind, this will tell us whether the port is available or not */ + if (!bind(rtp->s, (struct sockaddr*)&local_address, sizeof(local_address))) { + ast_debug(1, "Allocated port %d for RTP instance '%p'\n", x, instance); + ast_rtp_instance_set_local_address(instance, &local_address); + break; + } + + x += 2; + if (x > rtpend) { + x = (rtpstart + 1) & ~1; + } + + /* See if we ran out of ports or if the bind actually failed because of something other than the address being in use */ + if (x == startplace || errno != EADDRINUSE) { + ast_log(LOG_ERROR, "Oh dear... we couldn't allocate a port for RTP instance '%p'\n", instance); + return -1; + } + } + + /* Record any information we may need */ + rtp->sched = sched; + + /* Associate the RTP structure with the RTP instance and be done */ + ast_rtp_instance_set_data(instance, rtp); + + return 0; +} + +static int ast_rtp_destroy(struct ast_rtp_instance *instance) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + + /* Destroy the smoother that was smoothing out audio if present */ + if (rtp->smoother) { + ast_smoother_free(rtp->smoother); + } + + /* Close our own socket so we no longer get packets */ + if (rtp->s > -1) { + close(rtp->s); + } + + /* Destroy RTCP if it was being used */ + if (rtp->rtcp) { + AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); + close(rtp->rtcp->s); + ast_free(rtp->rtcp); + } + + /* Destroy RED if it was being used */ + if (rtp->red) { + AST_SCHED_DEL(rtp->sched, rtp->red->schedid); + ast_free(rtp->red); + } + + /* Finally destroy ourselves */ + ast_free(rtp); + + return 0; +} + +static int ast_rtp_dtmf_begin(struct ast_rtp_instance *instance, char digit) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + struct sockaddr_in remote_address; + int hdrlen = 12, res = 0, i = 0, payload = 101; + char data[256]; + unsigned int *rtpheader = (unsigned int*)data; + + ast_rtp_instance_get_remote_address(instance, &remote_address); + + /* If we have no remote address information bail out now */ + if (!remote_address.sin_addr.s_addr || !remote_address.sin_port) { + return -1; + } + + /* Convert given digit into what we want to transmit */ + if ((digit <= '9') && (digit >= '0')) { + digit -= '0'; + } else if (digit == '*') { + digit = 10; + } else if (digit == '#') { + digit = 11; + } else if ((digit >= 'A') && (digit <= 'D')) { + digit = digit - 'A' + 12; + } else if ((digit >= 'a') && (digit <= 'd')) { + digit = digit - 'a' + 12; + } else { + ast_log(LOG_WARNING, "Don't know how to represent '%c'\n", digit); + return -1; + } + + /* Grab the payload that they expect the RFC2833 packet to be received in */ + payload = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(instance), 0, AST_RTP_DTMF); + + rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000)); + rtp->send_duration = 160; + rtp->lastdigitts = rtp->lastts + rtp->send_duration; + + /* Create the actual packet that we will be sending */ + rtpheader[0] = htonl((2 << 30) | (1 << 23) | (payload << 16) | (rtp->seqno)); + rtpheader[1] = htonl(rtp->lastdigitts); + rtpheader[2] = htonl(rtp->ssrc); + + /* Actually send the packet */ + for (i = 0; i < 2; i++) { + rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (rtp->send_duration)); + res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &remote_address, sizeof(remote_address)); + if (res < 0) { + ast_log(LOG_ERROR, "RTP Transmission error to %s:%u: %s\n", + ast_inet_ntoa(remote_address.sin_addr), ntohs(remote_address.sin_port), strerror(errno)); + } + if (rtp_debug_test_addr(&remote_address)) { + ast_verbose("Sent RTP DTMF packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", + ast_inet_ntoa(remote_address.sin_addr), + ntohs(remote_address.sin_port), payload, rtp->seqno, rtp->lastdigitts, res - hdrlen); + } + rtp->seqno++; + rtp->send_duration += 160; + rtpheader[0] = htonl((2 << 30) | (payload << 16) | (rtp->seqno)); + } + + /* Record that we are in the process of sending a digit and information needed to continue doing so */ + rtp->sending_digit = 1; + rtp->send_digit = digit; + rtp->send_payload = payload; + + return 0; +} + +static int ast_rtp_dtmf_continuation(struct ast_rtp_instance *instance) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + struct sockaddr_in remote_address; + int hdrlen = 12, res = 0; + char data[256]; + unsigned int *rtpheader = (unsigned int*)data; + + ast_rtp_instance_get_remote_address(instance, &remote_address); + + /* Make sure we know where the other side is so we can send them the packet */ + if (!remote_address.sin_addr.s_addr || !remote_address.sin_port) { + return -1; + } + + /* Actually create the packet we will be sending */ + rtpheader[0] = htonl((2 << 30) | (1 << 23) | (rtp->send_payload << 16) | (rtp->seqno)); + rtpheader[1] = htonl(rtp->lastdigitts); + rtpheader[2] = htonl(rtp->ssrc); + rtpheader[3] = htonl((rtp->send_digit << 24) | (0xa << 16) | (rtp->send_duration)); + rtpheader[0] = htonl((2 << 30) | (rtp->send_payload << 16) | (rtp->seqno)); + + /* Boom, send it on out */ + res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &remote_address, sizeof(remote_address)); + if (res < 0) { + ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n", + ast_inet_ntoa(remote_address.sin_addr), + ntohs(remote_address.sin_port), strerror(errno)); + } + + if (rtp_debug_test_addr(&remote_address)) { + ast_verbose("Sent RTP DTMF packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", + ast_inet_ntoa(remote_address.sin_addr), + ntohs(remote_address.sin_port), rtp->send_payload, rtp->seqno, rtp->lastdigitts, res - hdrlen); + } + + /* And now we increment some values for the next time we swing by */ + rtp->seqno++; + rtp->send_duration += 160; + + return 0; +} + +static int ast_rtp_dtmf_end(struct ast_rtp_instance *instance, char digit) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + struct sockaddr_in remote_address; + int hdrlen = 12, res = 0, i = 0; + char data[256]; + unsigned int *rtpheader = (unsigned int*)data; + + ast_rtp_instance_get_remote_address(instance, &remote_address); + + /* Make sure we know where the remote side is so we can send them the packet we construct */ + if (!remote_address.sin_addr.s_addr || !remote_address.sin_port) { + return -1; + } + + /* Convert the given digit to the one we are going to send */ + if ((digit <= '9') && (digit >= '0')) { + digit -= '0'; + } else if (digit == '*') { + digit = 10; + } else if (digit == '#') { + digit = 11; + } else if ((digit >= 'A') && (digit <= 'D')) { + digit = digit - 'A' + 12; + } else if ((digit >= 'a') && (digit <= 'd')) { + digit = digit - 'a' + 12; + } else { + ast_log(LOG_WARNING, "Don't know how to represent '%c'\n", digit); + return -1; + } + + rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000)); + + /* Construct the packet we are going to send */ + rtpheader[0] = htonl((2 << 30) | (1 << 23) | (rtp->send_payload << 16) | (rtp->seqno)); + rtpheader[1] = htonl(rtp->lastdigitts); + rtpheader[2] = htonl(rtp->ssrc); + rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (rtp->send_duration)); + rtpheader[3] |= htonl((1 << 23)); + rtpheader[0] = htonl((2 << 30) | (rtp->send_payload << 16) | (rtp->seqno)); + + /* Send it 3 times, that's the magical number */ + for (i = 0; i < 3; i++) { + res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &remote_address, sizeof(remote_address)); + if (res < 0) { + ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n", + ast_inet_ntoa(remote_address.sin_addr), + ntohs(remote_address.sin_port), strerror(errno)); + } + if (rtp_debug_test_addr(&remote_address)) { + ast_verbose("Sent RTP DTMF packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", + ast_inet_ntoa(remote_address.sin_addr), + ntohs(remote_address.sin_port), rtp->send_payload, rtp->seqno, rtp->lastdigitts, res - hdrlen); + } + } + + /* Oh and we can't forget to turn off the stuff that says we are sending DTMF */ + rtp->lastts += rtp->send_duration; + rtp->sending_digit = 0; + rtp->send_digit = 0; + + return 0; +} + +static void ast_rtp_new_source(struct ast_rtp_instance *instance) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + + /* We simply set this bit so that the next packet sent will have the marker bit turned on */ + ast_set_flag(rtp, FLAG_NEED_MARKER_BIT); + + return; +} + +static unsigned int calc_txstamp(struct ast_rtp *rtp, struct timeval *delivery) +{ + struct timeval t; + long ms; + + if (ast_tvzero(rtp->txcore)) { + rtp->txcore = ast_tvnow(); + rtp->txcore.tv_usec -= rtp->txcore.tv_usec % 20000; + } + + t = (delivery && !ast_tvzero(*delivery)) ? *delivery : ast_tvnow(); + if ((ms = ast_tvdiff_ms(t, rtp->txcore)) < 0) { + ms = 0; + } + rtp->txcore = t; + + return (unsigned int) ms; +} + +static void timeval2ntp(struct timeval tv, unsigned int *msw, unsigned int *lsw) +{ + unsigned int sec, usec, frac; + sec = tv.tv_sec + 2208988800u; /* Sec between 1900 and 1970 */ + usec = tv.tv_usec; + frac = (usec << 12) + (usec << 8) - ((usec * 3650) >> 6); + *msw = sec; + *lsw = frac; +} + +/*! \brief Send RTCP recipient's report */ +static int ast_rtcp_write_rr(const void *data) +{ + struct ast_rtp *rtp = (struct ast_rtp *)data; + int res; + int len = 32; + unsigned int lost; + unsigned int extended; + unsigned int expected; + unsigned int expected_interval; + unsigned int received_interval; + int lost_interval; + struct timeval now; + unsigned int *rtcpheader; + char bdata[1024]; + struct timeval dlsr; + int fraction; + + double rxlost_current; + + if (!rtp || !rtp->rtcp || (&rtp->rtcp->them.sin_addr == 0)) + return 0; + + if (!rtp->rtcp->them.sin_addr.s_addr) { + ast_log(LOG_ERROR, "RTCP RR transmission error, rtcp halted\n"); + AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); + return 0; + } + + extended = rtp->cycles + rtp->lastrxseqno; + expected = extended - rtp->seedrxseqno + 1; + lost = expected - rtp->rxcount; + expected_interval = expected - rtp->rtcp->expected_prior; + rtp->rtcp->expected_prior = expected; + received_interval = rtp->rxcount - rtp->rtcp->received_prior; + rtp->rtcp->received_prior = rtp->rxcount; + lost_interval = expected_interval - received_interval; + + if (lost_interval <= 0) + rtp->rtcp->rxlost = 0; + else rtp->rtcp->rxlost = rtp->rtcp->rxlost; + if (rtp->rtcp->rxlost_count == 0) + rtp->rtcp->minrxlost = rtp->rtcp->rxlost; + if (lost_interval < rtp->rtcp->minrxlost) + rtp->rtcp->minrxlost = rtp->rtcp->rxlost; + if (lost_interval > rtp->rtcp->maxrxlost) + rtp->rtcp->maxrxlost = rtp->rtcp->rxlost; + + rxlost_current = normdev_compute(rtp->rtcp->normdev_rxlost, rtp->rtcp->rxlost, rtp->rtcp->rxlost_count); + rtp->rtcp->stdev_rxlost = stddev_compute(rtp->rtcp->stdev_rxlost, rtp->rtcp->rxlost, rtp->rtcp->normdev_rxlost, rxlost_current, rtp->rtcp->rxlost_count); + rtp->rtcp->normdev_rxlost = rxlost_current; + rtp->rtcp->rxlost_count++; + + if (expected_interval == 0 || lost_interval <= 0) + fraction = 0; + else + fraction = (lost_interval << 8) / expected_interval; + gettimeofday(&now, NULL); + timersub(&now, &rtp->rtcp->rxlsr, &dlsr); + rtcpheader = (unsigned int *)bdata; + rtcpheader[0] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_RR << 16) | ((len/4)-1)); + rtcpheader[1] = htonl(rtp->ssrc); + rtcpheader[2] = htonl(rtp->themssrc); + rtcpheader[3] = htonl(((fraction & 0xff) << 24) | (lost & 0xffffff)); + rtcpheader[4] = htonl((rtp->cycles) | ((rtp->lastrxseqno & 0xffff))); + rtcpheader[5] = htonl((unsigned int)(rtp->rxjitter * 65536.)); + rtcpheader[6] = htonl(rtp->rtcp->themrxlsr); + rtcpheader[7] = htonl((((dlsr.tv_sec * 1000) + (dlsr.tv_usec / 1000)) * 65536) / 1000); + + /*! \note Insert SDES here. Probably should make SDES text equal to mimetypes[code].type (not subtype 'cos + it can change mid call, and SDES can't) */ + rtcpheader[len/4] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2); + rtcpheader[(len/4)+1] = htonl(rtp->ssrc); /* Our SSRC */ + rtcpheader[(len/4)+2] = htonl(0x01 << 24); /* Empty for the moment */ + len += 12; + + res = sendto(rtp->rtcp->s, (unsigned int *)rtcpheader, len, 0, (struct sockaddr *)&rtp->rtcp->them, sizeof(rtp->rtcp->them)); + + if (res < 0) { + ast_log(LOG_ERROR, "RTCP RR transmission error, rtcp halted: %s\n",strerror(errno)); + /* Remove the scheduler */ + AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); + return 0; + } + + rtp->rtcp->rr_count++; + if (rtcp_debug_test_addr(&rtp->rtcp->them)) { + ast_verbose("\n* Sending RTCP RR to %s:%d\n" + " Our SSRC: %u\nTheir SSRC: %u\niFraction lost: %d\nCumulative loss: %u\n" + " IA jitter: %.4f\n" + " Their last SR: %u\n" + " DLSR: %4.4f (sec)\n\n", + ast_inet_ntoa(rtp->rtcp->them.sin_addr), + ntohs(rtp->rtcp->them.sin_port), + rtp->ssrc, rtp->themssrc, fraction, lost, + rtp->rxjitter, + rtp->rtcp->themrxlsr, + (double)(ntohl(rtcpheader[7])/65536.0)); + } + + return res; +} + +/*! \brief Send RTCP sender's report */ +static int ast_rtcp_write_sr(const void *data) +{ + struct ast_rtp *rtp = (struct ast_rtp *)data; + int res; + int len = 0; + struct timeval now; + unsigned int now_lsw; + unsigned int now_msw; + unsigned int *rtcpheader; + unsigned int lost; + unsigned int extended; + unsigned int expected; + unsigned int expected_interval; + unsigned int received_interval; + int lost_interval; + int fraction; + struct timeval dlsr; + char bdata[512]; + + /* Commented condition is always not NULL if rtp->rtcp is not NULL */ + if (!rtp || !rtp->rtcp/* || (&rtp->rtcp->them.sin_addr == 0)*/) + return 0; + + if (!rtp->rtcp->them.sin_addr.s_addr) { /* This'll stop rtcp for this rtp session */ + ast_verbose("RTCP SR transmission error, rtcp halted\n"); + AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); + return 0; + } + + gettimeofday(&now, NULL); + timeval2ntp(now, &now_msw, &now_lsw); /* fill thses ones in from utils.c*/ + rtcpheader = (unsigned int *)bdata; + rtcpheader[1] = htonl(rtp->ssrc); /* Our SSRC */ + rtcpheader[2] = htonl(now_msw); /* now, MSW. gettimeofday() + SEC_BETWEEN_1900_AND_1970*/ + rtcpheader[3] = htonl(now_lsw); /* now, LSW */ + rtcpheader[4] = htonl(rtp->lastts); /* FIXME shouldn't be that, it should be now */ + rtcpheader[5] = htonl(rtp->txcount); /* No. packets sent */ + rtcpheader[6] = htonl(rtp->txoctetcount); /* No. bytes sent */ + len += 28; + + extended = rtp->cycles + rtp->lastrxseqno; + expected = extended - rtp->seedrxseqno + 1; + if (rtp->rxcount > expected) + expected += rtp->rxcount - expected; + lost = expected - rtp->rxcount; + expected_interval = expected - rtp->rtcp->expected_prior; + rtp->rtcp->expected_prior = expected; + received_interval = rtp->rxcount - rtp->rtcp->received_prior; + rtp->rtcp->received_prior = rtp->rxcount; + lost_interval = expected_interval - received_interval; + if (expected_interval == 0 || lost_interval <= 0) + fraction = 0; + else + fraction = (lost_interval << 8) / expected_interval; + timersub(&now, &rtp->rtcp->rxlsr, &dlsr); + rtcpheader[7] = htonl(rtp->themssrc); + rtcpheader[8] = htonl(((fraction & 0xff) << 24) | (lost & 0xffffff)); + rtcpheader[9] = htonl((rtp->cycles) | ((rtp->lastrxseqno & 0xffff))); + rtcpheader[10] = htonl((unsigned int)(rtp->rxjitter * 65536.)); + rtcpheader[11] = htonl(rtp->rtcp->themrxlsr); + rtcpheader[12] = htonl((((dlsr.tv_sec * 1000) + (dlsr.tv_usec / 1000)) * 65536) / 1000); + len += 24; + + rtcpheader[0] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SR << 16) | ((len/4)-1)); + + /* Insert SDES here. Probably should make SDES text equal to mimetypes[code].type (not subtype 'cos */ + /* it can change mid call, and SDES can't) */ + rtcpheader[len/4] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2); + rtcpheader[(len/4)+1] = htonl(rtp->ssrc); /* Our SSRC */ + rtcpheader[(len/4)+2] = htonl(0x01 << 24); /* Empty for the moment */ + len += 12; + + res = sendto(rtp->rtcp->s, (unsigned int *)rtcpheader, len, 0, (struct sockaddr *)&rtp->rtcp->them, sizeof(rtp->rtcp->them)); + if (res < 0) { + ast_log(LOG_ERROR, "RTCP SR transmission error to %s:%d, rtcp halted %s\n",ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port), strerror(errno)); + AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); + return 0; + } + + /* FIXME Don't need to get a new one */ + gettimeofday(&rtp->rtcp->txlsr, NULL); + rtp->rtcp->sr_count++; + + rtp->rtcp->lastsrtxcount = rtp->txcount; + + if (rtcp_debug_test_addr(&rtp->rtcp->them)) { + ast_verbose("* Sent RTCP SR to %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); + ast_verbose(" Our SSRC: %u\n", rtp->ssrc); + ast_verbose(" Sent(NTP): %u.%010u\n", (unsigned int)now.tv_sec, (unsigned int)now.tv_usec*4096); + ast_verbose(" Sent(RTP): %u\n", rtp->lastts); + ast_verbose(" Sent packets: %u\n", rtp->txcount); + ast_verbose(" Sent octets: %u\n", rtp->txoctetcount); + ast_verbose(" Report block:\n"); + ast_verbose(" Fraction lost: %u\n", fraction); + ast_verbose(" Cumulative loss: %u\n", lost); + ast_verbose(" IA jitter: %.4f\n", rtp->rxjitter); + ast_verbose(" Their last SR: %u\n", rtp->rtcp->themrxlsr); + ast_verbose(" DLSR: %4.4f (sec)\n\n", (double)(ntohl(rtcpheader[12])/65536.0)); + } + manager_event(EVENT_FLAG_REPORTING, "RTCPSent", "To %s:%d\r\n" + "OurSSRC: %u\r\n" + "SentNTP: %u.%010u\r\n" + "SentRTP: %u\r\n" + "SentPackets: %u\r\n" + "SentOctets: %u\r\n" + "ReportBlock:\r\n" + "FractionLost: %u\r\n" + "CumulativeLoss: %u\r\n" + "IAJitter: %.4f\r\n" + "TheirLastSR: %u\r\n" + "DLSR: %4.4f (sec)\r\n", + ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port), + rtp->ssrc, + (unsigned int)now.tv_sec, (unsigned int)now.tv_usec*4096, + rtp->lastts, + rtp->txcount, + rtp->txoctetcount, + fraction, + lost, + rtp->rxjitter, + rtp->rtcp->themrxlsr, + (double)(ntohl(rtcpheader[12])/65536.0)); + return res; +} + +/*! \brief Write and RTCP packet to the far end + * \note Decide if we are going to send an SR (with Reception Block) or RR + * RR is sent if we have not sent any rtp packets in the previous interval */ +static int ast_rtcp_write(const void *data) +{ + struct ast_rtp *rtp = (struct ast_rtp *)data; + int res; + + if (!rtp || !rtp->rtcp) + return 0; + + if (rtp->txcount > rtp->rtcp->lastsrtxcount) + res = ast_rtcp_write_sr(data); + else + res = ast_rtcp_write_rr(data); + + return res; +} + +static int ast_rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame *frame, int codec) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + int pred, mark = 0; + unsigned int ms = calc_txstamp(rtp, &frame->delivery); + struct sockaddr_in remote_address; + + if (rtp->sending_digit) { + return 0; + } + + if (frame->frametype == AST_FRAME_VOICE) { + pred = rtp->lastts + frame->samples; + + /* Re-calculate last TS */ + rtp->lastts = rtp->lastts + ms * 8; + if (ast_tvzero(frame->delivery)) { + /* If this isn't an absolute delivery time, Check if it is close to our prediction, + and if so, go with our prediction */ + if (abs(rtp->lastts - pred) < MAX_TIMESTAMP_SKEW) { + rtp->lastts = pred; + } else { + ast_debug(3, "Difference is %d, ms is %d\n", abs(rtp->lastts - pred), ms); + mark = 1; + } + } + } else if (frame->frametype == AST_FRAME_VIDEO) { + mark = frame->subclass & 0x1; + pred = rtp->lastovidtimestamp + frame->samples; + /* Re-calculate last TS */ + rtp->lastts = rtp->lastts + ms * 90; + /* If it's close to our prediction, go for it */ + if (ast_tvzero(frame->delivery)) { + if (abs(rtp->lastts - pred) < 7200) { + rtp->lastts = pred; + rtp->lastovidtimestamp += frame->samples; + } else { + ast_debug(3, "Difference is %d, ms is %d (%d), pred/ts/samples %d/%d/%d\n", abs(rtp->lastts - pred), ms, ms * 90, rtp->lastts, pred, frame->samples); + rtp->lastovidtimestamp = rtp->lastts; + } + } + } else { + pred = rtp->lastotexttimestamp + frame->samples; + /* Re-calculate last TS */ + rtp->lastts = rtp->lastts + ms * 90; + /* If it's close to our prediction, go for it */ + if (ast_tvzero(frame->delivery)) { + if (abs(rtp->lastts - pred) < 7200) { + rtp->lastts = pred; + rtp->lastotexttimestamp += frame->samples; + } else { + ast_debug(3, "Difference is %d, ms is %d (%d), pred/ts/samples %d/%d/%d\n", abs(rtp->lastts - pred), ms, ms * 90, rtp->lastts, pred, frame->samples); + rtp->lastotexttimestamp = rtp->lastts; + } + } + } + + /* If we have been explicitly told to set the marker bit then do so */ + if (ast_test_flag(rtp, FLAG_NEED_MARKER_BIT)) { + mark = 1; + ast_clear_flag(rtp, FLAG_NEED_MARKER_BIT); + } + + /* If the timestamp for non-digt packets has moved beyond the timestamp for digits, update the digit timestamp */ + if (rtp->lastts > rtp->lastdigitts) { + rtp->lastdigitts = rtp->lastts; + } + + if (ast_test_flag(frame, AST_FRFLAG_HAS_TIMING_INFO)) { + rtp->lastts = frame->ts * 8; + } + + ast_rtp_instance_get_remote_address(instance, &remote_address); + + /* If we know the remote address construct a packet and send it out */ + if (remote_address.sin_port && remote_address.sin_addr.s_addr) { + int hdrlen = 12, res; + unsigned char *rtpheader = (unsigned char *)(frame->data.ptr - hdrlen); + + put_unaligned_uint32(rtpheader, htonl((2 << 30) | (codec << 16) | (rtp->seqno) | (mark << 23))); + put_unaligned_uint32(rtpheader + 4, htonl(rtp->lastts)); + put_unaligned_uint32(rtpheader + 8, htonl(rtp->ssrc)); + + if ((res = sendto(rtp->s, (void *)rtpheader, frame->datalen + hdrlen, 0, (struct sockaddr *)&remote_address, sizeof(remote_address))) < 0) { + if (!ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT) || (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT) && (ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) { + ast_debug(1, "RTP Transmission error of packet %d to %s:%d: %s\n", rtp->seqno, ast_inet_ntoa(remote_address.sin_addr), ntohs(remote_address.sin_port), strerror(errno)); + } else if (((ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug) && !ast_test_flag(rtp, FLAG_NAT_INACTIVE_NOWARN)) { + /* Only give this error message once if we are not RTP debugging */ + if (option_debug || rtpdebug) + ast_debug(0, "RTP NAT: Can't write RTP to private address %s:%d, waiting for other end to send audio...\n", ast_inet_ntoa(remote_address.sin_addr), ntohs(remote_address.sin_port)); + ast_set_flag(rtp, FLAG_NAT_INACTIVE_NOWARN); + } + } else { + rtp->txcount++; + rtp->txoctetcount += (res - hdrlen); + + if (rtp->rtcp && rtp->rtcp->schedid < 1) { + ast_debug(1, "Starting RTCP transmission on RTP instance '%p'\n", instance); + rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, rtp); + } + } + + if (rtp_debug_test_addr(&remote_address)) { + ast_verbose("Sent RTP packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", + ast_inet_ntoa(remote_address.sin_addr), ntohs(remote_address.sin_port), codec, rtp->seqno, rtp->lastts, res - hdrlen); + } + } + + rtp->seqno++; + + return 0; +} + +static struct ast_frame *red_t140_to_red(struct rtp_red *red) { + unsigned char *data = red->t140red.data.ptr; + int len = 0; + int i; + + /* replace most aged generation */ + if (red->len[0]) { + for (i = 1; i < red->num_gen+1; i++) + len += red->len[i]; + + memmove(&data[red->hdrlen], &data[red->hdrlen+red->len[0]], len); + } + + /* Store length of each generation and primary data length*/ + for (i = 0; i < red->num_gen; i++) + red->len[i] = red->len[i+1]; + red->len[i] = red->t140.datalen; + + /* write each generation length in red header */ + len = red->hdrlen; + for (i = 0; i < red->num_gen; i++) + len += data[i*4+3] = red->len[i]; + + /* add primary data to buffer */ + memcpy(&data[len], red->t140.data.ptr, red->t140.datalen); + red->t140red.datalen = len + red->t140.datalen; + + /* no primary data and no generations to send */ + if (len == red->hdrlen && !red->t140.datalen) + return NULL; + + /* reset t.140 buffer */ + red->t140.datalen = 0; + + return &red->t140red; +} + +static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *frame) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + struct sockaddr_in remote_address; + int codec, subclass; + + ast_rtp_instance_get_remote_address(instance, &remote_address); + + /* If we don't actually know the remote address don't even bother doing anything */ + if (!remote_address.sin_addr.s_addr) { + ast_debug(1, "No remote address on RTP instance '%p' so dropping frame\n", instance); + return -1; + } + + /* If there is no data length we can't very well send the packet */ + if (!frame->datalen) { + ast_debug(1, "Received frame with no data for RTP instance '%p' so dropping frame\n", instance); + return -1; + } + + /* If the packet is not one our RTP stack supports bail out */ + if (frame->frametype != AST_FRAME_VOICE && frame->frametype != AST_FRAME_VIDEO && frame->frametype != AST_FRAME_TEXT) { + ast_log(LOG_WARNING, "RTP can only send voice, video, and text\n"); + return -1; + } + + if (rtp->red) { + /* return 0; */ + /* no primary data or generations to send */ + if ((frame = red_t140_to_red(rtp->red)) == NULL) + return 0; + } + + /* Grab the subclass and look up the payload we are going to use */ + subclass = frame->subclass; + if (frame->frametype == AST_FRAME_VIDEO) { + subclass &= ~0x1; + } + if ((codec = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(instance), 1, subclass)) < 0) { + ast_log(LOG_WARNING, "Don't know how to send format %s packets with RTP\n", ast_getformatname(frame->subclass)); + return -1; + } + + /* Oh dear, if the format changed we will have to set up a new smoother */ + if (rtp->lasttxformat != subclass) { + ast_debug(1, "Ooh, format changed from %s to %s\n", ast_getformatname(rtp->lasttxformat), ast_getformatname(subclass)); + rtp->lasttxformat = subclass; + if (rtp->smoother) { + ast_smoother_free(rtp->smoother); + rtp->smoother = NULL; + } + } + + /* If no smoother is present see if we have to set one up */ + if (!rtp->smoother) { + struct ast_format_list fmt = ast_codec_pref_getsize(&ast_rtp_instance_get_codecs(instance)->pref, subclass); + + switch (subclass) { + case AST_FORMAT_SPEEX: + case AST_FORMAT_G723_1: + case AST_FORMAT_SIREN7: + case AST_FORMAT_SIREN14: + /* these are all frame-based codecs and cannot be safely run through + a smoother */ + break; + default: + if (fmt.inc_ms) { + if (!(rtp->smoother = ast_smoother_new((fmt.cur_ms * fmt.fr_len) / fmt.inc_ms))) { + ast_log(LOG_WARNING, "Unable to create smoother: format %d ms: %d len: %d\n", subclass, fmt.cur_ms, ((fmt.cur_ms * fmt.fr_len) / fmt.inc_ms)); + return -1; + } + if (fmt.flags) { + ast_smoother_set_flags(rtp->smoother, fmt.flags); + } + ast_debug(1, "Created smoother: format: %d ms: %d len: %d\n", subclass, fmt.cur_ms, ((fmt.cur_ms * fmt.fr_len) / fmt.inc_ms)); + } + } + } + + /* Feed audio frames into the actual function that will create a frame and send it */ + if (rtp->smoother) { + struct ast_frame *f; + + if (ast_smoother_test_flag(rtp->smoother, AST_SMOOTHER_FLAG_BE)) { + ast_smoother_feed_be(rtp->smoother, frame); + } else { + ast_smoother_feed(rtp->smoother, frame); + } + + while ((f = ast_smoother_read(rtp->smoother)) && (f->data.ptr)) { + if (f->subclass == AST_FORMAT_G722) { + f->samples /= 2; + } + + ast_rtp_raw_write(instance, f, codec); + } + } else { + int hdrlen = 12; + struct ast_frame *f = NULL; + + if (frame->offset < hdrlen) { + f = ast_frdup(frame); + } else { + f = frame; + } + if (f->data.ptr) { + ast_rtp_raw_write(instance, f, codec); + } + if (f != frame) { + ast_frfree(f); + } + + } + + return 0; +} + +static void calc_rxstamp(struct timeval *tv, struct ast_rtp *rtp, unsigned int timestamp, int mark) +{ + struct timeval now; + double transit; + double current_time; + double d; + double dtv; + double prog; + + double normdev_rxjitter_current; + if ((!rtp->rxcore.tv_sec && !rtp->rxcore.tv_usec) || mark) { + gettimeofday(&rtp->rxcore, NULL); + rtp->drxcore = (double) rtp->rxcore.tv_sec + (double) rtp->rxcore.tv_usec / 1000000; + /* map timestamp to a real time */ + rtp->seedrxts = timestamp; /* Their RTP timestamp started with this */ + rtp->rxcore.tv_sec -= timestamp / 8000; + rtp->rxcore.tv_usec -= (timestamp % 8000) * 125; + /* Round to 0.1ms for nice, pretty timestamps */ + rtp->rxcore.tv_usec -= rtp->rxcore.tv_usec % 100; + if (rtp->rxcore.tv_usec < 0) { + /* Adjust appropriately if necessary */ + rtp->rxcore.tv_usec += 1000000; + rtp->rxcore.tv_sec -= 1; + } + } + + gettimeofday(&now,NULL); + /* rxcore is the mapping between the RTP timestamp and _our_ real time from gettimeofday() */ + tv->tv_sec = rtp->rxcore.tv_sec + timestamp / 8000; + tv->tv_usec = rtp->rxcore.tv_usec + (timestamp % 8000) * 125; + if (tv->tv_usec >= 1000000) { + tv->tv_usec -= 1000000; + tv->tv_sec += 1; + } + prog = (double)((timestamp-rtp->seedrxts)/8000.); + dtv = (double)rtp->drxcore + (double)(prog); + current_time = (double)now.tv_sec + (double)now.tv_usec/1000000; + transit = current_time - dtv; + d = transit - rtp->rxtransit; + rtp->rxtransit = transit; + if (d<0) + d=-d; + rtp->rxjitter += (1./16.) * (d - rtp->rxjitter); + + if (rtp->rtcp) { + if (rtp->rxjitter > rtp->rtcp->maxrxjitter) + rtp->rtcp->maxrxjitter = rtp->rxjitter; + if (rtp->rtcp->rxjitter_count == 1) + rtp->rtcp->minrxjitter = rtp->rxjitter; + if (rtp->rtcp && rtp->rxjitter < rtp->rtcp->minrxjitter) + rtp->rtcp->minrxjitter = rtp->rxjitter; + + normdev_rxjitter_current = normdev_compute(rtp->rtcp->normdev_rxjitter,rtp->rxjitter,rtp->rtcp->rxjitter_count); + rtp->rtcp->stdev_rxjitter = stddev_compute(rtp->rtcp->stdev_rxjitter,rtp->rxjitter,rtp->rtcp->normdev_rxjitter,normdev_rxjitter_current,rtp->rtcp->rxjitter_count); + + rtp->rtcp->normdev_rxjitter = normdev_rxjitter_current; + rtp->rtcp->rxjitter_count++; + } +} + +static struct ast_frame *send_dtmf(struct ast_rtp_instance *instance, enum ast_frame_type type, int compensate) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + struct sockaddr_in remote_address; + + ast_rtp_instance_get_remote_address(instance, &remote_address); + + if (((compensate && type == AST_FRAME_DTMF_END) || (type == AST_FRAME_DTMF_BEGIN)) && ast_tvcmp(ast_tvnow(), rtp->dtmfmute) < 0) { + ast_debug(1, "Ignore potential DTMF echo from '%s'\n", ast_inet_ntoa(remote_address.sin_addr)); + rtp->resp = 0; + rtp->dtmfsamples = 0; + return &ast_null_frame; + } + ast_debug(1, "Sending dtmf: %d (%c), at %s\n", rtp->resp, rtp->resp, ast_inet_ntoa(remote_address.sin_addr)); + if (rtp->resp == 'X') { + rtp->f.frametype = AST_FRAME_CONTROL; + rtp->f.subclass = AST_CONTROL_FLASH; + } else { + rtp->f.frametype = type; + rtp->f.subclass = rtp->resp; + } + rtp->f.datalen = 0; + rtp->f.samples = 0; + rtp->f.mallocd = 0; + rtp->f.src = "RTP"; + + return &rtp->f; +} + +static struct ast_frame *process_dtmf_rfc2833(struct ast_rtp_instance *instance, unsigned char *data, int len, unsigned int seqno, unsigned int timestamp, struct sockaddr_in *sin, int payloadtype, int mark) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + struct sockaddr_in remote_address; + unsigned int event, event_end, samples; + char resp = 0; + struct ast_frame *f = NULL; + + ast_rtp_instance_get_remote_address(instance, &remote_address); + + /* Figure out event, event end, and samples */ + event = ntohl(*((unsigned int *)(data))); + event >>= 24; + event_end = ntohl(*((unsigned int *)(data))); + event_end <<= 8; + event_end >>= 24; + samples = ntohl(*((unsigned int *)(data))); + samples &= 0xFFFF; + + ast_verbose("Got RTP RFC2833 from %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u, mark %d, event %08x, end %d, duration %-5.5d) \n", ast_inet_ntoa(remote_address.sin_addr), + ntohs(remote_address.sin_port), payloadtype, seqno, timestamp, len, (mark?1:0), event, ((event_end & 0x80)?1:0), samples); + + /* Print out debug if turned on */ + if (rtpdebug || option_debug > 2) + ast_debug(0, "- RTP 2833 Event: %08x (len = %d)\n", event, len); + + /* Figure out what digit was pressed */ + if (event < 10) { + resp = '0' + event; + } else if (event < 11) { + resp = '*'; + } else if (event < 12) { + resp = '#'; + } else if (event < 16) { + resp = 'A' + (event - 12); + } else if (event < 17) { /* Event 16: Hook flash */ + resp = 'X'; + } else { + /* Not a supported event */ + ast_log(LOG_DEBUG, "Ignoring RTP 2833 Event: %08x. Not a DTMF Digit.\n", event); + return &ast_null_frame; + } + + if (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_DTMF_COMPENSATE)) { + if ((rtp->lastevent != timestamp) || (rtp->resp && rtp->resp != resp)) { + rtp->resp = resp; + rtp->dtmfcount = 0; + f = send_dtmf(instance, AST_FRAME_DTMF_END, ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_DTMF_COMPENSATE)); + f->len = 0; + rtp->lastevent = timestamp; + } + } else { + if ((!(rtp->resp) && (!(event_end & 0x80))) || (rtp->resp && rtp->resp != resp)) { + rtp->resp = resp; + f = send_dtmf(instance, AST_FRAME_DTMF_BEGIN, 0); + rtp->dtmfcount = dtmftimeout; + } else if ((event_end & 0x80) && (rtp->lastevent != seqno) && rtp->resp) { + f = send_dtmf(instance, AST_FRAME_DTMF_END, 0); + f->len = ast_tvdiff_ms(ast_samp2tv(samples, 8000), ast_tv(0, 0)); /* XXX hard coded 8kHz */ + rtp->resp = 0; + rtp->dtmfcount = 0; + rtp->lastevent = seqno; + } + } + + rtp->dtmfsamples = samples; + + return f; +} + +static struct ast_frame *process_dtmf_cisco(struct ast_rtp_instance *instance, unsigned char *data, int len, unsigned int seqno, unsigned int timestamp, struct sockaddr_in *sin, int payloadtype, int mark) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + unsigned int event, flags, power; + char resp = 0; + unsigned char seq; + struct ast_frame *f = NULL; + + if (len < 4) { + return NULL; + } + + /* The format of Cisco RTP DTMF packet looks like next: + +0 - sequence number of DTMF RTP packet (begins from 1, + wrapped to 0) + +1 - set of flags + +1 (bit 0) - flaps by different DTMF digits delimited by audio + or repeated digit without audio??? + +2 (+4,+6,...) - power level? (rises from 0 to 32 at begin of tone + then falls to 0 at its end) + +3 (+5,+7,...) - detected DTMF digit (0..9,*,#,A-D,...) + Repeated DTMF information (bytes 4/5, 6/7) is history shifted right + by each new packet and thus provides some redudancy. + + Sample of Cisco RTP DTMF packet is (all data in hex): + 19 07 00 02 12 02 20 02 + showing end of DTMF digit '2'. + + The packets + 27 07 00 02 0A 02 20 02 + 28 06 20 02 00 02 0A 02 + shows begin of new digit '2' with very short pause (20 ms) after + previous digit '2'. Bit +1.0 flips at begin of new digit. + + Cisco RTP DTMF packets comes as replacement of audio RTP packets + so its uses the same sequencing and timestamping rules as replaced + audio packets. Repeat interval of DTMF packets is 20 ms and not rely + on audio framing parameters. Marker bit isn't used within stream of + DTMFs nor audio stream coming immediately after DTMF stream. Timestamps + are not sequential at borders between DTMF and audio streams, + */ + + seq = data[0]; + flags = data[1]; + power = data[2]; + event = data[3] & 0x1f; + + if (option_debug > 2 || rtpdebug) + ast_debug(0, "Cisco DTMF Digit: %02x (len=%d, seq=%d, flags=%02x, power=%d, history count=%d)\n", event, len, seq, flags, power, (len - 4) / 2); + if (event < 10) { + resp = '0' + event; + } else if (event < 11) { + resp = '*'; + } else if (event < 12) { + resp = '#'; + } else if (event < 16) { + resp = 'A' + (event - 12); + } else if (event < 17) { + resp = 'X'; + } + if ((!rtp->resp && power) || (rtp->resp && (rtp->resp != resp))) { + rtp->resp = resp; + /* Why we should care on DTMF compensation at reception? */ + if (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_DTMF_COMPENSATE)) { + f = send_dtmf(instance, AST_FRAME_DTMF_BEGIN, 0); + rtp->dtmfsamples = 0; + } + } else if ((rtp->resp == resp) && !power) { + f = send_dtmf(instance, AST_FRAME_DTMF_END, ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_DTMF_COMPENSATE)); + f->samples = rtp->dtmfsamples * 8; + rtp->resp = 0; + } else if (rtp->resp == resp) + rtp->dtmfsamples += 20 * 8; + rtp->dtmfcount = dtmftimeout; + + return f; +} + +static struct ast_frame *process_cn_rfc3389(struct ast_rtp_instance *instance, unsigned char *data, int len, unsigned int seqno, unsigned int timestamp, struct sockaddr_in *sin, int payloadtype, int mark) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + + /* Convert comfort noise into audio with various codecs. Unfortunately this doesn't + totally help us out becuase we don't have an engine to keep it going and we are not + guaranteed to have it every 20ms or anything */ + if (rtpdebug) + ast_debug(0, "- RTP 3389 Comfort noise event: Level %d (len = %d)\n", rtp->lastrxformat, len); + + if (ast_test_flag(rtp, FLAG_3389_WARNING)) { + struct sockaddr_in remote_address; + + ast_rtp_instance_get_remote_address(instance, &remote_address); + + ast_log(LOG_NOTICE, "Comfort noise support incomplete in Asterisk (RFC 3389). Please turn off on client if possible. Client IP: %s\n", + ast_inet_ntoa(remote_address.sin_addr)); + ast_set_flag(rtp, FLAG_3389_WARNING); + } + + /* Must have at least one byte */ + if (!len) + return NULL; + if (len < 24) { + rtp->f.data.ptr = rtp->rawdata + AST_FRIENDLY_OFFSET; + rtp->f.datalen = len - 1; + rtp->f.offset = AST_FRIENDLY_OFFSET; + memcpy(rtp->f.data.ptr, data + 1, len - 1); + } else { + rtp->f.data.ptr = NULL; + rtp->f.offset = 0; + rtp->f.datalen = 0; + } + rtp->f.frametype = AST_FRAME_CNG; + rtp->f.subclass = data[0] & 0x7f; + rtp->f.datalen = len - 1; + rtp->f.samples = 0; + rtp->f.delivery.tv_usec = rtp->f.delivery.tv_sec = 0; + + return &rtp->f; +} + +static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + struct sockaddr_in sin; + socklen_t len = sizeof(sin); + unsigned int rtcpdata[8192 + AST_FRIENDLY_OFFSET]; + unsigned int *rtcpheader = (unsigned int *)(rtcpdata + AST_FRIENDLY_OFFSET); + int res, packetwords, position = 0; + struct ast_frame *f = &ast_null_frame; + + /* Read in RTCP data from the socket */ + if ((res = recvfrom(rtp->rtcp->s, rtcpdata + AST_FRIENDLY_OFFSET, sizeof(rtcpdata) - sizeof(unsigned int) * AST_FRIENDLY_OFFSET, 0, (struct sockaddr *)&sin, &len)) < 0) { + ast_assert(errno != EBADF); + if (errno != EAGAIN) { + ast_log(LOG_WARNING, "RTCP Read error: %s. Hanging up.\n", strerror(errno)); + return NULL; + } + return &ast_null_frame; + } + + packetwords = res / 4; + + if (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT)) { + /* Send to whoever sent to us */ + if ((rtp->rtcp->them.sin_addr.s_addr != sin.sin_addr.s_addr) || + (rtp->rtcp->them.sin_port != sin.sin_port)) { + memcpy(&rtp->rtcp->them, &sin, sizeof(rtp->rtcp->them)); + if (option_debug || rtpdebug) + ast_debug(0, "RTCP NAT: Got RTCP from other end. Now sending to address %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); + } + } + + ast_debug(1, "Got RTCP report of %d bytes\n", res); + + while (position < packetwords) { + int i, pt, rc; + unsigned int length, dlsr, lsr, msw, lsw, comp; + struct timeval now; + double rttsec, reported_jitter, reported_normdev_jitter_current, normdevrtt_current, reported_lost, reported_normdev_lost_current; + uint64_t rtt = 0; + + i = position; + length = ntohl(rtcpheader[i]); + pt = (length & 0xff0000) >> 16; + rc = (length & 0x1f000000) >> 24; + length &= 0xffff; + + if ((i + length) > packetwords) { + if (option_debug || rtpdebug) + ast_log(LOG_DEBUG, "RTCP Read too short\n"); + return &ast_null_frame; + } + + if (rtcp_debug_test_addr(&sin)) { + ast_verbose("\n\nGot RTCP from %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + ast_verbose("PT: %d(%s)\n", pt, (pt == 200) ? "Sender Report" : (pt == 201) ? "Receiver Report" : (pt == 192) ? "H.261 FUR" : "Unknown"); + ast_verbose("Reception reports: %d\n", rc); + ast_verbose("SSRC of sender: %u\n", rtcpheader[i + 1]); + } + + i += 2; /* Advance past header and ssrc */ + + switch (pt) { + case RTCP_PT_SR: + gettimeofday(&rtp->rtcp->rxlsr,NULL); /* To be able to populate the dlsr */ + rtp->rtcp->spc = ntohl(rtcpheader[i+3]); + rtp->rtcp->soc = ntohl(rtcpheader[i + 4]); + rtp->rtcp->themrxlsr = ((ntohl(rtcpheader[i]) & 0x0000ffff) << 16) | ((ntohl(rtcpheader[i + 1]) & 0xffff0000) >> 16); /* Going to LSR in RR*/ + + if (rtcp_debug_test_addr(&sin)) { + ast_verbose("NTP timestamp: %lu.%010lu\n", (unsigned long) ntohl(rtcpheader[i]), (unsigned long) ntohl(rtcpheader[i + 1]) * 4096); + ast_verbose("RTP timestamp: %lu\n", (unsigned long) ntohl(rtcpheader[i + 2])); + ast_verbose("SPC: %lu\tSOC: %lu\n", (unsigned long) ntohl(rtcpheader[i + 3]), (unsigned long) ntohl(rtcpheader[i + 4])); + } + i += 5; + if (rc < 1) + break; + /* Intentional fall through */ + case RTCP_PT_RR: + /* Don't handle multiple reception reports (rc > 1) yet */ + /* Calculate RTT per RFC */ + gettimeofday(&now, NULL); + timeval2ntp(now, &msw, &lsw); + if (ntohl(rtcpheader[i + 4]) && ntohl(rtcpheader[i + 5])) { /* We must have the LSR && DLSR */ + comp = ((msw & 0xffff) << 16) | ((lsw & 0xffff0000) >> 16); + lsr = ntohl(rtcpheader[i + 4]); + dlsr = ntohl(rtcpheader[i + 5]); + rtt = comp - lsr - dlsr; + + /* Convert end to end delay to usec (keeping the calculation in 64bit space) + sess->ee_delay = (eedelay * 1000) / 65536; */ + if (rtt < 4294) { + rtt = (rtt * 1000000) >> 16; + } else { + rtt = (rtt * 1000) >> 16; + rtt *= 1000; + } + rtt = rtt / 1000.; + rttsec = rtt / 1000.; + rtp->rtcp->rtt = rttsec; + + if (comp - dlsr >= lsr) { + rtp->rtcp->accumulated_transit += rttsec; + + if (rtp->rtcp->rtt_count == 0) + rtp->rtcp->minrtt = rttsec; + + if (rtp->rtcp->maxrtt<rttsec) + rtp->rtcp->maxrtt = rttsec; + if (rtp->rtcp->minrtt>rttsec) + rtp->rtcp->minrtt = rttsec; + + normdevrtt_current = normdev_compute(rtp->rtcp->normdevrtt, rttsec, rtp->rtcp->rtt_count); + + rtp->rtcp->stdevrtt = stddev_compute(rtp->rtcp->stdevrtt, rttsec, rtp->rtcp->normdevrtt, normdevrtt_current, rtp->rtcp->rtt_count); + + rtp->rtcp->normdevrtt = normdevrtt_current; + + rtp->rtcp->rtt_count++; + } else if (rtcp_debug_test_addr(&sin)) { + ast_verbose("Internal RTCP NTP clock skew detected: " + "lsr=%u, now=%u, dlsr=%u (%d:%03dms), " + "diff=%d\n", + lsr, comp, dlsr, dlsr / 65536, + (dlsr % 65536) * 1000 / 65536, + dlsr - (comp - lsr)); + } + } + + rtp->rtcp->reported_jitter = ntohl(rtcpheader[i + 3]); + reported_jitter = (double) rtp->rtcp->reported_jitter; + + if (rtp->rtcp->reported_jitter_count == 0) + rtp->rtcp->reported_minjitter = reported_jitter; + + if (reported_jitter < rtp->rtcp->reported_minjitter) + rtp->rtcp->reported_minjitter = reported_jitter; + + if (reported_jitter > rtp->rtcp->reported_maxjitter) + rtp->rtcp->reported_maxjitter = reported_jitter; + + reported_normdev_jitter_current = normdev_compute(rtp->rtcp->reported_normdev_jitter, reported_jitter, rtp->rtcp->reported_jitter_count); + + rtp->rtcp->reported_stdev_jitter = stddev_compute(rtp->rtcp->reported_stdev_jitter, reported_jitter, rtp->rtcp->reported_normdev_jitter, reported_normdev_jitter_current, rtp->rtcp->reported_jitter_count); + + rtp->rtcp->reported_normdev_jitter = reported_normdev_jitter_current; + + rtp->rtcp->reported_lost = ntohl(rtcpheader[i + 1]) & 0xffffff; + + reported_lost = (double) rtp->rtcp->reported_lost; + + /* using same counter as for jitter */ + if (rtp->rtcp->reported_jitter_count == 0) + rtp->rtcp->reported_minlost = reported_lost; + + if (reported_lost < rtp->rtcp->reported_minlost) + rtp->rtcp->reported_minlost = reported_lost; + + if (reported_lost > rtp->rtcp->reported_maxlost) + rtp->rtcp->reported_maxlost = reported_lost; + reported_normdev_lost_current = normdev_compute(rtp->rtcp->reported_normdev_lost, reported_lost, rtp->rtcp->reported_jitter_count); + + rtp->rtcp->reported_stdev_lost = stddev_compute(rtp->rtcp->reported_stdev_lost, reported_lost, rtp->rtcp->reported_normdev_lost, reported_normdev_lost_current, rtp->rtcp->reported_jitter_count); + + rtp->rtcp->reported_normdev_lost = reported_normdev_lost_current; + + rtp->rtcp->reported_jitter_count++; + + if (rtcp_debug_test_addr(&sin)) { + ast_verbose(" Fraction lost: %ld\n", (((long) ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24)); + ast_verbose(" Packets lost so far: %d\n", rtp->rtcp->reported_lost); + ast_verbose(" Highest sequence number: %ld\n", (long) (ntohl(rtcpheader[i + 2]) & 0xffff)); + ast_verbose(" Sequence number cycles: %ld\n", (long) (ntohl(rtcpheader[i + 2]) & 0xffff) >> 16); + ast_verbose(" Interarrival jitter: %u\n", rtp->rtcp->reported_jitter); + ast_verbose(" Last SR(our NTP): %lu.%010lu\n",(unsigned long) ntohl(rtcpheader[i + 4]) >> 16,((unsigned long) ntohl(rtcpheader[i + 4]) << 16) * 4096); + ast_verbose(" DLSR: %4.4f (sec)\n",ntohl(rtcpheader[i + 5])/65536.0); + if (rtt) + ast_verbose(" RTT: %lu(sec)\n", (unsigned long) rtt); + } + if (rtt) { + manager_event(EVENT_FLAG_REPORTING, "RTCPReceived", "From %s:%d\r\n" + "PT: %d(%s)\r\n" + "ReceptionReports: %d\r\n" + "SenderSSRC: %u\r\n" + "FractionLost: %ld\r\n" + "PacketsLost: %d\r\n" + "HighestSequence: %ld\r\n" + "SequenceNumberCycles: %ld\r\n" + "IAJitter: %u\r\n" + "LastSR: %lu.%010lu\r\n" + "DLSR: %4.4f(sec)\r\n" + "RTT: %llu(sec)\r\n", + ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), + pt, (pt == 200) ? "Sender Report" : (pt == 201) ? "Receiver Report" : (pt == 192) ? "H.261 FUR" : "Unknown", + rc, + rtcpheader[i + 1], + (((long) ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24), + rtp->rtcp->reported_lost, + (long) (ntohl(rtcpheader[i + 2]) & 0xffff), + (long) (ntohl(rtcpheader[i + 2]) & 0xffff) >> 16, + rtp->rtcp->reported_jitter, + (unsigned long) ntohl(rtcpheader[i + 4]) >> 16, ((unsigned long) ntohl(rtcpheader[i + 4]) << 16) * 4096, + ntohl(rtcpheader[i + 5])/65536.0, + (unsigned long long)rtt); + } else { + manager_event(EVENT_FLAG_REPORTING, "RTCPReceived", "From %s:%d\r\n" + "PT: %d(%s)\r\n" + "ReceptionReports: %d\r\n" + "SenderSSRC: %u\r\n" + "FractionLost: %ld\r\n" + "PacketsLost: %d\r\n" + "HighestSequence: %ld\r\n" + "SequenceNumberCycles: %ld\r\n" + "IAJitter: %u\r\n" + "LastSR: %lu.%010lu\r\n" + "DLSR: %4.4f(sec)\r\n", + ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), + pt, (pt == 200) ? "Sender Report" : (pt == 201) ? "Receiver Report" : (pt == 192) ? "H.261 FUR" : "Unknown", + rc, + rtcpheader[i + 1], + (((long) ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24), + rtp->rtcp->reported_lost, + (long) (ntohl(rtcpheader[i + 2]) & 0xffff), + (long) (ntohl(rtcpheader[i + 2]) & 0xffff) >> 16, + rtp->rtcp->reported_jitter, + (unsigned long) ntohl(rtcpheader[i + 4]) >> 16, + ((unsigned long) ntohl(rtcpheader[i + 4]) << 16) * 4096, + ntohl(rtcpheader[i + 5])/65536.0); + } + break; + case RTCP_PT_FUR: + if (rtcp_debug_test_addr(&sin)) + ast_verbose("Received an RTCP Fast Update Request\n"); + rtp->f.frametype = AST_FRAME_CONTROL; + rtp->f.subclass = AST_CONTROL_VIDUPDATE; + rtp->f.datalen = 0; + rtp->f.samples = 0; + rtp->f.mallocd = 0; + rtp->f.src = "RTP"; + f = &rtp->f; + break; + case RTCP_PT_SDES: + if (rtcp_debug_test_addr(&sin)) + ast_verbose("Received an SDES from %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); + break; + case RTCP_PT_BYE: + if (rtcp_debug_test_addr(&sin)) + ast_verbose("Received a BYE from %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); + break; + default: + ast_debug(1, "Unknown RTCP packet (pt=%d) received from %s:%d\n", pt, ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); + break; + } + position += (length + 1); + } + + rtp->rtcp->rtcp_info = 1; + + return f; +} + +static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int *rtpheader, int len, int hdrlen) +{ + struct ast_rtp_instance *instance1 = ast_rtp_instance_get_bridged(instance); + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance), *bridged = ast_rtp_instance_get_data(instance1); + int res = 0, payload = 0, bridged_payload = 0, mark; + struct ast_rtp_payload_type payload_type; + int reconstruct = ntohl(rtpheader[0]); + struct sockaddr_in remote_address; + + /* Get fields from packet */ + payload = (reconstruct & 0x7f0000) >> 16; + mark = (((reconstruct & 0x800000) >> 23) != 0); + + /* Check what the payload value should be */ + payload_type = ast_rtp_codecs_payload_lookup(ast_rtp_instance_get_codecs(instance), payload); + + /* Otherwise adjust bridged payload to match */ + bridged_payload = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(instance1), payload_type.asterisk_format, payload_type.code); + + /* If the payload coming in is not one of the negotiated ones then send it to the core, this will cause formats to change and the bridge to break */ + if (!(ast_rtp_instance_get_codecs(instance1)->payloads[bridged_payload].code)) { + return -1; + } + + /* If the marker bit has been explicitly set turn it on */ + if (ast_test_flag(rtp, FLAG_NEED_MARKER_BIT)) { + mark = 1; + ast_clear_flag(rtp, FLAG_NEED_MARKER_BIT); + } + + /* Reconstruct part of the packet */ + reconstruct &= 0xFF80FFFF; + reconstruct |= (bridged_payload << 16); + reconstruct |= (mark << 23); + rtpheader[0] = htonl(reconstruct); + + ast_rtp_instance_get_remote_address(instance1, &remote_address); + + /* Send the packet back out */ + res = sendto(bridged->s, (void *)rtpheader, len, 0, (struct sockaddr *)&remote_address, sizeof(remote_address)); + if (res < 0) { + if (!ast_rtp_instance_get_prop(instance1, AST_RTP_PROPERTY_NAT) || (ast_rtp_instance_get_prop(instance1, AST_RTP_PROPERTY_NAT) && (ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) { + ast_debug(1, "RTP Transmission error of packet to %s:%d: %s\n", ast_inet_ntoa(remote_address.sin_addr), ntohs(remote_address.sin_port), strerror(errno)); + } else if (((ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug) && !ast_test_flag(bridged, FLAG_NAT_INACTIVE_NOWARN)) { + if (option_debug || rtpdebug) + ast_debug(0, "RTP NAT: Can't write RTP to private address %s:%d, waiting for other end to send audio...\n", ast_inet_ntoa(remote_address.sin_addr), ntohs(remote_address.sin_port)); + ast_set_flag(bridged, FLAG_NAT_INACTIVE_NOWARN); + } + return 0; + } else if (rtp_debug_test_addr(&remote_address)) { + ast_verbose("Sent RTP P2P packet to %s:%u (type %-2.2d, len %-6.6u)\n", ast_inet_ntoa(remote_address.sin_addr), ntohs(remote_address.sin_port), bridged_payload, len - hdrlen); + } + + return 0; +} + +static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtcp) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + struct sockaddr_in sin; + socklen_t len = sizeof(sin); + int res, hdrlen = 12, version, payloadtype, padding, mark, ext, cc, prev_seqno; + unsigned int *rtpheader = (unsigned int*)(rtp->rawdata + AST_FRIENDLY_OFFSET), seqno, ssrc, timestamp; + struct ast_rtp_payload_type payload; + struct sockaddr_in remote_address; + + /* If this is actually RTCP let's hop on over and handle it */ + if (rtcp) { + if (rtp->rtcp) { + return ast_rtcp_read(instance); + } + return &ast_null_frame; + } + + /* If we are currently sending DTMF to the remote party send a continuation packet */ + if (rtp->sending_digit) { + ast_rtp_dtmf_continuation(instance); + } + + /* Actually read in the data from the socket */ + if ((res = recvfrom(rtp->s, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET, 0, (struct sockaddr*)&sin, &len)) < 0) { + ast_assert(errno != EBADF); + if (errno != EAGAIN) { + ast_log(LOG_WARNING, "RTP Read error: %s. Hanging up.\n", strerror(errno)); + return NULL; + } + return &ast_null_frame; + } + + /* Make sure the data that was read in is actually enough to make up an RTP packet */ + if (res < hdrlen) { + ast_log(LOG_WARNING, "RTP Read too short\n"); + return &ast_null_frame; + } + + /* If strict RTP protection is enabled see if we need to learn the remote address or if we need to drop the packet */ + if (rtp->strict_rtp_state == STRICT_RTP_LEARN) { + memcpy(&rtp->strict_rtp_address, &sin, sizeof(rtp->strict_rtp_address)); + rtp->strict_rtp_state = STRICT_RTP_CLOSED; + } else if (rtp->strict_rtp_state == STRICT_RTP_CLOSED) { + if ((rtp->strict_rtp_address.sin_addr.s_addr != sin.sin_addr.s_addr) || (rtp->strict_rtp_address.sin_port != sin.sin_port)) { + ast_debug(1, "Received RTP packet from %s:%d, dropping due to strict RTP protection. Expected it to be from %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), ast_inet_ntoa(rtp->strict_rtp_address.sin_addr), ntohs(rtp->strict_rtp_address.sin_port)); + return &ast_null_frame; + } + } + + /* Get fields and verify this is an RTP packet */ + seqno = ntohl(rtpheader[0]); + + ast_rtp_instance_get_remote_address(instance, &remote_address); + + if (!(version = (seqno & 0xC0000000) >> 30)) { + if ((ast_stun_handle_packet(rtp->s, &sin, rtp->rawdata + AST_FRIENDLY_OFFSET, res, NULL, NULL) == AST_STUN_ACCEPT) && + (!remote_address.sin_port && !remote_address.sin_addr.s_addr)) { + ast_rtp_instance_set_remote_address(instance, &sin); + } + return &ast_null_frame; + } + + /* If symmetric RTP is enabled see if the remote side is not what we expected and change where we are sending audio */ + if (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT)) { + if ((remote_address.sin_addr.s_addr != sin.sin_addr.s_addr) || + (remote_address.sin_port != sin.sin_port)) { + ast_rtp_instance_set_remote_address(instance, &sin); + memcpy(&remote_address, &sin, sizeof(remote_address)); + if (rtp->rtcp) { + memcpy(&rtp->rtcp->them, &sin, sizeof(rtp->rtcp->them)); + rtp->rtcp->them.sin_port = htons(ntohs(sin.sin_port)+1); + } + rtp->rxseqno = 0; + ast_set_flag(rtp, FLAG_NAT_ACTIVE); + if (option_debug || rtpdebug) + ast_debug(0, "RTP NAT: Got audio from other end. Now sending to address %s:%d\n", ast_inet_ntoa(remote_address.sin_addr), ntohs(remote_address.sin_port)); + } + } + + /* If we are directly bridged to another instance send the audio directly out */ + if (ast_rtp_instance_get_bridged(instance) && !bridge_p2p_rtp_write(instance, rtpheader, res, hdrlen)) { + return &ast_null_frame; + } + + /* If the version is not what we expected by this point then just drop the packet */ + if (version != 2) { + return &ast_null_frame; + } + + /* Pull out the various other fields we will need */ + payloadtype = (seqno & 0x7f0000) >> 16; + padding = seqno & (1 << 29); + mark = seqno & (1 << 23); + ext = seqno & (1 << 28); + cc = (seqno & 0xF000000) >> 24; + seqno &= 0xffff; + timestamp = ntohl(rtpheader[1]); + ssrc = ntohl(rtpheader[2]); + + /* Force a marker bit if the SSRC changes */ + if (!mark && rtp->rxssrc && rtp->rxssrc != ssrc) { + if (option_debug || rtpdebug) { + ast_debug(1, "Forcing Marker bit, because SSRC has changed\n"); + } + mark = 1; + } + + /* Remove any padding bytes that may be present */ + if (padding) { + res -= rtp->rawdata[AST_FRIENDLY_OFFSET + res - 1]; + } + + /* Skip over any CSRC fields */ + if (cc) { + hdrlen += cc * 4; + } + + /* Look for any RTP extensions, currently we do not support any */ + if (ext) { + hdrlen += (ntohl(rtpheader[hdrlen/4]) & 0xffff) << 2; + hdrlen += 4; + if (option_debug) { + int profile; + profile = (ntohl(rtpheader[3]) & 0xffff0000) >> 16; + if (profile == 0x505a) + ast_debug(1, "Found Zfone extension in RTP stream - zrtp - not supported.\n"); + else + ast_debug(1, "Found unknown RTP Extensions %x\n", profile); + } + } + + /* Make sure after we potentially mucked with the header length that it is once again valid */ + if (res < hdrlen) { + ast_log(LOG_WARNING, "RTP Read too short (%d, expecting %d\n", res, hdrlen); + return &ast_null_frame; + } + + rtp->rxcount++; + if (rtp->rxcount == 1) { + rtp->seedrxseqno = seqno; + } + + /* Do not schedule RR if RTCP isn't run */ + if (rtp->rtcp && rtp->rtcp->them.sin_addr.s_addr && rtp->rtcp->schedid < 1) { + /* Schedule transmission of Receiver Report */ + rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, rtp); + } + if ((int)rtp->lastrxseqno - (int)seqno > 100) /* if so it would indicate that the sender cycled; allow for misordering */ + rtp->cycles += RTP_SEQ_MOD; + + prev_seqno = rtp->lastrxseqno; + rtp->lastrxseqno = seqno; + + if (!rtp->themssrc) { + rtp->themssrc = ntohl(rtpheader[2]); /* Record their SSRC to put in future RR */ + } + + if (rtp_debug_test_addr(&sin)) { + ast_verbose("Got RTP packet from %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", + ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), payloadtype, seqno, timestamp,res - hdrlen); + } + + payload = ast_rtp_codecs_payload_lookup(ast_rtp_instance_get_codecs(instance), payloadtype); + + /* If the payload is not actually an Asterisk one but a special one pass it off to the respective handler */ + if (!payload.asterisk_format) { + struct ast_frame *f = NULL; + + if (payload.code == AST_RTP_DTMF) { + f = process_dtmf_rfc2833(instance, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno, timestamp, &sin, payloadtype, mark); + } else if (payload.code == AST_RTP_CISCO_DTMF) { + f = process_dtmf_cisco(instance, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno, timestamp, &sin, payloadtype, mark); + } else if (payload.code == AST_RTP_CN) { + f = process_cn_rfc3389(instance, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno, timestamp, &sin, payloadtype, mark); + } else { + ast_log(LOG_NOTICE, "Unknown RTP codec %d received from '%s'\n", payloadtype, ast_inet_ntoa(remote_address.sin_addr)); + } + + return f ? f : &ast_null_frame; + } + + rtp->lastrxformat = rtp->f.subclass = payload.code; + rtp->f.frametype = (rtp->f.subclass & AST_FORMAT_AUDIO_MASK) ? AST_FRAME_VOICE : (rtp->f.subclass & AST_FORMAT_VIDEO_MASK) ? AST_FRAME_VIDEO : AST_FRAME_TEXT; + + rtp->rxseqno = seqno; + rtp->lastrxts = timestamp; + + rtp->f.src = "RTP"; + rtp->f.mallocd = 0; + rtp->f.datalen = res - hdrlen; + rtp->f.data.ptr = rtp->rawdata + hdrlen + AST_FRIENDLY_OFFSET; + rtp->f.offset = hdrlen + AST_FRIENDLY_OFFSET; + rtp->f.seqno = seqno; + + if (rtp->f.subclass == AST_FORMAT_T140 && (int)seqno - (prev_seqno+1) > 0 && (int)seqno - (prev_seqno+1) < 10) { + unsigned char *data = rtp->f.data.ptr; + + memmove(rtp->f.data.ptr+3, rtp->f.data.ptr, rtp->f.datalen); + rtp->f.datalen +=3; + *data++ = 0xEF; + *data++ = 0xBF; + *data = 0xBD; + } + + if (rtp->f.subclass == AST_FORMAT_T140RED) { + unsigned char *data = rtp->f.data.ptr; + unsigned char *header_end; + int num_generations; + int header_length; + int len; + int diff =(int)seqno - (prev_seqno+1); /* if diff = 0, no drop*/ + int x; + + rtp->f.subclass = AST_FORMAT_T140; + header_end = memchr(data, ((*data) & 0x7f), rtp->f.datalen); + header_end++; + + header_length = header_end - data; + num_generations = header_length / 4; + len = header_length; + + if (!diff) { + for (x = 0; x < num_generations; x++) + len += data[x * 4 + 3]; + + if (!(rtp->f.datalen - len)) + return &ast_null_frame; + + rtp->f.data.ptr += len; + rtp->f.datalen -= len; + } else if (diff > num_generations && diff < 10) { + len -= 3; + rtp->f.data.ptr += len; + rtp->f.datalen -= len; + + data = rtp->f.data.ptr; + *data++ = 0xEF; + *data++ = 0xBF; + *data = 0xBD; + } else { + for ( x = 0; x < num_generations - diff; x++) + len += data[x * 4 + 3]; + + rtp->f.data.ptr += len; + rtp->f.datalen -= len; + } + } + + if (rtp->f.subclass & AST_FORMAT_AUDIO_MASK) { + rtp->f.samples = ast_codec_get_samples(&rtp->f); + if (rtp->f.subclass == AST_FORMAT_SLINEAR) + ast_frame_byteswap_be(&rtp->f); + calc_rxstamp(&rtp->f.delivery, rtp, timestamp, mark); + /* Add timing data to let ast_generic_bridge() put the frame into a jitterbuf */ + ast_set_flag(&rtp->f, AST_FRFLAG_HAS_TIMING_INFO); + rtp->f.ts = timestamp / 8; + rtp->f.len = rtp->f.samples / ((ast_format_rate(rtp->f.subclass) / 1000)); + } else if (rtp->f.subclass & AST_FORMAT_VIDEO_MASK) { + /* Video -- samples is # of samples vs. 90000 */ + if (!rtp->lastividtimestamp) + rtp->lastividtimestamp = timestamp; + rtp->f.samples = timestamp - rtp->lastividtimestamp; + rtp->lastividtimestamp = timestamp; + rtp->f.delivery.tv_sec = 0; + rtp->f.delivery.tv_usec = 0; + /* Pass the RTP marker bit as bit 0 in the subclass field. + * This is ok because subclass is actually a bitmask, and + * the low bits represent audio formats, that are not + * involved here since we deal with video. + */ + if (mark) + rtp->f.subclass |= 0x1; + } else { + /* TEXT -- samples is # of samples vs. 1000 */ + if (!rtp->lastitexttimestamp) + rtp->lastitexttimestamp = timestamp; + rtp->f.samples = timestamp - rtp->lastitexttimestamp; + rtp->lastitexttimestamp = timestamp; + rtp->f.delivery.tv_sec = 0; + rtp->f.delivery.tv_usec = 0; + } + + return &rtp->f; +} + +static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_property property, int value) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + + if (property == AST_RTP_PROPERTY_RTCP) { + if (rtp->rtcp) { + ast_debug(1, "Ignoring duplicate RTCP property on RTP instance '%p'\n", instance); + return; + } + if (!(rtp->rtcp = ast_calloc(1, sizeof(*rtp->rtcp)))) { + return; + } + if ((rtp->rtcp->s = create_new_socket("RTCP")) < 0) { + ast_debug(1, "Failed to create a new socket for RTCP on instance '%p'\n", instance); + ast_free(rtp->rtcp); + rtp->rtcp = NULL; + return; + } + + /* Grab the IP address and port we are going to use */ + ast_rtp_instance_get_local_address(instance, &rtp->rtcp->us); + rtp->rtcp->us.sin_port = htons(ntohs(rtp->rtcp->us.sin_port) + 1); + + /* Try to actually bind to the IP address and port we are going to use for RTCP, if this fails we have to bail out */ + if (bind(rtp->rtcp->s, (struct sockaddr*)&rtp->rtcp->us, sizeof(rtp->rtcp->us))) { + ast_debug(1, "Failed to setup RTCP on RTP instance '%p'\n", instance); + close(rtp->rtcp->s); + ast_free(rtp->rtcp); + rtp->rtcp = NULL; + return; + } + + ast_debug(1, "Setup RTCP on RTP instance '%p'\n", instance); + rtp->rtcp->schedid = -1; + + return; + } + + return; +} + +static int ast_rtp_fd(struct ast_rtp_instance *instance, int rtcp) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + + return rtcp ? (rtp->rtcp ? rtp->rtcp->s : -1) : rtp->s; +} + +static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct sockaddr_in *sin) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + + if (rtp->rtcp) { + ast_debug(1, "Setting RTCP address on RTP instance '%p'\n", instance); + memcpy(&rtp->rtcp->them, sin, sizeof(rtp->rtcp->them)); + rtp->rtcp->them.sin_port = htons(ntohs(sin->sin_port) + 1); + } + + rtp->rxseqno = 0; + + if (strictrtp) { + rtp->strict_rtp_state = STRICT_RTP_LEARN; + } + + return; +} + +/*! \brief Write t140 redundacy frame + * \param data primary data to be buffered + */ +static int red_write(const void *data) +{ + struct ast_rtp_instance *instance = (struct ast_rtp_instance*) data; + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + + ast_rtp_write(instance, &rtp->red->t140); + + return 1; +} + +static int rtp_red_init(struct ast_rtp_instance *instance, int buffer_time, int *payloads, int generations) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + int x; + + if (!(rtp->red = ast_calloc(1, sizeof(*rtp->red)))) { + return -1; + } + + rtp->red->t140.frametype = AST_FRAME_TEXT; + rtp->red->t140.subclass = AST_FORMAT_T140RED; + rtp->red->t140.data.ptr = &rtp->red->buf_data; + + rtp->red->t140.ts = 0; + rtp->red->t140red = rtp->red->t140; + rtp->red->t140red.data.ptr = &rtp->red->t140red_data; + rtp->red->t140red.datalen = 0; + rtp->red->ti = buffer_time; + rtp->red->num_gen = generations; + rtp->red->hdrlen = generations * 4 + 1; + rtp->red->prev_ts = 0; + + for (x = 0; x < generations; x++) { + rtp->red->pt[x] = payloads[x]; + rtp->red->pt[x] |= 1 << 7; /* mark redundant generations pt */ + rtp->red->t140red_data[x*4] = rtp->red->pt[x]; + } + rtp->red->t140red_data[x*4] = rtp->red->pt[x] = payloads[x]; /* primary pt */ + rtp->red->schedid = ast_sched_add(rtp->sched, generations, red_write, instance); + + rtp->red->t140.datalen = 0; + + return 0; +} + +static int rtp_red_buffer(struct ast_rtp_instance *instance, struct ast_frame *frame) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + + if (frame->datalen > -1) { + struct rtp_red *red = rtp->red; + memcpy(&red->buf_data[red->t140.datalen], frame->data.ptr, frame->datalen); + red->t140.datalen += frame->datalen; + red->t140.ts = frame->ts; + } + + return 0; +} + +static int ast_rtp_local_bridge(struct ast_rtp_instance *instance0, struct ast_rtp_instance *instance1) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance0); + + ast_set_flag(rtp, FLAG_NEED_MARKER_BIT); + + return 0; +} + +static int ast_rtp_get_stat(struct ast_rtp_instance *instance, struct ast_rtp_instance_stats *stats, enum ast_rtp_instance_stat stat) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + + if (!rtp->rtcp) { + return -1; + } + + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_TXCOUNT, -1, stats->txcount, rtp->txcount); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_RXCOUNT, -1, stats->rxcount, rtp->rxcount); + + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_TXPLOSS, AST_RTP_INSTANCE_STAT_COMBINED_LOSS, stats->txploss, rtp->rtcp->reported_lost); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_RXPLOSS, AST_RTP_INSTANCE_STAT_COMBINED_LOSS, stats->rxploss, rtp->rtcp->expected_prior - rtp->rtcp->received_prior); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_MAXRXPLOSS, AST_RTP_INSTANCE_STAT_COMBINED_LOSS, stats->remote_maxrxploss, rtp->rtcp->reported_maxlost); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_MINRXPLOSS, AST_RTP_INSTANCE_STAT_COMBINED_LOSS, stats->remote_minrxploss, rtp->rtcp->reported_minlost); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_NORMDEVRXPLOSS, AST_RTP_INSTANCE_STAT_COMBINED_LOSS, stats->remote_normdevrxploss, rtp->rtcp->reported_normdev_lost); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_STDEVRXPLOSS, AST_RTP_INSTANCE_STAT_COMBINED_LOSS, stats->remote_stdevrxploss, rtp->rtcp->reported_stdev_lost); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_LOCAL_MAXRXPLOSS, AST_RTP_INSTANCE_STAT_COMBINED_LOSS, stats->local_maxrxploss, rtp->rtcp->maxrxlost); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_LOCAL_MINRXPLOSS, AST_RTP_INSTANCE_STAT_COMBINED_LOSS, stats->local_minrxploss, rtp->rtcp->minrxlost); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_LOCAL_NORMDEVRXPLOSS, AST_RTP_INSTANCE_STAT_COMBINED_LOSS, stats->local_normdevrxploss, rtp->rtcp->normdev_rxlost); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_LOCAL_STDEVRXPLOSS, AST_RTP_INSTANCE_STAT_COMBINED_LOSS, stats->local_stdevrxploss, rtp->rtcp->stdev_rxlost); + AST_RTP_STAT_TERMINATOR(AST_RTP_INSTANCE_STAT_COMBINED_LOSS); + + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_TXJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->txjitter, rtp->rxjitter); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_RXJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->rxjitter, rtp->rtcp->reported_jitter / (unsigned int) 65536.0); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_MAXJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->remote_maxjitter, rtp->rtcp->reported_maxjitter); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_MINJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->remote_minjitter, rtp->rtcp->reported_minjitter); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_NORMDEVJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->remote_normdevjitter, rtp->rtcp->reported_normdev_jitter); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_STDEVJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->remote_stdevjitter, rtp->rtcp->reported_stdev_jitter); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_LOCAL_MAXJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->local_maxjitter, rtp->rtcp->maxrxjitter); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_LOCAL_MINJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->local_minjitter, rtp->rtcp->minrxjitter); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_LOCAL_NORMDEVJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->local_normdevjitter, rtp->rtcp->normdev_rxjitter); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_LOCAL_STDEVJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->local_stdevjitter, rtp->rtcp->stdev_rxjitter); + AST_RTP_STAT_TERMINATOR(AST_RTP_INSTANCE_STAT_COMBINED_JITTER); + + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_RTT, AST_RTP_INSTANCE_STAT_COMBINED_RTT, stats->rtt, rtp->rtcp->rtt); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_MAX_RTT, AST_RTP_INSTANCE_STAT_COMBINED_RTT, stats->maxrtt, rtp->rtcp->maxrtt); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_MIN_RTT, AST_RTP_INSTANCE_STAT_COMBINED_RTT, stats->minrtt, rtp->rtcp->minrtt); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_NORMDEVRTT, AST_RTP_INSTANCE_STAT_COMBINED_RTT, stats->normdevrtt, rtp->rtcp->normdevrtt); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_STDEVRTT, AST_RTP_INSTANCE_STAT_COMBINED_RTT, stats->stdevrtt, rtp->rtcp->stdevrtt); + AST_RTP_STAT_TERMINATOR(AST_RTP_INSTANCE_STAT_COMBINED_RTT); + + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_LOCAL_SSRC, -1, stats->local_ssrc, rtp->ssrc); + AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_SSRC, -1, stats->remote_ssrc, rtp->themssrc); + + return 0; +} + +static int ast_rtp_dtmf_compatible(struct ast_channel *chan0, struct ast_rtp_instance *instance0, struct ast_channel *chan1, struct ast_rtp_instance *instance1) +{ + /* If both sides are not using the same method of DTMF transmission + * (ie: one is RFC2833, other is INFO... then we can not do direct media. + * -------------------------------------------------- + * | DTMF Mode | HAS_DTMF | Accepts Begin Frames | + * |-----------|------------|-----------------------| + * | Inband | False | True | + * | RFC2833 | True | True | + * | SIP INFO | False | False | + * -------------------------------------------------- + */ + return (((ast_rtp_instance_get_prop(instance0, AST_RTP_PROPERTY_DTMF) != ast_rtp_instance_get_prop(instance1, AST_RTP_PROPERTY_DTMF)) || + (!chan0->tech->send_digit_begin != !chan1->tech->send_digit_begin)) ? 0 : 1); +} + +static void ast_rtp_stun_request(struct ast_rtp_instance *instance, struct sockaddr_in *suggestion, const char *username) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + + ast_stun_request(rtp->s, suggestion, username, NULL); +} + +static void ast_rtp_stop(struct ast_rtp_instance *instance) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + struct sockaddr_in sin = { 0, }; + + if (rtp->rtcp) { + AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid); + } + if (rtp->red) { + AST_SCHED_DEL(rtp->sched, rtp->red->schedid); + free(rtp->red); + rtp->red = NULL; + } + + ast_rtp_instance_set_remote_address(instance, &sin); + if (rtp->rtcp) { + memset(&rtp->rtcp->them.sin_addr, 0, sizeof(rtp->rtcp->them.sin_addr)); + memset(&rtp->rtcp->them.sin_port, 0, sizeof(rtp->rtcp->them.sin_port)); + } + + ast_set_flag(rtp, FLAG_NEED_MARKER_BIT); +} + +static char *rtp_do_debug_ip(struct ast_cli_args *a) +{ + struct hostent *hp; + struct ast_hostent ahp; + int port = 0; + char *p, *arg; + + arg = a->argv[3]; + p = strstr(arg, ":"); + if (p) { + *p = '\0'; + p++; + port = atoi(p); + } + hp = ast_gethostbyname(arg, &ahp); + if (hp == NULL) { + ast_cli(a->fd, "Lookup failed for '%s'\n", arg); + return CLI_FAILURE; + } + rtpdebugaddr.sin_family = AF_INET; + memcpy(&rtpdebugaddr.sin_addr, hp->h_addr, sizeof(rtpdebugaddr.sin_addr)); + rtpdebugaddr.sin_port = htons(port); + if (port == 0) + ast_cli(a->fd, "RTP Debugging Enabled for IP: %s\n", ast_inet_ntoa(rtpdebugaddr.sin_addr)); + else + ast_cli(a->fd, "RTP Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(rtpdebugaddr.sin_addr), port); + rtpdebug = 1; + return CLI_SUCCESS; +} + +static char *rtcp_do_debug_ip(struct ast_cli_args *a) +{ + struct hostent *hp; + struct ast_hostent ahp; + int port = 0; + char *p, *arg; + + arg = a->argv[3]; + p = strstr(arg, ":"); + if (p) { + *p = '\0'; + p++; + port = atoi(p); + } + hp = ast_gethostbyname(arg, &ahp); + if (hp == NULL) { + ast_cli(a->fd, "Lookup failed for '%s'\n", arg); + return CLI_FAILURE; + } + rtcpdebugaddr.sin_family = AF_INET; + memcpy(&rtcpdebugaddr.sin_addr, hp->h_addr, sizeof(rtcpdebugaddr.sin_addr)); + rtcpdebugaddr.sin_port = htons(port); + if (port == 0) + ast_cli(a->fd, "RTCP Debugging Enabled for IP: %s\n", ast_inet_ntoa(rtcpdebugaddr.sin_addr)); + else + ast_cli(a->fd, "RTCP Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(rtcpdebugaddr.sin_addr), port); + rtcpdebug = 1; + return CLI_SUCCESS; +} + +static char *handle_cli_rtp_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "rtp set debug {on|off|ip}"; + e->usage = + "Usage: rtp set debug {on|off|ip host[:port]}\n" + " Enable/Disable dumping of all RTP packets. If 'ip' is\n" + " specified, limit the dumped packets to those to and from\n" + " the specified 'host' with optional port.\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc == e->args) { /* set on or off */ + if (!strncasecmp(a->argv[e->args-1], "on", 2)) { + rtpdebug = 1; + memset(&rtpdebugaddr, 0, sizeof(rtpdebugaddr)); + ast_cli(a->fd, "RTP Debugging Enabled\n"); + return CLI_SUCCESS; + } else if (!strncasecmp(a->argv[e->args-1], "off", 3)) { + rtpdebug = 0; + ast_cli(a->fd, "RTP Debugging Disabled\n"); + return CLI_SUCCESS; + } + } else if (a->argc == e->args +1) { /* ip */ + return rtp_do_debug_ip(a); + } + + return CLI_SHOWUSAGE; /* default, failure */ +} + +static char *handle_cli_rtcp_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "rtcp set debug {on|off|ip}"; + e->usage = + "Usage: rtcp set debug {on|off|ip host[:port]}\n" + " Enable/Disable dumping of all RTCP packets. If 'ip' is\n" + " specified, limit the dumped packets to those to and from\n" + " the specified 'host' with optional port.\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc == e->args) { /* set on or off */ + if (!strncasecmp(a->argv[e->args-1], "on", 2)) { + rtcpdebug = 1; + memset(&rtcpdebugaddr, 0, sizeof(rtcpdebugaddr)); + ast_cli(a->fd, "RTCP Debugging Enabled\n"); + return CLI_SUCCESS; + } else if (!strncasecmp(a->argv[e->args-1], "off", 3)) { + rtcpdebug = 0; + ast_cli(a->fd, "RTCP Debugging Disabled\n"); + return CLI_SUCCESS; + } + } else if (a->argc == e->args +1) { /* ip */ + return rtcp_do_debug_ip(a); + } + + return CLI_SHOWUSAGE; /* default, failure */ +} + +static char *handle_cli_rtcp_set_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "rtcp set stats {on|off}"; + e->usage = + "Usage: rtcp set stats {on|off}\n" + " Enable/Disable dumping of RTCP stats.\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc != e->args) + return CLI_SHOWUSAGE; + + if (!strncasecmp(a->argv[e->args-1], "on", 2)) + rtcpstats = 1; + else if (!strncasecmp(a->argv[e->args-1], "off", 3)) + rtcpstats = 0; + else + return CLI_SHOWUSAGE; + + ast_cli(a->fd, "RTCP Stats %s\n", rtcpstats ? "Enabled" : "Disabled"); + return CLI_SUCCESS; +} + +static struct ast_cli_entry cli_rtp[] = { + AST_CLI_DEFINE(handle_cli_rtp_set_debug, "Enable/Disable RTP debugging"), + AST_CLI_DEFINE(handle_cli_rtcp_set_debug, "Enable/Disable RTCP debugging"), + AST_CLI_DEFINE(handle_cli_rtcp_set_stats, "Enable/Disable RTCP stats"), +}; + +static int rtp_reload(int reload) +{ + struct ast_config *cfg; + const char *s; + struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; + + cfg = ast_config_load2("rtp.conf", "rtp", config_flags); + if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) { + return 0; + } + + rtpstart = DEFAULT_RTP_START; + rtpend = DEFAULT_RTP_END; + dtmftimeout = DEFAULT_DTMF_TIMEOUT; + strictrtp = STRICT_RTP_OPEN; + if (cfg) { + if ((s = ast_variable_retrieve(cfg, "general", "rtpstart"))) { + rtpstart = atoi(s); + if (rtpstart < MINIMUM_RTP_PORT) + rtpstart = MINIMUM_RTP_PORT; + if (rtpstart > MAXIMUM_RTP_PORT) + rtpstart = MAXIMUM_RTP_PORT; + } + if ((s = ast_variable_retrieve(cfg, "general", "rtpend"))) { + rtpend = atoi(s); + if (rtpend < MINIMUM_RTP_PORT) + rtpend = MINIMUM_RTP_PORT; + if (rtpend > MAXIMUM_RTP_PORT) + rtpend = MAXIMUM_RTP_PORT; + } + if ((s = ast_variable_retrieve(cfg, "general", "rtcpinterval"))) { + rtcpinterval = atoi(s); + if (rtcpinterval == 0) + rtcpinterval = 0; /* Just so we're clear... it's zero */ + if (rtcpinterval < RTCP_MIN_INTERVALMS) + rtcpinterval = RTCP_MIN_INTERVALMS; /* This catches negative numbers too */ + if (rtcpinterval > RTCP_MAX_INTERVALMS) + rtcpinterval = RTCP_MAX_INTERVALMS; + } + if ((s = ast_variable_retrieve(cfg, "general", "rtpchecksums"))) { +#ifdef SO_NO_CHECK + nochecksums = ast_false(s) ? 1 : 0; +#else + if (ast_false(s)) + ast_log(LOG_WARNING, "Disabling RTP checksums is not supported on this operating system!\n"); +#endif + } + if ((s = ast_variable_retrieve(cfg, "general", "dtmftimeout"))) { + dtmftimeout = atoi(s); + if ((dtmftimeout < 0) || (dtmftimeout > 20000)) { + ast_log(LOG_WARNING, "DTMF timeout of '%d' outside range, using default of '%d' instead\n", + dtmftimeout, DEFAULT_DTMF_TIMEOUT); + dtmftimeout = DEFAULT_DTMF_TIMEOUT; + }; + } + if ((s = ast_variable_retrieve(cfg, "general", "strictrtp"))) { + strictrtp = ast_true(s); + } + ast_config_destroy(cfg); + } + if (rtpstart >= rtpend) { + ast_log(LOG_WARNING, "Unreasonable values for RTP start/end port in rtp.conf\n"); + rtpstart = DEFAULT_RTP_START; + rtpend = DEFAULT_RTP_END; + } + ast_verb(2, "RTP Allocating from port range %d -> %d\n", rtpstart, rtpend); + return 0; +} + +static int reload_module(void) +{ + rtp_reload(1); + return 0; +} + +static int load_module(void) +{ + if (ast_rtp_engine_register(&asterisk_rtp_engine)) { + return AST_MODULE_LOAD_DECLINE; + } + + if (ast_cli_register_multiple(cli_rtp, ARRAY_LEN(cli_rtp))) { + ast_rtp_engine_unregister(&asterisk_rtp_engine); + return AST_MODULE_LOAD_DECLINE; + } + + rtp_reload(0); + + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + ast_rtp_engine_unregister(&asterisk_rtp_engine); + ast_cli_unregister_multiple(cli_rtp, ARRAY_LEN(cli_rtp)); + + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk RTP Stack", + .load = load_module, + .unload = unload_module, + .reload = reload_module, + ); -- GitLab