rtsp 0.0.1.alpha → 0.1.0

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 (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