rtsp_server 0.0.2-universal-java

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.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gemtest +0 -0
  3. data/ChangeLog.rdoc +74 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE.rdoc +20 -0
  6. data/README.rdoc +152 -0
  7. data/Rakefile +23 -0
  8. data/bin/rtsp_client +133 -0
  9. data/features/client_changes_state.feature +58 -0
  10. data/features/client_requests.feature +27 -0
  11. data/features/control_streams_as_client.feature +26 -0
  12. data/features/step_definitions/client_changes_state_steps.rb +52 -0
  13. data/features/step_definitions/client_requests_steps.rb +68 -0
  14. data/features/step_definitions/control_streams_as_client_steps.rb +34 -0
  15. data/features/support/env.rb +50 -0
  16. data/features/support/hooks.rb +3 -0
  17. data/lib/ext/logger.rb +8 -0
  18. data/lib/rtsp/client.rb +520 -0
  19. data/lib/rtsp/common.rb +148 -0
  20. data/lib/rtsp/error.rb +6 -0
  21. data/lib/rtsp/global.rb +63 -0
  22. data/lib/rtsp/helpers.rb +28 -0
  23. data/lib/rtsp/message.rb +272 -0
  24. data/lib/rtsp/request.rb +39 -0
  25. data/lib/rtsp/response.rb +47 -0
  26. data/lib/rtsp/server.rb +311 -0
  27. data/lib/rtsp/socat_streaming.rb +320 -0
  28. data/lib/rtsp/stream_server.rb +37 -0
  29. data/lib/rtsp/transport_parser.rb +96 -0
  30. data/lib/rtsp/version.rb +4 -0
  31. data/lib/rtsp.rb +6 -0
  32. data/rtsp.gemspec +44 -0
  33. data/spec/rtsp/client_spec.rb +326 -0
  34. data/spec/rtsp/helpers_spec.rb +53 -0
  35. data/spec/rtsp/message_spec.rb +420 -0
  36. data/spec/rtsp/response_spec.rb +306 -0
  37. data/spec/rtsp/transport_parser_spec.rb +137 -0
  38. data/spec/rtsp_spec.rb +27 -0
  39. data/spec/spec_helper.rb +88 -0
  40. data/spec/support/fake_rtsp_server.rb +123 -0
  41. data/tasks/roodi.rake +9 -0
  42. data/tasks/roodi_config.yaml +14 -0
  43. data/tasks/stats.rake +12 -0
  44. metadata +280 -0
@@ -0,0 +1,306 @@
1
+ require_relative '../spec_helper'
2
+ require 'rtsp/response'
3
+
4
+ describe RTSP::Response do
5
+ describe "#initialize" do
6
+ it "expects a non-nil string on" do
7
+ expect { RTSP::Response.new(nil) }.to raise_exception RTSP::Error
8
+ end
9
+
10
+ it "expects a non-empty string on" do
11
+ expect { RTSP::Response.new("") }.to raise_exception RTSP::Error
12
+ end
13
+ end
14
+
15
+ describe "#parse_head" do
16
+ let(:head) do
17
+ head = double "head"
18
+ head.stub(:split).and_return(['', session_line])
19
+ head.stub(:each_with_index).and_yield
20
+
21
+ head
22
+ end
23
+
24
+ context "Session header contains session-id and timeout" do
25
+ let(:session_line) { "Session: 118;timeout=49" }
26
+ subject { RTSP::Response.new SETUP_RESPONSE_WITH_SESSION_TIMEOUT }
27
+
28
+ it "creates a :session reader with value being a Hash with key/value" do
29
+ subject.stub(:extract_status_line)
30
+ subject.should_receive(:create_reader).with("session",
31
+ { session_id: 118, timeout: 49 })
32
+ subject.parse_head(head)
33
+ end
34
+ end
35
+
36
+ context "Session header contains just session-id" do
37
+ let(:session_line) { "Session: 118" }
38
+ subject { RTSP::Response.new SETUP_RESPONSE_WITH_SESSION_TIMEOUT }
39
+
40
+ it "creates a :session reader with value being a Hash with key/value" do
41
+ subject.stub(:extract_status_line)
42
+ subject.should_receive(:create_reader).with("session",
43
+ { session_id: 118 })
44
+ subject.parse_head(head)
45
+ end
46
+ end
47
+
48
+ subject { RTSP::Response.new OPTIONS_RESPONSE }
49
+
50
+ it "raises when RTSP version is corrupted" do
51
+ expect { subject.parse_head "RTSP/ 200 OK\r\n" }.to raise_error RTSP::Error
52
+ end
53
+
54
+ it "raises when the response code is corrupted" do
55
+ expect { subject.parse_head "RTSP/1.0 2 OK\r\n" }.to raise_error RTSP::Error
56
+ end
57
+
58
+ it "raises when the response message is corrupted" do
59
+ expect { subject.parse_head "RTSP/1.0 200 \r\n" }.to raise_error RTSP::Error
60
+ end
61
+ end
62
+
63
+ describe "#parse_body" do
64
+ it "returns an SDP::Description when @content_type is 'application/sdp" do
65
+ response = RTSP::Response.new DESCRIBE_RESPONSE
66
+ sdp = SDP::Description.new
67
+ sdp.username = "me"
68
+ sdp.id = 12345
69
+ sdp.version = 12345
70
+ sdp.network_type = "IN"
71
+ sdp.address_type = "IP4"
72
+ body = response.parse_body sdp.to_s
73
+ body.class.should == SDP::Description
74
+ end
75
+
76
+ it "returns the text that was passed to it but with line feeds removed" do
77
+ response = RTSP::Response.new OPTIONS_RESPONSE
78
+ string = "hi\r\nguys\r\n\r\n"
79
+ body = response.parse_body string
80
+ body.class.should == String
81
+ body.should == string
82
+ end
83
+ end
84
+
85
+ describe "#to_s" do
86
+ it "returns the text that was passed in" do
87
+ response = RTSP::Response.new OPTIONS_RESPONSE
88
+ response.to_s.should == OPTIONS_RESPONSE
89
+ end
90
+ end
91
+
92
+ describe "#inspect" do
93
+ before do
94
+ @response = RTSP::Response.new OPTIONS_RESPONSE
95
+ end
96
+
97
+ it "contains the class name and object ID first" do
98
+ @response.inspect.should match(/^#<RTSP::Response:\d+/)
99
+ end
100
+
101
+ it "begins with <# and ends with >" do
102
+ @response.inspect.should match(/^#<.*>$/)
103
+ end
104
+ end
105
+
106
+ context "options" do
107
+ before do
108
+ @response = RTSP::Response.new OPTIONS_RESPONSE
109
+ end
110
+
111
+ it "returns a 200 code" do
112
+ @response.code.should == 200
113
+ end
114
+
115
+ it "returns 'OK' message" do
116
+ @response.message.should == 'OK'
117
+ end
118
+
119
+ it "returns the date header" do
120
+ @response.date.should == 'Fri, Jan 28 2011 01:14:42 GMT'
121
+ end
122
+
123
+ it "returns the supported methods in the Public header" do
124
+ @response.public.should == 'OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE'
125
+ end
126
+ end
127
+
128
+ context "describe" do
129
+ before do
130
+ @response = RTSP::Response.new DESCRIBE_RESPONSE
131
+ end
132
+
133
+ it "returns a 200 code" do
134
+ @response.code.should == 200
135
+ end
136
+
137
+ it "returns 'OK' message" do
138
+ @response.message.should == "OK"
139
+ end
140
+
141
+ it "returns all header fields" do
142
+ @response.server.should == "DSS/5.5 (Build/489.7; Platform/Linux; Release/Darwin; )"
143
+ @response.cseq.should == 1
144
+ @response.cache_control.should == "no-cache"
145
+ @response.content_length.should == 406
146
+ @response.date.should == "Sun, 23 Jan 2011 00:36:45 GMT"
147
+ @response.expires.should == "Sun, 23 Jan 2011 00:36:45 GMT"
148
+ @response.content_type.should == "application/sdp"
149
+ @response.x_accept_retransmit.should == "our-retransmit"
150
+ @response.x_accept_dynamic_rate.should == 1
151
+ @response.content_base.should == "rtsp://64.202.98.91:554/gs.sdp/"
152
+ end
153
+
154
+ it "body is a parsed SDP::Description" do
155
+ @response.body.should be_kind_of SDP::Description
156
+ sdp_info = @response.body
157
+ sdp_info.protocol_version.should == "0"
158
+ sdp_info.name.should == "Groove Salad from SomaFM [aacPlus]"
159
+ end
160
+ end
161
+
162
+ context "setup" do
163
+ before do
164
+ @response = RTSP::Response.new SETUP_RESPONSE
165
+ end
166
+
167
+ it "returns a 200 code" do
168
+ @response.code.should == 200
169
+ end
170
+
171
+ it "returns 'OK' message" do
172
+ @response.message.should == 'OK'
173
+ end
174
+
175
+ it "returns the date header" do
176
+ @response.date.should == 'Fri, Jan 28 2011 01:14:42 GMT'
177
+ end
178
+
179
+ it "returns the supported transport" do
180
+ @response.transport.should == 'RTP/AVP;unicast;destination=10.221.222.186;source=10.221.222.235;client_port=9000-9001;server_port=6700-6701'
181
+ end
182
+
183
+ it "returns the session" do
184
+ @response.session[:session_id].should == 118
185
+ end
186
+ end
187
+
188
+ context "play" do
189
+ before do
190
+ @response = RTSP::Response.new PLAY_RESPONSE
191
+ end
192
+
193
+ it "returns a 200 code" do
194
+ @response.code.should == 200
195
+ end
196
+
197
+ it "returns 'OK' message" do
198
+ @response.message.should == 'OK'
199
+ end
200
+
201
+ it "returns the date header" do
202
+ @response.date.should == 'Fri, Jan 28 2011 01:14:42 GMT'
203
+ end
204
+
205
+ it "returns the supported range" do
206
+ @response.range.should == 'npt=0.000-'
207
+ end
208
+
209
+ it "returns the session" do
210
+ @response.session[:session_id].should == 118
211
+ end
212
+
213
+ it "returns the rtp_info" do
214
+ @response.rtp_info.should == 'url=rtsp://10.221.222.235/stream1/track1;seq=17320;rtptime=400880602'
215
+ end
216
+ end
217
+
218
+ context "teardown" do
219
+ before do
220
+ @response = RTSP::Response.new TEARDOWN_RESPONSE
221
+ end
222
+
223
+ it "returns a 200 code" do
224
+ @response.code.should == 200
225
+ end
226
+
227
+ it "returns 'OK' message" do
228
+ @response.message.should == 'OK'
229
+ end
230
+
231
+ it "returns the date header" do
232
+ @response.date.should == 'Fri, Jan 28 2011 01:14:47 GMT'
233
+ end
234
+ end
235
+
236
+ context "#parse_head" do
237
+ before do
238
+ @response = RTSP::Response.new OPTIONS_RESPONSE
239
+ end
240
+
241
+ it "extracts the RTSP version from the header" do
242
+ @response.rtsp_version.should == "1.0"
243
+ end
244
+
245
+ it "extracts the response code from the header as a Fixnum" do
246
+ @response.code.is_a?(Fixnum).should be_true
247
+ @response.code.should == 200
248
+ end
249
+
250
+ it "extracts the response message from the header" do
251
+ @response.message.should == "OK"
252
+ end
253
+
254
+ it "returns empty value string when header has no value" do
255
+ response = RTSP::Response.new NO_CSEQ_VALUE_RESPONSE
256
+ response.parse_head NO_CSEQ_VALUE_RESPONSE
257
+ response.instance_variable_get(:@cseq).should == ""
258
+ end
259
+ end
260
+
261
+ describe "#split_head_and_body_from" do
262
+ it "splits responses with headers and no body" do
263
+ response = RTSP::Response.new OPTIONS_RESPONSE
264
+ head_and_body = response.split_head_and_body_from OPTIONS_RESPONSE
265
+ head_and_body.first.should == %Q{RTSP/1.0 200 OK\r
266
+ CSeq: 1\r
267
+ Date: Fri, Jan 28 2011 01:14:42 GMT\r
268
+ Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE\r
269
+ }
270
+ end
271
+
272
+ it "splits responses with headers and body" do
273
+ response = RTSP::Response.new DESCRIBE_RESPONSE
274
+ head_and_body = response.split_head_and_body_from DESCRIBE_RESPONSE
275
+ head_and_body.first.should == %{RTSP/1.0 200 OK\r
276
+ Server: DSS/5.5 (Build/489.7; Platform/Linux; Release/Darwin; )\r
277
+ Cseq: 1\r
278
+ Cache-Control: no-cache\r
279
+ Content-length: 406\r
280
+ Date: Sun, 23 Jan 2011 00:36:45 GMT\r
281
+ Expires: Sun, 23 Jan 2011 00:36:45 GMT\r
282
+ Content-Type: application/sdp\r
283
+ x-Accept-Retransmit: our-retransmit\r
284
+ x-Accept-Dynamic-Rate: 1\r
285
+ Content-Base: rtsp://64.202.98.91:554/gs.sdp/}
286
+
287
+ head_and_body.last.should == %{\r
288
+ v=0\r
289
+ o=- 545877020 467920391 IN IP4 127.0.0.1\r
290
+ s=Groove Salad from SomaFM [aacPlus]\r
291
+ i=Downtempo Ambient Groove\r
292
+ c=IN IP4 0.0.0.0\r
293
+ t=0 0\r
294
+ a=x-qt-text-cmt:Orban Opticodec-PC\r
295
+ a=x-qt-text-nam:Groove Salad from SomaFM [aacPlus]\r
296
+ a=x-qt-text-inf:Downtempo Ambient Groove\r
297
+ a=control:*\r
298
+ m=audio 0 RTP/AVP 96\r
299
+ b=AS:48\r
300
+ a=rtpmap:96 MP4A-LATM/44100/2\r
301
+ a=fmtp:96 cpresent=0;config=400027200000\r
302
+ a=control:trackID=1\r
303
+ }
304
+ end
305
+ end
306
+ end
@@ -0,0 +1,137 @@
1
+ require_relative '../spec_helper'
2
+ require 'rtsp/transport_parser'
3
+
4
+ describe RTSP::TransportParser do
5
+ let(:result) { subject.parse transport }
6
+
7
+ describe :transport_specifier do
8
+ context "RTP/AVP" do
9
+ let(:transport) { "RTP/AVP" }
10
+ specify { result[:streaming_protocol].should == 'RTP' }
11
+ specify { result[:profile].should == 'AVP' }
12
+ end
13
+
14
+ context "RTP/AVP/TCP" do
15
+ let(:transport) { "RTP/AVP/TCP" }
16
+ specify { result[:streaming_protocol].should == 'RTP' }
17
+ specify { result[:profile].should == 'AVP' }
18
+ specify { result[:transport_protocol].should == 'TCP' }
19
+ end
20
+
21
+ context "rtp/avp/tcp" do
22
+ let(:transport) { "rtp/avp/tcp" }
23
+ specify { result[:streaming_protocol].should == 'rtp' }
24
+ specify { result[:profile].should == 'avp' }
25
+ specify { result[:transport_protocol].should == 'tcp' }
26
+ end
27
+ end
28
+
29
+ describe :broadcast_type do
30
+ context "RTP/AVP;unicast" do
31
+ let(:transport) { "RTP/AVP;unicast" }
32
+ specify { result[:broadcast_type].should == 'unicast' }
33
+ end
34
+
35
+ context "RTP/AVP;multicast" do
36
+ let(:transport) { "RTP/AVP;multicast" }
37
+ specify { result[:broadcast_type].should == 'multicast' }
38
+ end
39
+ end
40
+
41
+ describe :destination do
42
+ context "RTP/AVP;multicast;destination=224.2.0.1" do
43
+ let(:transport) { "RTP/AVP;multicast;destination=224.2.0.1" }
44
+ specify { result[:destination].should == '224.2.0.1' }
45
+ end
46
+ end
47
+
48
+ describe :source do
49
+ context "RTP/AVP;multicast;destination=22.2.0.1;source=10.0.0.10" do
50
+ let(:transport) { "RTP/AVP;multicast;destination=22.2.0.1;source=10.0.0.10" }
51
+ specify { result[:source].should == '10.0.0.10' }
52
+ end
53
+ end
54
+
55
+ describe :client_port do
56
+ context "RTP/AVP;unicast;client_port=9000-9001" do
57
+ let(:transport) { "RTP/AVP;unicast;client_port=9000-9001" }
58
+ specify { result[:client_port][:rtp].should == '9000' }
59
+ specify { result[:client_port][:rtcp].should == '9001' }
60
+ end
61
+ end
62
+
63
+ describe :server_port do
64
+ context "RTP/AVP/UCP;unicast;client_port=3058-3059;server_port=5002-5003" do
65
+ let(:transport) { "RTP/AVP/UCP;unicast;client_port=3058-3059;server_port=5002-5003" }
66
+ specify { result[:server_port][:rtp].should == "5002" }
67
+ specify { result[:server_port][:rtcp].should == "5003" }
68
+ end
69
+ end
70
+
71
+ describe :interleaved do
72
+ context "RTP/AVP/TCP;unicast;interleaved=0-1" do
73
+ let(:transport) { "RTP/AVP/TCP;unicast;interleaved=0-1" }
74
+ specify { result[:interleaved][:rtp_channel].should == '0' }
75
+ specify { result[:interleaved][:rtcp_channel].should == '1' }
76
+ end
77
+ end
78
+
79
+ describe :ttl do
80
+ context 'RTP/AVP;unicast;ttl=127' do
81
+ let(:transport) { 'RTP/AVP;unicast;ttl=127' }
82
+ specify { result[:ttl].should == "127" }
83
+ end
84
+ end
85
+
86
+ describe :port do
87
+ context 'RTP/AVP;unicast;port=1234' do
88
+ let(:transport) { 'RTP/AVP;unicast;port=1234' }
89
+ specify { result[:port][:rtp].should == "1234" }
90
+ specify { result[:port][:rtcp].should be_nil }
91
+ end
92
+
93
+ context 'RTP/AVP;unicast;port=1234-1235' do
94
+ let(:transport) { 'RTP/AVP;unicast;port=1234-1235' }
95
+ specify { result[:port][:rtp].should == "1234" }
96
+ specify { result[:port][:rtcp].should == "1235" }
97
+ end
98
+ end
99
+
100
+ describe :ssrc do
101
+ context 'RTP/AVP;unicast;ssrc=ABCD1234' do
102
+ let(:transport) { 'RTP/AVP;unicast;ssrc=ABCD1234' }
103
+ specify { result[:ssrc].should == "ABCD1234" }
104
+ end
105
+ end
106
+
107
+ describe :channel do
108
+ context 'RTP/AVP;unicast;channel=RTP' do
109
+ let(:transport) { 'RTP/AVP;unicast;channel=RTP' }
110
+ specify { result[:channel].should == "RTP" }
111
+ end
112
+ end
113
+
114
+ describe :address do
115
+ context 'RTP/AVP;unicast;address=192.168.14.18' do
116
+ let(:transport) { 'RTP/AVP;unicast;address=192.168.14.18' }
117
+ specify { result[:address].should == "192.168.14.18" }
118
+ end
119
+
120
+ context 'RTP/AVP;unicast;address=mycomputer.com' do
121
+ let(:transport) { 'RTP/AVP;unicast;address=mycomputer.com' }
122
+ specify { result[:address].should == "mycomputer.com" }
123
+ end
124
+ end
125
+
126
+ describe :mode do
127
+ context 'RTP/AVP;unicast;mode="PLAY"' do
128
+ let(:transport) { 'RTP/AVP;unicast;mode="PLAY"' }
129
+ specify { result[:mode].should == "PLAY" }
130
+ end
131
+
132
+ context 'with ttl=127;mode=RECORD' do
133
+ let(:transport) { 'RTP/AVP;unicast;mode=RECORD' }
134
+ specify { result[:mode].should == "RECORD" }
135
+ end
136
+ end
137
+ end
data/spec/rtsp_spec.rb ADDED
@@ -0,0 +1,27 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe Kernel do
4
+ def self.get_requires
5
+ Dir[File.dirname(__FILE__) + '/../lib/**/*.rb']
6
+ end
7
+
8
+ # Try to require each of the files in RTSP
9
+ get_requires.each do |r|
10
+ it "should require #{r}" do
11
+
12
+ # A require returns true if it was required, false if it had already been
13
+ # required, and nil if it couldn't require.
14
+ Kernel.require(r.to_s).should_not be_nil
15
+ end
16
+ end
17
+ end
18
+
19
+ describe RTSP do
20
+ it "should have a VERSION constant" do
21
+ RTSP.const_defined?('VERSION').should be_true
22
+ end
23
+
24
+ it "has version 0.4.0" do
25
+ RTSP::VERSION.should == '0.4.0'
26
+ end
27
+ end
@@ -0,0 +1,88 @@
1
+ require 'simplecov'
2
+
3
+ SimpleCov.start do
4
+ add_filter "/spec/"
5
+ end
6
+
7
+ require 'rspec'
8
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
9
+ require 'rtsp'
10
+
11
+ OPTIONS_RESPONSE = %Q{RTSP/1.0 200 OK\r
12
+ CSeq: 1\r
13
+ Date: Fri, Jan 28 2011 01:14:42 GMT\r
14
+ Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE\r
15
+ }
16
+
17
+ DESCRIBE_RESPONSE = %{RTSP/1.0 200 OK\r
18
+ Server: DSS/5.5 (Build/489.7; Platform/Linux; Release/Darwin; )\r
19
+ Cseq: 1\r
20
+ Cache-Control: no-cache\r
21
+ Content-length: 406\r
22
+ Date: Sun, 23 Jan 2011 00:36:45 GMT\r
23
+ Expires: Sun, 23 Jan 2011 00:36:45 GMT\r
24
+ Content-Type: application/sdp\r
25
+ x-Accept-Retransmit: our-retransmit\r
26
+ x-Accept-Dynamic-Rate: 1\r
27
+ Content-Base: rtsp://64.202.98.91:554/gs.sdp/\r
28
+ \r\n\r
29
+ v=0\r
30
+ o=- 545877020 467920391 IN IP4 127.0.0.1\r
31
+ s=Groove Salad from SomaFM [aacPlus]\r
32
+ i=Downtempo Ambient Groove\r
33
+ c=IN IP4 0.0.0.0\r
34
+ t=0 0\r
35
+ a=x-qt-text-cmt:Orban Opticodec-PC\r
36
+ a=x-qt-text-nam:Groove Salad from SomaFM [aacPlus]\r
37
+ a=x-qt-text-inf:Downtempo Ambient Groove\r
38
+ a=control:*\r
39
+ m=audio 0 RTP/AVP 96\r
40
+ b=AS:48\r
41
+ a=rtpmap:96 MP4A-LATM/44100/2\r
42
+ a=fmtp:96 cpresent=0;config=400027200000\r
43
+ a=control:trackID=1\r
44
+ }
45
+
46
+ SETUP_RESPONSE = %Q{RTSP/1.0 200 OK\r
47
+ CSeq: 1\r
48
+ Date: Fri, Jan 28 2011 01:14:42 GMT\r
49
+ Transport: RTP/AVP;unicast;destination=10.221.222.186;source=10.221.222.235;client_port=9000-9001;server_port=6700-6701\r
50
+ Session: 118\r
51
+ \r}
52
+
53
+ SETUP_RESPONSE_WITH_SESSION_TIMEOUT = %Q{RTSP/1.0 200 OK\r
54
+ CSeq: 1\r
55
+ Date: Fri, Jan 28 2011 01:14:42 GMT\r
56
+ Transport: RTP/AVP;unicast;destination=10.221.222.186;source=10.221.222.235;client_port=9000-9001;server_port=6700-6701\r
57
+ Session: 118;timeout=49\r
58
+ \r}
59
+
60
+ PLAY_RESPONSE = %Q{RTSP/1.0 200 OK\r
61
+ CSeq: 1\r
62
+ Date: Fri, Jan 28 2011 01:14:42 GMT\r
63
+ Range: npt=0.000-\r
64
+ Session: 118\r
65
+ RTP-Info: url=rtsp://10.221.222.235/stream1/track1;seq=17320;rtptime=400880602\r
66
+ \r}
67
+
68
+ TEARDOWN_RESPONSE = %Q{RTSP/1.0 200 OK\r
69
+ CSeq: 1\r
70
+ Date: Fri, Jan 28 2011 01:14:47 GMT\r
71
+ \r}
72
+
73
+ NO_CSEQ_VALUE_RESPONSE = %Q{ RTSP/1.0 460 Only Aggregate Option Allowed\r
74
+ Server: DSS/5.5 (Build/489.7; Platform/Linux; Release/Darwin; )\r
75
+ Cseq: \r
76
+ Connection: Close\r
77
+ \r}
78
+
79
+
80
+ # Define #describe so when RTSP::Message calls #method_missing, RSpec doesn't
81
+ # get in the way (and cause tests to fail).
82
+ module RTSP
83
+ class Message
84
+ def self.describe request_uri
85
+ self.new(:describe, request_uri)
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,123 @@
1
+ require 'time'
2
+
3
+ class FakeRTSPServer
4
+ def initialize(*args)
5
+ end
6
+
7
+ def send(*args)
8
+ message = args.first
9
+ message =~ /^(\w+) .+CSeq: (\S+)/m
10
+ @message_type = $1.downcase
11
+ @cseq = $2
12
+ @session = 1234567890
13
+ end
14
+
15
+ def recvfrom(size)
16
+ response = eval @message_type
17
+ [response]
18
+ end
19
+
20
+ def options
21
+ message = "RTSP/1.0 200 OK\r\n"
22
+ message << "CSeq: #{@cseq}\r\n"
23
+ message << "Date: #{Time.now.httpdate}\r\n"
24
+ message << "Public: DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE\r\n"
25
+ message << "\r\n"
26
+ end
27
+
28
+ def describe
29
+ message = "RTSP/1.0 200 OK\r\n"
30
+ message << "CSeq: #{@cseq}\r\n"
31
+ message << "Server: DSS/5.5 (Build/489.7; Platform/Linux; Release/Darwin; )\r\n"
32
+ message << "Cache-Control: no-cache\r\n"
33
+ message << "Content-length: 380\r\n"
34
+ message << "Date: Tue, 15 Mar 2011 01:28:57 GMT\r\n"
35
+ message << "Expires: Tue, 15 Mar 2011 01:28:57 GMT\r\n"
36
+ message << "Content-Type: application/sdp\r\n"
37
+ message << "x-Accept-Retransmit: our-retransmit\r\n"
38
+ message << "x-Accept-Dynamic-Rate: 1\r\n"
39
+ message << "Content-Base: rtsp://64.202.98.91:554/sa.sdp/\r\n"
40
+ message << "\r\n"
41
+ message << "v=\r\n"
42
+ message << "o=- 1905836198 1274535354 IN IP4 127.0.0.1\r\n"
43
+ message << "s=Secret Agent from SomaFM\r\n"
44
+ message << "i=Downtempo Spy Lounge\r\n"
45
+ message << "c=IN IP4 0.0.0.0\r\n"
46
+ message << "t=0 0\r\n"
47
+ message << "a=x-qt-text-cmt:Orban Opticodec-PC\r\n"
48
+ message << "a=x-qt-text-nam:Secret Agent from SomaFM\r\n"
49
+ message << "a=x-qt-text-inf:Downtempo Spy Lounge\r\n"
50
+ message << "a=control:*\r\n"
51
+ message << "m=audio 0 RTP/AVP 96\r\n"
52
+ message << "b=AS:40\r\n"
53
+ message << "a=rtpmap:96 MP4A-LATM/44100/2\r\n"
54
+ message << "a=fmtp:96 cpresent=0;config=400027200000\r\n"
55
+ message << "a=control:trackID=1\r\n"
56
+ message << "\r\n"
57
+ end
58
+
59
+ def announce
60
+ message = "RTSP/1.0 200 OK\r\n"
61
+ message << "CSeq: #{@cseq}\r\n"
62
+ message << "\r\n"
63
+ end
64
+
65
+ def setup
66
+ %Q{RTSP/1.0 200 OK\r
67
+ CSeq: #{@cseq}\r
68
+ Date: #{Time.now.httpdate}\r
69
+ Transport: RTP/AVP;unicast;destination=10.221.222.186;source=10.221.222.235;client_port=9000-9001;server_port=6700-6701\r
70
+ Session: #{@session}\r
71
+ \r\n}
72
+ end
73
+
74
+ def play
75
+ %Q{RTSP/1.0 200 OK\r
76
+ CSeq: #{@cseq}\r
77
+ Date: #{Time.now.httpdate}\r
78
+ Range: npt=0.000-\r
79
+ Session: #{@session}\r
80
+ RTP-Info: url=rtsp://10.221.222.235/stream1/track1;seq=17320;rtptime=400880602\r
81
+ \r\n}
82
+ end
83
+
84
+ def pause
85
+ %Q{RTSP/1.0 200 OK\r
86
+ CSeq: #{@cseq}\r
87
+ Date: #{Time.now.httpdate}\r
88
+ \r\n}
89
+ end
90
+
91
+ def teardown
92
+ %Q{RTSP/1.0 200 OK\r
93
+ CSeq: #{@cseq}\r
94
+ Server: DSS/5.5 (Build/489.7; Platform/Linux; Release/Darwin; )\r
95
+ Session: #{@session}\r
96
+ Connection: Close\r
97
+ \r\n}
98
+ end
99
+
100
+ def get_parameter
101
+ %Q{RTSP/1.0 200 OK\r
102
+ CSeq: #{@cseq}\r
103
+ Content-Length: 8\r
104
+ \r
105
+ response\r
106
+ \r\n}
107
+ end
108
+
109
+ def set_parameter
110
+ %Q{RTSP/1.0 200 OK\r
111
+ CSeq: #{@cseq}\r
112
+ Content-Length: 8\r
113
+ \r
114
+ response\r
115
+ \r\n}
116
+ end
117
+
118
+ def record
119
+ %Q{RTSP/1.0 200 OK\r
120
+ CSeq: #{@cseq}\r
121
+ \r\n}
122
+ end
123
+ end
data/tasks/roodi.rake ADDED
@@ -0,0 +1,9 @@
1
+ require 'rake/tasklib' # roodi_task fails without this.
2
+ require 'roodi'
3
+ require 'roodi_task'
4
+
5
+ RoodiTask.new do |t|
6
+ t.config = 'tasks/roodi_config.yaml'
7
+ t.patterns = Dir.glob("{features,lib,spec}/**/*.rb")
8
+ t.verbose = true
9
+ 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 }