Newer
Older
break;
}
Olle Johansson
committed
AST_LIST_UNLOCK(&protos);
return cur;
}
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
int ast_rtp_early_media(struct ast_channel *dest, struct ast_channel *src)
{
struct ast_rtp *destp, *srcp=NULL; /* Audio RTP Channels */
struct ast_rtp *vdestp, *vsrcp=NULL; /* Video RTP channels */
struct ast_rtp_protocol *destpr, *srcpr=NULL;
int srccodec;
/* 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>");
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
ast_channel_unlock(dest);
if (src)
ast_channel_unlock(src);
return 0;
}
/* Get audio and video interface (if native bridge is possible) */
destp = destpr->get_rtp_info(dest);
vdestp = (destpr->get_vrtp_info) ? destpr->get_vrtp_info(dest) : NULL;
if (srcpr) {
srcp = srcpr->get_rtp_info(src);
vsrcp = (srcpr->get_vrtp_info) ? srcpr->get_vrtp_info(src) : NULL;
}
/* Check if bridge is still possible (In SIP canreinvite=no stops this, like NAT) */
if (!destp) {
/* Somebody doesn't want to play... */
ast_channel_unlock(dest);
if (src)
ast_channel_unlock(src);
return 0;
}
if (srcpr && srcpr->get_codec)
srccodec = srcpr->get_codec(src);
else
srccodec = 0;
/* Consider empty media as non-existant */
if (srcp && !srcp->them.sin_addr.s_addr)
srcp = NULL;
/* Bridge early media */
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 send early media 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 media 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)
{
struct ast_rtp *destp, *srcp; /* Audio RTP Channels */
struct ast_rtp *vdestp, *vsrcp; /* Video RTP channels */
struct ast_rtp_protocol *destpr, *srcpr;
int srccodec;
/* 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 */
destpr = get_proto(dest);
srcpr = get_proto(src);
if (!destpr) {
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;
}
if (!srcpr) {
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) */
destp = destpr->get_rtp_info(dest);
vdestp = (destpr->get_vrtp_info) ? destpr->get_vrtp_info(dest) : NULL;
srcp = srcpr->get_rtp_info(src);
vsrcp = (srcpr->get_vrtp_info) ? srcpr->get_vrtp_info(src) : NULL;
/* Check if bridge is still possible (In SIP canreinvite=no stops this, like NAT) */
if (!destp || !srcp) {
/* 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 media */
if (destpr->set_rtp_peer(dest, srcp, vsrcp, srccodec, ast_test_flag(srcp, FLAG_NAT_ACTIVE)))
ast_log(LOG_WARNING, "Channel '%s' failed to send early media 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;
}
Olle Johansson
committed
/*! \brief Make a note of a RTP paymoad 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)
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
a SDP "a=rtpmap:" line. */
void ast_rtp_set_rtpmap_type(struct ast_rtp* rtp, int pt,
char* mimeType, char* mimeSubtype)
{
if (pt < 0 || pt > MAX_RTP_PT)
return; /* bogus payload type */
for (i = 0; i < sizeof mimeTypes/sizeof mimeTypes[0]; ++i) {
if (strcasecmp(mimeSubtype, mimeTypes[i].subtype) == 0 &&
strcasecmp(mimeType, mimeTypes[i].type) == 0) {
rtp->current_RTP_PT[pt] = mimeTypes[i].payloadType;
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 the 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;
char* ast_rtp_lookup_mime_subtype(const int isAstFormat, const int code)
{
int i;
for (i = 0; i < sizeof mimeTypes/sizeof mimeTypes[0]; ++i) {
if (mimeTypes[i].payloadType.code == code && mimeTypes[i].payloadType.isAstFormat == isAstFormat)
return mimeTypes[i].subtype;
char *ast_rtp_lookup_mime_multiple(char *buf, int size, const int capability, const int isAstFormat)
{
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);
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;
if (rtcp->s < 0) {
free(rtcp);
ast_log(LOG_WARNING, "Unable to allocate 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_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);
}
if (io && sched && callbackmode) {
/* Operate this one in a callback mode */
rtp->sched = sched;
rtp->io = io;
rtp->ioid = ast_io_add(rtp->io, rtp->s, rtpread, AST_IO_IN, rtp);
}
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)
{
void ast_rtp_stop(struct ast_rtp *rtp)
{
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->them.sin_addr));
memset(&rtp->rtcp->them.sin_port, 0, sizeof(rtp->them.sin_port));
}
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;
}
void ast_rtp_destroy(struct ast_rtp *rtp)
{
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) {
close(rtp->rtcp->s);
free(rtp->rtcp);
}
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 iabuf[INET_ADDRSTRLEN];
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);
rtpheader[2] = htonl(rtp->ssrc);
rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (0));
if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) {
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(iabuf, sizeof(iabuf), rtp->them.sin_addr),
ntohs(rtp->them.sin_port), strerror(errno));
if (rtp_debug_test_addr(&rtp->them))
ast_verbose("Sent RTP packet to %s:%d (type %d, seq %u, ts %u, len %u)\n",
ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->them.sin_addr),
ntohs(rtp->them.sin_port), payload, rtp->seqno, rtp->lastdigitts, res - hdrlen);
/* Sequence number of last two end packets does not get incremented */
if (x < 3)
rtp->seqno++;
/* Clear marker bit and set seqno */
rtpheader[0] = htonl((2 << 30) | (payload << 16) | (rtp->seqno));
/* For the last three packets, set the duration and the end bit */
if (x == 2) {
#if 0
/* No, this is wrong... Do not increment lastdigitts, that's not according
to the RFC, as best we can determine */
rtp->lastdigitts++; /* or else the SPA3000 will click instead of beeping... */
rtpheader[1] = htonl(rtp->lastdigitts);
#endif
/* Make duration 800 (100ms) */
rtpheader[3] |= htonl((800));
/* Set the End bit */
rtpheader[3] |= htonl((1 << 23));
Kevin P. Fleming
committed
/* Increment the digit timestamp by 120ms, to ensure that digits
sent sequentially with no intervening non-digit packets do not
Kevin P. Fleming
committed
get sent with the same timestamp, and that sequential digits
have some 'dead air' in between them
*/
rtp->lastdigitts += 960;
/* Increment the sequence number to reflect the last packet
that was sent
*/
rtp->seqno++;
int ast_rtp_sendcng(struct ast_rtp *rtp, int level)
{
unsigned int *rtpheader;
int hdrlen = 12;
int res;
int payload;
char data[256];
char iabuf[INET_ADDRSTRLEN];
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;
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->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(iabuf, sizeof(iabuf), 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:%d (type %d, seq %d, ts %d, len %d)\n"
, ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->them.sin_addr), ntohs(rtp->them.sin_port), payload, rtp->seqno, rtp->lastts,res - hdrlen);
}
return 0;
}
static int ast_rtp_raw_write(struct ast_rtp *rtp, struct ast_frame *f, int codec)
unsigned char *rtpheader;
char iabuf[INET_ADDRSTRLEN];
int mark = 0;
ms = calc_txstamp(rtp, &f->delivery);
if (f->subclass < AST_FORMAT_MAX_AUDIO) {
/* Re-calculate last TS */
rtp->lastts = rtp->lastts + ms * 8;
Kevin P. Fleming
committed
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)
if (option_debug > 2)
ast_log(LOG_DEBUG, "Difference is %d, ms is %d\n", abs(rtp->lastts - pred), ms);
} else {
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 */
Kevin P. Fleming
committed
if (ast_tvzero(f->delivery)) {
Mark Spencer
committed
if (abs(rtp->lastts - pred) < 7200) {
rtp->lastts = pred;
rtp->lastovidtimestamp += f->samples;
} else {
if (option_debug > 2)
ast_log(LOG_DEBUG, "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);
Mark Spencer
committed
rtp->lastovidtimestamp = rtp->lastts;
}
/* 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;
rtpheader = (unsigned char *)(f->data - 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));
Kevin P. Fleming
committed
if (res <0) {
if (!rtp->nat || (rtp->nat && (ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) {
ast_log(LOG_DEBUG, "RTP Transmission error of packet %d to %s:%d: %s\n", rtp->seqno, ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno));
} else if ((ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug) {
/* Only give this error message once if we are not RTP debugging */
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(iabuf, sizeof(iabuf), rtp->them.sin_addr), ntohs(rtp->them.sin_port));
ast_set_flag(rtp, FLAG_NAT_INACTIVE_NOWARN);
}
}
ast_verbose("Sent RTP packet to %s:%d (type %d, seq %u, ts %u, len %u)\n"
, ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->them.sin_addr), ntohs(rtp->them.sin_port), codec, rtp->seqno, rtp->lastts,res - hdrlen);
rtp->seqno++;
int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *_f)
{
struct ast_frame *f;
int codec;
int hdrlen = 12;
/* 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)
return 0;
/* Make sure we have enough space for RTP header */
if ((_f->frametype != AST_FRAME_VOICE) && (_f->frametype != AST_FRAME_VIDEO)) {
ast_log(LOG_WARNING, "RTP can only send voice\n");
return -1;
}
subclass = _f->subclass;
if (_f->frametype == AST_FRAME_VIDEO)
subclass &= ~0x1;
codec = ast_rtp_lookup_code(rtp, 1, subclass);
ast_log(LOG_WARNING, "Don't know how to send format %s packets with RTP\n", ast_getformatname(_f->subclass));
if (option_debug)
ast_log(LOG_DEBUG, "Ooh, format changed from %s to %s\n", ast_getformatname(rtp->lasttxformat), ast_getformatname(subclass));
if (rtp->smoother)
ast_smoother_free(rtp->smoother);
rtp->smoother = NULL;
}
Kevin P. Fleming
committed
case AST_FORMAT_SLINEAR:
if (!rtp->smoother) {
rtp->smoother = ast_smoother_new(320);
}
if (!rtp->smoother) {
ast_log(LOG_WARNING, "Unable to create smoother :(\n");
return -1;
}
ast_smoother_feed_be(rtp->smoother, _f);
while((f = ast_smoother_read(rtp->smoother)))
ast_rtp_raw_write(rtp, f, codec);
break;
case AST_FORMAT_ULAW:
case AST_FORMAT_ALAW:
if (!rtp->smoother) {
rtp->smoother = ast_smoother_new(160);
}
if (!rtp->smoother) {
ast_log(LOG_WARNING, "Unable to create smoother :(\n");
return -1;
}
ast_smoother_feed(rtp->smoother, _f);
while((f = ast_smoother_read(rtp->smoother)))
ast_rtp_raw_write(rtp, f, codec);
break;
case AST_FORMAT_G726:
if (!rtp->smoother) {
rtp->smoother = ast_smoother_new(80);
}
if (!rtp->smoother) {
ast_log(LOG_WARNING, "Unable to create smoother :(\n");
return -1;
}
ast_smoother_feed(rtp->smoother, _f);
while((f = ast_smoother_read(rtp->smoother)))
ast_rtp_raw_write(rtp, f, codec);
break;
case AST_FORMAT_G729A:
if (!rtp->smoother) {
rtp->smoother = ast_smoother_new(20);
if (rtp->smoother)
ast_smoother_set_flags(rtp->smoother, AST_SMOOTHER_FLAG_G729);
}
if (!rtp->smoother) {
ast_log(LOG_WARNING, "Unable to create g729 smoother :(\n");
return -1;
}
ast_smoother_feed(rtp->smoother, _f);
while((f = ast_smoother_read(rtp->smoother)))
ast_rtp_raw_write(rtp, f, codec);
break;
case AST_FORMAT_GSM:
if (!rtp->smoother) {
rtp->smoother = ast_smoother_new(33);
}
if (!rtp->smoother) {
ast_log(LOG_WARNING, "Unable to create GSM smoother :(\n");
return -1;
}
ast_smoother_feed(rtp->smoother, _f);
while((f = ast_smoother_read(rtp->smoother)))
ast_rtp_raw_write(rtp, f, codec);
break;
}
if (!rtp->smoother) {
ast_log(LOG_WARNING, "Unable to create ILBC smoother :(\n");
return -1;
}
ast_smoother_feed(rtp->smoother, _f);
while((f = ast_smoother_read(rtp->smoother)))
ast_rtp_raw_write(rtp, f, codec);
break;
ast_log(LOG_WARNING, "Not sure about sending format %s packets\n", ast_getformatname(subclass));
case AST_FORMAT_H261:
case AST_FORMAT_H263:
/* Don't buffer outgoing frames; send them one-per-packet: */
if (_f->offset < hdrlen) {
f = ast_frdup(_f);
} else {
f = _f;
}
ast_rtp_raw_write(rtp, f, codec);
}
return 0;
}
Olle Johansson
committed
/*! \brief Unregister interface to channel driver */
void ast_rtp_proto_unregister(struct ast_rtp_protocol *proto)
{
Olle Johansson
committed
AST_LIST_LOCK(&protos);
AST_LIST_REMOVE(&protos, proto, list);
Olle Johansson
committed
AST_LIST_UNLOCK(&protos);
Olle Johansson
committed
/*! \brief Register interface to channel driver */
int ast_rtp_proto_register(struct ast_rtp_protocol *proto)
{
struct ast_rtp_protocol *cur;
Olle Johansson
committed
AST_LIST_LOCK(&protos);
AST_LIST_TRAVERSE(&protos, cur, list) {
if (!strcmp(cur->type, proto->type)) {
ast_log(LOG_WARNING, "Tried to register same protocol '%s' twice\n", cur->type);
Olle Johansson
committed
AST_LIST_UNLOCK(&protos);
Olle Johansson
committed
AST_LIST_INSERT_HEAD(&protos, proto, list);
AST_LIST_UNLOCK(&protos);
Olle Johansson
committed
/*! \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_channel *who, *other, *cs[3];
struct ast_rtp *p0, *p1; /* Audio RTP Channels */
struct ast_rtp *vp0, *vp1; /* Video RTP channels */
struct sockaddr_in vac0, vac1;
struct sockaddr_in vt0, vt1;
char iabuf[INET_ADDRSTRLEN];
int codec0,codec1, oldcodec0, oldcodec1;
memset(&vt0, 0, sizeof(vt0));
memset(&vt1, 0, sizeof(vt1));
memset(&vac0, 0, sizeof(vac0));
memset(&vac1, 0, sizeof(vac1));
/* if need DTMF, cant native bridge */
if (flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1))
Joshua Colp
committed
ast_channel_lock(c0);
while(ast_channel_trylock(c1)) {
ast_channel_unlock(c0);
Joshua Colp
committed
ast_channel_lock(c0);
/* Find channel driver interfaces */
pr0 = get_proto(c0);
pr1 = get_proto(c1);
if (!pr0) {
ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c0->name);
Joshua Colp
committed
ast_channel_unlock(c0);
ast_channel_unlock(c1);
}
if (!pr1) {
ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c1->name);
Joshua Colp
committed
ast_channel_unlock(c0);
ast_channel_unlock(c1);
/* Get channel specific interface structures */
pvt0 = c0->tech_pvt;
pvt1 = c1->tech_pvt;
/* Get audio and video interface (if native bridge is possible) */
vp0 = pr0->get_vrtp_info ? pr0->get_vrtp_info(c0) : NULL;
vp1 = pr1->get_vrtp_info ? pr1->get_vrtp_info(c1) : NULL;
/* Check if bridge is still possible (In SIP canreinvite=no stops this, like NAT) */
if (!p0 || !p1) {
/* Somebody doesn't want to play... */
Joshua Colp
committed
ast_channel_unlock(c0);
ast_channel_unlock(c1);
/* Get codecs from both sides */
codec0 = pr0->get_codec ? pr0->get_codec(c0) : 0;
codec1 = pr1->get_codec ? pr1->get_codec(c1) : 0;
if (pr0->get_codec && pr1->get_codec) {
/* Hey, we can't do reinvite if both parties speak different codecs */
if (!(codec0 & codec1)) {
if (option_debug)
ast_log(LOG_DEBUG, "Channel codec0 = %d is not codec1 = %d, cannot native bridge in RTP.\n", codec0, codec1);
Joshua Colp
committed
ast_channel_unlock(c0);
ast_channel_unlock(c1);
Martin Pycko
committed
}
Kevin P. Fleming
committed
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Native bridging %s and %s\n", c0->name, c1->name);
/* Ok, we should be able to redirect the media. Start with one channel */
Kevin P. Fleming
committed
if (pr0->set_rtp_peer(c0, p1, vp1, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE)))
ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c0->name, c1->name);
else {
/* Store RTP peer */
ast_rtp_get_peer(p1, &ac1);
if (vp1)
/* Then test the other channel */
Kevin P. Fleming
committed
if (pr1->set_rtp_peer(c1, p0, vp0, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE)))
ast_log(LOG_WARNING, "Channel '%s' failed to talk back to '%s'\n", c1->name, c0->name);
else {
/* Store RTP peer */
ast_rtp_get_peer(p0, &ac0);
if (vp0)
Joshua Colp
committed
ast_channel_unlock(c0);
ast_channel_unlock(c1);