rtcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +2 -0
- data/README.rdoc +79 -0
- data/Rakefile +8 -0
- data/lib/rtcp.rb +99 -0
- data/lib/rtcp/app.rb +36 -0
- data/lib/rtcp/bye.rb +48 -0
- data/lib/rtcp/decode_error.rb +4 -0
- data/lib/rtcp/psfb.rb +55 -0
- data/lib/rtcp/rr.rb +45 -0
- data/lib/rtcp/rsi.rb +82 -0
- data/lib/rtcp/sdes.rb +77 -0
- data/lib/rtcp/sr.rb +86 -0
- data/lib/rtcp/version.rb +4 -0
- data/lib/rtcp/xr.rb +170 -0
- data/rtcp.gemspec +25 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/support/test_descriptions.rb +124 -0
- data/spec/unit/rtcp/app_spec.rb +26 -0
- data/spec/unit/rtcp/bye_spec.rb +25 -0
- data/spec/unit/rtcp/psfb_spec.rb +25 -0
- data/spec/unit/rtcp/rr_spec.rb +59 -0
- data/spec/unit/rtcp/rsi_spec.rb +28 -0
- data/spec/unit/rtcp/sdes_spec.rb +44 -0
- data/spec/unit/rtcp/sr_spec.rb +62 -0
- data/spec/unit/rtcp/xr_spec.rb +95 -0
- data/spec/unit/rtcp_spec.rb +43 -0
- metadata +137 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 79f2f783d38dba6bdba307f5755c6132787b34589abae10722db511c4cd1c0c5
|
|
4
|
+
data.tar.gz: 3cd788f1b867909219ca14aeb97cdc791acce681fbcc1e806cb68436d82eaf5a
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: ce3a1522f2ff96cfc9b87ccc17317cd884609b09f87a55b59f28b1d816374f44fd666c0e212d85baf1af8fde844043061b6c51daba48869895518d6ba60aa70e
|
|
7
|
+
data.tar.gz: b932fec91523c05f1f5e157e0eb548545877c49fe0543bc8784160ba640916ec1e248989c769951d244f687885eab44a442edc607c6eea14a9618c36bd0382ef
|
data/Gemfile
ADDED
data/README.rdoc
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
= rtcp {<img src="https://codeclimate.com/github/rusch/rtcp.png" />}[https://codeclimate.com/github/rusch/rtcp] {<img src="https://travis-ci.org/rusch/rtcp.png?branch=master" alt="Build Status" />}[https://travis-ci.org/rusch/rtcp]
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
* https://github.com/rusch/rtcp
|
|
5
|
+
* http://www.ietf.org/rfc/rfc3550.txt
|
|
6
|
+
* http://www.ietf.org/rfc/rfc4585.txt
|
|
7
|
+
|
|
8
|
+
== Description
|
|
9
|
+
|
|
10
|
+
The RTP Control Protocol (RTCP) gathers statistical data about RTP sessions,
|
|
11
|
+
for example transmitted octet and packet counts, lost packet counts,
|
|
12
|
+
round-trip delay time and interarrival jitter.
|
|
13
|
+
|
|
14
|
+
This library parses RTCP data into ruby objects.
|
|
15
|
+
|
|
16
|
+
== Features
|
|
17
|
+
|
|
18
|
+
* Parse RTCP Packets into Ruby Objects
|
|
19
|
+
* Supports the following RTCP Packet types:
|
|
20
|
+
* 200: Sender Report
|
|
21
|
+
* 201: Receiver Report
|
|
22
|
+
* 202: Source Description
|
|
23
|
+
* 203: Goodbye
|
|
24
|
+
* 204: Application-Defined (Does not decode application-dependent data)
|
|
25
|
+
* 206: Payload-Specific FB message (Does not decode FCI block)
|
|
26
|
+
* 207: Extended Report
|
|
27
|
+
* 209: Receiver Summary Information
|
|
28
|
+
|
|
29
|
+
== Examples
|
|
30
|
+
|
|
31
|
+
=== Parse an RTCP Packet
|
|
32
|
+
|
|
33
|
+
require 'rtcp'
|
|
34
|
+
|
|
35
|
+
rr = RTCP.decode(rtcp_rr_data) # => [RTCP::RR Object]
|
|
36
|
+
rr.ssrc # => 3945864703
|
|
37
|
+
rr.length # => 8
|
|
38
|
+
rr.version # => 2
|
|
39
|
+
rr.report_blocks # => []
|
|
40
|
+
|
|
41
|
+
=== Parse a sequence of RTCP Packets
|
|
42
|
+
|
|
43
|
+
require 'rtcp'
|
|
44
|
+
|
|
45
|
+
msgs = RTCP.decode(rtcp_data) # => [Array of RTCP::* Objects]
|
|
46
|
+
|
|
47
|
+
msgs[0].class # => RTCP::RR
|
|
48
|
+
msgs[0].ssrc # => 3945864703
|
|
49
|
+
|
|
50
|
+
msgs[1].class # => RTCP::SDES
|
|
51
|
+
msgs[1].chunks # => [Array of Hashes]
|
|
52
|
+
msgs[1].chunks[0][:ssrc] # => 3945864703
|
|
53
|
+
msgs[1].chunks[0][:type] # => :cname
|
|
54
|
+
msgs[1].chunks[0][:data] # => "00-09-df-1b-ec-0a"
|
|
55
|
+
|
|
56
|
+
msgs[2].class # => RTCP::RSI
|
|
57
|
+
msgs[2].ssrc # => 3945864703
|
|
58
|
+
msge[2].ntp_timestamp # => [Time Object]
|
|
59
|
+
|
|
60
|
+
msgs[3].class # => RTCP::APP
|
|
61
|
+
msgs[3].name # => "PLII"
|
|
62
|
+
msgs[3].app_data # => [Binary Data -- The application data]
|
|
63
|
+
|
|
64
|
+
msgs[4].class # => RTCP
|
|
65
|
+
msgs[4].to_s # => [Binary Data -- The whole RTCP packet]
|
|
66
|
+
|
|
67
|
+
== Requirements
|
|
68
|
+
|
|
69
|
+
* Rubies (tested, at least):
|
|
70
|
+
* 1.9.3
|
|
71
|
+
* JRuby 1.7.3 (1.9 mode)
|
|
72
|
+
|
|
73
|
+
== Install
|
|
74
|
+
|
|
75
|
+
$ gem install
|
|
76
|
+
|
|
77
|
+
== Copyright
|
|
78
|
+
|
|
79
|
+
Copyright (c) 2013 Christian Rusch
|
data/Rakefile
ADDED
data/lib/rtcp.rb
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
require_relative 'rtcp/version'
|
|
2
|
+
require_relative 'rtcp/decode_error'
|
|
3
|
+
require_relative 'rtcp/sr'
|
|
4
|
+
require_relative 'rtcp/rr'
|
|
5
|
+
require_relative 'rtcp/sdes'
|
|
6
|
+
require_relative 'rtcp/bye'
|
|
7
|
+
require_relative 'rtcp/xr'
|
|
8
|
+
require_relative 'rtcp/app'
|
|
9
|
+
require_relative 'rtcp/rsi'
|
|
10
|
+
require_relative 'rtcp/psfb'
|
|
11
|
+
|
|
12
|
+
class RTCP
|
|
13
|
+
|
|
14
|
+
attr_reader :length, :type_id
|
|
15
|
+
|
|
16
|
+
@@packet_classes = {}
|
|
17
|
+
self.constants.each do |sym|
|
|
18
|
+
const = self.const_get(sym)
|
|
19
|
+
if const.is_a?(Class) && const <= self
|
|
20
|
+
@@packet_classes[const::PT_ID] = const
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Decodes the supplied RTCP packet and returns it
|
|
25
|
+
def self.decode(data)
|
|
26
|
+
raise(RTCP::DecodeError, "Truncated Packet") if (data.length < 4)
|
|
27
|
+
|
|
28
|
+
packet_type, length = data.unpack('xCn')
|
|
29
|
+
length = 4 * (length + 1)
|
|
30
|
+
raise(RTCP::DecodeError, "Truncated Packet") if (data.length < length)
|
|
31
|
+
|
|
32
|
+
self.packet_class(packet_type).new.decode(data.slice(0..(length - 1)))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Decodes all RTCP packets in the supplied string returns them in an array
|
|
36
|
+
def self.decode_all(data)
|
|
37
|
+
packets = []
|
|
38
|
+
while data && data.length > 0
|
|
39
|
+
packet = self.decode(data)
|
|
40
|
+
packets.push(packet)
|
|
41
|
+
data = data.slice(packet.length..-1)
|
|
42
|
+
end
|
|
43
|
+
packets
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def decode(packet_data)
|
|
47
|
+
@type_id, length = packet_data.unpack('xCn')
|
|
48
|
+
@length = 4 * (length + 1)
|
|
49
|
+
|
|
50
|
+
@packet_data = packet_data
|
|
51
|
+
self
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Returns the packet as RTCP data string
|
|
55
|
+
def to_s
|
|
56
|
+
@packet_data
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
protected
|
|
60
|
+
|
|
61
|
+
# Ensures that the current RTCP Packet object is able to decode the RTCP
|
|
62
|
+
# packet with the given Packet Type ID.
|
|
63
|
+
#
|
|
64
|
+
# Raises an RTCP::DecodeError exception when this is not the case.
|
|
65
|
+
def ensure_packet_type(packet_type)
|
|
66
|
+
if packet_type != self.class::PT_ID
|
|
67
|
+
raise(RTCP::DecodeError, "Wrong Packet Type. packet_type=#{packet_type}")
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Extracts and returns the payload data from the given packet_data using the
|
|
72
|
+
# supplied packet length and header_length values.
|
|
73
|
+
#
|
|
74
|
+
# It also sets the @packet_data instance variable, which is currently used
|
|
75
|
+
# by the to_s method for returning the packet data.
|
|
76
|
+
#
|
|
77
|
+
# Raises an RTCP::DecodeError exception when the packet_data is shorter
|
|
78
|
+
# than packet_length.
|
|
79
|
+
def payload_data(packet_data, packet_length, header_length)
|
|
80
|
+
if packet_data.length > packet_length
|
|
81
|
+
@packet_data = packet_data[0..packet_length]
|
|
82
|
+
elsif packet_data.length == packet_length
|
|
83
|
+
@packet_data = packet_data
|
|
84
|
+
else
|
|
85
|
+
raise RTCP::DecodeError, "Truncated Packet"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
@packet_data[header_length..-1]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
# Returns the Class to use for handling RTCP packets of the given packet
|
|
94
|
+
# type.
|
|
95
|
+
def self.packet_class(packet_type)
|
|
96
|
+
@@packet_classes[packet_type] || self
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
end
|
data/lib/rtcp/app.rb
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# APP: Application-Defined RTCP Packet
|
|
2
|
+
# Documentation: RFC 3550, 6.7
|
|
3
|
+
#
|
|
4
|
+
# 0 1 2 3
|
|
5
|
+
# 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
|
|
6
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
7
|
+
# |V=2|P| subtype | PT=APP=204 | length |
|
|
8
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
9
|
+
# | SSRC/CSRC |
|
|
10
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
11
|
+
# | name (ASCII) |
|
|
12
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
13
|
+
# | application-dependent data ...
|
|
14
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
15
|
+
# 0 1 2 3
|
|
16
|
+
|
|
17
|
+
class RTCP::APP < RTCP
|
|
18
|
+
|
|
19
|
+
PT_ID = 204
|
|
20
|
+
|
|
21
|
+
attr_reader :version, :subtype, :ssrc, :name, :app_data
|
|
22
|
+
|
|
23
|
+
def decode(packet_data)
|
|
24
|
+
vpst, packet_type, length, @ssrc, @name = packet_data.unpack('CCnNa4')
|
|
25
|
+
ensure_packet_type(packet_type)
|
|
26
|
+
|
|
27
|
+
@length = 4 * (length + 1)
|
|
28
|
+
@version = vpst >> 6
|
|
29
|
+
@subtype = vpst & 31
|
|
30
|
+
|
|
31
|
+
@app_data = payload_data(packet_data, @length, 12)
|
|
32
|
+
|
|
33
|
+
self
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
data/lib/rtcp/bye.rb
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# BYE: Goodbye RTCP Packet
|
|
2
|
+
# Documentation: RFC 3550, 6.6
|
|
3
|
+
#
|
|
4
|
+
# 0 1 2 3
|
|
5
|
+
# 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
|
|
6
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
7
|
+
# |V=2|P| SC | PT=BYE=203 | length |
|
|
8
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
9
|
+
# | SSRC/CSRC |
|
|
10
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
11
|
+
# : ... :
|
|
12
|
+
# +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
|
13
|
+
# (opt) | length | reason for leaving ...
|
|
14
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
15
|
+
|
|
16
|
+
class RTCP::BYE < RTCP
|
|
17
|
+
|
|
18
|
+
PT_ID = 203
|
|
19
|
+
|
|
20
|
+
attr_reader :version, :ssrcs, :reason, :padding
|
|
21
|
+
|
|
22
|
+
def decode(packet_data)
|
|
23
|
+
vpsc, packet_type, length = packet_data.unpack('CCn')
|
|
24
|
+
ensure_packet_type(packet_type)
|
|
25
|
+
|
|
26
|
+
@length = 4 * (length + 1)
|
|
27
|
+
@version = vpsc >> 6
|
|
28
|
+
count = vpsc & 15
|
|
29
|
+
|
|
30
|
+
bye_data = payload_data(packet_data, @length, 4)
|
|
31
|
+
|
|
32
|
+
@ssrcs = bye_data.unpack("N#{count}")
|
|
33
|
+
|
|
34
|
+
if (4 * count) < bye_data.length
|
|
35
|
+
rlen, data = bye_data.unpack("x#{4 * count}Ca*")
|
|
36
|
+
@reason = data[0..(rlen - 1)]
|
|
37
|
+
|
|
38
|
+
# If the string fills the packet to the next 32-bit boundary,
|
|
39
|
+
# the string is not null terminated. If not, the BYE packet
|
|
40
|
+
# MUST be padded with null octets to the next 32- bit boundary.
|
|
41
|
+
# $TODO: Remove padding?
|
|
42
|
+
|
|
43
|
+
# $TODO: Check for/extract packet padding
|
|
44
|
+
end
|
|
45
|
+
self
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
end
|
data/lib/rtcp/psfb.rb
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# PSFB: Payload-specific FB message
|
|
2
|
+
# Documentation: RFC 4585, 6.1.
|
|
3
|
+
#
|
|
4
|
+
# 0 1 2 3
|
|
5
|
+
# 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
|
|
6
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
7
|
+
# |V=2|P| FMT | PT | length |
|
|
8
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
9
|
+
# | SSRC of packet sender |
|
|
10
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
11
|
+
# | SSRC of media source |
|
|
12
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
13
|
+
# : Feedback Control Information (FCI) :
|
|
14
|
+
# : :
|
|
15
|
+
|
|
16
|
+
class RTCP::PSFB < RTCP
|
|
17
|
+
|
|
18
|
+
FORMATS = {
|
|
19
|
+
1 => :pli, # Picture Loss Indication (PLI)
|
|
20
|
+
2 => :sli, # Slice Loss Indication (SLI)
|
|
21
|
+
3 => :rpsi, # Reference Picture Selection Indication (RPSI)
|
|
22
|
+
15 => :afb, # Application layer FB (AFB) message
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
PT_ID = 206
|
|
26
|
+
|
|
27
|
+
attr_reader :version, :format, :sender_ssrc, :source_ssrc, :fci,
|
|
28
|
+
:first_mb, :number, :picture_id
|
|
29
|
+
|
|
30
|
+
def decode(packet_data)
|
|
31
|
+
vpfmt, packet_type, length, @sender_ssrc, @source_ssrc =
|
|
32
|
+
packet_data.unpack('CCnN2')
|
|
33
|
+
ensure_packet_type(packet_type)
|
|
34
|
+
|
|
35
|
+
@length = 4 * (length + 1)
|
|
36
|
+
@version = vpfmt >> 6
|
|
37
|
+
format = vpfmt & 31
|
|
38
|
+
@format = FORMATS[format] || format
|
|
39
|
+
|
|
40
|
+
@fci_data = payload_data(packet_data, @length, 12)
|
|
41
|
+
|
|
42
|
+
case @format
|
|
43
|
+
when :sli
|
|
44
|
+
pl = @fci_data.unpack('L')
|
|
45
|
+
@first_mb = pl >> 19
|
|
46
|
+
@number = (pl >> 6) & 8191
|
|
47
|
+
@picture_id = pl & 63
|
|
48
|
+
# when :pli # No parameters
|
|
49
|
+
# when :rpsi
|
|
50
|
+
# when :afb
|
|
51
|
+
end
|
|
52
|
+
self
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
data/lib/rtcp/rr.rb
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# RR: Receiver Report RTCP Packet
|
|
2
|
+
# Documentation: RFC 3550, 6.4.2
|
|
3
|
+
#
|
|
4
|
+
# 0 1 2 3
|
|
5
|
+
# 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
|
|
6
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
7
|
+
# header |V=2|P| RC | PT=RR=201 | length |
|
|
8
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
9
|
+
# | SSRC of packet sender |
|
|
10
|
+
# +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
|
11
|
+
# report | SSRC_1 (SSRC of first source) |
|
|
12
|
+
# block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
13
|
+
# 1 | fraction lost | cumulative number of packets lost |
|
|
14
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
15
|
+
# | extended highest sequence number received |
|
|
16
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
17
|
+
# | interarrival jitter |
|
|
18
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
19
|
+
# | last SR (LSR) |
|
|
20
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
21
|
+
# | delay since last SR (DLSR) |
|
|
22
|
+
# +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
|
23
|
+
# report | SSRC_2 (SSRC of second source) |
|
|
24
|
+
# block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
25
|
+
# 2 : ... :
|
|
26
|
+
# +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
|
27
|
+
# | profile-specific extensions |
|
|
28
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
29
|
+
|
|
30
|
+
class RTCP::RR < RTCP::SR
|
|
31
|
+
|
|
32
|
+
PT_ID = 201
|
|
33
|
+
|
|
34
|
+
attr_reader :version, :ssrc, :report_blocks, :padding
|
|
35
|
+
|
|
36
|
+
def decode(packet_data)
|
|
37
|
+
vprc, packet_type, length, @ssrc = packet_data.unpack('CCnN')
|
|
38
|
+
ensure_packet_type(packet_type)
|
|
39
|
+
@length = 4 * (length + 1)
|
|
40
|
+
@version, @padding, rc = decode_vprc(vprc, @length - 8)
|
|
41
|
+
@report_blocks = decode_reports(payload_data(packet_data, @length, 8), rc)
|
|
42
|
+
self
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
data/lib/rtcp/rsi.rb
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# RSI: Receiver Summary Information Packet
|
|
2
|
+
# Documentation: RFC 5760, 7.1.1.
|
|
3
|
+
#
|
|
4
|
+
# 0 1 2 3
|
|
5
|
+
# 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
|
|
6
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
7
|
+
# |V=2|P|reserved | PT=RSI=209 | length |
|
|
8
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
9
|
+
# | SSRC |
|
|
10
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
11
|
+
# | Summarized SSRC |
|
|
12
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
13
|
+
# | NTP Timestamp (most significant word) |
|
|
14
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
15
|
+
# | NTP Timestamp (least significant word) |
|
|
16
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
17
|
+
# : Sub-report blocks :
|
|
18
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
19
|
+
# 0 1 2 3
|
|
20
|
+
#
|
|
21
|
+
# Sub-Report-Block Type
|
|
22
|
+
#
|
|
23
|
+
# 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
|
|
24
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
25
|
+
# | SRBT | Length | |
|
|
26
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ SRBT-specific data +
|
|
27
|
+
# | |
|
|
28
|
+
# : :
|
|
29
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
30
|
+
#
|
|
31
|
+
# Generic Sub-Report Block Fields
|
|
32
|
+
#
|
|
33
|
+
# 0 1 2 3
|
|
34
|
+
# 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
|
|
35
|
+
# +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
|
36
|
+
# | SRBT | Length | NDB | MF |
|
|
37
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
38
|
+
# | Minimum Distribution Value |
|
|
39
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
40
|
+
# | Maximum Distribution Value |
|
|
41
|
+
# +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
|
42
|
+
# | Distribution Buckets |
|
|
43
|
+
# | ... |
|
|
44
|
+
# | ... |
|
|
45
|
+
# +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
|
46
|
+
|
|
47
|
+
class RTCP::RSI < RTCP
|
|
48
|
+
|
|
49
|
+
PT_ID = 209
|
|
50
|
+
|
|
51
|
+
attr_reader :version, :ssrc, :summarized_ssrc, :ntp_timestamp, :report_blocks
|
|
52
|
+
|
|
53
|
+
def decode(packet_data)
|
|
54
|
+
vp, packet_type, length, @ssrc, @summarized_ssrc, ntp_h, ntp_l =
|
|
55
|
+
packet_data.unpack('CCnN4')
|
|
56
|
+
ensure_packet_type(packet_type)
|
|
57
|
+
|
|
58
|
+
@length = 4 * (length + 1)
|
|
59
|
+
@version = vp >> 6
|
|
60
|
+
@ntp_timestamp = Time.at(ntp_h - 2208988800 + (ntp_l.to_f / 0x100000000))
|
|
61
|
+
@report_blocks = decode_reports(payload_data(packet_data, @length, 20))
|
|
62
|
+
self
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def decode_reports(data)
|
|
68
|
+
blocks = []
|
|
69
|
+
while data && data.length >= 2
|
|
70
|
+
type, len = report_block_data.unpack('CC')
|
|
71
|
+
if data.length < len
|
|
72
|
+
raise DecodeError, "Truncated Packet"
|
|
73
|
+
end
|
|
74
|
+
blocks.push({
|
|
75
|
+
type: type,
|
|
76
|
+
data: data.slice!(0..(len-1))
|
|
77
|
+
})
|
|
78
|
+
end
|
|
79
|
+
blocks
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
end
|