rtsp_server 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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 +303 -0
  27. data/lib/rtsp/socat_streaming.rb +309 -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 +42 -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 }