Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked

Binary conversion, RTP header

by atistler (Acolyte)
on Dec 19, 2005 at 21:08 UTC ( #517896=perlquestion: print w/ replies, xml ) Need Help??
atistler has asked for the wisdom of the Perl Monks concerning the following question:

Hi all, I am basically attempting to write a perl script that will process real time transport(RTP) data and extract the timestamp and sequence number from the RTP header. I know that I can use NetPacket to get the IP, UDP, and Ethernet headers, however when I strip out those headers and am left with the 'data' (which include the rtp header), I do no know how to get any further. I basically will see a bunch of jumbled characters on the screen because it is seeing the 'data' as binary and I do not know how to convert binary output to binary "string". Here is my code, there are a few extraneous functions, sorry.

[root@goatse1 root]# less #!/usr/bin/perl -w use Asterisk::AGI; use Data::Dumper; use Net::Pcap; use NetPacket::Ethernet; use NetPacket::IP; use NetPacket::UDP; use File::Basename; use Digest::MD5 qw(md5_hex); #use strict; my $RTP_listener; my $agi = new Asterisk::AGI; my $error; my $Host_IP = ''; my $Host_Listen_Port = 5061; # Use network device passed arguments else have lookupdev() find it my $device = $ARGV[0]; unless (defined $device) { $device = Net::Pcap::lookupdev(\$error); if (defined $error) { die 'Unable to find network device - ', $error; } } # Look up network address information about network my ($address, $netmask); if (Net::Pcap::lookupnet($device, \$address, \$netmask, \$error)) { die 'Unable to look up device information for ', $device, ' - ', $ +error; } # Create packet capture object for device my $object; $object = Net::Pcap::open_live($device, 1500, 0, 0, \$error); unless (defined $object) { die 'Unable to capture anything on device ', $device, ' - ', $erro +r; } # Compile and set packet filter for packet capture my $filterSIP; Net::Pcap::compile( $object, \$filterSIP, 'src '.$Host_IP.' and udp src port '.$Host_Listen_Port, 0, $netmask ) && die 'Unable to compile packet capture filter for SIP Port'; Net::Pcap::setfilter($object, $filterSIP) && die 'Unable to set SIP packet capture filter'; # Start SIP packet capture loop Net::Pcap::loop($object, -1, \&process_sip_packets, '') || die 'Unable to perform SIP packet capture'; #$agi->answer(); #$agi->ReadParse(); #my ($text_to_say) = 'hello this is a fine day for a drive in the park +'; #my $hash = nd5_hex($text_to_say_); #my $sound_dir = "/var/lib/asterisk/sounds"; #my $wave_file = "$sound_dir/say_text-$hash.wav"; # #unless (-f $wavefile) { # require File::Temp; # my (undef, $tmpfile) = File::Temp::tempfile(DIR => $sounddir); # my $pid = open my $pipe, "|-"; # die "can't fork: $!" unless (defined $pid); # if (!$pid) { # open STDOUT, ">$tmpfile" or die "can't redir to $tmpfi +le: $!"; # exec qw( text2wave -F 8000 - ); # die "exec in child failed: $!"; # } # else { Net::Pcap::close($object); sub process_sip_packets { my ($user_data, $header, $packet) = @_; if (($packet =~ /17005551000/) and ($packet =~ /200 OK/)) { #print "$packet\n"; my @line_split1 = split(/audio/, $packet); my @line_split2 = split(/ /, $line_split1[1]); $RTP_listener = @line_split2[1]; print "RTP listening port is: $RTP_listener . . . .\n\n\n"; print 'Length of Captured SIP Packet is:'; print("$header->{caplen}\n\n\n"); #Going to RTPStream subroutine RTPStream($RTP_listener); } } sub process_rtp_packets { my ($user_data, $header, $packet) = @_; # Create ethernet object and print it out my $eth_obj = NetPacket::Ethernet->decode($packet); #print("$eth_obj->{src_mac}:$eth_obj->{dest_mac} $eth_obj->{type}\ +n"); # Strip ethernet layer of packet for use as input for decoding n +etwork layer my $ether_data = NetPacket::Ethernet::strip($packet); my $ip_obj = NetPacket::IP->decode($ether_data); #print("$ip_obj->{src_ip}:$ip_obj->{dest_ip}\t $ip_obj->{proto}\t +$ip_obj->{len}\t $ip_obj->{id}\n"); # Strip the network layer of packet for use as input for decodin +g transport layer my $ip_data = NetPacket::IP::strip($ether_data); my $udp_obj = NetPacket::UDP->decode($ip_data); #print("$udp_obj->{src_port}:$udp_obj->{dest_port}\t $udp_obj->{le +n}"); #use bytes; #my $RTP = unpack ("%32C*", $udp_obj->{data}); #$RTP = dec2bin($RTP); $RTP = bin2dec($RTP); print "$RTP\n"; #no bytes; #open (RTPDATA, '>> /tmp/RTPDATA.txt'); #print binmode RTPDATA $RTP; #close (RTPDATA); #my $bin = sprintf("%b", $udp_obj->{data}); #my $num = oct("0b" . $bin); #print "$num\n\n\n"; #my $RTP = $udp_obj->{data}; #my $HRTP = hex($RTP); #my $DRTP = bin2dec($RTP); #my $KRTP = dec2bin($RTP); #my $ORTP = oct($RTP); #printf("%b", $RTP); #print "$ORTP\n\n"; } sub RTPStream { print "ENTERING RTPSTREAM SUBROUTINE . . .\n\n\n"; my $rtp_port = $_[0]; print "RTP PORT is still $rtp_port\n\n"; my $rtp_object; $rtp_object = Net::Pcap::open_live($device, 1500, 0, 0, \$error); unless (defined $rtp_object) { die 'Unable to capture anything on device ', $device, ' - ', $ +error; } my $filterRTP; Net::Pcap::compile( $rtp_object, \$filterRTP, 'dst '.$Host_IP.' and udp dst port '.$rtp_port, 0, $netmask ) && die 'Unable to compile packet capture filter for RTP Port'; Net::Pcap::setfilter($rtp_object, $filterRTP) && die 'Unable to set RTP packet capture filter'; # Start RTP packet capture loop Net::Pcap::loop($rtp_object, -1, \&process_rtp_packets, '') || die 'Unable to perform RTP packet capture'; Net::Pcap::close($object); } sub count_RTP { my @list; my $count; while (<>) { push (@list, $_); print "$_"; $count++; } $last = pop(@list); $first = shift(@list); $diff = $last - $first + 1; $missing = $diff - $count; $percent = $missing/$diff; print "Percent: $percent\n"; } sub StdDev { my $sum_of_squares; my $sum; my $diff; while(<>) { push(@timestamps, $_); } my $array_length = scalar(@timestamps); for (my $n = 1; $n < $array_length; $n = $n + 1) { $diff = $timestamps[$n] - $timestamps[$n - 1]; $sum = $sum + $diff; } my $mean = $sum / ($array_length - 1); for (my $i = 0; $i < $array_length; $i = $i + 1) { $sum_of_squares = $sum_of_squares + ($timestamps[$i] - + $mean)**2; } my $std_dev = sqrt($sum_of_squares/6); print "STD DEV: $std_dev\n"; } sub bin2dec { return unpack("N", pack("B32", shift)); } sub dec2bin { my $str = unpack("B32", pack("N", shift)); return $str; }

Edited by planetscape - added readmore tags

Comment on Binary conversion, RTP header
Download Code
Replies are listed 'Best First'.
Re: Binary conversion, RTP header (unpack)
by tye (Sage) on Dec 20, 2005 at 06:53 UTC

    BTW, anyone trying to extract information from RTP packets should note that nearly all of your code is going to great lengths to get 'raw' packets and then going to great lengths to strip off the nested headers to get to $user_data. Yes, I realize that you have reasons for doing it this way, but I wanted to note that people who just want what you asked for can just grab RTP packets in much simpler ways, where they come with the IP and UDP headers already stripped off. So forget all of the above code and just read the RTP packet into $user_data. (:

    If you google for rtp rfc, then you'll quickly find this:

    5.1 RTP Fixed Header Fields The RTP header has the following format: |0 | 1 | 2 | 3 | |0 1 2 3 4 5 6 7|8 9 0 1 2 3 4 5|6 7 8 9 0 1 2 3|4 5 6 7 8 9 0 1| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |V=2|P|X| CC |M| PT | sequence number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | timestamp | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | synchronization source (SSRC) identifier | +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ | contributing source (CSRC) identifiers | | .... | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    Then you read the pack documentation and then the unpack docs and extracting these fields is quite simple:

    my( $bits, $type, $seq, $time )= unpack "C C n N", $user_data;

    - tye        

      You have no idea how much this has helped me. Thank you so much for spreading your wisdom to a perl newbie. One more thing, the $header variable in this case is a reference to a hash?? I believe that it contians references to packet recv time, len, etc. How can I view that information??
      sub process_rtp_packets { my ($user_data, $header, $packet) = @_; for my $key ( keys %header ) { my $value = $header{$key}; print "$key => $value\n"; } my $eth_obj = NetPacket::Ethernet->decode($packet); my $ether_data = NetPacket::Ethernet::strip($packet); my $ip_obj = NetPacket::IP->decode($ether_data); my $ip_data = NetPacket::IP::strip($ether_data); my $udp_obj = NetPacket::UDP->decode($ip_data); my $RTP_data = $udp_obj->{data}; my( $bits, $type, $seq, $time )= unpack "C C n N", $RTP_data; print "$bits\n"; print "$type\n"; print "$seq\n"; print "$time\n"; }

        Add 'use strict;' and change %header to %$header and change $header{...} to $header->{...}. You might find References quick reference helpful. For more in-depth information on references, start with "perldoc perlreftut"

        - tye        

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://517896]
Approved by Corion
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others having an uproarious good time at the Monastery: (7)
As of 2016-05-30 13:14 GMT
Find Nodes?
    Voting Booth?