rtsp 0.0.1.alpha → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/.document +3 -0
  2. data/.infinity_test +1 -1
  3. data/.yardopts +4 -0
  4. data/ChangeLog.rdoc +9 -0
  5. data/Gemfile +15 -6
  6. data/Gemfile.lock +78 -40
  7. data/LICENSE.rdoc +20 -0
  8. data/PostInstall.txt +0 -3
  9. data/README.rdoc +85 -36
  10. data/Rakefile +33 -49
  11. data/bin/rtsp_client +129 -0
  12. data/features/client_changes_state.feature +58 -0
  13. data/features/client_requests.feature +27 -0
  14. data/features/control_streams_as_client.feature +26 -0
  15. data/features/step_definitions/client_changes_state_steps.rb +46 -0
  16. data/features/step_definitions/client_requests_steps.rb +74 -0
  17. data/features/step_definitions/control_streams_as_client_steps.rb +34 -0
  18. data/features/support/env.rb +31 -29
  19. data/features/support/hooks.rb +3 -0
  20. data/gemspec.yml +30 -0
  21. data/lib/ext/logger.rb +8 -0
  22. data/lib/rtsp.rb +3 -6
  23. data/lib/rtsp/capturer.rb +105 -0
  24. data/lib/rtsp/client.rb +446 -204
  25. data/lib/rtsp/error.rb +6 -0
  26. data/lib/rtsp/global.rb +63 -0
  27. data/lib/rtsp/helpers.rb +28 -0
  28. data/lib/rtsp/message.rb +270 -0
  29. data/lib/rtsp/response.rb +89 -29
  30. data/lib/rtsp/transport_parser.rb +64 -0
  31. data/lib/rtsp/version.rb +4 -0
  32. data/nsm_test.rb +26 -0
  33. data/rtsp.gemspec +284 -0
  34. data/sarix_test.rb +23 -0
  35. data/soma_test.rb +39 -0
  36. data/spec/rtsp/client_spec.rb +302 -27
  37. data/spec/rtsp/helpers_spec.rb +53 -0
  38. data/spec/rtsp/message_spec.rb +420 -0
  39. data/spec/rtsp/response_spec.rb +144 -58
  40. data/spec/rtsp/transport_parser_spec.rb +54 -0
  41. data/spec/rtsp_spec.rb +3 -3
  42. data/spec/spec_helper.rb +66 -7
  43. data/spec/support/fake_rtsp_server.rb +123 -0
  44. data/tasks/metrics.rake +27 -0
  45. data/tasks/roodi_config.yml +14 -0
  46. data/tasks/stats.rake +12 -0
  47. metadata +174 -183
  48. data/.autotest +0 -23
  49. data/History.txt +0 -4
  50. data/Manifest.txt +0 -26
  51. data/bin/rtsp +0 -121
  52. data/features/step_definitions/stream_steps.rb +0 -50
  53. data/features/stream.feature +0 -17
  54. data/features/support/common.rb +0 -1
  55. data/features/support/world.rb +0 -1
  56. data/lib/rtsp/request_messages.rb +0 -104
  57. data/lib/rtsp/status_code.rb +0 -7
@@ -1,38 +1,313 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ require 'sdp'
2
+ require_relative '../spec_helper'
2
3
  require 'rtsp/client'
4
+ require 'support/fake_rtsp_server'
3
5
 
4
6
  describe RTSP::Client do
5
- context "parses the URI on init" do
6
- it "with scheme, IP, port, and path" do
7
- @rtsp_client = RTSP::Client.new "rtsp://64.202.98.91:554/sa.sdp"
8
- @rtsp_client.server_uri.scheme.should == "rtsp"
9
- @rtsp_client.server_uri.host.should == "64.202.98.91"
10
- @rtsp_client.server_uri.port.should == 554
11
- @rtsp_client.server_uri.path.should == "/sa.sdp"
7
+ def setup_client_at(url)
8
+ fake_rtsp_server = FakeRTSPServer.new
9
+ mock_logger = double 'MockLogger', :send => nil
10
+
11
+ client = RTSP::Client.new(url) do |connection|
12
+ connection.socket = fake_rtsp_server
13
+ end
14
+
15
+ RTSP::Client.reset_config!
16
+ RTSP::Client.configure { |config| config.log = false }
17
+ client.logger = mock_logger
18
+
19
+ client
20
+ end
21
+
22
+ describe "#initialize" do
23
+ before :each do
24
+ mock_socket = double 'MockSocket'
25
+ @client = RTSP::Client.new("rtsp://localhost") do |connection|
26
+ connection.socket = mock_socket
27
+ end
28
+ end
29
+
30
+ it "sets @cseq to 1" do
31
+ @client.instance_variable_get(:@cseq).should == 1
32
+ end
33
+
34
+ it "sets @session_state to :init" do
35
+ @client.instance_variable_get(:@session_state).should == :init
36
+ end
37
+
38
+ it "sets @server_uri to be a URI containing the first init param + 554" do
39
+ @client.instance_variable_get(:@server_uri).should be_a(URI)
40
+ @client.instance_variable_get(:@server_uri).to_s.should ==
41
+ "rtsp://localhost:554"
42
+ end
43
+ end
44
+
45
+ describe ".configure" do
46
+ around do |example|
47
+ RTSP::Client.reset_config!
48
+ example.run
49
+ RTSP::Client.reset_config!
50
+ RTSP::Client.log = false
51
+ end
52
+
53
+ before :each do
54
+ mock_socket = double 'MockSocket'
55
+
56
+ @client = RTSP::Client.new("rtsp://localhost") do |connection|
57
+ connection.socket = mock_socket
58
+ end
59
+ end
60
+
61
+ describe "log" do
62
+ it "should default to true" do
63
+ RTSP::Client.log?.should be_true
64
+ end
65
+
66
+ it "should set whether to log RTSP requests/responses" do
67
+ RTSP::Client.configure { |config| config.log = false }
68
+ RTSP::Client.log?.should be_false
69
+ end
70
+ end
71
+
72
+ describe "logger" do
73
+ it "should set the logger to use" do
74
+ MyLogger = Class.new
75
+ RTSP::Client.configure { |config| config.logger = MyLogger }
76
+ RTSP::Client.logger.should == MyLogger
77
+ end
78
+
79
+ it "should default to Logger writing to STDOUT" do
80
+ RTSP::Client.logger.should be_a(Logger)
81
+ end
82
+ end
83
+
84
+ describe "log_level" do
85
+ it "should default to :debug" do
86
+ RTSP::Client.log_level.should == :debug
87
+ end
88
+
89
+ it "should set the log level to use" do
90
+ RTSP::Client.configure { |config| config.log_level = :info }
91
+ RTSP::Client.log_level.should == :info
92
+ end
93
+ end
94
+ end
95
+
96
+ it "handles empty non-existent CSeq header" do
97
+ pending
98
+ end
99
+
100
+ context "#server_url" do
101
+ before :each do
102
+ @client = setup_client_at "rtsp://localhost"
103
+ end
104
+
105
+ it "allows for changing the server URL on the fly" do
106
+ @client.server_uri.to_s.should == "rtsp://localhost:554"
107
+ @client.server_url = "rtsp://localhost:8080"
108
+ @client.server_uri.to_s.should == "rtsp://localhost:8080"
109
+ end
110
+
111
+ it "raises when passing in something other than a String" do
112
+ @client.server_uri.to_s.should == "rtsp://localhost:554"
113
+ lambda { @client.server_url = [] }.should raise_error
114
+ end
115
+ end
116
+
117
+ describe "#options" do
118
+ before :each do
119
+ @client = setup_client_at "rtsp://localhost"
120
+ end
121
+
122
+ it "extracts the server's supported methods" do
123
+ @client.options
124
+ @client.supported_methods.should ==
125
+ [:describe, :setup, :teardown, :play, :pause]
126
+ end
127
+
128
+ it "returns a Response" do
129
+ response = @client.options
130
+ response.is_a?(RTSP::Response).should be_true
131
+ end
132
+ end
133
+
134
+ describe "#describe" do
135
+ before do
136
+ @client = setup_client_at "rtsp://localhost"
137
+ @response = @client.describe
138
+ end
139
+
140
+ it "extracts the aggregate control track" do
141
+ @client.aggregate_control_track.should == "rtsp://64.202.98.91:554/sa.sdp/"
142
+ end
143
+
144
+ it "extracts the media control tracks" do
145
+ @client.media_control_tracks.should == ["rtsp://64.202.98.91:554/sa.sdp/trackID=1"]
146
+ end
147
+
148
+ it "extracts the SDP object" do
149
+ @client.instance_variable_get(:@session_description).should ==
150
+ @response.body
151
+ end
152
+
153
+ it "extracts the Content-Base header" do
154
+ @client.instance_variable_get(:@content_base).should ==
155
+ URI.parse("rtsp://64.202.98.91:554/sa.sdp/")
12
156
  end
13
157
 
14
- it "with scheme, IP, path; port defaults to 554" do
15
- @rtsp_client = RTSP::Client.new "rtsp://64.202.98.91/sa.sdp"
16
- @rtsp_client.server_uri.scheme.should == "rtsp"
17
- @rtsp_client.server_uri.host.should == "64.202.98.91"
18
- @rtsp_client.server_uri.port.should == 554
19
- @rtsp_client.server_uri.path.should == "/sa.sdp"
158
+ it "returns a Response" do
159
+ @response.is_a?(RTSP::Response).should be_true
160
+ end
161
+ end
162
+
163
+ describe "#announce" do
164
+ it "returns a Response" do
165
+ client = setup_client_at "rtsp://localhost"
166
+ sdp = SDP::Description.new
167
+ client.setup("rtsp://localhost/another_track")
168
+ response = client.announce("rtsp://localhost/another_track", sdp)
169
+ response.is_a?(RTSP::Response).should be_true
170
+ end
171
+ end
172
+
173
+ describe "#setup" do
174
+ before :each do
175
+ @client = setup_client_at "rtsp://localhost"
176
+ end
177
+
178
+ it "extracts the session number" do
179
+ @client.session.should be_zero
180
+ @client.setup("rtsp://localhost/some_track")
181
+ @client.session.should == 1234567890
182
+ end
183
+
184
+ it "changes the session_state to :ready" do
185
+ @client.setup("rtsp://localhost/some_track")
186
+ @client.session_state.should == :ready
187
+ end
188
+
189
+ it "extracts the transport header info" do
190
+ @client.instance_variable_get(:@transport).should be_nil
191
+ @client.setup("rtsp://localhost/some_track")
192
+ @client.instance_variable_get(:@transport).should_not be_nil
193
+ end
194
+
195
+ it "returns a Response" do
196
+ response = @client.setup("rtsp://localhost/some_track")
197
+ response.is_a?(RTSP::Response).should be_true
198
+ end
199
+ end
200
+
201
+ describe "#play" do
202
+ before :each do
203
+ @client = setup_client_at "rtsp://localhost"
204
+ end
205
+
206
+ it "changes the session_state to :playing" do
207
+ @client.setup("rtsp://localhost/some_track")
208
+ @client.play("rtsp://localhost/some_track")
209
+ @client.session_state.should == :playing
210
+ end
211
+
212
+ it "returns a Response" do
213
+ @client.setup("rtsp://localhost/some_track")
214
+ response = @client.play("rtsp://localhost/some_track")
215
+ response.is_a?(RTSP::Response).should be_true
216
+ end
217
+ end
218
+
219
+ describe "#pause" do
220
+ before :each do
221
+ @client = setup_client_at "rtsp://localhost"
222
+ @client.setup("rtsp://localhost/some_track")
223
+ end
224
+
225
+ it "changes the session_state from :playing to :ready" do
226
+ @client.play("rtsp://localhost/some_track")
227
+ @client.pause("rtsp://localhost/some_track")
228
+ @client.session_state.should == :ready
229
+ end
230
+
231
+ it "changes the session_state from :recording to :ready" do
232
+ @client.record("rtsp://localhost/some_track")
233
+ @client.pause("rtsp://localhost/some_track")
234
+ @client.session_state.should == :ready
235
+ end
236
+
237
+ it "returns a Response" do
238
+ response = @client.pause("rtsp://localhost/some_track")
239
+ response.is_a?(RTSP::Response).should be_true
240
+ end
241
+ end
242
+
243
+ describe "#teardown" do
244
+ before :each do
245
+ @client = setup_client_at "rtsp://localhost"
246
+ @client.setup("rtsp://localhost/some_track")
247
+ end
248
+
249
+ it "changes the session_state to :init" do
250
+ @client.session_state.should_not == :init
251
+ @client.teardown("rtsp://localhost/some_track")
252
+ @client.session_state.should == :init
253
+ end
254
+
255
+ it "changes the session back to 0" do
256
+ @client.session.should_not be_zero
257
+ @client.teardown("rtsp://localhost/some_track")
258
+ @client.session.should be_zero
259
+ end
260
+
261
+ it "returns a Response" do
262
+ response = @client.teardown("rtsp://localhost/some_track")
263
+ response.is_a?(RTSP::Response).should be_true
264
+ end
265
+ end
266
+
267
+ describe "#get_parameter" do
268
+ before :each do
269
+ @client = setup_client_at "rtsp://localhost"
20
270
  end
21
271
 
22
- it "with IP, path; port defaults to 554; scheme defaults to 'rtsp'" do
23
- @rtsp_client = RTSP::Client.new "64.202.98.91/sa.sdp"
24
- @rtsp_client.server_uri.scheme.should == "rtsp"
25
- @rtsp_client.server_uri.host.should == "64.202.98.91"
26
- @rtsp_client.server_uri.port.should == 554
27
- @rtsp_client.server_uri.path.should == "/sa.sdp"
272
+ it "returns a Response" do
273
+ response = @client.get_parameter("rtsp://localhost/some_track", "ping!")
274
+ response.is_a?(RTSP::Response).should be_true
28
275
  end
276
+ end
277
+
278
+ describe "#set_parameter" do
279
+ before :each do
280
+ @client = setup_client_at "rtsp://localhost"
281
+ end
282
+
283
+ it "returns a Response" do
284
+ response = @client.set_parameter("rtsp://localhost/some_track", "ping!")
285
+ response.is_a?(RTSP::Response).should be_true
286
+ end
287
+ end
288
+
289
+ describe "#record" do
290
+ before :each do
291
+ @client = setup_client_at "rtsp://localhost"
292
+ @client.setup("rtsp://localhost/some_track")
293
+ end
294
+
295
+ it "returns a Response" do
296
+ response = @client.record("rtsp://localhost/some_track")
297
+ response.is_a?(RTSP::Response).should be_true
298
+ end
299
+
300
+ it "changes the session_state to :recording" do
301
+ @client.session_state.should == :ready
302
+ @client.record("rtsp://localhost/some_track")
303
+ @client.session_state.should == :recording
304
+ end
305
+ end
29
306
 
30
- it "with scheme, IP, port" do
31
- @rtsp_client = RTSP::Client.new "rtsp://64.202.98.91:554"
32
- @rtsp_client.server_uri.scheme.should == "rtsp"
33
- @rtsp_client.server_uri.host.should == "64.202.98.91"
34
- @rtsp_client.server_uri.port.should == 554
35
- @rtsp_client.server_uri.path.should == ""
307
+ describe "#send_message" do
308
+ it "raises if the send takes longer than the timeout" do
309
+ pending "until I figure out how to test the time out raises"
310
+ @client = setup_client_at "rtsp://localhost"
36
311
  end
37
312
  end
38
- end
313
+ end
@@ -0,0 +1,53 @@
1
+ require_relative '../spec_helper'
2
+ require 'rtsp/helpers'
3
+
4
+ class HelperTest
5
+ include RTSP::Helpers
6
+ end
7
+
8
+ describe RTSP::Helpers do
9
+ describe "#build_resource_uri_from" do
10
+ before do
11
+ @my_object = HelperTest.new
12
+ end
13
+
14
+ context "parses the resource URL to a URI" do
15
+ it "with scheme, IP, path; port defaults to 554" do
16
+ uri = @my_object.build_resource_uri_from "rtsp://64.202.98.91/sa.sdp"
17
+ uri.scheme.should == "rtsp"
18
+ uri.host.should == "64.202.98.91"
19
+ uri.port.should == 554
20
+ uri.path.should == "/sa.sdp"
21
+ end
22
+
23
+ it "with IP, path; port defaults to 554; scheme defaults to 'rtsp'" do
24
+ uri = @my_object.build_resource_uri_from "64.202.98.91/sa.sdp"
25
+ uri.scheme.should == "rtsp"
26
+ uri.host.should == "64.202.98.91"
27
+ uri.port.should == 554
28
+ uri.path.should == "/sa.sdp"
29
+ end
30
+
31
+ it "with scheme, IP, port" do
32
+ uri = @my_object.build_resource_uri_from "rtsp://64.202.98.91"
33
+ uri.scheme.should == "rtsp"
34
+ uri.host.should == "64.202.98.91"
35
+ uri.port.should == 554
36
+ uri.path.should == ""
37
+ uri.to_s.should == "rtsp://64.202.98.91:554"
38
+ end
39
+
40
+ it "handles passing in a URI" do
41
+ uri = @my_object.build_resource_uri_from "rtsp://64.202.98.91"
42
+ lambda { @my_object.build_resource_uri_from uri
43
+ }.should raise_error
44
+ end
45
+
46
+ it "raises if not given a String" do
47
+ lambda do
48
+ @my_object.build_resource_uri_from URI.parse "rtsp://64.202.98.91"
49
+ end.should raise_exception RTSP::Error
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,420 @@
1
+ require 'sdp'
2
+ require_relative '../spec_helper'
3
+ require 'rtsp/message'
4
+
5
+ describe "RTSP::Message" do
6
+ before do
7
+ @stream = "rtsp://1.2.3.4/stream1"
8
+ end
9
+
10
+ it "raises if the header type isn't a Symbol" do
11
+ message = RTSP::Message.options(@stream)
12
+ lambda { message.header "hi", "everyone"
13
+ }.should raise_error RTSP::Error
14
+ end
15
+
16
+ it "adds a User-Agent header to every method" do
17
+ RTSP::Message.instance_variable_get(:@message_types).each do |method|
18
+ message = RTSP::Message.send(method, @stream)
19
+ message.to_s.should include "User-Agent: RubyRTSP/"
20
+ end
21
+ end
22
+
23
+ context "builds an OPTIONS string" do
24
+ it "with default sequence number" do
25
+ message = RTSP::Message.options(@stream)
26
+ message.to_s.should == "OPTIONS rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\nCSeq: 1\r\nUser-Agent: RubyRTSP/0.1.0 (Ruby #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL})\r\n\r\n"
27
+ end
28
+
29
+ it "with new sequence number" do
30
+ message = RTSP::Message.options(@stream)
31
+ message.header :cseq, 2345
32
+ message.to_s.should == "OPTIONS rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\nCSeq: 2345\r\nUser-Agent: RubyRTSP/0.1.0 (Ruby #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL})\r\n\r\n"
33
+ end
34
+ end
35
+
36
+ context "builds a DESCRIBE string" do
37
+ it "with default sequence and accept values" do
38
+ message = RTSP::Message.describe(@stream)
39
+ message.to_s.should match /^DESCRIBE rtsp:/
40
+ message.to_s.should include "DESCRIBE rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
41
+ message.to_s.should include "CSeq: 1\r\n"
42
+ message.to_s.should include "Accept: application/sdp\r\n"
43
+ message.to_s.should match /\r\n\r\n$/
44
+ end
45
+
46
+ it "with default sequence value" do
47
+ message = RTSP::Message.describe(@stream).with_headers({
48
+ accept: 'application/sdp, application/rtsl' })
49
+ message.to_s.should match /^DESCRIBE rtsp:/
50
+ message.to_s.should include "DESCRIBE rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
51
+ message.to_s.should include "CSeq: 1\r\n"
52
+ message.to_s.should include "Accept: application/sdp, application/rtsl\r\n"
53
+ message.to_s.should match /\r\n\r\n$/
54
+ end
55
+
56
+ it "with new sequence and accept values" do
57
+ message = RTSP::Message.describe(@stream).with_headers({
58
+ accept: 'application/sdp, application/rtsl',
59
+ cseq: 2345 })
60
+
61
+ message.to_s.should match /^DESCRIBE rtsp:/
62
+ message.to_s.should include "DESCRIBE rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
63
+ message.to_s.should include "CSeq: 2345\r\n"
64
+ message.to_s.should include "Accept: application/sdp, application/rtsl\r\n"
65
+ message.to_s.should match /\r\n\r\n$/
66
+ end
67
+ end
68
+
69
+ context "builds a ANNOUNCE string" do
70
+ it "with default sequence, content type, but no body" do
71
+ message = RTSP::Message.announce(@stream).with_headers({ session: 123456789 })
72
+
73
+ message.to_s.should match /^ANNOUNCE rtsp:/
74
+ message.to_s.should include "ANNOUNCE rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
75
+ message.to_s.should include "CSeq: 1\r\n"
76
+ message.to_s.should include "Session: 123456789\r\n"
77
+ message.to_s.should include "Content-Type: application/sdp\r\n"
78
+ message.to_s.should match /\r\n\r\n$/
79
+ end
80
+
81
+ it "with passed-in session and content type but no body" do
82
+ message = RTSP::Message.announce(@stream).with_headers({
83
+ session: 123456789,
84
+ content_type: 'application/sdp, application/rtsl'})
85
+
86
+ message.to_s.should match /^ANNOUNCE rtsp:/
87
+ message.to_s.should include "ANNOUNCE rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
88
+ message.to_s.should include "CSeq: 1\r\n"
89
+ message.to_s.should include "Session: 123456789\r\n"
90
+ message.to_s.should include "Content-Type: application/sdp, application/rtsl\r\n"
91
+ message.to_s.should match /\r\n\r\n$/
92
+ end
93
+
94
+ it "with passed-in sequence, session, content-type, but no body " do
95
+ message = RTSP::Message.announce(@stream).with_headers({
96
+ session: 123456789,
97
+ content_type: 'application/sdp, application/rtsl',
98
+ cseq: 2345})
99
+
100
+ message.to_s.should match /^ANNOUNCE rtsp:/
101
+ message.to_s.should include "ANNOUNCE rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
102
+ message.to_s.should include "CSeq: 2345\r\n"
103
+ message.to_s.should include "Session: 123456789\r\n"
104
+ message.to_s.should include "Content-Type: application/sdp, application/rtsl\r\n"
105
+ message.to_s.should match /\r\n\r\n$/
106
+ end
107
+
108
+ it "with passed-in sequence, session, content-type, and SDP body" do
109
+ sdp = SDP::Description.new
110
+ sdp.protocol_version = 1
111
+ sdp.username = 'bobo'
112
+
113
+ message = RTSP::Message.announce(@stream).with_headers({
114
+ session: 123456789,
115
+ content_type: 'application/sdp',
116
+ cseq: 2345 })
117
+ message.body = sdp.to_s
118
+
119
+ message.to_s.should match /^ANNOUNCE rtsp/
120
+ message.to_s.should include "ANNOUNCE rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
121
+ message.to_s.should include "CSeq: 2345\r\n"
122
+ message.to_s.should include "Session: 123456789\r\n"
123
+ message.to_s.should include "Content-Type: application/sdp\r\n"
124
+ message.to_s.should include "Content-Length: 29\r\n"
125
+ message.to_s.should match /\r\n\r\nv=1\r\no=bobo \r\ns=\r\nt= \r\n\r\n$/
126
+ end
127
+ end
128
+
129
+ context "builds a SETUP string" do
130
+ it "with default sequence, client_port, and routing values" do
131
+ message = RTSP::Message.setup(@stream)
132
+
133
+ message.to_s.should match /^SETUP rtsp/
134
+ message.to_s.should include "SETUP rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
135
+ message.to_s.should include "CSeq: 1\r\n"
136
+ message.to_s.should match /\r\n\r\n$/
137
+ end
138
+
139
+ it "with default sequence, transport, and client_port values" do
140
+ message = RTSP::Message.setup(@stream).with_headers({
141
+ transport: ["RTP/AVP", "multicast", { :client_port => "9000-9001" }] })
142
+
143
+ message.to_s.should match /^SETUP rtsp/
144
+ message.to_s.should include "SETUP rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
145
+ message.to_s.should include "CSeq: 1\r\n"
146
+ message.to_s.should include "Transport: RTP/AVP;multicast;client_port=9000-9001\r\n"
147
+ message.to_s.should match /\r\n\r\n$/
148
+ end
149
+
150
+ it "with default transport, client_port, and routing values" do
151
+ message = RTSP::Message.setup(@stream).with_headers({
152
+ transport: ["RTP/AVP", "multicast", { :client_port => "9000-9001" }],
153
+ cseq: 2345 })
154
+
155
+ message.to_s.should match /^SETUP rtsp/
156
+ message.to_s.should include "SETUP rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
157
+ message.to_s.should include "CSeq: 2345\r\n"
158
+ message.to_s.should include "Transport: RTP/AVP;multicast;client_port=9000-9001\r\n"
159
+ message.to_s.should match /\r\n\r\n$/
160
+ end
161
+ end
162
+
163
+ context "builds a PLAY string" do
164
+ it "with default sequence and range values" do
165
+ message = RTSP::Message.play(@stream).with_headers({
166
+ session: 123456789 })
167
+
168
+ message.to_s.should match /^PLAY rtsp/
169
+ message.to_s.should include "PLAY rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
170
+ message.to_s.should include "CSeq: 1\r\n"
171
+ message.to_s.should include "Session: 123456789\r\n"
172
+ message.to_s.should include "Range: npt=0.000-\r\n"
173
+ message.to_s.should match /\r\n\r\n/
174
+ end
175
+
176
+ it "with default sequence value" do
177
+ message = RTSP::Message.play(@stream).with_headers({
178
+ session: 123456789,
179
+ range: { :npt => "0.000-1.234" } })
180
+
181
+ message.to_s.should match /^PLAY rtsp/
182
+ message.to_s.should include "PLAY rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
183
+ message.to_s.should include "CSeq: 1\r\n"
184
+ message.to_s.should include "Session: 123456789\r\n"
185
+ message.to_s.should include "Range: npt=0.000-1.234\r\n"
186
+ message.to_s.should match /\r\n\r\n/
187
+ end
188
+ end
189
+
190
+ context "builds a PAUSE string" do
191
+ it "with required Request values" do
192
+ message = RTSP::Message.pause(@stream)
193
+
194
+ message.to_s.should match /^PAUSE rtsp/
195
+ message.to_s.should include "PAUSE rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
196
+ message.to_s.should include "CSeq: 1\r\n"
197
+ message.to_s.should match /\r\n\r\n/
198
+ end
199
+
200
+ it "with session and range headers" do
201
+ message = RTSP::Message.pause(@stream).with_headers({
202
+ session: 123456789,
203
+ range: { :npt => "0.000" } })
204
+
205
+ message.to_s.should match /^PAUSE rtsp/
206
+ message.to_s.should include "PAUSE rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
207
+ message.to_s.should include "CSeq: 1\r\n"
208
+ message.to_s.should include "Session: 123456789\r\n"
209
+ message.to_s.should include "Range: npt=0.000\r\n"
210
+ message.to_s.should match /\r\n\r\n/
211
+ end
212
+ end
213
+
214
+ context "builds a TEARDOWN string" do
215
+ it "with required Request values" do
216
+ message = RTSP::Message.teardown(@stream)
217
+
218
+ message.to_s.should match /^TEARDOWN rtsp/
219
+ message.to_s.should include "TEARDOWN rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
220
+ message.to_s.should include "CSeq: 1\r\n"
221
+ message.to_s.should match /\r\n\r\n/
222
+ end
223
+
224
+ it "with session and range headers" do
225
+ message = RTSP::Message.teardown(@stream).with_headers({
226
+ session: 123456789 })
227
+
228
+ message.to_s.should match /^TEARDOWN rtsp/
229
+ message.to_s.should include "TEARDOWN rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
230
+ message.to_s.should include "CSeq: 1\r\n"
231
+ message.to_s.should include "Session: 123456789\r\n"
232
+ message.to_s.should match /\r\n\r\n/
233
+ end
234
+ end
235
+
236
+ context "builds a GET_PARAMETER string" do
237
+ it "with required Request values" do
238
+ message = RTSP::Message.get_parameter(@stream)
239
+
240
+ message.to_s.should match /^GET_PARAMETER rtsp/
241
+ message.to_s.should include "GET_PARAMETER rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
242
+ message.to_s.should include "CSeq: 1\r\n"
243
+ message.to_s.should match /\r\n\r\n/
244
+ end
245
+
246
+ it "with cseq, content type, session headers, and text body" do
247
+ the_body = "packets_received\r\njitter\r\n"
248
+
249
+ message = RTSP::Message.get_parameter(@stream).with_headers({
250
+ cseq: 431,
251
+ content_type: 'text/parameters',
252
+ session: 123456789 })
253
+
254
+ message.body = the_body
255
+
256
+ message.to_s.should match /^GET_PARAMETER rtsp/
257
+ message.to_s.should include "GET_PARAMETER rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
258
+ message.to_s.should include "CSeq: 431\r\n"
259
+ message.to_s.should include "Session: 123456789\r\n"
260
+ message.to_s.should include "Content-Type: text/parameters\r\n"
261
+ message.to_s.should include "Content-Length: #{the_body.length}\r\n"
262
+ message.to_s.should include the_body
263
+ message.to_s.should match /\r\n\r\n/
264
+ end
265
+ end
266
+
267
+ context "builds a SET_PARAMETER string" do
268
+ it "with required Request values" do
269
+ message = RTSP::Message.set_parameter(@stream)
270
+
271
+ message.to_s.should match /^SET_PARAMETER rtsp/
272
+ message.to_s.should include "SET_PARAMETER rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
273
+ message.to_s.should include "CSeq: 1\r\n"
274
+ message.to_s.should match /\r\n\r\n/
275
+ end
276
+
277
+ it "with cseq, content type, session headers, and text body" do
278
+ the_body = "barparam: barstuff\r\n"
279
+
280
+ message = RTSP::Message.set_parameter(@stream).with_headers({
281
+ cseq: 431,
282
+ content_type: 'text/parameters',
283
+ session: 123456789 })
284
+ message.body = the_body
285
+
286
+ message.to_s.should match /^SET_PARAMETER rtsp/
287
+ message.to_s.should include "SET_PARAMETER rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
288
+ message.to_s.should include "CSeq: 431\r\n"
289
+ message.to_s.should include "Session: 123456789\r\n"
290
+ message.to_s.should include "Content-Type: text/parameters\r\n"
291
+ message.to_s.should include "Content-Length: #{the_body.length}\r\n"
292
+ message.to_s.should include the_body
293
+ message.to_s.should match /\r\n\r\n/
294
+ end
295
+ end
296
+
297
+ context "builds a REDIRECT string" do
298
+ it "with required Request values" do
299
+ message = RTSP::Message.redirect(@stream)
300
+
301
+ message.to_s.should match /^REDIRECT rtsp/
302
+ message.to_s.should include "REDIRECT rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
303
+ message.to_s.should include "CSeq: 1\r\n"
304
+ message.to_s.should match /\r\n\r\n/
305
+ end
306
+
307
+ it "with cseq, location, and range headers" do
308
+ message = RTSP::Message.redirect(@stream).with_headers({
309
+ cseq: 732,
310
+ location: "rtsp://bigserver.com:8001",
311
+ range: { :clock => "19960213T143205Z-" } })
312
+
313
+ message.to_s.should match /^REDIRECT rtsp/
314
+ message.to_s.should include "REDIRECT rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
315
+ message.to_s.should include "CSeq: 732\r\n"
316
+ message.to_s.should include "Location: rtsp://bigserver.com:8001\r\n"
317
+ message.to_s.should include "Range: clock=19960213T143205Z-\r\n"
318
+ message.to_s.should match /\r\n\r\n/
319
+ end
320
+ end
321
+
322
+ context "builds a RECORD string" do
323
+ it "with required Request values" do
324
+ message = RTSP::Message.record(@stream)
325
+
326
+ message.to_s.should match /^RECORD rtsp/
327
+ message.to_s.should include "RECORD rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
328
+ message.to_s.should include "CSeq: 1\r\n"
329
+ message.to_s.should match /\r\n\r\n/
330
+ end
331
+
332
+ it "with cseq, session, and conference headers" do
333
+ message = RTSP::Message.record(@stream).with_headers({
334
+ cseq: 954,
335
+ session: 12345678,
336
+ conference: "128.16.64.19/32492374" })
337
+
338
+ message.to_s.should match /^RECORD rtsp/
339
+ message.to_s.should include "RECORD rtsp://1.2.3.4:554/stream1 RTSP/1.0\r\n"
340
+ message.to_s.should include "CSeq: 954\r\n"
341
+ message.to_s.should include "Session: 12345678\r\n"
342
+ message.to_s.should include "Conference: 128.16.64.19/32492374\r\n"
343
+ message.to_s.should match /\r\n\r\n/
344
+ end
345
+ end
346
+
347
+ context "#to_s turns a Hash into an String of header strings" do
348
+ it "single header, non-hyphenated name, hash value" do
349
+ message = RTSP::Message.play(@stream).with_headers({
350
+ range: { npt: "0.000-" }
351
+ })
352
+
353
+ string = message.to_s
354
+ string.is_a?(String).should be_true
355
+ string.should include "Range: npt=0.000-"
356
+ end
357
+
358
+ it "single header, hyphenated, non-hash value" do
359
+ message = RTSP::Message.play(@stream).with_headers({
360
+ :if_modified_since => "Sat, 29 Oct 1994 19:43:31 GMT"
361
+ })
362
+
363
+ string = message.to_s
364
+ string.is_a?(String).should be_true
365
+ string.should include "If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT"
366
+ end
367
+
368
+ it "two headers, mixed hyphenated, array & hash values" do
369
+ message = RTSP::Message.redirect(@stream).with_headers({
370
+ :cache_control => ["no-cache", { :max_age => 12345 }],
371
+ :content_type => ['application/sdp', 'application/x-rtsp-mh']
372
+ })
373
+
374
+ string = message.to_s
375
+ string.is_a?(String).should be_true
376
+ string.should include "Cache-Control: no-cache;max_age=12345"
377
+ string.should include "Content-Type: application/sdp, application/x-rtsp-mh"
378
+ end
379
+ end
380
+
381
+ describe "#with_headers" do
382
+ it "returns an RTSP::Message" do
383
+ message = RTSP::Message.options(@stream)
384
+ result = message.with_headers({ test: "test" })
385
+ result.class.should == RTSP::Message
386
+ end
387
+ end
388
+
389
+ describe "#with_body" do
390
+ it "adds the passed-in text to the body of the message" do
391
+ new_body = "1234567890"
392
+ message = RTSP::Message.record("rtsp://localhost/track").with_body(new_body)
393
+ message.to_s.should match /\r\n\r\n#{new_body}$/
394
+ end
395
+
396
+ it "adds the Content-Length header to reflect the body" do
397
+ new_body = "1234567890"
398
+ message = RTSP::Message.record("rtsp://localhost/track").with_body(new_body)
399
+ message.to_s.should match /Content-Length: #{new_body.size}\r\n/
400
+ end
401
+ end
402
+
403
+ describe "#respond_to?" do
404
+ it "returns true to each method in the list of supported method types" do
405
+ RTSP::Message.instance_variable_get(:@message_types).each do |m|
406
+ RTSP::Message.respond_to?(m).should be_true
407
+ end
408
+ end
409
+
410
+ it "returns false to a method that's not in the list of supported methods" do
411
+ RTSP::Message.respond_to?(:meow).should be_false
412
+ end
413
+ end
414
+
415
+ describe "#method_missing" do
416
+ it "returns " do
417
+ lambda { RTSP::Message.meow }.should raise_error NoMethodError
418
+ end
419
+ end
420
+ end