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.
rtpengine/perl/NGCP/Rtpclient/RTP.pm

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;