experimental support for RTP/AVP<>AVPF bridging

git.mgm/mediaproxy-ng/github/master
Richard Fuchs 13 years ago
parent 145139d0db
commit d8bc7d92a2

@ -32,7 +32,7 @@ LDFLAGS+= `dpkg-buildflags --get LDFLAGS`
endif
SRCS= main.c kernel.c poller.c aux.c control_tcp.c streambuf.c call.c control_udp.c redis.c \
bencode.c cookie_cache.c udp_listener.c control_ng.c sdp.c str.c stun.c
bencode.c cookie_cache.c udp_listener.c control_ng.c sdp.c str.c stun.c rtcp.c
OBJS= $(SRCS:.c=.o)

@ -27,6 +27,7 @@
#include "sdp.h"
#include "str.h"
#include "stun.h"
#include "rtcp.h"
@ -111,6 +112,12 @@ static char *rtp_codecs[] = {
[33] = "MP2T",
[34] = "H263",
};
const char *transport_protocol_strings[__PROTO_RTP_LAST] = {
[PROTO_RTP_AVP] = "RTP/AVP",
[PROTO_RTP_SAVP] = "RTP/SAVP",
[PROTO_RTP_AVPF] = "RTP/AVPF",
[PROTO_RTP_SAVPF] = "RTP/SAVPF",
};
@ -208,12 +215,39 @@ void kernelize(struct callstream *c) {
int __dummy_stream_handler(str *s) {
abort();
return 0;
}
static stream_handler determine_handler(struct streamrelay *in) {
if (in->peer.protocol == in->peer_advertised.protocol)
goto dummy;
if (in->peer.protocol == PROTO_UNKNOWN)
goto dummy;
if (in->peer_advertised.protocol == PROTO_UNKNOWN)
goto dummy;
if (in->peer.protocol == PROTO_RTP_AVPF && in->peer_advertised.protocol == PROTO_RTP_AVP) {
if (!in->rtcp)
goto dummy;
return rtcp_avpf2avp;
}
if (in->peer.protocol == PROTO_RTP_AVP && in->peer_advertised.protocol == PROTO_RTP_AVPF)
goto dummy;
/* XXX warn? */
dummy:
return __dummy_stream_handler;
}
/* called with r->up (== cs) locked */
static int stream_packet(struct streamrelay *sr_incoming, str *s, struct sockaddr_in6 *fsin) {
struct streamrelay *sr_outgoing, *sr_out_rtcp;
struct peer *p_incoming, *p_outgoing;
struct callstream *cs_incoming;
int ret, update = 0, stun_ret = 0;
int ret, update = 0, stun_ret = 0, handler_ret = 0;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
struct msghdr mh;
@ -255,6 +289,11 @@ static int stream_packet(struct streamrelay *sr_incoming, str *s, struct sockadd
return 0;
}
if (!sr_incoming->handler)
sr_incoming->handler = determine_handler(sr_incoming);
if (sr_incoming->handler != __dummy_stream_handler)
handler_ret = sr_incoming->handler(s);
use_cand:
if (p_incoming->confirmed || !p_incoming->filled || sr_incoming->idx != 0)
goto forward;
@ -283,15 +322,18 @@ peerinfo:
sr_out_rtcp->peer.ip46 = sr_outgoing->peer.ip46;
sr_out_rtcp->peer.port = sr_outgoing->peer.port + 1; /* sr_out_rtcp->idx == 1 */
update = 1;
if (sr_incoming->handler != __dummy_stream_handler)
goto forward;
if (p_incoming->confirmed && p_outgoing->confirmed && p_outgoing->filled)
kernelize(cs_incoming);
update = 1;
forward:
if (is_addr_unspecified(&sr_incoming->peer_advertised.ip46)
|| !sr_incoming->peer_advertised.port || !sr_incoming->fd.fd_family
|| stun_ret)
|| stun_ret || handler_ret)
goto drop;
ZERO(mh);
@ -1017,11 +1059,11 @@ static int setup_peer(struct peer *p, struct stream_input *s, const str *tag) {
a->peer.port = b->peer.port = s->stream.port;
if (b->peer.port)
b->peer.port++;
a->peer.protocol = b->peer.protocol = s->stream.protocol;
a->peer_advertised = a->peer;
b->peer_advertised = b->peer;
a->rtcp = s->is_rtcp;
b->rtcp = 1;
p->protocol = s->protocol;
for (i = 0; i < 2; i++) {
switch (s->direction[i]) {
@ -1072,7 +1114,6 @@ static void steal_peer(struct peer *dest, struct peer *src) {
dest->desired_family = src->desired_family;
dest->ice_ufrag = src->ice_ufrag;
dest->ice_pwd = src->ice_pwd;
dest->protocol = src->protocol;
for (i = 0; i < 2; i++) {
sr = &dest->rtps[i];
@ -2302,6 +2343,8 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, GQueue *streams, ben
else if (!str_cmp(&s, "force"))
out->ice_force = 1;
}
bencode_dictionary_get_str(input, "transport-protocol", &out->transport_protocol);
}
static unsigned int stream_hash(struct stream_input *s) {

@ -53,7 +53,10 @@ enum transport_protocol {
PROTO_RTP_SAVP,
PROTO_RTP_AVPF,
PROTO_RTP_SAVPF,
__PROTO_RTP_LAST
};
extern const char *transport_protocol_strings[__PROTO_RTP_LAST];
struct stats {
u_int64_t packets;
@ -65,12 +68,12 @@ struct stream {
struct in6_addr ip46;
u_int16_t port;
int num;
enum transport_protocol protocol;
};
struct stream_input {
struct stream stream;
enum stream_direction direction[2];
int consecutive_num;
enum transport_protocol protocol;
int has_rtcp:1;
int is_rtcp:1;
};
@ -79,6 +82,10 @@ struct udp_fd {
int fd_family;
u_int16_t localport;
};
typedef int (*stream_handler)(str *);
int __dummy_stream_handler(str *);
struct streamrelay {
struct udp_fd fd;
struct stream peer;
@ -89,6 +96,7 @@ struct streamrelay {
struct stats stats;
struct stats kstats;
time_t last;
stream_handler handler;
int stun:1;
int rtcp:1;
};
@ -108,7 +116,6 @@ struct peer {
int desired_family;
str ice_ufrag;
str ice_pwd;
enum transport_protocol protocol;
int kernelized:1;
int filled:1;
int confirmed:1;

@ -0,0 +1,322 @@
#include "rtcp.h"
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include "str.h"
#include "call.h"
#include "log.h"
#define RTCP_PT_SR 200 /* sender report */
#define RTCP_PT_RR 201 /* receiver report */
#define RTCP_PT_SDES 202 /* source description */
#define RTCP_PT_BYE 203 /* bye */
#define RTCP_PT_APP 204 /* application specific */
#define RTCP_PT_RTPFB 205 /* transport layer feedback message (RTP/AVPF) */
#define RTCP_PT_PSFB 206 /* payload-specific feedback message (RTP/AVPF) */
#define SDES_TYPE_END 0
#define SDES_TYPE_CNAME 1
#define SDES_TYPE_NAME 2
#define SDES_TYPE_EMAIL 3
#define SDES_TYPE_PHONE 4
#define SDES_TYPE_LOC 5
#define SDES_TYPE_TOOL 6
#define SDES_TYPE_NOTE 7
#define SDES_TYPE_PRIV 8
struct rtcp_header {
unsigned char v_p_x;
unsigned char pt;
u_int16_t length;
} __attribute__ ((packed));
struct rtcp_packet {
struct rtcp_header header;
u_int32_t ssrc;
} __attribute__ ((packed));
struct report_block {
u_int32_t ssrc;
unsigned char fraction_lost;
unsigned char number_lost[3];
u_int32_t high_seq_received;
u_int32_t jitter;
u_int32_t lsr;
u_int32_t dlsr;
} __attribute__ ((packed));
struct sender_report_packet {
struct rtcp_packet rtcp;
u_int32_t ntp_msw;
u_int32_t ntp_lsw;
u_int32_t timestamp;
u_int32_t packet_count;
u_int32_t octet_count;
struct report_block reports[0];
} __attribute__ ((packed));
struct receiver_report_packet {
struct rtcp_packet rtcp;
struct report_block reports[0];
} __attribute__ ((packed));
struct sdes_item {
unsigned char type;
unsigned char length;
unsigned char data[0];
} __attribute__ ((packed));
struct sdes_chunk {
u_int32_t ssrc;
struct sdes_item items[0];
} __attribute__ ((packed));
struct source_description_packet {
struct rtcp_header header;
struct sdes_chunk chunks[0];
} __attribute__ ((packed));
struct bye_packet {
struct rtcp_header header;
u_int32_t ssrcs[0];
} __attribute__ ((packed));
struct app_packet {
struct rtcp_packet rtcp;
unsigned char name[4];
unsigned char data[0];
} __attribute__ ((packed));
struct fb_packet {
struct rtcp_packet rtcp;
u_int32_t media_ssrc;
unsigned char information[0];
} __attribute__ ((packed));
struct rtcp_chain_element {
int type;
unsigned int len;
union {
void *buf;
struct rtcp_packet *rtcp_packet;
struct sender_report_packet *sr;
struct receiver_report_packet *rr;
struct source_description_packet *sdes;
struct bye_packet *bye;
struct app_packet *app;
} u;
};
typedef struct rtcp_chain_element *(*rtcp_handler_func)(str *);
static struct rtcp_chain_element *rtcp_sr(str *s);
static struct rtcp_chain_element *rtcp_rr(str *s);
static struct rtcp_chain_element *rtcp_sdes(str *s);
static struct rtcp_chain_element *rtcp_bye(str *s);
static struct rtcp_chain_element *rtcp_app(str *s);
static struct rtcp_chain_element *rtcp_rtpfb(str *s);
static struct rtcp_chain_element *rtcp_psfb(str *s);
static const rtcp_handler_func handler_funcs[] = {
[RTCP_PT_SR] = rtcp_sr,
[RTCP_PT_RR] = rtcp_rr,
[RTCP_PT_SDES] = rtcp_sdes,
[RTCP_PT_BYE] = rtcp_bye,
[RTCP_PT_APP] = rtcp_app,
[RTCP_PT_RTPFB] = rtcp_rtpfb,
[RTCP_PT_PSFB] = rtcp_psfb,
};
static void *rtcp_length_check(str *s, size_t min_len, unsigned int *len_p) {
struct rtcp_header *h;
if (s->len < min_len)
return NULL;
h = (void *) s->s;
*len_p = (ntohs(h->length) + 1) << 2;
if (*len_p > s->len)
return NULL;
return h;
}
static struct rtcp_chain_element *rtcp_new_element(void *p, unsigned int len, int type) {
struct rtcp_chain_element *el;
el = g_slice_alloc(sizeof(*el));
el->type = type;
el->len = len;
el->u.buf = p;
return el;
}
static struct rtcp_chain_element *rtcp_generic(str *s, int type) {
struct rtcp_header *p;
unsigned int len;
if (!(p = rtcp_length_check(s, sizeof(*p), &len)))
return NULL;
return rtcp_new_element(p, len, type);
}
static struct rtcp_chain_element *rtcp_Xr(str *s, int type, size_t struct_len) {
struct rtcp_packet *p;
unsigned int len;
if (!(p = rtcp_length_check(s, struct_len, &len)))
return NULL;
if (len < (p->header.v_p_x & 0x1f) * sizeof(struct report_block))
return NULL;
return rtcp_new_element(p, len, type);
}
static struct rtcp_chain_element *rtcp_sr(str *s) {
return rtcp_Xr(s, RTCP_PT_SR, sizeof(struct sender_report_packet));
}
static struct rtcp_chain_element *rtcp_rr(str *s) {
return rtcp_Xr(s, RTCP_PT_RR, sizeof(struct receiver_report_packet));
}
static struct rtcp_chain_element *rtcp_sdes(str *s) {
struct source_description_packet *p;
unsigned int len;
if (!(p = rtcp_length_check(s, sizeof(*p), &len)))
return NULL;
/* sdes items ... */
return rtcp_new_element(p, len, RTCP_PT_SDES);
}
static struct rtcp_chain_element *rtcp_bye(str *s) {
return rtcp_generic(s, RTCP_PT_BYE);
}
static struct rtcp_chain_element *rtcp_app(str *s) {
return rtcp_generic(s, RTCP_PT_APP);
}
static struct rtcp_chain_element *rtcp_rtpfb(str *s) {
return rtcp_generic(s, RTCP_PT_RTPFB);
}
static struct rtcp_chain_element *rtcp_psfb(str *s) {
return rtcp_generic(s, RTCP_PT_PSFB);
}
static void rtcp_list_free_cb(void *d) {
g_slice_free1(sizeof(struct rtcp_chain_element), d);
}
static void rtcp_list_free(GQueue *q) {
g_queue_free_full(q, rtcp_list_free_cb);
}
static int rtcp_parse(GQueue *q, str *_s) {
struct rtcp_packet *hdr;
struct rtcp_chain_element *el;
rtcp_handler_func func;
str s = *_s;
while (1) {
if (s.len < sizeof(*hdr))
break;
hdr = (void *) s.s;
if ((hdr->header.v_p_x & 0xc0) != 0x80) /* version 2 */
goto error;
if (hdr->header.pt >= ARRAYSIZE(handler_funcs))
goto error;
func = handler_funcs[hdr->header.pt];
if (!func)
goto error;
el = func(&s);
if (!el)
goto error;
g_queue_push_tail(q, el);
if (str_shift(&s, el->len))
abort();
}
return 0;
error:
rtcp_list_free(q);
return -1;
}
int rtcp_avpf2avp(str *s) {
GQueue rtcp_list = G_QUEUE_INIT;
GList *l;
struct rtcp_chain_element *el;
void *start;
unsigned int removed, left;
if (rtcp_parse(&rtcp_list, s))
return 0;
left = s->len;
removed = 0;
for (l = rtcp_list.head; l; l = l->next) {
el = l->data;
left -= el->len;
switch (el->type) {
case RTCP_PT_SR:
case RTCP_PT_RR:
case RTCP_PT_SDES:
case RTCP_PT_BYE:
case RTCP_PT_APP:
break;
case RTCP_PT_RTPFB:
case RTCP_PT_PSFB:
start = el->u.buf;
memmove(start - removed, start + el->len - removed, left);
removed += el->len;
break;
default:
abort();
}
}
s->len -= removed;
if (!s->len)
return -1;
return 0;
}

@ -0,0 +1,8 @@
#ifndef _RTCP_H_
#define _RTCP_H_
#include "str.h"
int rtcp_avpf2avp(str *);
#endif

@ -562,15 +562,23 @@ static enum transport_protocol transport_protocol(str *s) {
case 7:
if (!str_cmp(s, "RTP/AVP"))
return PROTO_RTP_AVP;
if (!str_cmp(s, "rtp/avp"))
return PROTO_RTP_AVP;
break;
case 8:
if (!str_cmp(s, "RTP/SAVP"))
return PROTO_RTP_SAVP;
if (!str_cmp(s, "rtp/savp"))
return PROTO_RTP_SAVP;
if (!str_cmp(s, "RTP/AVPF"))
return PROTO_RTP_AVPF;
if (!str_cmp(s, "rtp/avpf"))
return PROTO_RTP_AVPF;
break;
case 9:
if (!str_cmp(s, "RTP/SAVPF"))
if (!str_cmp(s, "rtp/savpf"))
return PROTO_RTP_SAVPF;
if (!str_cmp(s, "rtp/savpf"))
return PROTO_RTP_SAVPF;
break;
}
@ -611,7 +619,7 @@ int sdp_streams(const GQueue *sessions, GQueue *streams, GHashTable *streamhash,
si->stream.num = ++num;
si->consecutive_num = (i == 0) ? media->port_count : 1;
si->protocol = tp;
si->stream.protocol = tp;
g_hash_table_insert(streamhash, si, si);
g_queue_push_tail(streams, si);
@ -634,7 +642,7 @@ int sdp_streams(const GQueue *sessions, GQueue *streams, GHashTable *streamhash,
si->stream.num = ++num;
si->consecutive_num = 1;
si->is_rtcp = 1;
si->protocol = tp;
si->stream.protocol = tp;
g_hash_table_insert(streamhash, si, si);
g_queue_push_tail(streams, si);
@ -748,6 +756,24 @@ static void fill_relays(struct streamrelay **rtp, struct streamrelay **rtcp, GLi
}
}
static int replace_transport_protocol(struct sdp_chopper *chop,
struct sdp_media *media, struct streamrelay *sr)
{
str *tp = &media->transport;
const char *new_tp = transport_protocol_strings[sr->peer.protocol];
if (!new_tp)
return 0; /* XXX correct? or give warning? */
if (copy_up_to(chop, tp))
return -1;
chopper_append_c(chop, new_tp);
if (skip_over(chop, tp))
return -1;
return 0;
}
static int replace_media_port(struct sdp_chopper *chop, struct sdp_media *media, struct streamrelay *sr) {
str *port = &media->port;
@ -1096,10 +1122,15 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call *call,
goto error;
fill_relays(&rtp, &rtcp, m, off, sip);
rtp->peer.protocol = transport_protocol(&flags->transport_protocol);
rtcp->peer.protocol = rtp->peer.protocol;
if (replace_media_port(chop, media, rtp))
goto error;
if (replace_consecutive_port_count(chop, media, rtp, m, off))
goto error;
if (replace_transport_protocol(chop, media, rtp))
goto error;
if (media->connection.parsed && flags->replace_sess_conn) {
if (replace_network_address(chop, &media->connection.address, rtp))

@ -10,6 +10,7 @@ struct sdp_ng_flags {
int desired_family[2];
str received_from_family;
str received_from_address;
str transport_protocol;
struct in6_addr parsed_address;
int asymmetric:1,
symmetric:1,

@ -8,6 +8,7 @@ use BSD::Resource;
use Getopt::Long;
use Socket6;
use Bencode qw( bencode bdecode );
use Time::HiRes;
my ($NUM, $RUNTIME, $STREAMS) = (1000, 30, 1);
my ($NODEL, $IP, $IPV6, $KEEPGOING, $REINVITES, $BRANCHES);
@ -62,28 +63,79 @@ msg({command => 'ping'})->{result} eq 'pong' or die;
my (@calls, %branches);
sub send_receive {
my ($send_fd, $receive_fd, $payload, $destination) = @_;
send($send_fd, $payload, 0, $destination) or die $!;
my $x;
my $err = '';
alarm(1);
recv($receive_fd, $x, 0xffff, 0) or $err = "$!";
alarm(0);
$err && $err !~ /interrupt/i and die $err;
return $x;
}
sub send_expect {
my ($send_fd, $receive_fd, $payload, $expect, $destination) = @_;
my $x = send_receive($send_fd, $receive_fd, $payload, $destination);
if (($x || '') ne $expect) {
return 0;
}
return 1;
}
sub rtcp_sr {
my @now = Time::HiRes::gettimeofday();
my $secs = $now[0] + 2208988800;
my $frac = $now[1] / 1000000 * 2**32;
my $sr = pack('CCnN NNN NN', (2 << 6) | 1, 200, 12, rand() * 2**32, $secs, $frac,
12345, 0, 0);
$sr .= pack('N CCCC NNNN', 0, 0, 0, 0, 0, 0, 0, 0, 0);
return $sr;
}
sub rtcp_rtpfb {
return pack('CCn NN', (2 << 6) | 1, 205, 2, rand() * 2**32, rand() * 2**32);
}
sub rtcp_avp {
my $sr = rtcp_sr();
return ($sr, $sr);
}
sub rtcp_avpf {
my ($recv) = @_;
my $sr = rtcp_sr();
my $fb = rtcp_rtpfb();
my $exp = $sr;
$$recv{name} eq 'RTP/AVPF' and $exp .= $fb;
return ($sr . $fb, $exp);
}
sub do_rtp {
print("sending rtp\n");
for my $c (@calls) {
$c or next;
my ($fds,$outputs,$protos) = @$c[0,4,6];
my ($fds,$outputs,$protos,$cfds,$trans) = @$c[0,4,6,7,8];
for my $j (0 .. $#{$$fds[0]}) {
for my $i ([0,1],[1,0]) {
my ($a, $b) = @$i;
my $pr = $$protos[$a];
my $addr = inet_pton($$pr{family}, $$outputs[$b][$j][1]);
my $payload = rand_str(100);
send($$fds[$a][$j], $payload, 0, $$pr{sockaddr}($$outputs[$b][$j][0],
inet_pton($$pr{family}, $$outputs[$b][$j][1]))) or die $!;
my $x;
my $err = '';
alarm(1);
recv($$fds[$b][$j], $x, 0xffff, 0) or $err = "$!";
alarm(0);
$err && $err !~ /interrupt/i and die $err;
if (($x || '') ne $payload) {
my $dst = $$pr{sockaddr}($$outputs[$b][$j][0], $addr);
if (!send_expect($$fds[$a][$j], $$fds[$b][$j], $payload, $payload, $dst)) {
warn("no rtp reply received, ports $$outputs[$b][$j][0] and $$outputs[$a][$j][0]");
$KEEPGOING or undef($c);
}
my $expect;
($payload, $expect) = $$trans[$a]{func}($$trans[$b]);
$dst = $$pr{sockaddr}($$outputs[$b][$j][0] + 1, $addr);
my $repl = send_receive($$cfds[$a][$j], $$cfds[$b][$j], $payload, $dst);
$repl eq $expect or die;
}
}
}
@ -114,6 +166,17 @@ $IP and push(@protos_avail, $proto_defs{ipv4});
$IPV6 and push(@protos_avail, $proto_defs{ipv6});
my @sides = qw(A B);
my @transports = (
{
name => 'RTP/AVP',
func => \&rtcp_avp,
},
{
name => 'RTP/AVPF',
func => \&rtcp_avpf,
},
);
sub callid {
my $i = rand_str(50);
$BRANCHES or return [$i];
@ -135,13 +198,21 @@ sub update_lookup {
my ($callid, $viabranch) = @$c_v;
my $protos = $$c[6] || ($$c[6] = []);
my $trans = $$c[8] || ($$c[8] = []);
my $fds_a = $$c[0] || ($$c[0] = []);
my $cfds_a = $$c[7] || ($$c[7] = []);
for my $x (0,1) {
$$protos[$x] and next;
$$protos[$x] = $protos_avail[rand(@protos_avail)];
undef($$fds_a[$x]);
}
for my $x (0,1) {
$$trans[$x] and next;
#$$trans[$x] = $transports[rand(@transports)];
$$trans[$x] = $transports[rand(@transports)];
}
my ($pr, $pr_o) = @$protos[$i, $j];
my ($tr, $tr_o) = @$trans[$i, $j];
my @commands = qw(offer answer);
my $ports_a = $$c[1] || ($$c[1] = []);
@ -150,15 +221,23 @@ sub update_lookup {
my $ips_t = $$ips_a[$i] || ($$ips_a[$i] = []);
my $fds_t = $$fds_a[$i] || ($$fds_a[$i] = []);
my $fds_o = $$fds_a[$j];
my $cfds_t = $$cfds_a[$i] || ($$cfds_a[$i] = []);
my $cfds_o = $$cfds_a[$j];
my $num_streams = int(rand($STREAMS));
($fds_o && @$fds_o) and $num_streams = $#$fds_o;
for my $j (0 .. $num_streams) {
if (!$$fds_t[$j]) {
socket($$fds_t[$j], $$pr{family}, SOCK_DGRAM, 0) or die $!;
while (1) {
undef($$fds_t[$j]);
undef($$cfds_t[$j]);
socket($$fds_t[$j], $$pr{family}, SOCK_DGRAM, 0) or die $!;
socket($$cfds_t[$j], $$pr{family}, SOCK_DGRAM, 0) or die $!;
my $port = rand(0x7000) << 1 + 1024;
bind($$fds_t[$j], $$pr{sockaddr}($port,
inet_pton($$pr{family}, $$pr{address}))) and last;
inet_pton($$pr{family}, $$pr{address}))) or next;
bind($$cfds_t[$j], $$pr{sockaddr}($port + 1,
inet_pton($$pr{family}, $$pr{address}))) or next;
last;
}
my $addr = getsockname($$fds_t[$j]);
my $ip;
@ -178,9 +257,11 @@ c=IN $$pr{family_str} $$ips_t[0]
t=0 0
!
for my $p (@$ports_t) {
my $cp = $p + 1;
$sdp .= <<"!";
m=audio $p RTP/AVP 8
m=audio $p $$tr{name} 8
a=rtpmap:8 PCMA/8000
a=rtcp:$cp
!
}
@ -190,6 +271,7 @@ a=rtpmap:8 PCMA/8000
replace => [ qw( origin session-connection ) ],
direction => [ $$pr{direction}, $$pr_o{direction} ],
'received-from' => [ qw(IP4 127.0.0.1) ],
'transport-protocol' => $$tr_o{name},
};
$viabranch and $dict->{'via-branch'} = $viabranch;
$i == 1 and $dict->{'to-tag'} = $$tags[1];
@ -197,7 +279,7 @@ a=rtpmap:8 PCMA/8000
my $o = msg($dict);
$$o{result} eq 'ok' or die;
my ($rp_af, $rp_add) = $$o{sdp} =~ /c=IN IP([46]) (\S+)/s or die;
my @rp_ports = $$o{sdp} =~ /m=audio (\d+)/gs or die;
my @rp_ports = $$o{sdp} =~ /m=audio (\d+) \Q$$tr_o{name}\E /gs or die;
$rp_af ne $$pr_o{reply} and die "incorrect address family reply code";
my $rpl_a = $$c[4] || ($$c[4] = []);
my $rpl_t = $$rpl_a[$i] || ($$rpl_a[$i] = []);

Loading…
Cancel
Save