rtsp_server 0.0.2-universal-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gemtest +0 -0
  3. data/ChangeLog.rdoc +74 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE.rdoc +20 -0
  6. data/README.rdoc +152 -0
  7. data/Rakefile +23 -0
  8. data/bin/rtsp_client +133 -0
  9. data/features/client_changes_state.feature +58 -0
  10. data/features/client_requests.feature +27 -0
  11. data/features/control_streams_as_client.feature +26 -0
  12. data/features/step_definitions/client_changes_state_steps.rb +52 -0
  13. data/features/step_definitions/client_requests_steps.rb +68 -0
  14. data/features/step_definitions/control_streams_as_client_steps.rb +34 -0
  15. data/features/support/env.rb +50 -0
  16. data/features/support/hooks.rb +3 -0
  17. data/lib/ext/logger.rb +8 -0
  18. data/lib/rtsp/client.rb +520 -0
  19. data/lib/rtsp/common.rb +148 -0
  20. data/lib/rtsp/error.rb +6 -0
  21. data/lib/rtsp/global.rb +63 -0
  22. data/lib/rtsp/helpers.rb +28 -0
  23. data/lib/rtsp/message.rb +272 -0
  24. data/lib/rtsp/request.rb +39 -0
  25. data/lib/rtsp/response.rb +47 -0
  26. data/lib/rtsp/server.rb +311 -0
  27. data/lib/rtsp/socat_streaming.rb +320 -0
  28. data/lib/rtsp/stream_server.rb +37 -0
  29. data/lib/rtsp/transport_parser.rb +96 -0
  30. data/lib/rtsp/version.rb +4 -0
  31. data/lib/rtsp.rb +6 -0
  32. data/rtsp.gemspec +44 -0
  33. data/spec/rtsp/client_spec.rb +326 -0
  34. data/spec/rtsp/helpers_spec.rb +53 -0
  35. data/spec/rtsp/message_spec.rb +420 -0
  36. data/spec/rtsp/response_spec.rb +306 -0
  37. data/spec/rtsp/transport_parser_spec.rb +137 -0
  38. data/spec/rtsp_spec.rb +27 -0
  39. data/spec/spec_helper.rb +88 -0
  40. data/spec/support/fake_rtsp_server.rb +123 -0
  41. data/tasks/roodi.rake +9 -0
  42. data/tasks/roodi_config.yaml +14 -0
  43. data/tasks/stats.rake +12 -0
  44. metadata +280 -0
@@ -0,0 +1,420 @@
1
+ require 'sdp'
2
+ require_relative '../spec_helper'
3
+ require 'rtsp/message'
4
+
5
+ describe "RTSP::Message" do
6
+ let(:stream) { "rtsp://1.2.3.4/stream1" }
7
+
8
+ it "raises if the header type isn't a Symbol" do
9
+ message = RTSP::Message.options(stream)
10
+
11
+ lambda {
12
+ message.header "hi", "everyone"
13
+ }.should raise_error RTSP::Error
14
+ end
15
+
16
+ RTSP::Message.instance_variable_get(:@message_types).each do |method|
17
+ it "adds a User-Agent header to the #{method} method" do
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/#{RTSP::VERSION} (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/#{RTSP::VERSION} (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