|
|
|
|
@ -96,6 +96,7 @@ sub new {
|
|
|
|
|
|
|
|
|
|
$self->{media_port} = 2000;
|
|
|
|
|
$self->{timers} = [];
|
|
|
|
|
$self->{clients} = [];
|
|
|
|
|
|
|
|
|
|
$self->{rtpe} = Rtpengine->new('localhost', 2223);
|
|
|
|
|
$self->{callid} = rand();
|
|
|
|
|
@ -105,7 +106,9 @@ sub new {
|
|
|
|
|
|
|
|
|
|
sub client {
|
|
|
|
|
my ($self, %args) = @_;
|
|
|
|
|
return Rtpengine::Test::Client->_new($self, %args);
|
|
|
|
|
my $cl = Rtpengine::Test::Client->_new($self, %args);
|
|
|
|
|
push(@{$self->{clients}}, $cl);
|
|
|
|
|
return $cl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub run {
|
|
|
|
|
@ -124,6 +127,11 @@ sub mux_input {
|
|
|
|
|
my ($self, $mux, $fh, $input) = @_;
|
|
|
|
|
|
|
|
|
|
my $peer = $mux->udp_peer($fh);
|
|
|
|
|
|
|
|
|
|
for my $cl (@{$self->{clients}}) {
|
|
|
|
|
$$input eq '' and last;
|
|
|
|
|
$cl->_input($fh, $input, $peer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub mux_timeout {
|
|
|
|
|
@ -154,6 +162,7 @@ sub _new {
|
|
|
|
|
my @addresses = @{$parent->{all_addresses}};
|
|
|
|
|
@addresses = List::Util::shuffle @addresses;
|
|
|
|
|
my (@sockets, @rtp, @rtcp);
|
|
|
|
|
# XXX support rtcp-mux and rtcp-less media
|
|
|
|
|
|
|
|
|
|
for my $address (@addresses) {
|
|
|
|
|
my $rtp = IO::Socket::IP->new(Type => &Socket::SOCK_DGRAM, Proto => 'udp',
|
|
|
|
|
@ -174,28 +183,57 @@ sub _new {
|
|
|
|
|
|
|
|
|
|
$self->{main_sockets} = $sockets[0]; # for m= and o=
|
|
|
|
|
$self->{local_sdp} = SDP->new($self->{main_sockets}->[0]); # no global c=
|
|
|
|
|
$self->{component_peers} = []; # keep track of source addresses
|
|
|
|
|
|
|
|
|
|
# default protocol
|
|
|
|
|
my $proto = 'RTP/AVP';
|
|
|
|
|
$args{dtls} and $proto = 'UDP/TLS/RTP/SAVP';
|
|
|
|
|
$args{protocol} and $proto = $args{protocol};
|
|
|
|
|
|
|
|
|
|
$self->{local_media} = $self->{local_sdp}->add_media(SDP::Media->new(
|
|
|
|
|
$self->{main_sockets}->[0], $self->{main_sockets}->[1], 'RTP/AVP')); # main rtp and rtcp
|
|
|
|
|
$self->{main_sockets}->[0], $self->{main_sockets}->[1], $proto)); # main rtp and rtcp
|
|
|
|
|
# XXX support multiple medias
|
|
|
|
|
|
|
|
|
|
if ($args{dtls}) {
|
|
|
|
|
$self->{dtls} = DTLS::Group->new($parent->{mux}, $self, [ \@rtp, \@rtcp ]);
|
|
|
|
|
$self->{local_media}->add_attrs($self->{dtls}->encode());
|
|
|
|
|
$self->{dtls}->accept(); # XXX support other modes
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub dtls_send {
|
|
|
|
|
my ($self, $component, $s) = @_;
|
|
|
|
|
$self->{main_sockets}->[$component]->send($s, 0, $self->{component_peers}->[$component]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub _default_req_args {
|
|
|
|
|
my ($self, $cmd, %args) = @_;
|
|
|
|
|
|
|
|
|
|
my $req = { command => $cmd, 'call-id' => $self->{parent}->{callid} };
|
|
|
|
|
|
|
|
|
|
for my $cp (qw(sdp from-tag to-tag ICE transport-protocol)) {
|
|
|
|
|
$args{$cp} and $req->{$cp} = $args{$cp};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $req;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub offer {
|
|
|
|
|
my ($self, $other) = @_;
|
|
|
|
|
my ($self, $other, %args) = @_;
|
|
|
|
|
|
|
|
|
|
my $sdp_body = $self->{local_sdp}->encode();
|
|
|
|
|
# XXX validate SDP
|
|
|
|
|
|
|
|
|
|
my $req = { command => 'offer', ICE => 'remove', 'call-id' => $self->{parent}->{callid},
|
|
|
|
|
'from-tag' => $self->{tag}, sdp => $sdp_body };
|
|
|
|
|
my $req = $self->_default_req_args('offer', 'from-tag' => $self->{tag}, sdp => $sdp_body, %args);
|
|
|
|
|
|
|
|
|
|
my $out = $self->{parent}->{rtpe}->req($req);
|
|
|
|
|
|
|
|
|
|
$other->offered($out);
|
|
|
|
|
$other->_offered($out);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub offered {
|
|
|
|
|
sub _offered {
|
|
|
|
|
my ($self, $req) = @_;
|
|
|
|
|
|
|
|
|
|
my $sdp_body = $req->{sdp} or die;
|
|
|
|
|
@ -206,20 +244,20 @@ sub offered {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub answer {
|
|
|
|
|
my ($self, $other) = @_;
|
|
|
|
|
my ($self, $other, %args) = @_;
|
|
|
|
|
|
|
|
|
|
my $sdp_body = $self->{local_sdp}->encode();
|
|
|
|
|
# XXX validate SDP
|
|
|
|
|
|
|
|
|
|
my $req = { command => 'answer', ICE => 'remove', 'call-id' => $self->{parent}->{callid},
|
|
|
|
|
'from-tag' => $other->{tag}, 'to-tag' => $self->{tag}, sdp => $sdp_body };
|
|
|
|
|
my $req = $self->_default_req_args('answer', 'from-tag' => $other->{tag}, 'to-tag' => $self->{tag},
|
|
|
|
|
sdp => $sdp_body, %args);
|
|
|
|
|
|
|
|
|
|
my $out = $self->{parent}->{rtpe}->req($req);
|
|
|
|
|
|
|
|
|
|
$other->answered($out);
|
|
|
|
|
$other->_answered($out);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub answered {
|
|
|
|
|
sub _answered {
|
|
|
|
|
my ($self, $req) = @_;
|
|
|
|
|
|
|
|
|
|
my $sdp_body = $req->{sdp} or die;
|
|
|
|
|
@ -229,4 +267,19 @@ sub answered {
|
|
|
|
|
$self->{remote_media} = $self->{remote_sdp}->{medias}->[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub _input {
|
|
|
|
|
my ($self, $fh, $input, $peer) = @_;
|
|
|
|
|
|
|
|
|
|
_peer_addr_check($fh, $peer, $self->{rtp_sockets}, $self->{component_peers}, 0);
|
|
|
|
|
_peer_addr_check($fh, $peer, $self->{rtcp_sockets}, $self->{component_peers}, 1);
|
|
|
|
|
|
|
|
|
|
$self->{dtls} and $self->{dtls}->input($fh, $input, $peer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub _peer_addr_check {
|
|
|
|
|
my ($fh, $peer, $sockets, $dest_list, $idx) = @_;
|
|
|
|
|
if (List::Util::any {$fh == $_} @$sockets) {
|
|
|
|
|
$dest_list->[$idx] = $peer;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
1;
|
|
|
|
|
|