Newer
Older
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",
Russell Bryant
committed
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:%d (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n",
Russell Bryant
committed
ast_inet_ntoa(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));
/*! \note 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++;
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
/* \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(void *data)
{
struct ast_rtp *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];
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 %s\n",strerror(errno));
if (rtp->rtcp->schedid > 0)
ast_sched_del(rtp->sched, rtp->rtcp->schedid);
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
rtp->rtcp->schedid = -1;
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);
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) {
Russell Bryant
committed
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));
if (rtp->rtcp->schedid > 0)
ast_sched_del(rtp->sched, rtp->rtcp->schedid);
rtp->rtcp->schedid = -1;
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)) {
Russell Bryant
committed
ast_verbose("* Sent RTCP SR to %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port));
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
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));
}
return res;
}
/*! \brief Send RTCP recepient's report */
static int ast_rtcp_write_rr(void *data)
{
struct ast_rtp *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;
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 to, rtcp halted %s\n",strerror(errno));
if (rtp->rtcp->schedid > 0)
ast_sched_del(rtp->sched, rtp->rtcp->schedid);
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
rtp->rtcp->schedid = -1;
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 (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);
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 */
if (rtp->rtcp->schedid > 0)
ast_sched_del(rtp->sched, rtp->rtcp->schedid);
rtp->rtcp->schedid = -1;
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",
Russell Bryant
committed
ast_inet_ntoa(rtp->rtcp->them.sin_addr),
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
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(void *data)
{
struct ast_rtp *rtp = data;
int res;
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;
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)
Russell Bryant
committed
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:%d (type %d, seq %d, ts %u, len %d)\n"
Russell Bryant
committed
, ast_inet_ntoa(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;
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;
Russell Bryant
committed
if (f->has_timing_info)
rtp->lastts = f->ts * 8;
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))) {
Russell Bryant
committed
ast_log(LOG_DEBUG, "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));
Joshua Colp
committed
} else if (((ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug) && !ast_test_flag(rtp, FLAG_NAT_INACTIVE_NOWARN)) {
Kevin P. Fleming
committed
/* Only give this error message once if we are not RTP debugging */
if (option_debug || rtpdebug)
Russell Bryant
committed
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(rtp->them.sin_addr), ntohs(rtp->them.sin_port));
Kevin P. Fleming
committed
ast_set_flag(rtp, FLAG_NAT_INACTIVE_NOWARN);
}
} else {
rtp->txcount++;
rtp->txoctetcount +=(res - hdrlen);
if (rtp->rtcp && rtp->rtcp->schedid < 1)
rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, rtp);
Kevin P. Fleming
committed
}
if (rtp_debug_test_addr(&rtp->them))
ast_verbose("Sent RTP packet to %s:%d (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n",
Russell Bryant
committed
ast_inet_ntoa(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 and video\n");
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);
Joshua Colp
committed
/*! \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_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)
Joshua Colp
committed
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,}, ac0 = {0,}, vac0 = {0,};
struct sockaddr_in t1 = {0,}, vt1 = {0,}, t0 = {0,}, vt0 = {0,};
Joshua Colp
committed
/* Set it up so audio goes directly between the two endpoints */
Kevin P. Fleming
committed
Joshua Colp
committed
/* Test the first channel */
if (!(pr0->set_rtp_peer(c0, p1, vp1, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE)))) {
if (vp1)
Joshua Colp
committed
} 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, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE)))) {
if (vp0)
Joshua Colp
committed
} 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 */
Joshua Colp
committed
ast_channel_unlock(c0);
ast_channel_unlock(c1);
Joshua Colp
committed
/* Throw our channels into the structure and enter the loop */
cs[0] = c0;
cs[1] = c1;
cs[2] = NULL;
for (;;) {
Joshua Colp
committed
/* Check if anything changed */
if ((c0->tech_pvt != pvt0) ||
(c1->tech_pvt != pvt1) ||
(c0->masq || c0->masqr || c1->masq || c1->masqr)) {
ast_log(LOG_DEBUG, "Oooh, something is weird, backing out\n");
if (c0->tech_pvt == pvt0)
if (pr0->set_rtp_peer(c0, 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, 0, 0))
ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name);
return AST_BRIDGE_RETRY;
Joshua Colp
committed
/* Check if they have changed their address */
if (vp1)
ast_rtp_get_peer(vp1, &vt1);
Joshua Colp
committed
if (pr1->get_codec)
codec1 = pr1->get_codec(c1);
ast_rtp_get_peer(p0, &t0);
if (vp0)
ast_rtp_get_peer(vp0, &vt0);
Joshua Colp
committed
if (pr0->get_codec)
codec0 = pr0->get_codec(c0);
if ((inaddrcmp(&t1, &ac1)) ||
(vp1 && inaddrcmp(&vt1, &vac1)) ||
(codec1 != oldcodec1)) {
Joshua Colp
committed
ast_log(LOG_DEBUG, "Oooh, '%s' changed end address to %s:%d (format %d)\n",
Russell Bryant
committed
c1->name, ast_inet_ntoa(t1.sin_addr), ntohs(t1.sin_port), codec1);
Joshua Colp
committed
ast_log(LOG_DEBUG, "Oooh, '%s' changed end vaddress to %s:%d (format %d)\n",
Russell Bryant
committed
c1->name, ast_inet_ntoa(vt1.sin_addr), ntohs(vt1.sin_port), codec1);
Joshua Colp
committed
ast_log(LOG_DEBUG, "Oooh, '%s' was %s:%d/(format %d)\n",
Russell Bryant
committed
c1->name, ast_inet_ntoa(ac1.sin_addr), ntohs(ac1.sin_port), oldcodec1);
Joshua Colp
committed
ast_log(LOG_DEBUG, "Oooh, '%s' was %s:%d/(format %d)\n",
Russell Bryant
committed
c1->name, ast_inet_ntoa(vac1.sin_addr), ntohs(vac1.sin_port), oldcodec1);
Joshua Colp
committed
if (pr0->set_rtp_peer(c0, t1.sin_addr.s_addr ? p1 : NULL, vt1.sin_addr.s_addr ? vp1 : 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));
Joshua Colp
committed
if ((inaddrcmp(&t0, &ac0)) ||
(vp0 && inaddrcmp(&vt0, &vac0))) {
if (option_debug > 1) {
ast_log(LOG_DEBUG, "Oooh, '%s' changed end address to %s:%d (format %d)\n",
Russell Bryant
committed
c0->name, ast_inet_ntoa(t0.sin_addr), ntohs(t0.sin_port), codec0);
Joshua Colp
committed
ast_log(LOG_DEBUG, "Oooh, '%s' was %s:%d/(format %d)\n",
Russell Bryant
committed
c0->name, ast_inet_ntoa(ac0.sin_addr), ntohs(ac0.sin_port), oldcodec0);
Kevin P. Fleming
committed
if (pr1->set_rtp_peer(c1, t0.sin_addr.s_addr ? p0 : NULL, vt0.sin_addr.s_addr ? vp0 : 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));
Joshua Colp
committed
/* Wait for frame to come in on the channels */
if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) {
if (!timeoutms)
Mark Spencer
committed
return AST_BRIDGE_RETRY;
if (option_debug)
ast_log(LOG_DEBUG, "Ooh, empty read...\n");
if (ast_check_hangup(c0) || ast_check_hangup(c1))
break;
Joshua Colp
committed
fr = ast_read(who);
other = (who == c0) ? c1 : c0;
if (!fr || ((fr->frametype == AST_FRAME_DTMF) &&
(((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) ||
((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1))))) {
/* Break out of bridge */
*fo = fr;
Joshua Colp
committed
ast_log(LOG_DEBUG, "Oooh, got a %s\n", fr ? "digit" : "hangup");
if (c0->tech_pvt == pvt0)
if (pr0->set_rtp_peer(c0, NULL, NULL, 0, 0))
ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name);
Joshua Colp
committed
if (c1->tech_pvt == pvt1)
if (pr1->set_rtp_peer(c1, NULL, NULL, 0, 0))
ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name);
Joshua Colp
committed
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
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)) {
ast_indicate(other, fr->subclass);
ast_frfree(fr);
} else {
*fo = fr;
*rc = who;
ast_log(LOG_DEBUG, "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) ||
(fr->frametype == AST_FRAME_VOICE) ||
(fr->frametype == AST_FRAME_VIDEO)) {
ast_write(other, fr);
}
ast_frfree(fr);
}
/* Swap priority */
cs[2] = cs[0];
cs[0] = cs[1];
cs[1] = cs[2];
}
return AST_BRIDGE_FAILED;
}
Joshua Colp
committed
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
/*! \brief P2P RTP/RTCP Callback */
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;
int is_rtp = 0, is_rtcp = 0;
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);
/* Determine what this file descriptor is for */
if (rtp->s == fd)
is_rtp = 1;
else if (rtp->rtcp && rtp->rtcp->s == fd)
is_rtcp = 1;
/* If NAT support is turned on, then see if we need to change their address */
if (rtp->nat) {
/* If this is for RTP, check that - if it's for RTCP, check that */
if (is_rtp) {
if ((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_log(LOG_DEBUG, "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));
}
} else if (is_rtcp) {
if ((rtp->rtcp->them.sin_addr.s_addr != sin.sin_addr.s_addr) ||
(rtp->rtcp->them.sin_port != sin.sin_port)) {
rtp->rtcp->them = sin;
if (option_debug || rtpdebug)
ast_log(LOG_DEBUG, "P2P 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));
}
}
}
/* If this came from the RTP stream, write out via RTP - if it's RTCP, write out via RTCP */
if (is_rtp)
bridge_p2p_rtp_write(rtp, header, res, hdrlen);
else if (is_rtcp)
bridge_p2p_rtcp_write(rtp, header, res);
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 *fds, int **iod)
{
/* If we need DTMF or we have no IO structure, then we can't do direct callback */
if (ast_test_flag(rtp, FLAG_P2P_NEED_DTMF) || !rtp->io)
return 0;
Joshua Colp
committed
/* 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;
}
Joshua Colp
committed
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
/* Steal the file descriptors from the channel and stash them away */
fds[0] = chan->fds[0];
fds[1] = chan->fds[1];
chan->fds[0] = -1;
chan->fds[1] = -1;
/* Now, fire up callback mode */
iod[0] = ast_io_add(rtp->io, fds[0], p2p_rtp_callback, AST_IO_IN, rtp);
iod[1] = ast_io_add(rtp->io, fds[1], p2p_rtp_callback, AST_IO_IN, rtp);
return 1;
}
/*! \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 *fds, int **iod)
{
ast_channel_lock(chan);
/* Remove the callback from the IO context */
ast_io_remove(rtp->io, iod[0]);
ast_io_remove(rtp->io, iod[1]);
/* Restore file descriptors */
chan->fds[0] = fds[0];
chan->fds[1] = fds[1];
ast_channel_unlock(chan);
Joshua Colp
committed
/* Restore callback mode if previously used */
if (ast_test_flag(rtp, FLAG_CALLBACK_MODE))
rtp->ioid = ast_io_add(rtp->io, rtp->s, rtpread, AST_IO_IN, rtp);
Joshua Colp
committed
return 0;
}
Joshua Colp
committed
/*! \brief Bridge loop for partial native bridge (packet2packet) */
static enum ast_bridge_result bridge_p2p_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, 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, };
Joshua Colp
committed
int p0_fds[2] = {-1, -1}, p1_fds[2] = {-1, -1};
int *p0_iod[2] = {NULL, }, *p1_iod[2] = {NULL, };
int p0_callback = 0, p1_callback = 0;
enum ast_bridge_result res = AST_BRIDGE_FAILED;
Joshua Colp
committed
/* Okay, setup each RTP structure to do P2P forwarding */
ast_clear_flag(p0, FLAG_P2P_SENT_MARK);
p0->bridged = p1;
ast_clear_flag(p1, FLAG_P2P_SENT_MARK);
p1->bridged = p0;
if (vp0) {
ast_clear_flag(vp0, FLAG_P2P_SENT_MARK);
vp0->bridged = vp1;
ast_clear_flag(vp1, FLAG_P2P_SENT_MARK);
vp1->bridged = vp0;
}
Joshua Colp
committed
/* Activate callback modes if possible */
p0_callback = p2p_callback_enable(c0, p0, &p0_fds[0], &p0_iod[0]);
p1_callback = p2p_callback_enable(c1, p1, &p1_fds[0], &p1_iod[0]);
Joshua Colp
committed
/* Now let go of the channel locks and be on our way */
ast_channel_unlock(c0);
ast_channel_unlock(c1);
/* Go into a loop forwarding frames until we don't need to anymore */
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)) {
ast_log(LOG_DEBUG, "Oooh, something is weird, backing out\n");
Joshua Colp
committed
res = AST_BRIDGE_RETRY;
break;
Joshua Colp
committed
}
/* Wait on a channel to feed us a frame */
if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) {
Joshua Colp
committed
if (!timeoutms) {
res = AST_BRIDGE_RETRY;
break;
}
Joshua Colp
committed
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
if (option_debug)
ast_log(LOG_NOTICE, "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;
/* Dependong on the frame we may need to break out of our bridge */
if (!fr || ((fr->frametype == AST_FRAME_DTMF) &&
((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;
if (option_debug)
ast_log(LOG_DEBUG, "Oooh, got a %s\n", fr ? "digit" : "hangup");
/* Break out of the bridge */
p0->bridged = NULL;
p1->bridged = NULL;
if (vp0) {
vp0->bridged = NULL;
vp1->bridged = NULL;
Joshua Colp
committed
res = AST_BRIDGE_COMPLETE;
break;
Joshua Colp
committed
} 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)) {
Joshua Colp
committed
/* If we are going on hold, then break callback mode */
if (fr->subclass == AST_CONTROL_HOLD) {
if (p0_callback)
p0_callback = p2p_callback_disable(c0, p0, &p0_fds[0], &p0_iod[0]);
if (p1_callback)
p1_callback = p2p_callback_disable(c1, p1, &p1_fds[0], &p1_iod[0]);
} else if (fr->subclass == AST_CONTROL_UNHOLD) {
/* If we are off hold, then go back to callback mode */
p0_callback = p2p_callback_enable(c0, p0, &p0_fds[0], &p0_iod[0]);
p1_callback = p2p_callback_enable(c1, p1, &p1_fds[0], &p1_iod[0]);
}
Joshua Colp
committed
ast_indicate(other, fr->subclass);
ast_frfree(fr);
Joshua Colp
committed
*fo = fr;
Joshua Colp
committed
ast_log(LOG_DEBUG, "Got a FRAME_CONTROL (%d) frame on channel %s\n", fr->subclass, who->name);
Joshua Colp
committed
res = AST_BRIDGE_COMPLETE;
break;
Joshua Colp
committed
/* If this is a DTMF, voice, or video frame write it to the other channel */
if ((fr->frametype == AST_FRAME_DTMF) ||
(fr->frametype == AST_FRAME_VOICE) ||
(fr->frametype == AST_FRAME_VIDEO)) {
ast_write(other, fr);
Joshua Colp
committed
ast_frfree(fr);
Joshua Colp
committed
/* Swap priority */
cs[2] = cs[0];
cs[0] = cs[1];
cs[1] = cs[2];
}
Joshua Colp
committed
Joshua Colp
committed
/* If we are totally avoiding the core, then restore our link to it */
if (p0_callback)
p0_callback = p2p_callback_disable(c0, p0, &p0_fds[0], &p0_iod[0]);
if (p1_callback)
p1_callback = p2p_callback_disable(c1, p1, &p1_fds[0], &p1_iod[0]);
return res;
Joshua Colp
committed
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
/*! \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_protocol *pr0 = NULL, *pr1 = NULL;
enum ast_rtp_get_result audio_p0_res = AST_RTP_GET_FAILED, video_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;
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);
}
/* 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;
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;