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.
- data/.document +3 -0
- data/.infinity_test +1 -1
- data/.yardopts +4 -0
- data/ChangeLog.rdoc +9 -0
- data/Gemfile +15 -6
- data/Gemfile.lock +78 -40
- data/LICENSE.rdoc +20 -0
- data/PostInstall.txt +0 -3
- data/README.rdoc +85 -36
- data/Rakefile +33 -49
- data/bin/rtsp_client +129 -0
- data/features/client_changes_state.feature +58 -0
- data/features/client_requests.feature +27 -0
- data/features/control_streams_as_client.feature +26 -0
- data/features/step_definitions/client_changes_state_steps.rb +46 -0
- data/features/step_definitions/client_requests_steps.rb +74 -0
- data/features/step_definitions/control_streams_as_client_steps.rb +34 -0
- data/features/support/env.rb +31 -29
- data/features/support/hooks.rb +3 -0
- data/gemspec.yml +30 -0
- data/lib/ext/logger.rb +8 -0
- data/lib/rtsp.rb +3 -6
- data/lib/rtsp/capturer.rb +105 -0
- data/lib/rtsp/client.rb +446 -204
- data/lib/rtsp/error.rb +6 -0
- data/lib/rtsp/global.rb +63 -0
- data/lib/rtsp/helpers.rb +28 -0
- data/lib/rtsp/message.rb +270 -0
- data/lib/rtsp/response.rb +89 -29
- data/lib/rtsp/transport_parser.rb +64 -0
- data/lib/rtsp/version.rb +4 -0
- data/nsm_test.rb +26 -0
- data/rtsp.gemspec +284 -0
- data/sarix_test.rb +23 -0
- data/soma_test.rb +39 -0
- data/spec/rtsp/client_spec.rb +302 -27
- data/spec/rtsp/helpers_spec.rb +53 -0
- data/spec/rtsp/message_spec.rb +420 -0
- data/spec/rtsp/response_spec.rb +144 -58
- data/spec/rtsp/transport_parser_spec.rb +54 -0
- data/spec/rtsp_spec.rb +3 -3
- data/spec/spec_helper.rb +66 -7
- data/spec/support/fake_rtsp_server.rb +123 -0
- data/tasks/metrics.rake +27 -0
- data/tasks/roodi_config.yml +14 -0
- data/tasks/stats.rake +12 -0
- metadata +174 -183
- data/.autotest +0 -23
- data/History.txt +0 -4
- data/Manifest.txt +0 -26
- data/bin/rtsp +0 -121
- data/features/step_definitions/stream_steps.rb +0 -50
- data/features/stream.feature +0 -17
- data/features/support/common.rb +0 -1
- data/features/support/world.rb +0 -1
- data/lib/rtsp/request_messages.rb +0 -104
- data/lib/rtsp/status_code.rb +0 -7
data/spec/rtsp/client_spec.rb
CHANGED
@@ -1,38 +1,313 @@
|
|
1
|
-
require
|
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
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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 "
|
15
|
-
@
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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 "
|
23
|
-
|
24
|
-
|
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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
@
|
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
|