File Coverage

blib/lib/Net/RTP/Packet.pm
Criterion Covered Total %
statement 120 141 85.1
branch 27 38 71.0
condition n/a
subroutine 19 22 86.3
pod 18 19 94.7
total 184 220 83.6


line stmt bran cond sub pod time code
1             package Net::RTP::Packet;
2              
3             ################
4             #
5             # Net::RTP::Packet: Pure Perl RTP Packet object (RFC3550)
6             #
7             # Nicholas J Humfrey, njh@cpan.org
8             #
9              
10 3     3   52813 use strict;
  3         7  
  3         113  
11 3     3   18 use Carp;
  3         6  
  3         199  
12              
13 3     3   35 use vars qw/$VERSION/;
  3         4  
  3         5667  
14             $VERSION="0.05";
15              
16              
17             # Seed a random number for old versions of perl
18             if ($] < 5.004) {
19             srand(time ^ ($$ + ($$ << 15)));
20             }
21              
22              
23             sub new {
24 4     4 1 667 my $class = shift;
25 4         11 my ($bindata) = @_;
26              
27             # Store parameters
28 4         62 my $self = {
29             version => 2,
30             padding => 0,
31             extension => 0,
32             marker => 0,
33             payload_type => 0,
34             seq_num => 0,
35             timestamp => 0,
36             ssrc => 0,
37             csrc => [],
38             payload => '',
39             size => undef,
40             source_ip => undef,
41             source_port => undef,
42             };
43 4         13 bless $self, $class;
44              
45              
46             # Decode binary packet?
47 4 100       17 if (defined $bindata) {
48 1         6 $self->decode( $bindata );
49             } else {
50             # Randomise sequence, timestamp and SSRC
51 3         134 $self->{'seq_num'} = int(rand(2**16));
52 3         9 $self->{'timestamp'} = int(rand(2**32));
53 3         7 $self->{'ssrc'} = int(rand(2**32));
54             }
55            
56 4         14 return $self;
57             }
58              
59              
60             sub version {
61 1     1 1 418 my $self = shift;
62 1         3 my ($version) = @_;
63 1 50       4 $self->{'version'} = $version if (defined $version);
64 1         5 return $self->{'version'};
65             }
66              
67             sub padding {
68 2     2 1 8 my $self = shift;
69 2         4 my ($padding) = @_;
70 2 100       7 $self->{'padding'} = $padding if (defined $padding);
71 2         12 return $self->{'padding'};
72             }
73              
74             sub extension {
75 1     1 1 2 my $self = shift;
76 1         5 return $self->{'extension'};
77             }
78              
79             sub marker {
80 2     2 1 5 my $self = shift;
81 2         4 my ($marker) = @_;
82 2 100       38 $self->{'marker'} = $marker if (defined $marker);
83 2         9 return $self->{'marker'};
84             }
85              
86             sub payload_type {
87 3     3 1 12 my $self = shift;
88 3         29 my ($payload_type) = @_;
89 3 100       12 $self->{'payload_type'} = $payload_type if (defined $payload_type);
90 3         16 return $self->{'payload_type'};
91             }
92              
93             sub seq_num {
94 4     4 1 10 my $self = shift;
95 4         8 my ($seq_num) = @_;
96 4 100       11 $self->{'seq_num'} = $seq_num if (defined $seq_num);
97 4         17 return $self->{'seq_num'};
98             }
99              
100             sub seq_num_increment {
101 1     1 1 6 my $self = shift;
102              
103 1         1 my ($value) = @_;
104 1 50       6 $value = 1 unless (defined $value);
105 1         3 $self->{'seq_num'} += $value;
106              
107 1         5 return $self->{'seq_num'};
108             }
109            
110             sub timestamp {
111 4     4 1 8 my $self = shift;
112 4         10 my ($timestamp) = @_;
113 4 100       14 $self->{'timestamp'} = $timestamp if (defined $timestamp);
114 4         19 return $self->{'timestamp'};
115             }
116              
117             sub timestamp_increment {
118 1     1 1 8 my $self = shift;
119              
120 1         2 my ($value) = @_;
121 1 50       17 $value = 1 unless (defined $value);
122 1         3 $self->{'timestamp'} += $value;
123              
124 1         14 return $self->{'timestamp'};
125             }
126              
127             sub ssrc {
128 3     3 1 7 my $self = shift;
129 3         5 my ($ssrc) = @_;
130 3 100       17 $self->{'ssrc'} = $ssrc if (defined $ssrc);
131 3         16 return $self->{'ssrc'};
132             }
133              
134             sub csrc {
135 0     0 1 0 my $self = shift;
136 0         0 my ($csrc) = @_;
137 0 0       0 if (defined $csrc) {
138 0 0       0 if (ref($csrc) ne 'ARRAY') {
139 0         0 carp "CSRC should be an ARRAYREF";
140             } else {
141 0         0 $self->{'csrc'} = $csrc ;
142             }
143             }
144 0         0 return $self->{'csrc'};
145             }
146              
147             sub payload {
148 3     3 1 8 my $self = shift;
149 3         7 my ($payload) = @_;
150 3 100       11 $self->{'payload'} = $payload if (defined $payload);
151 3         17 return $self->{'payload'};
152             }
153              
154             sub payload_size {
155 1     1 1 3 my $self = shift;
156 1         7 return length($self->{'payload'});
157             }
158              
159             sub source_ip {
160 0     0 1 0 my $self = shift;
161 0         0 return $self->{'source_ip'};
162             }
163              
164             sub source_port {
165 0     0 1 0 my $self = shift;
166 0         0 return $self->{'source_port'};
167             }
168              
169             sub size {
170 2     2 1 202 my $self = shift;
171            
172             # Encode the packet if the size isn't known
173 2 50       8 unless (defined $self->{'size'}) {
174             # Not very efficient, but sure to work
175 0         0 $self->encode();
176             }
177            
178 2         15 return $self->{'size'};
179             }
180              
181             sub decode {
182 1     1 1 2 my $self = shift;
183 1         3 my ($bindata) = @_;
184            
185             # Store the size of the packet we are decoding
186 1         14 $self->{'size'} = length( $bindata );
187              
188             # Decode the binary header (network endian)
189 1         11 my ($vpxcc, $mpt, $seq_num, $timestamp, $ssrc) = unpack( 'CCnNN', $bindata );
190 1         5 $bindata = substr( $bindata, 12 );
191            
192             # We only know how to parse version 2 of RTP
193 1         3 $self->{'version'} = ($vpxcc & 0xC0) >> 6;
194 1 50       7 if ($self->{'version'} != 2) {
195 0         0 carp "Warning: unsupported RTP packet version ($self->{'version'})";
196 0         0 return 0;
197             }
198            
199             # Extract from the bit fields
200 1         4 $self->{'padding'} = ($vpxcc & 0x20) >> 5;
201 1         3 $self->{'extension'} = ($vpxcc & 0x10) >> 4;
202 1         3 my $csrc_count = ($vpxcc & 0x0F) >> 0;
203 1         2 $self->{'marker'} = ($mpt & 0x80) >> 7;
204 1         3 $self->{'payload_type'} = ($mpt & 0x7F) >> 0;
205 1         3 $self->{'seq_num'} = $seq_num;
206 1         3 $self->{'timestamp'} = $timestamp;
207 1         3 $self->{'ssrc'} = $ssrc;
208              
209            
210             # Process CSRC list
211 1         15 for(my $c=0; $c<$csrc_count; $c++) {
212 0         0 my $csrc = unpack('N', $bindata );
213 0         0 $bindata = substr( $bindata, 4 );
214            
215             # Append it on to the list
216 0         0 push( @{$self->{'csrc'}}, $csrc );
  0         0  
217             }
218            
219             # Ignore any header extention
220 1 50       6 if ($self->{'extension'}) {
221 0         0 my ($foo, $len) = unpack('nn', $bindata );
222 0         0 $bindata = substr( $bindata, ($len+1)*4 );
223             }
224            
225             # Ignore padding on end of packet
226 1 50       4 if ($self->{'padding'}) {
227 1         5 $self->{'padding'} = unpack('C', substr( $bindata, -1, 1 ));
228             }
229            
230             # Whats left is the payload
231 1         4 my $len = length( $bindata ) - $self->{'padding'};
232 1         3 $self->{'payload'} = substr($bindata,0,$len);
233            
234             # Undefine the source IP and port
235             # (it is unknown and set elsewhere)
236 1         3 $self->{'source_ip'} = undef;
237 1         2 $self->{'source_port'} = undef;
238            
239             # Success
240 1         3 return 1;
241             }
242              
243              
244             sub encode {
245 2     2 0 5 my $self = shift;
246 2         5 my $bindata = '';
247            
248 2         4 my $csrc_count = scalar(@{$self->{'csrc'}});
  2         7  
249 2 100       5 my $pad = 0; $pad = 1 if ($self->{'padding'});
  2         10  
250            
251 2         5 my $vpxcc = 0;
252 2         6 $vpxcc |= ($self->{'version'} << 6) & 0xC0;
253 2         5 $vpxcc |= ($pad << 5) & 0x20;
254 2         5 $vpxcc |= ($self->{'extension'} << 4) & 0x10;
255 2         4 $vpxcc |= ($csrc_count & 0x0F);
256 2         12 $bindata .= pack('C', $vpxcc);
257            
258 2         4 my $mpt = 0;
259 2         11 $mpt |= ($self->{'marker'} << 7) & 0x80;
260 2         5 $mpt |= ($self->{'payload_type'} & 0x7F);
261 2         6 $bindata .= pack('C', $mpt);
262            
263 2         608 $bindata .= pack('n', $self->{'seq_num'});
264 2         6 $bindata .= pack('N', $self->{'timestamp'});
265 2         8 $bindata .= pack('N', $self->{'ssrc'});
266            
267             # Append list of CSRC
268 2         4 foreach( @{$self->{'csrc'}} ) {
  2         13  
269 0         0 $bindata .= pack('N', $_);
270             }
271              
272             # Append the payload
273 2         7 $bindata .= $self->{'payload'};
274            
275             # Append the padding
276 2 100       8 if ($self->{'padding'}) {
277 1         5 for(my $p=0; $p<($self->{'padding'}-1); $p++) {
278 3         8 $bindata .= pack('C', 0);
279             }
280 1         3 $bindata .= pack('C', $self->{'padding'});
281             }
282            
283             # Store the size of the encoded packet
284 2         5 $self->{'size'} = length( $bindata );
285            
286 2         7 return $bindata;
287             }
288              
289              
290             1;
291              
292             __END__