mirror of https://github.com/sipwise/rtpengine.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
151 lines
3.8 KiB
151 lines
3.8 KiB
package NGCP::Rtpclient::RTP;
|
|
|
|
use strict;
|
|
use warnings;
|
|
use Time::HiRes qw(time);
|
|
use Math::BigInt;
|
|
use Math::BigFloat;
|
|
|
|
sub new {
|
|
my ($class, $cb_obj) = @_;
|
|
|
|
my $self = {};
|
|
bless $self, $class;
|
|
|
|
$self->{cb_obj} = $cb_obj;
|
|
|
|
$self->{ssrc} = int(rand(2**32));
|
|
$self->{next_send} = time();
|
|
$self->{ptime} = 20;
|
|
$self->{clockrate} = 8000;
|
|
$self->{timestamp} = Math::BigInt->new(int(rand(2**32)));
|
|
$self->{seq} = rand(2**16);
|
|
$self->{payload} = 100;
|
|
$self->{packet_count} = 0;
|
|
$self->{octet_count} = 0;
|
|
$self->{other_ssrcs} = {};
|
|
|
|
return $self;
|
|
}
|
|
|
|
sub timer {
|
|
my ($self) = @_;
|
|
|
|
time() < $self->{next_send} and return;
|
|
|
|
my $hdr = pack("CCnNN", 0x80, 0x00, $self->{seq}, $self->{timestamp}->bstr(), $self->{ssrc});
|
|
my $payload = chr(rand(256)) x $self->{payload}; # XXX adapt to codec
|
|
|
|
$self->{cb_obj}->rtp_send($hdr . $payload);
|
|
|
|
$self->{seq}++;
|
|
$self->{seq} > 0xffff and $self->{seq} -= 0x10000;
|
|
|
|
$self->{next_send} = $self->{next_send} + $self->{ptime} / 1000;
|
|
|
|
$self->{timestamp} += $self->{clockrate} / (1.0 / ($self->{ptime} / 1000)); # XXX might be fractional
|
|
$self->{timestamp} > 0xffffffff and $self->{timestamp} -= Math::BigInt->new('0x100000000');
|
|
|
|
$self->{packet_count}++;
|
|
$self->{octet_count} += length($payload);
|
|
}
|
|
|
|
sub input {
|
|
my ($self, $packet) = @_;
|
|
|
|
my $now = time();
|
|
|
|
my ($vpxcc, $pt, $seq, $ts, $ssrc, $payload) = unpack("CCnNN a*", $packet);
|
|
$vpxcc == 0x80 or die;
|
|
$pt == 0 or die;
|
|
|
|
my $remote = ($self->{other_ssrcs}->{$ssrc} //= {
|
|
ssrc => $ssrc,
|
|
packets_received => 0,
|
|
packets_lost => 0,
|
|
octets_received => 0,
|
|
roc => 0,
|
|
seq => $seq, # highest seen
|
|
jitter => 0,
|
|
queue_seq => $seq, # next expected seq -- to detect lost packets
|
|
queue => {},
|
|
lost_last => 0, # since last SR/RR
|
|
received_last => 0, # since last SR/RR
|
|
dupes => 0,
|
|
});
|
|
|
|
$remote->{packets_received}++;
|
|
$remote->{received_last}++;
|
|
$remote->{octets_received} += length($payload);
|
|
|
|
# normalize seq using roc
|
|
my $extseq = ($remote->{roc} << 16) | $seq;
|
|
my $diff = $extseq - $remote->{seq};
|
|
if ($diff < -0x8000) {
|
|
$extseq += 0x10000;
|
|
}
|
|
elsif ($diff >= 0x8000) {
|
|
$extseq -= 0x10000;
|
|
}
|
|
|
|
# update seq/roc if necessary -- highest seq seen
|
|
if ($extseq > $remote->{seq}) {
|
|
$remote->{seq} = $extseq;
|
|
$remote->{roc} = $extseq >> 16;
|
|
}
|
|
|
|
# check dupes and packet loss
|
|
if ($extseq == $remote->{queue_seq}) {
|
|
# in sequence and expected
|
|
$remote->{queue_seq}++;
|
|
# see if we can pull packets out of the queue
|
|
while (exists($remote->{queue}->{$remote->{queue_seq}})) {
|
|
delete($remote->{queue}->{$remote->{queue_seq}});
|
|
$remote->{queue_seq}++;
|
|
}
|
|
}
|
|
elsif ($extseq < $remote->{queue_seq}) {
|
|
$remote->{dupes}++;
|
|
}
|
|
else {
|
|
# ahead of sequence -- queue it up if not a dupe
|
|
if (exists($remote->{queue}->{$extseq})) {
|
|
$remote->{dupes}++;
|
|
}
|
|
else {
|
|
$remote->{queue}->{$extseq} = $packet;
|
|
# see if our "jitter buffer" is full and account for packet loss
|
|
my @seqs = keys(%{$remote->{queue}});
|
|
if (@seqs >= 20) {
|
|
@seqs = sort {$a <=> $b} (@seqs);
|
|
# seek up to the lowest seq in buffer and count each missing
|
|
# seq as a lost packet
|
|
my $min = $seqs[0];
|
|
$remote->{lost_since} += $min - $remote->{queue_seq};
|
|
$remote->{packets_lost} += $min - $remote->{queue_seq};
|
|
# now unqueue what we have as much as we can
|
|
$remote->{queue_seq} = $min;
|
|
while (my $qseq = shift(@seqs)) {
|
|
$qseq != $remote->{queue_seq} and last;
|
|
delete($remote->{queue}->{$qseq});
|
|
$remote->{queue_seq}++;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
# calc jitter
|
|
if ($remote->{last_ts} && $remote->{last_seq}) {
|
|
my $lt = Math::BigFloat->new($remote->{last_ts});
|
|
$lt->bsub(Math::BigFloat->new($now));
|
|
$lt->bmul($self->{clockrate});
|
|
my $diff = $lt->bstr() - ($remote->{last_seq} - $extseq);
|
|
$remote->{jitter} = $remote->{jitter} + (abs($diff) - $remote->{jitter}) / 16;
|
|
}
|
|
$remote->{last_ts} = $now;
|
|
$remote->{last_seq} = $extseq;
|
|
}
|
|
|
|
1;
|