rtp 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: