rtp 0.0.1

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.
@@ -0,0 +1 @@
1
+
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source :rubygems
2
+ gemspec
3
+
4
+ group :test do
5
+ gem 'rcov', '~> 0.9'
6
+ end
@@ -0,0 +1,4 @@
1
+ === 0.0.1 / 2012-03-02
2
+
3
+ * Initial release.
4
+
@@ -0,0 +1,57 @@
1
+ = rtp
2
+
3
+ http://github.com/turboladen/rtp
4
+
5
+ == DESCRIPTION
6
+
7
+ This is a RTP library that provides the most basic features of the Real-Time
8
+ Transport Protocol (RTP, {RFC 3550}[http://tools.ietf.org/html/rfc3550]. While
9
+ the protocol allows for extensions that define new ways for transporting encoded
10
+ audio/video, this library doesn't yet deal with any specific media types. My
11
+ current goal is simply for transporting a single RTP stream of data and allowing
12
+ you to decode its fixed headers. Quite frankly, this first release is just a
13
+ break out of the Receiver stuff that was built in to the
14
+ {rtsp library}[https://github.com/turboladen/rtsp], with the ability to parse
15
+ RTP header data.
16
+
17
+ Future goals will include:
18
+
19
+ * Implement "sink"--sort RTP packets
20
+ * Implementing RTCP
21
+ * Multiplexed sessions
22
+ * Decoding of basic audio/video codec headers
23
+ * Decoding of some popular A/V extension headers (h.264, et al.)
24
+ * An easy way to hand over your data to an app that will decode your A/V stream(s)
25
+
26
+ == FEATURES/PROBLEMS
27
+
28
+ Features:
29
+
30
+ * Receive a single RTP stream over UDP
31
+ * Parse RTP packet headers
32
+
33
+ == SYNOPSIS
34
+
35
+ FIX (explain how to use, etc)
36
+
37
+ == REQUIREMENTS
38
+
39
+ * (Tested) Rubies
40
+ * 1.9.2
41
+ * 1.9.3
42
+ * RubyGems:
43
+ * bindata
44
+ * log_switch
45
+
46
+ == INSTALL
47
+
48
+ $ gem install rtp
49
+
50
+ == DEVELOPERS
51
+
52
+ After checking out the source, run:
53
+
54
+ $ bundle install
55
+
56
+ This task will install any missing dependencies for you.
57
+
@@ -0,0 +1,15 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ require 'yard'
4
+
5
+ # Load all extra rake task definitions
6
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].each { |ext| load ext }
7
+
8
+ task default: :install
9
+
10
+ YARD::Rake::YardocTask.new
11
+ RSpec::Core::RakeTask.new
12
+
13
+ # Alias for rubygems-test
14
+ task test: :spec
15
+
@@ -0,0 +1,7 @@
1
+ require_relative 'rtp/version'
2
+ require 'pathname'
3
+ require 'log_switch'
4
+
5
+ module RTP
6
+ extend LogSwitch
7
+ end
@@ -0,0 +1,5 @@
1
+ module RTP
2
+ class Error < RuntimeError
3
+
4
+ end
5
+ end
@@ -0,0 +1,43 @@
1
+ require 'bindata'
2
+
3
+ module RTP
4
+ class Packet < BinData::Record
5
+ endian :big
6
+
7
+ bit2 :version
8
+ bit1 :padding
9
+ bit1 :extension
10
+ bit4 :csrc_count
11
+
12
+ bit1 :marker
13
+ bit7 :payload_type
14
+
15
+ uint16 :sequence_number
16
+ uint32 :timestamp
17
+ uint32 :ssrc_id
18
+ array :csrc_ids, :type => :uint32, :initial_length => lambda { csrc_count }
19
+
20
+ # Extension header is variable length if :extension == 1
21
+ #uint16 :extension_id
22
+ #uint16 :extension_length
23
+
24
+ =begin
25
+ # h.264 payload
26
+ # NAL section of RTP Payload
27
+ # NAL unit header && payload header
28
+ bit1 :nal_unit_forbidden_zero
29
+ bit2 :nal_ref_idc
30
+ bit5 :nal_unit_type
31
+
32
+ # Payload byte string?
33
+ # FU header
34
+ bit1 :start_bit
35
+ bit1 :end_bit
36
+ bit1 :reserved # must be 0 and must be ignored
37
+ bit5 :nal_unit_payload_type
38
+ =end
39
+ count_bytes_remaining :bytes_remaining
40
+ string :rtp_payload, read_length: lambda { bytes_remaining }
41
+ end
42
+ end
43
+
@@ -0,0 +1,220 @@
1
+ require 'tempfile'
2
+ require 'socket'
3
+
4
+ require_relative '../rtp'
5
+ require_relative 'error'
6
+ require_relative 'packet'
7
+
8
+ module RTP
9
+
10
+ # Objects of this type can be used with a +RTSP::Client+ object in order to
11
+ # capture the RTP data transmitted to the client as a result of an RTSP
12
+ # PLAY call.
13
+ #
14
+ # In this version, objects of this type don't do much other than just capture
15
+ # the data to a file; in later versions, objects of this type will be able
16
+ # to provide a "sink" and allow for ensuring that the received RTP packets
17
+ # will be reassembled in the correct order, as they're written to file
18
+ # (objects of this type don't don't currently allow for checking RTP sequence
19
+ # numbers on the data that's been received).
20
+ class Receiver
21
+
22
+ # Name of the file the data will be captured to unless #rtp_file is set.
23
+ DEFAULT_CAPFILE_NAME = "rtp_capture.raw"
24
+
25
+ # Maximum number of bytes to receive on the socket.
26
+ MAX_BYTES_TO_RECEIVE = 1500
27
+
28
+ # Maximum times to retry using the next greatest port number.
29
+ MAX_PORT_NUMBER_RETRIES = 50
30
+
31
+ # @param [File] rtp_file The file to capture the RTP data to.
32
+ # @return [File]
33
+ attr_accessor :rtp_file
34
+
35
+ # @param [Boolean] strip_headers True if you want to strip the RTP headers.
36
+ attr_accessor :strip_headers
37
+
38
+ # @return [Array<Time>] packet_timestamps The packet receipt timestamps.
39
+ attr_accessor :packet_timestamps
40
+
41
+ # @param [Fixnum] rtp_port The port on which to capture the RTP data.
42
+ # @return [Fixnum]
43
+ attr_accessor :rtp_port
44
+
45
+ # @param [Symbol] transport_protocol +:UDP+ or +:TCP+.
46
+ # @return [Symbol]
47
+ attr_accessor :transport_protocol
48
+
49
+ # @param [Symbol] broadcast_type +:multicast+ or +:unicast+.
50
+ # @return [Symbol]
51
+ attr_accessor :broadcast_type
52
+
53
+ # @param [Symbol] transport_protocol The type of socket to use for capturing
54
+ # the data. +:UDP+ or +:TCP+.
55
+ # @param [Fixnum] rtp_port The port on which to capture RTP data.
56
+ # @param [File] rtp_capture_file The file object to capture the RTP data to.
57
+ def initialize(transport_protocol=:UDP, rtp_port=9000, rtp_capture_file=nil)
58
+ @transport_protocol = transport_protocol
59
+ @rtp_port = rtp_port
60
+ @rtp_file = rtp_capture_file || Tempfile.new(DEFAULT_CAPFILE_NAME)
61
+ @packet_timestamps = []
62
+ @listener = nil
63
+ @sequence_list = []
64
+ @payload_data = []
65
+ @strip_headers = false
66
+ end
67
+
68
+ # Initializes a server of the correct socket type.
69
+ #
70
+ # @return [UDPSocket, TCPSocket]
71
+ # @raise [RTP::Error] If +@transport_protocol was not set to +:UDP+ or
72
+ # +:TCP+.
73
+ def init_server(protocol, port=9000)
74
+ if protocol == :UDP
75
+ server = init_udp_server(port)
76
+ elsif protocol == :TCP
77
+ server = init_tcp_server(port)
78
+ else
79
+ raise RTP::Error, "Unknown streaming_protocol requested: #{@transport_protocol}"
80
+ end
81
+
82
+ server
83
+ end
84
+
85
+ # Simply calls #start_listener.
86
+ def run
87
+ RTP.log "Starting #{self.class} on port #{@rtp_port}..."
88
+ start_listener
89
+ end
90
+
91
+
92
+ # Starts the +@listener+ thread that starts up the server, then takes the
93
+ # data received from the server and pushes it on to the +@@payload_data+.
94
+ #
95
+ # @return [Thread] The listener thread (+@listener+).
96
+ def start_listener
97
+ return @listener if listening?
98
+
99
+ @listener = Thread.start do
100
+ server = init_server(@transport_protocol, @rtp_port)
101
+
102
+ loop do
103
+ begin
104
+ msg = server.recvmsg_nonblock(MAX_BYTES_TO_RECEIVE)
105
+ data = msg.first
106
+ @packet_timestamps << msg.last.timestamp
107
+ RTP.log "received data with size: #{data.size}"
108
+ packet = RTP::Packet.read(data)
109
+ RTP.log "rtp payload size: #{packet["rtp_payload"].size}"
110
+ write_buffer_to_file if @sequence_list.include? packet["sequence_number"].to_i
111
+ @sequence_list << packet["sequence_number"].to_i
112
+
113
+ if @strip_headers
114
+ @payload_data[packet["sequence_number"].to_i] = packet["rtp_payload"]
115
+ else
116
+ @payload_data[packet["sequence_number"].to_i] = data
117
+ end
118
+ rescue Errno::EAGAIN;
119
+ write_buffer_to_file
120
+ end # rescue error when no data is available to read.
121
+ end
122
+ end
123
+
124
+ @listener.abort_on_exception = true
125
+ end
126
+
127
+ # Sorts the sequence numbers and writes the data in the buffer to file.
128
+ def write_buffer_to_file
129
+ @sequence_list.sort.each do |sequence|
130
+ @rtp_file.write @payload_data[sequence]
131
+ end
132
+
133
+ @sequence_list.clear
134
+ @payload_data.clear
135
+ end
136
+
137
+ # @return [Boolean] true if the +@listener+ thread is running; false if not.
138
+ def listening?
139
+ !@listener.nil? ? @listener.alive? : false
140
+ end
141
+
142
+ # Returns if the #run loop is in action.
143
+ #
144
+ # @return [Boolean] true if the run loop is running.
145
+ def running?
146
+ listening?
147
+ end
148
+
149
+ # Breaks out of the run loop.
150
+ def stop
151
+ RTP.log "Stopping #{self.class} on port #{@rtp_port}..."
152
+ stop_listener
153
+ RTP.log "listening? #{listening?}"
154
+ write_buffer_to_file
155
+ RTP.log "running? #{running?}"
156
+ end
157
+
158
+ # Kills the +@listener+ thread and sets the variable to nil.
159
+ def stop_listener
160
+ #@listener.kill if @listener
161
+ @listener.kill if listening?
162
+ @listener = nil
163
+ end
164
+
165
+ # Sets up to receive data on a UDP socket, using +@rtp_port+.
166
+ #
167
+ # @param [Fixnum] port Port number to listen for RTP data on.
168
+ # @return [UDPSocket]
169
+ def init_udp_server(port)
170
+ port_retries = 0
171
+
172
+ begin
173
+ server = UDPSocket.open
174
+ server.bind('0.0.0.0', port)
175
+ server.setsockopt(:SOCKET, :TIMESTAMP, true)
176
+ optval = [0, 1].pack("l_2")
177
+ server.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval)
178
+ rescue Errno::EADDRINUSE
179
+ RTP.log "RTP port #{port} in use, trying #{port + 1}..."
180
+ port += 1
181
+ port_retries += 1
182
+ retry until port_retries == MAX_PORT_NUMBER_RETRIES + 1
183
+ port = 9000
184
+ raise
185
+ end
186
+
187
+ @rtp_port = port
188
+ RTP.log "UDP server setup to receive on port #{@rtp_port}"
189
+
190
+ server
191
+ end
192
+
193
+ # Sets up to receive data on a TCP socket, using +@rtp_port+.
194
+ #
195
+ # @param [Fixnum] port Port number to listen for RTP data on.
196
+ # @return [TCPServer]
197
+ def init_tcp_server(port)
198
+ port_retries = 0
199
+
200
+ begin
201
+ server = TCPServer.new(port)
202
+ server.setsockopt(:SOCKET, :TIMESTAMP, true)
203
+ optval = [0, 1].pack("l_2")
204
+ server.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval)
205
+ rescue Errno::EADDRINUSE
206
+ RTP.log "RTP port #{port} in use, trying #{port + 1}..."
207
+ port += 1
208
+ port_retries += 1
209
+ retry until port_retries == MAX_PORT_NUMBER_RETRIES + 1
210
+ port = 9000
211
+ raise
212
+ end
213
+
214
+ @rtp_port = port
215
+ RTP.log "TCP server setup to receive on port #{@rtp_port}"
216
+
217
+ server
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,3 @@
1
+ module RTP
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,29 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require 'rtp/version'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "rtp"
6
+ s.version = RTP::VERSION
7
+ s.authors = ["Steve Loveless", "Sujin Philip"]
8
+ s.homepage = %q(http://github.com/turboladen/rtp)
9
+ s.email = %w(steve.loveless@gmail.com)
10
+ s.description = %q(This is a pure Ruby implementation of RTP, initially geared
11
+ towards use with RTSP (but not limited to).)
12
+ s.summary = %q(Pure Ruby implementation of RTP)
13
+
14
+ s.required_rubygems_version = ">=1.8.0"
15
+ s.required_ruby_version = Gem::Requirement.new(">= 1.9.2")
16
+ s.files = Dir.glob("{lib,spec,tasks}/**/*") + Dir.glob("*.rdoc") +
17
+ %w(.gemtest Gemfile rtp.gemspec Rakefile)
18
+ s.test_files = Dir.glob("spec/**/*")
19
+ s.require_paths = %w(lib)
20
+
21
+ s.add_dependency("bindata", "~> 1.4")
22
+ s.add_dependency("log_switch", ">=0.2.0")
23
+
24
+ s.add_development_dependency("bundler", ">= 0")
25
+ s.add_development_dependency("rake", ">= 0")
26
+ s.add_development_dependency("rspec", "~> 2.7")
27
+ s.add_development_dependency("simplecov", ">= 0")
28
+ s.add_development_dependency("yard", ">= 0.7.2")
29
+ end
@@ -0,0 +1,368 @@
1
+ require_relative '../spec_helper'
2
+ require 'rtp/receiver'
3
+
4
+ Thread.abort_on_exception = true
5
+ RTP.log = false
6
+
7
+ describe RTP::Receiver do
8
+ describe "#initialize" do
9
+ context "with default parameters" do
10
+ it "uses UDP" do
11
+ subject.instance_variable_get(:@transport_protocol).should == :UDP
12
+ end
13
+
14
+ it "uses port 9000" do
15
+ subject.instance_variable_get(:@rtp_port).should == 9000
16
+ end
17
+
18
+ it "creates a new Tempfile" do
19
+ subject.instance_variable_get(:@rtp_file).should be_a Tempfile
20
+ end
21
+
22
+ it "initializes @packet_timestamps" do
23
+ subject.instance_variable_get(:@packet_timestamps).should == []
24
+ end
25
+
26
+ it "initializes an Array for holding the data buffer" do
27
+ subject.instance_variable_get(:@payload_data).should be_a Array
28
+ end
29
+
30
+ it "initializes an Array for holding the sequence_list" do
31
+ subject.instance_variable_get(:@sequence_list).should be_a Array
32
+ end
33
+ end
34
+
35
+ context "non-default parameters" do
36
+ it "can use TCP" do
37
+ RTP::Receiver.new(:TCP).instance_variable_get(:@transport_protocol).should == :TCP
38
+ end
39
+
40
+ it "can take another port" do
41
+ RTP::Receiver.new(:UDP, 12345).instance_variable_get(:@rtp_port).should == 12345
42
+ end
43
+
44
+ it "can take an IO object" do
45
+ fd = IO.sysopen("/dev/null", "w")
46
+ io = IO.new(fd, 'w')
47
+ capturer = RTP::Receiver.new(:UDP, 12345, io)
48
+ capturer.instance_variable_get(:@rtp_file).should be_a IO
49
+ end
50
+ end
51
+
52
+ it "isn't running" do
53
+ subject.should_not be_running
54
+ end
55
+ end
56
+
57
+ describe "#init_server" do
58
+ context "UDP" do
59
+ it "calls #init_udp_server with port 9000" do
60
+ subject.should_receive(:init_udp_server).with(9000)
61
+ subject.init_server(:UDP)
62
+ end
63
+
64
+ it "returns a UDPSocket" do
65
+ subject.init_server(:UDP).should be_a UDPSocket
66
+ end
67
+ end
68
+
69
+ context "TCP" do
70
+ it "calls #init_tcp_server with port 9000" do
71
+ subject.should_receive(:init_tcp_server).with(9000)
72
+ subject.init_server(:TCP)
73
+ end
74
+
75
+ it "returns a TCPServer" do
76
+ subject.init_server(:TCP).should be_a(TCPServer)
77
+ end
78
+ end
79
+
80
+ it "raises an RTP::Error when some other protocol is given" do
81
+ expect { subject.init_server(:BOBO) }.to raise_error RTP::Error
82
+ end
83
+ end
84
+
85
+ describe "#init_udp_server" do
86
+ let(:udp_server) do
87
+ double "UDPSocket", setsockopt: nil
88
+ end
89
+
90
+ it "returns a UDPSocket" do
91
+ subject.init_udp_server(subject.rtp_port).should be_a UDPSocket
92
+ end
93
+
94
+ context "when port 9000 - 9048 are taken" do
95
+ it "retries MAX_PORT_NUMBER_RETRIES times then returns the UDPSocket" do
96
+ udp_server.should_receive(:bind).exactly(50).times.and_raise(Errno::EADDRINUSE)
97
+ udp_server.should_receive(:bind).with('0.0.0.0', 9050)
98
+ UDPSocket.stub(:open).and_return(udp_server)
99
+
100
+ subject.init_udp_server(9000).should == udp_server
101
+
102
+ UDPSocket.unstub(:open)
103
+ end
104
+ end
105
+
106
+ context "when no available ports" do
107
+ before do
108
+ UDPSocket.should_receive(:open).exactly(51).times.and_raise(Errno::EADDRINUSE)
109
+ end
110
+
111
+ it "retries 50 times to get a port then allows the Errno::EADDRINUSE to raise" do
112
+ expect { subject.init_udp_server(9000) }.to raise_error Errno::EADDRINUSE
113
+ end
114
+
115
+ it "sets @rtp_port back to 9000 after trying all" do
116
+ expect { subject.init_udp_server(9000) }.to raise_error Errno::EADDRINUSE
117
+ subject.rtp_port.should == 9000
118
+ end
119
+ end
120
+ end
121
+
122
+ describe "#init_tcp_server" do
123
+ it "returns a TCPSocket" do
124
+ subject.init_tcp_server(3456).should be_a TCPSocket
125
+ end
126
+
127
+ it "uses port a port between 9000 and 9000 + MAX_PORT_NUMBER_RETRIES" do
128
+ subject.init_tcp_server(9000)
129
+ subject.rtp_port.should >= 9000
130
+ subject.rtp_port.should <= 9000 + RTP::Receiver::MAX_PORT_NUMBER_RETRIES
131
+ end
132
+ end
133
+
134
+ describe "#run" do
135
+ it "calls #start_listener" do
136
+ subject.should_receive(:start_listener)
137
+ subject.run
138
+ end
139
+ end
140
+
141
+ describe "#running?" do
142
+ context "#listening? returns true" do
143
+ before { subject.stub(:listening?).and_return(true) }
144
+ it { should be_true }
145
+ end
146
+
147
+ context "#listening? returns true, #file_building? returns true" do
148
+ before do
149
+ subject.stub(:listening? => true, :file_building? => true)
150
+ it { should be_true }
151
+ end
152
+ end
153
+
154
+ context "#listening? returns true, #file_building? returns false" do
155
+ before do
156
+ subject.stub(:listening? => true, :file_building? => false)
157
+ it { should be_false }
158
+ end
159
+ end
160
+
161
+ context "#listening? returns false, #file_building? returns false" do
162
+ before do
163
+ subject.stub(:listening? => false, :file_building? => false)
164
+ it { should be_false }
165
+ end
166
+ end
167
+ end
168
+
169
+ describe "#stop" do
170
+ it "calls #stop_listener" do
171
+ subject.should_receive(:stop_listener)
172
+ subject.stop
173
+ end
174
+
175
+ it "calls #write_buffer_to_file" do
176
+ subject.should_receive(:write_buffer_to_file)
177
+ subject.stop
178
+ end
179
+ end
180
+
181
+ describe "#start_listener" do
182
+ let(:listener) do
183
+ l = double "@listener"
184
+ l.stub(:abort_on_exception=)
185
+
186
+ l
187
+ end
188
+
189
+ context "#listening? is true" do
190
+ before { subject.stub(:listening?).and_return true }
191
+
192
+ it "returns @listener" do
193
+ subject.instance_variable_set(:@listener, listener)
194
+ subject.start_listener.should equal listener
195
+ end
196
+ end
197
+
198
+ context "#listening? is false" do
199
+ before { subject.stub(:listening?).and_return false }
200
+
201
+ it "starts a new Thread and assigns that to @listener" do
202
+ Thread.should_receive(:start).and_return listener
203
+ subject.start_listener
204
+ subject.instance_variable_get(:@listener).should equal listener
205
+ end
206
+
207
+ it "initializes the server socket" do
208
+ subject.instance_variable_set(:@listener, listener)
209
+ Thread.stub(:start).and_yield.and_return listener
210
+ subject.should_receive(:init_server)
211
+ subject.stub(:loop)
212
+
213
+ subject.start_listener
214
+
215
+ Thread.unstub(:start)
216
+ end
217
+
218
+ let!(:data) do
219
+ d = double "data"
220
+ d.stub(:size)
221
+
222
+ d
223
+ end
224
+
225
+ let!(:timestamp) { double "timestamp" }
226
+
227
+ let(:message) do
228
+ m = double "msg"
229
+ m.stub(:first).and_return data
230
+ m.stub_chain(:last, :timestamp).and_return timestamp
231
+
232
+ m
233
+ end
234
+
235
+ let(:server) do
236
+ double "Server"
237
+ end
238
+
239
+ it "receives data from the client and hands it to RTP::Packet to read" do
240
+ subject.instance_variable_set(:@listener, listener)
241
+ Thread.stub(:start).and_yield.and_return listener
242
+ server.should_receive(:recvmsg_nonblock).with(1500).and_return message
243
+ subject.should_receive(:init_server).and_return server
244
+ packet = double "RTP::Packet"
245
+ packet.stub_chain(:[], :size)
246
+ packet.stub_chain(:[], :to_i).and_return(10)
247
+ RTP::Packet.should_receive(:read).with(data).and_return packet
248
+ subject.stub(:write_buffer_to_file)
249
+ subject.stub(:loop).and_yield
250
+
251
+ subject.start_listener
252
+
253
+ Thread.unstub(:start)
254
+ end
255
+
256
+ it "extracts the timestamp of the received data and adds it to @packet_timestamps" do
257
+ pending
258
+ end
259
+
260
+ context "@strip_headers is false" do
261
+ it "adds the incoming data to @payload_data buffer" do
262
+ subject.instance_variable_set(:@listener, listener)
263
+ Thread.stub(:start).and_yield.and_return listener
264
+ server.should_receive(:recvmsg_nonblock).with(1500).and_return message
265
+ subject.should_receive(:init_server).and_return server
266
+ packet = double "RTP::Packet"
267
+ packet.stub_chain(:[], :size)
268
+ packet.stub_chain(:[], :to_i).and_return(0)
269
+ RTP::Packet.stub(:read).and_return packet
270
+ subject.stub(:write_buffer_to_file)
271
+ subject.stub(:loop).and_yield
272
+
273
+ subject.start_listener
274
+ subject.instance_variable_get(:@payload_data).should == [data]
275
+ Thread.unstub(:start)
276
+ end
277
+ end
278
+
279
+ context "@strip_headers is true" do
280
+ it "adds the stripped data to @payload_data buffer" do
281
+ subject.instance_variable_set(:@listener, listener)
282
+ subject.instance_variable_set(:@strip_headers, true)
283
+ Thread.stub(:start).and_yield.and_return listener
284
+ server.should_receive(:recvmsg_nonblock).with(1500).and_return message
285
+ subject.should_receive(:init_server).and_return server
286
+ packet = double "RTP::Packet"
287
+ packet.should_receive(:[]).with("rtp_payload").twice.and_return("payload_data")
288
+ packet.should_receive(:[]).with("sequence_number").exactly(3).times.and_return("0")
289
+ RTP::Packet.stub(:read).and_return packet
290
+ subject.stub(:write_buffer_to_file)
291
+ subject.stub(:loop).and_yield
292
+
293
+ subject.start_listener
294
+ subject.instance_variable_get(:@payload_data).should == ["payload_data"]
295
+ Thread.unstub(:start)
296
+ end
297
+ end
298
+ end
299
+ end
300
+
301
+ describe "#write_buffer_to_file" do
302
+ before do
303
+ sequence_list = [54322, 54323, 54320, 54324, 54325, 54321]
304
+ data = []
305
+
306
+ sequence_list.each do |sequence|
307
+ data[sequence] = "data#{sequence}"
308
+ end
309
+
310
+ subject.instance_variable_set(:@sequence_list, sequence_list)
311
+ subject.instance_variable_set(:@payload_data, data)
312
+ end
313
+
314
+ it "sorts the buffer and writes to file" do
315
+ output = StringIO.new
316
+ expected_output = "data54320data54321data54322data54323data54324data54325"
317
+ subject.instance_variable_set(:@rtp_file, output)
318
+ subject.write_buffer_to_file
319
+ output.string.should == expected_output
320
+ end
321
+
322
+ it "clears the sequence list after writing to file" do
323
+ output = StringIO.new
324
+ subject.instance_variable_set(:@rtp_file, output)
325
+ subject.write_buffer_to_file
326
+ subject.instance_variable_get(:@sequence_list).should == []
327
+ end
328
+
329
+ it "clears the data buffer after writing to file" do
330
+ output = StringIO.new
331
+ subject.instance_variable_set(:@rtp_file, output)
332
+ subject.write_buffer_to_file
333
+ subject.instance_variable_get(:@payload_data).should == []
334
+ end
335
+ end
336
+
337
+ describe "#stop_listener" do
338
+ let(:listener) { double "@listener" }
339
+
340
+ it "sets @listener to nil" do
341
+ local_listener = "test"
342
+ subject.stub(:listening?).and_return false
343
+ subject.instance_variable_set(:@listener, local_listener)
344
+ subject.stop_listener
345
+ subject.instance_variable_get(:@listener)
346
+ end
347
+
348
+ context "#listeining? is false" do
349
+ before { subject.stub(:listening?).and_return false }
350
+
351
+ it "doesn't get #kill called on it" do
352
+ listener.should_not_receive(:kill)
353
+ subject.instance_variable_set(:@listener, listener)
354
+ subject.stop_listener
355
+ end
356
+ end
357
+
358
+ context "#listening? is true" do
359
+ before { subject.stub(:listening?).and_return true }
360
+
361
+ it "gets killed" do
362
+ listener.should_receive(:kill)
363
+ subject.instance_variable_set(:@listener, listener)
364
+ subject.stop_listener
365
+ end
366
+ end
367
+ end
368
+ end
@@ -0,0 +1,24 @@
1
+ require_relative 'spec_helper'
2
+ require 'rtp'
3
+
4
+ describe Kernel do
5
+ def self.get_requires
6
+ Dir[File.dirname(__FILE__) + '/../lib/rtp/**/*.rb']
7
+ end
8
+
9
+ # Try to require each of the files in RTP
10
+ get_requires.each do |r|
11
+ it "should require #{r}" do
12
+
13
+ # A require returns true if it was required, false if it had already been
14
+ # required, and nil if it couldn't require.
15
+ Kernel.require(r.to_s).should_not be_nil
16
+ end
17
+ end
18
+ end
19
+
20
+ describe RTP do
21
+ it "should have a VERSION constant" do
22
+ RTP.const_defined?('VERSION').should be_true
23
+ end
24
+ end
@@ -0,0 +1,20 @@
1
+ require 'simplecov'
2
+ #require 'simplecov-rcov-text'
3
+
4
+ class SimpleCov::Formatter::MergedFormatter
5
+ def format(result)
6
+ SimpleCov::Formatter::HTMLFormatter.new.format(result)
7
+ # SimpleCov::Formatter::RcovTextFormatter.new.format(result)
8
+ end
9
+ end
10
+
11
+ SimpleCov.formatter = SimpleCov::Formatter::MergedFormatter
12
+
13
+ SimpleCov.start do
14
+ add_filter "/spec"
15
+ add_filter "/lib/deps"
16
+ end
17
+
18
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
19
+ require 'rtp'
20
+ require 'stringio'
@@ -0,0 +1,10 @@
1
+ require 'rake/tasklib' # roodi_task fails without this.
2
+ require 'roodi'
3
+ require 'roodi_task'
4
+ require 'erb'
5
+
6
+ RoodiTask.new do |t|
7
+ t.config = 'tasks/roodi_config.yaml'
8
+ t.patterns = Dir.glob("{features,lib,spec}/**/*.rb")
9
+ t.verbose = true
10
+ end
@@ -0,0 +1,14 @@
1
+ ---
2
+ AssignmentInConditionalCheck: { }
3
+ CaseMissingElseCheck: { }
4
+ ClassLineCountCheck: { line_count: 300 }
5
+ ClassNameCheck: { pattern: !ruby/regexp '/^[A-Z][a-zA-Z0-9]*$/' }
6
+ CyclomaticComplexityBlockCheck: { complexity: 4 }
7
+ CyclomaticComplexityMethodCheck: { complexity: 8 }
8
+ EmptyRescueBodyCheck: { }
9
+ ForLoopCheck: { }
10
+ MethodLineCountCheck: { line_count: 30 }
11
+ MethodNameCheck: { pattern: !ruby/regexp '/^[_a-z<>=\[\]|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/' }
12
+ ModuleLineCountCheck: { line_count: 300 }
13
+ ModuleNameCheck: { pattern: !ruby/regexp '/^[A-Z][a-zA-Z0-9]*$/' }
14
+ ParameterNumberCheck: { parameter_count: 5 }
@@ -0,0 +1,10 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ RSpec::Core::RakeTask.new(:spec) do |t|
4
+ t.ruby_opts = %w(-w)
5
+ t.rspec_opts = %w(--format documentation --color)
6
+ end
7
+
8
+ RSpec::Core::RakeTask.new(:spec_html) do |t|
9
+ t.rspec_opts = %w(--format html --out rspec_output.html)
10
+ end
@@ -0,0 +1,12 @@
1
+ require 'code_statistics'
2
+
3
+ STATS_DIRECTORIES = [
4
+ %w(Library lib),
5
+ %w(Behavior\ tests features/step_definitions),
6
+ %w(Unit\ tests spec)
7
+ ].collect { |name, dir| [ name, "#{dir}" ] }.select { |name, dir| File.directory?(dir) }
8
+
9
+ desc "Report code statistics (KLOCs, etc) from the application"
10
+ task :stats do
11
+ CodeStatistics::CodeStatistics.new(*STATS_DIRECTORIES).to_s
12
+ end
@@ -0,0 +1,8 @@
1
+ require 'yard'
2
+
3
+ YARD::Rake::YardocTask.new do |t|
4
+ t.files = %w(lib/**/*.rb - History.rdoc)
5
+ t.options = %w(--title rtp Documentation (#{RTP::VERSION}))
6
+ t.options += %w(--main README.rdoc)
7
+ t.options += %w(--private --protected --verbose)
8
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rtp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Steve Loveless
9
+ - Sujin Philip
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-03-02 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: bindata
17
+ requirement: &70231393476520 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: '1.4'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *70231393476520
26
+ - !ruby/object:Gem::Dependency
27
+ name: log_switch
28
+ requirement: &70231393472440 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 0.2.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *70231393472440
37
+ - !ruby/object:Gem::Dependency
38
+ name: bundler
39
+ requirement: &70231393468300 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *70231393468300
48
+ - !ruby/object:Gem::Dependency
49
+ name: rake
50
+ requirement: &70231393465820 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: *70231393465820
59
+ - !ruby/object:Gem::Dependency
60
+ name: rspec
61
+ requirement: &70231393463540 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ~>
65
+ - !ruby/object:Gem::Version
66
+ version: '2.7'
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: *70231393463540
70
+ - !ruby/object:Gem::Dependency
71
+ name: simplecov
72
+ requirement: &70231393457440 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: *70231393457440
81
+ - !ruby/object:Gem::Dependency
82
+ name: yard
83
+ requirement: &70231393454320 !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: 0.7.2
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: *70231393454320
92
+ description: ! "This is a pure Ruby implementation of RTP, initially geared\n towards
93
+ use with RTSP (but not limited to)."
94
+ email:
95
+ - steve.loveless@gmail.com
96
+ executables: []
97
+ extensions: []
98
+ extra_rdoc_files: []
99
+ files:
100
+ - lib/rtp/error.rb
101
+ - lib/rtp/packet.rb
102
+ - lib/rtp/receiver.rb
103
+ - lib/rtp/version.rb
104
+ - lib/rtp.rb
105
+ - spec/rtp/receiver_spec.rb
106
+ - spec/rtp_spec.rb
107
+ - spec/spec_helper.rb
108
+ - tasks/roodi.rake
109
+ - tasks/roodi_config.yaml
110
+ - tasks/rspec.rake
111
+ - tasks/stats.rake
112
+ - tasks/yard.rake
113
+ - History.rdoc
114
+ - README.rdoc
115
+ - .gemtest
116
+ - Gemfile
117
+ - rtp.gemspec
118
+ - Rakefile
119
+ homepage: http://github.com/turboladen/rtp
120
+ licenses: []
121
+ post_install_message:
122
+ rdoc_options: []
123
+ require_paths:
124
+ - lib
125
+ required_ruby_version: !ruby/object:Gem::Requirement
126
+ none: false
127
+ requirements:
128
+ - - ! '>='
129
+ - !ruby/object:Gem::Version
130
+ version: 1.9.2
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ none: false
133
+ requirements:
134
+ - - ! '>='
135
+ - !ruby/object:Gem::Version
136
+ version: 1.8.0
137
+ requirements: []
138
+ rubyforge_project:
139
+ rubygems_version: 1.8.17
140
+ signing_key:
141
+ specification_version: 3
142
+ summary: Pure Ruby implementation of RTP
143
+ test_files:
144
+ - spec/rtp/receiver_spec.rb
145
+ - spec/rtp_spec.rb
146
+ - spec/spec_helper.rb
147
+ has_rdoc: