Newer
Older
Joshua Colp
committed
if (!bridged->nat || (bridged->nat && (ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) {
ast_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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);
Joshua Colp
committed
}
Joshua Colp
committed
return -1;
} else if (rtp_debug_test_addr(&bridged->them))
ast_verbose("Sent RTP P2P packet to %s:%d (type %-2.2d, len %-6.6u)\n", ast_inet_ntoa(bridged->them.sin_addr), ntohs(bridged->them.sin_port), bridged_payload, len - hdrlen);
Joshua Colp
committed
return -1;
}
struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
{
socklen_t len;
int tseqno;
int mark;
unsigned int timestamp;
unsigned int *rtpheader;
/* Cache where the header will go */
res = recvfrom(rtp->s, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET,
rtpheader = (unsigned int *)(rtp->rawdata + AST_FRIENDLY_OFFSET);
ast_log(LOG_WARNING, "RTP Read error: %s\n", strerror(errno));
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 ((stun_handle_packet(rtp->s, &sin, rtp->rawdata + AST_FRIENDLY_OFFSET, res) == STUN_ACCEPT) &&
(!rtp->them.sin_port && !rtp->them.sin_addr.s_addr)) {
memcpy(&rtp->them, &sin, sizeof(rtp->them));
}
return &ast_null_frame;
}
/* 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;
/* Send to whoever send to us if NAT is turned on */
if ((rtp->them.sin_addr.s_addr != sin.sin_addr.s_addr) ||
(rtp->them.sin_port != sin.sin_port)) {
memcpy(&rtp->rtcp->them, &sin, sizeof(rtp->rtcp->them));
rtp->rtcp->them.sin_port = htons(ntohs(rtp->them.sin_port)+1);
}
Mark Spencer
committed
rtp->rxseqno = 0;
Kevin P. Fleming
committed
ast_set_flag(rtp, FLAG_NAT_ACTIVE);
if (option_debug || rtpdebug)
Russell Bryant
committed
ast_log(LOG_DEBUG, "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 */
Joshua Colp
committed
if (ast_rtp_get_bridged(rtp) && !bridge_p2p_rtp_write(rtp, rtpheader, res, hdrlen))
return &ast_null_frame;
if (version != 2)
return &ast_null_frame;
padding = seqno & (1 << 29);
mark = seqno & (1 << 23);
ext = seqno & (1 << 28);
seqno &= 0xffff;
timestamp = ntohl(rtpheader[1]);
ssrc = ntohl(rtpheader[2]);
if (!mark && rtp->rxssrc && rtp->rxssrc != ssrc) {
if (option_debug || rtpdebug)
ast_log(LOG_DEBUG, "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 (ext) {
/* RTP Extension present */
hdrlen += 4;
hdrlen += (ntohl(rtpheader[3]) & 0xffff) << 2;
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 */
tseqno = rtp->lastrxseqno +1;
/* This is the first RTP packet successfully received from source */
rtp->seedrxseqno = seqno;
}
if (rtp->rtcp && 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 (tseqno > RTP_SEQ_MOD) { /* if tseqno is greater than RTP_SEQ_MOD it would indicate that the sender cycled */
rtp->cycles += RTP_SEQ_MOD;
ast_verbose("SEQNO cycled: %u\t%d\n", rtp->cycles, seqno);
}
rtp->lastrxseqno = seqno;
rtp->themssrc = ntohl(rtpheader[2]); /* Record their SSRC to put in future RR */
ast_verbose("Got RTP packet from %s:%d (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n",
Russell Bryant
committed
ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), payloadtype, seqno, timestamp,res - hdrlen);
rtpPT = ast_rtp_lookup_pt(rtp, payloadtype);
/* 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 */
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;
Russell Bryant
committed
ast_verbose("Got RTP RFC2833 from %s:%d (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(sin.sin_addr), ntohs(sin.sin_port), payloadtype, seqno, timestamp, res - hdrlen, (mark?1:0), event, ((event_end & 0x80)?1:0), duration);
if (rtp->lasteventseqn <= seqno || rtp->resp == 0 || (rtp->lasteventseqn >= 65530 && seqno <= 6)) {
f = process_rfc2833(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno);
rtp->lasteventseqn = seqno;
} else if (rtpPT.code == AST_RTP_CISCO_DTMF) {
/* It's really special -- process it the Cisco way */
if (rtp->lasteventseqn <= seqno || rtp->resp == 0 || (rtp->lasteventseqn >= 65530 && seqno <= 6)) {
f = process_cisco_dtmf(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen);
rtp->lasteventseqn = seqno;
} else if (rtpPT.code == AST_RTP_CN) {
/* Comfort Noise */
f = process_rfc3389(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen);
} else {
Russell Bryant
committed
ast_log(LOG_NOTICE, "Unknown RTP codec %d received from '%s'\n", payloadtype, ast_inet_ntoa(rtp->them.sin_addr));
rtp->lastrxformat = rtp->f.subclass = rtpPT.code;
rtp->f.frametype = (rtp->f.subclass < AST_FORMAT_MAX_AUDIO) ? AST_FRAME_VOICE : AST_FRAME_VIDEO;
if (!rtp->lastrxts)
rtp->lastrxts = timestamp;
Mark Spencer
committed
rtp->rxseqno = seqno;
if (rtp->dtmfcount) {
#if 0
printf("dtmfcount was %d\n", rtp->dtmfcount);
#endif
rtp->dtmfcount -= (timestamp - rtp->lastrxts);
if (rtp->dtmfcount < 0)
rtp->dtmfcount = 0;
#if 0
if (dtmftimeout != rtp->dtmfcount)
printf("dtmfcount is %d\n", rtp->dtmfcount);
#endif
}
rtp->lastrxts = timestamp;
/* Send any pending DTMF */
if (rtp->resp && !rtp->dtmfcount) {
if (option_debug)
ast_log(LOG_DEBUG, "Sending pending DTMF\n");
rtp->f.data = rtp->rawdata + hdrlen + AST_FRIENDLY_OFFSET;
rtp->f.offset = hdrlen + AST_FRIENDLY_OFFSET;
if (rtp->f.subclass < AST_FORMAT_MAX_AUDIO) {
rtp->f.samples = ast_codec_get_samples(&rtp->f);
if (rtp->f.subclass == AST_FORMAT_SLINEAR)
Kevin P. Fleming
committed
ast_frame_byteswap_be(&rtp->f);
calc_rxstamp(&rtp->f.delivery, rtp, timestamp, mark);
Russell Bryant
committed
/* Add timing data to let ast_generic_bridge() put the frame into a jitterbuf */
rtp->f.has_timing_info = 1;
rtp->f.ts = timestamp / 8;
rtp->f.len = rtp->f.samples / 8;
rtp->f.seqno = seqno;
} else {
/* Video -- samples is # of samples vs. 90000 */
if (!rtp->lastividtimestamp)
rtp->lastividtimestamp = timestamp;
rtp->f.samples = timestamp - rtp->lastividtimestamp;
rtp->lastividtimestamp = timestamp;
Mark Spencer
committed
rtp->f.delivery.tv_sec = 0;
rtp->f.delivery.tv_usec = 0;
if (mark)
rtp->f.subclass |= 0x1;
/* The following array defines the MIME Media type (and subtype) for each
of our codecs, or RTP-specific data type. */
Olle Johansson
committed
struct rtpPayloadType payloadType;
char* type;
char* subtype;
Olle Johansson
committed
{{1, AST_FORMAT_G723_1}, "audio", "G723"},
{{1, AST_FORMAT_GSM}, "audio", "GSM"},
{{1, AST_FORMAT_ULAW}, "audio", "PCMU"},
{{1, AST_FORMAT_ALAW}, "audio", "PCMA"},
{{1, AST_FORMAT_G726}, "audio", "G726-32"},
{{1, AST_FORMAT_ADPCM}, "audio", "DVI4"},
{{1, AST_FORMAT_SLINEAR}, "audio", "L16"},
{{1, AST_FORMAT_LPC10}, "audio", "LPC"},
{{1, AST_FORMAT_G729A}, "audio", "G729"},
{{1, AST_FORMAT_SPEEX}, "audio", "speex"},
{{1, AST_FORMAT_ILBC}, "audio", "iLBC"},
{{1, AST_FORMAT_G726_AAL2}, "audio", "AAL2-G726-32"},
Olle Johansson
committed
{{0, AST_RTP_DTMF}, "audio", "telephone-event"},
{{0, AST_RTP_CISCO_DTMF}, "audio", "cisco-telephone-event"},
{{0, AST_RTP_CN}, "audio", "CN"},
{{1, AST_FORMAT_JPEG}, "video", "JPEG"},
{{1, AST_FORMAT_PNG}, "video", "PNG"},
{{1, AST_FORMAT_H261}, "video", "H261"},
{{1, AST_FORMAT_H263}, "video", "H263"},
{{1, AST_FORMAT_H263_PLUS}, "video", "h263-1998"},
{{1, AST_FORMAT_H264}, "video", "H264"},
Mark Spencer
committed
/* 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 */
static struct rtpPayloadType static_RTP_PT[MAX_RTP_PT] = {
Olle Johansson
committed
[0] = {1, AST_FORMAT_ULAW},
#ifdef USE_DEPRECATED_G726
Olle Johansson
committed
[2] = {1, AST_FORMAT_G726}, /* Technically this is G.721, but if Cisco can do it, so can we... */
Olle Johansson
committed
[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},
[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},
[103] = {1, AST_FORMAT_H263_PLUS},
[97] = {1, AST_FORMAT_ILBC},
[99] = {1, AST_FORMAT_H264},
Olle Johansson
committed
[101] = {0, AST_RTP_DTMF},
[110] = {1, AST_FORMAT_SPEEX},
[111] = {1, AST_FORMAT_G726},
Olle Johansson
committed
[121] = {0, AST_RTP_CISCO_DTMF}, /* Must be type 121 */
void ast_rtp_pt_clear(struct ast_rtp* rtp)
{
Kevin P. Fleming
committed
if (!rtp)
return;
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;
void ast_rtp_pt_default(struct ast_rtp* rtp)
{
int i;
/* 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;
void ast_rtp_pt_copy(struct ast_rtp *dest, const struct ast_rtp *src)
{
unsigned int i;
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;
}
Olle Johansson
committed
/*! \brief Get channel driver interface structure */
static struct ast_rtp_protocol *get_proto(struct ast_channel *chan)
{
struct ast_rtp_protocol *cur = NULL;
Olle Johansson
committed
AST_LIST_LOCK(&protos);
AST_LIST_TRAVERSE(&protos, cur, list) {
if (cur->type == chan->tech->type)
break;
}
Olle Johansson
committed
AST_LIST_UNLOCK(&protos);
return cur;
}
int ast_rtp_early_bridge(struct ast_channel *dest, struct ast_channel *src)
Joshua Colp
committed
struct ast_rtp *destp = NULL, *srcp = NULL; /* Audio RTP Channels */
struct ast_rtp *vdestp = NULL, *vsrcp = NULL; /* Video 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;
enum ast_rtp_get_result audio_src_res = AST_RTP_GET_FAILED, video_src_res = AST_RTP_GET_FAILED;
int srccodec;
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
/* Lock channels */
ast_channel_lock(dest);
if (src) {
while(ast_channel_trylock(src)) {
ast_channel_unlock(dest);
usleep(1);
ast_channel_lock(dest);
}
}
/* Find channel driver interfaces */
destpr = get_proto(dest);
if (src)
srcpr = get_proto(src);
if (!destpr) {
if (option_debug)
ast_log(LOG_DEBUG, "Channel '%s' has no RTP, not doing anything\n", dest->name);
ast_channel_unlock(dest);
if (src)
ast_channel_unlock(src);
return 0;
}
if (!srcpr) {
if (option_debug)
ast_log(LOG_DEBUG, "Channel '%s' has no RTP, not doing anything\n", src ? src->name : "<unspecified>");
ast_channel_unlock(dest);
if (src)
ast_channel_unlock(src);
return 0;
}
/* Get audio and video interface (if native bridge is possible) */
Joshua Colp
committed
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;
if (srcpr) {
Joshua Colp
committed
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;
}
/* Check if bridge is still possible (In SIP canreinvite=no stops this, like NAT) */
Joshua Colp
committed
if (audio_dest_res != AST_RTP_TRY_NATIVE) {
/* Somebody doesn't want to play... */
ast_channel_unlock(dest);
if (src)
ast_channel_unlock(src);
return 0;
}
Joshua Colp
committed
if (audio_src_res == AST_RTP_TRY_NATIVE && srcpr->get_codec)
srccodec = srcpr->get_codec(src);
else
srccodec = 0;
/* Consider empty media as non-existant */
Joshua Colp
committed
if (audio_src_res == AST_RTP_TRY_NATIVE && !srcp->them.sin_addr.s_addr)
srcp = NULL;
/* Bridge media early */
if (destpr->set_rtp_peer(dest, srcp, vsrcp, srccodec, srcp ? ast_test_flag(srcp, FLAG_NAT_ACTIVE) : 0))
ast_log(LOG_WARNING, "Channel '%s' failed to setup early bridge to '%s'\n", dest->name, src ? src->name : "<unspecified>");
ast_channel_unlock(dest);
if (src)
ast_channel_unlock(src);
if (option_debug)
ast_log(LOG_DEBUG, "Setting early bridge SDP of '%s' with that of '%s'\n", dest->name, src ? src->name : "<unspecified>");
return 1;
}
int ast_rtp_make_compatible(struct ast_channel *dest, struct ast_channel *src, int media)
{
Joshua Colp
committed
struct ast_rtp *destp = NULL, *srcp = NULL; /* Audio RTP Channels */
struct ast_rtp *vdestp = NULL, *vsrcp = NULL; /* Video 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;
enum ast_rtp_get_result audio_src_res = AST_RTP_GET_FAILED, video_src_res = AST_RTP_GET_FAILED;
int srccodec;
Joshua Colp
committed
/* Lock channels */
Joshua Colp
committed
ast_channel_lock(dest);
while(ast_channel_trylock(src)) {
ast_channel_unlock(dest);
usleep(1);
Joshua Colp
committed
ast_channel_lock(dest);
}
/* Find channel driver interfaces */
Joshua Colp
committed
if (!(destpr = get_proto(dest))) {
Olle Johansson
committed
if (option_debug)
ast_log(LOG_DEBUG, "Channel '%s' has no RTP, not doing anything\n", dest->name);
Joshua Colp
committed
ast_channel_unlock(dest);
ast_channel_unlock(src);
return 0;
}
Joshua Colp
committed
if (!(srcpr = get_proto(src))) {
Olle Johansson
committed
if (option_debug)
ast_log(LOG_DEBUG, "Channel '%s' has no RTP, not doing anything\n", src->name);
Joshua Colp
committed
ast_channel_unlock(dest);
ast_channel_unlock(src);
return 0;
}
/* Get audio and video interface (if native bridge is possible) */
Joshua Colp
committed
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;
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;
/* Check if bridge is still possible (In SIP canreinvite=no stops this, like NAT) */
Joshua Colp
committed
if (audio_dest_res != AST_RTP_TRY_NATIVE || audio_src_res != AST_RTP_TRY_NATIVE) {
/* Somebody doesn't want to play... */
Joshua Colp
committed
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 (srcpr->get_codec)
srccodec = srcpr->get_codec(src);
else
srccodec = 0;
if (media) {
/* Bridge early */
if (destpr->set_rtp_peer(dest, srcp, vsrcp, 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);
Joshua Colp
committed
ast_channel_unlock(dest);
ast_channel_unlock(src);
Olle Johansson
committed
if (option_debug)
ast_log(LOG_DEBUG, "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.
Olle Johansson
committed
* 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)
return; /* bogus payload type */
if (static_RTP_PT[pt].code != 0)
rtp->current_RTP_PT[pt] = static_RTP_PT[pt];
Olle Johansson
committed
/*! \brief Make a note of a RTP payload type (with MIME type) that was seen in
Kevin P. Fleming
committed
* an SDP "a=rtpmap:" line.
*/
void ast_rtp_set_rtpmap_type(struct ast_rtp *rtp, int pt,
char *mimeType, char *mimeSubtype,
enum ast_rtp_options options)
{
Kevin P. Fleming
committed
unsigned int i;
if (pt < 0 || pt > MAX_RTP_PT)
return; /* bogus payload type */
Kevin P. Fleming
committed
for (i = 0; i < sizeof(mimeTypes)/sizeof(mimeTypes[0]); ++i) {
if (strcasecmp(mimeSubtype, mimeTypes[i].subtype) == 0 &&
Kevin P. Fleming
committed
strcasecmp(mimeType, mimeTypes[i].type) == 0) {
rtp->current_RTP_PT[pt] = mimeTypes[i].payloadType;
Kevin P. Fleming
committed
if ((mimeTypes[i].payloadType.code == AST_FORMAT_G726) &&
mimeTypes[i].payloadType.isAstFormat &&
(options & AST_RTP_OPT_G726_NONSTANDARD))
rtp->current_RTP_PT[pt].code = AST_FORMAT_G726_AAL2;
Olle Johansson
committed
/*! \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;
*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;
}
}
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 */
result = rtp->current_RTP_PT[pt];
/* If it doesn't exist, check our static RTP type list, just in case */
if (!result.code)
result = static_RTP_PT[pt];
return result;
Olle Johansson
committed
/*! \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) {
Mark Spencer
committed
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 */
return rtp->rtp_lookup_code_cache_result;
}
/* 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;
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;
return pt;
}
}
return -1;
Kevin P. Fleming
committed
const char *ast_rtp_lookup_mime_subtype(const int isAstFormat, const int code,
enum ast_rtp_options options)
{
Kevin P. Fleming
committed
unsigned int i;
Kevin P. Fleming
committed
for (i = 0; i < sizeof(mimeTypes)/sizeof(mimeTypes[0]); ++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 "AAL2-G726-32";
else
return mimeTypes[i].subtype;
}
Kevin P. Fleming
committed
Kevin P. Fleming
committed
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) {
Kevin P. Fleming
committed
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)
snprintf(start, size, "nothing)");
else if (size > 1)
*(end -1) = ')';
return buf;
}
static int rtp_socket(void)
{
int s;
long flags;
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s > -1) {
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
/*!
* \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->us.sin_family = AF_INET;
rtcp->them.sin_family = AF_INET;
if (rtcp->s < 0) {
free(rtcp);
ast_log(LOG_WARNING, "Unable to allocate RTCP socket: %s\n", strerror(errno));
return NULL;
}
return rtcp;
}
struct ast_rtp *ast_rtp_new_with_bindaddr(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode, struct in_addr addr)
if (!(rtp = ast_calloc(1, sizeof(*rtp))))
return NULL;
rtp->them.sin_family = AF_INET;
rtp->us.sin_family = AF_INET;
Tilghman Lesher
committed
rtp->ssrc = ast_random();
rtp->seqno = ast_random() & 0xffff;
ast_set_flag(rtp, FLAG_HAS_DTMF);
ast_log(LOG_ERROR, "Unable to allocate socket: %s\n", strerror(errno));
if (sched && rtcpenable) {
rtp->sched = sched;
rtp->rtcp = ast_rtcp_new();
}
/* Select a random port number in the range of possible RTP */
Tilghman Lesher
committed
x = (ast_random() % (rtpend-rtpstart)) + rtpstart;
/* Save it for future references. */
/* Iterate tring to bind that port and incrementing it otherwise untill a port was found or no ports are available. */
for (;;) {
/* Must be an even port number by RTP spec */
rtp->us.sin_port = htons(x);
rtp->us.sin_addr = addr;
/* If there's rtcp, initialize it as well. */
if (rtp->rtcp)
rtp->rtcp->us.sin_port = htons(x + 1);
/* Try to bind it/them. */
if (!(first = bind(rtp->s, (struct sockaddr *)&rtp->us, sizeof(rtp->us))) &&
(!rtp->rtcp || !bind(rtp->rtcp->s, (struct sockaddr *)&rtp->rtcp->us, sizeof(rtp->rtcp->us))))
if (!first) {
/* Primary bind succeeded! Gotta recreate it */
close(rtp->s);
rtp->s = rtp_socket();
}
/* We got an error that wasn't expected, abort! */
ast_log(LOG_ERROR, "Unexpected bind error: %s\n", strerror(errno));
if (rtp->rtcp) {
close(rtp->rtcp->s);
free(rtp->rtcp);
}
/* The port was used, increment it (by two). */
/* Did we go over the limit ? */
/* then, start from the begingig. */
/* Check if we reached the place were we started. */
/* If so, there's no ports available. */
ast_log(LOG_ERROR, "No RTP ports remaining. Can't setup media stream for this call.\n");
if (rtp->rtcp) {
close(rtp->rtcp->s);
free(rtp->rtcp);
}
Joshua Colp
committed
rtp->sched = sched;
rtp->io = io;
Joshua Colp
committed
if (callbackmode) {
rtp->ioid = ast_io_add(rtp->io, rtp->s, rtpread, AST_IO_IN, rtp);
Joshua Colp
committed
ast_set_flag(rtp, FLAG_CALLBACK_MODE);
}
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_settos(struct ast_rtp *rtp, int tos)
{
int res;
if ((res = setsockopt(rtp->s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))))
ast_log(LOG_WARNING, "Unable to set TOS to %d\n", tos);
return res;
}
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) {
rtp->rtcp->them.sin_port = htons(ntohs(them->sin_port) + 1);
rtp->rtcp->them.sin_addr = them->sin_addr;
}
Mark Spencer
committed
rtp->rxseqno = 0;
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)
{
Joshua Colp
committed
struct ast_rtp *ast_rtp_get_bridged(struct ast_rtp *rtp)
{
return rtp->bridged;
}
void ast_rtp_stop(struct ast_rtp *rtp)
{
if (rtp->rtcp && rtp->rtcp->schedid > 0) {
ast_sched_del(rtp->sched, rtp->rtcp->schedid);
rtp->rtcp->schedid = -1;
}
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));
Joshua Colp
committed
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->lasteventseqn = 0;
rtp->lasteventendseqn = 0;
rtp->lasttxformat = 0;
rtp->lastrxformat = 0;
rtp->dtmfcount = 0;
rtp->dtmfduration = 0;
rtp->seqno = 0;
rtp->rxseqno = 0;
}
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
*/
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., rtp->txcount, rtp->rtcp->reported_lost, rtp->rtcp->rtt);
return rtp->rtcp->quality;
}
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->expected_prior - rtp->rtcp->received_prior);
ast_verbose(" Jitter: %.4f\n", rtp->rxjitter);
ast_verbose(" Transit: %.4f\n", rtp->rxtransit);
ast_verbose(" RR-count: %u\n", rtp->rtcp->rr_count);
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->reported_lost);
ast_verbose(" Jitter: %u\n", rtp->rtcp->reported_jitter);
ast_verbose(" SR-count: %u\n", rtp->rtcp->sr_count);
ast_verbose(" RTT: %f\n", rtp->rtcp->rtt);
}
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) {
if (rtp->rtcp->schedid > 0)
ast_sched_del(rtp->sched, rtp->rtcp->schedid);
close(rtp->rtcp->s);
free(rtp->rtcp);
rtp->rtcp=NULL;
static unsigned int calc_txstamp(struct ast_rtp *rtp, struct timeval *delivery)
Kevin P. Fleming
committed
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;
Kevin P. Fleming
committed
/* Use previous txcore if available */
t = (delivery && !ast_tvzero(*delivery)) ? *delivery : ast_tvnow();
ms = ast_tvdiff_ms(t, rtp->txcore);
Kevin P. Fleming
committed
/* Use what we just got for next time */
rtp->txcore = t;
return (unsigned int) ms;
int ast_rtp_senddigit(struct ast_rtp *rtp, char digit)
{
unsigned int *rtpheader;
int hdrlen = 12;
int res;
int x;
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 -1;
}
payload = ast_rtp_lookup_code(rtp, 0, AST_RTP_DTMF);
/* If we have no peer, return immediately */
if (!rtp->them.sin_addr.s_addr)
return 0;
Kevin P. Fleming
committed
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->lastdigitts);