rtsp_server 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3c843ce1ce132148c50404761e7ba0d910bf888a
4
- data.tar.gz: 7bca112c56925cfc50e616133626c662af08736d
3
+ metadata.gz: 372be55470885f88a585a23224c6ab1515333638
4
+ data.tar.gz: 6e48e3604608e14abd2f32fa22f1b5dadd294304
5
5
  SHA512:
6
- metadata.gz: dbf5866263aea266bc3b50eed4977399601de369c2fde90689da2d99dbec41cbfa160ebaf68018417dfe3fe52f9e34b67cfba832843356b90e1c9dbc5157f030
7
- data.tar.gz: 47c607f62b2e495946ae166df31dcd4ca2f7399d57de11851358cab0d68b2d0f1d9f71def7f199a12bec6940b18f423d0af28fb0fc472f115447b88908c53023
6
+ metadata.gz: ac7589b3ede843f3854fc7288e86cf2dd937cd44bfc641a5c939c8ea6dd2ebe4e79805f67061502a88819c134dc02317becba7196fb7cc31e735845d56ad1ef1
7
+ data.tar.gz: b1c6f34f9b02628347f0dd74516759ad47fdf7f0403f77d8b27c88fac087a51b7f7792fb37f986a32dc0b1c3cc60af2aa1067f28dfc613ac8e2353fde6aeab72
data/lib/rtsp/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module RTSP
2
2
  # rtsp version
3
- VERSION = "0.1.0"
3
+ VERSION = "0.1.1"
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rtsp_server
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Loveless, Mike Kirby
@@ -208,7 +208,6 @@ files:
208
208
  - features/support/hooks.rb
209
209
  - lib/ext/logger.rb
210
210
  - lib/rtsp.rb
211
- - lib/rtsp/client.rb
212
211
  - lib/rtsp/common.rb
213
212
  - lib/rtsp/error.rb
214
213
  - lib/rtsp/global.rb
data/lib/rtsp/client.rb DELETED
@@ -1,520 +0,0 @@
1
- require 'socket'
2
- require 'tempfile'
3
- require 'timeout'
4
- require 'rtp/receiver'
5
-
6
- require_relative 'transport_parser'
7
- require_relative 'error'
8
- require_relative 'global'
9
- require_relative 'helpers'
10
- require_relative 'message'
11
- require_relative 'response'
12
-
13
- module RTSP
14
-
15
- # This is the main interface to an RTSP server. A client object uses a couple
16
- # main objects for configuration: an +RTP::Receiver+ and a Connection Struct.
17
- # Use the capturer to configure how to capture the data which is the RTP
18
- # stream provided by the RTSP server. Use the connection object to control
19
- # the connection to the server.
20
- #
21
- # You can initialize your client object using a block:
22
- # client = RTSP::Client.new("rtsp://192.168.1.10") do |connection, capturer|
23
- # connection.timeout = 5
24
- # capturer.rtp_file = File.open("my_file.rtp", "wb")
25
- # end
26
- #
27
- # ...or, without the block:
28
- # client = RTSP::Client.new("rtsp://192.168.1.10")
29
- # client.connection.timeout = 5
30
- # client.capturer.rtp_file = File.open("my_file.rtp", "wb")
31
- #
32
- # After setting up the client object, call RTSP methods, Ruby style:
33
- # client.options
34
- #
35
- # Remember that, unlike HTTP, RTSP is state-based (and thus the ability to
36
- # call certain methods depends on calling other methods first). Your client
37
- # object tells you the current RTSP state that it's in:
38
- # client.options
39
- # client.session_state # => :init
40
- # client.describe
41
- # client.session_state # => :init
42
- # client.setup(client.media_control_tracks.first)
43
- # client.session_state # => :ready
44
- # client.play(client.aggregate_control_track)
45
- # client.session_state # => :playing
46
- # client.pause(client.aggregate_control_track)
47
- # client.session_state # => :ready
48
- # client.teardown(client.aggregate_control_track)
49
- # client.session_state # => :init
50
- #
51
- # To enable/disable logging for clients, class methods:
52
- # RTSP::Client.log? # => true
53
- # RTSP::Client.log = false
54
- # @todo Break Stream out in to its own class.
55
- class Client
56
- include RTSP::Helpers
57
- extend RTSP::Global
58
-
59
- MAX_BYTES_TO_RECEIVE = 3000
60
-
61
- # @return [URI] The URI that points to the RTSP server's resource.
62
- attr_reader :server_uri
63
-
64
- # @return [Fixnum] Also known as the "sequence" number, this starts at 1 and
65
- # increments after every request to the server. It is reset after
66
- # calling #teardown.
67
- attr_reader :cseq
68
-
69
- # @return [Fixnum] A session is only established after calling #setup;
70
- # otherwise returns nil.
71
- attr_reader :session
72
-
73
- # @return [Array<Symbol>] Only populated after calling #options; otherwise
74
- # returns nil. There's no sense in making any other requests than these
75
- # since the server doesn't support them.
76
- attr_reader :supported_methods
77
-
78
- # @return [Struct::Connection]
79
- attr_accessor :connection
80
-
81
- # Use to get/set an object for capturing received data.
82
- # @param [RTP::Receiver]
83
- # @return [RTP::Receiver]
84
- attr_accessor :capturer
85
-
86
- # @return [Symbol] See {RFC section A.1.}[http://tools.ietf.org/html/rfc2326#page-76]
87
- attr_reader :session_state
88
-
89
- # Use to configure options for all clients.
90
- # @see RTSP::Global
91
- def self.configure
92
- yield self if block_given?
93
- end
94
-
95
- # @param [String] server_url URL to the resource to stream. If no scheme is
96
- # given, "rtsp" is assumed. If no port is given, 554 is assumed.
97
- # @yield [Struct::Connection, RTP::Receiver]
98
- # @yieldparam [Struct::Connection] server_url=
99
- # @yieldparam [Struct::Connection] timeout=
100
- # @yieldparam [Struct::Connection] socket=
101
- # @yieldparam [Struct::Connection] do_capture=
102
- # @yieldparam [Struct::Connection] interleave=
103
- # @todo Use server_url everywhere; just use URI to ensure the port & rtspu.
104
- def initialize(server_url=nil)
105
- Thread.abort_on_exception = true
106
-
107
- unless defined? Struct::Connection
108
- Struct.new("Connection", :server_url, :timeout, :socket,
109
- :do_capture, :interleave)
110
- end
111
-
112
- @connection = Struct::Connection.new
113
- @capturer = RTP::Receiver.new
114
-
115
- yield(connection, capturer) if block_given?
116
-
117
- @connection.server_url = server_url || @connection.server_url
118
- @server_uri = build_resource_uri_from(@connection.server_url)
119
- @connection.timeout ||= 30
120
- @connection.socket ||= TCPSocket.new(@server_uri.host, @server_uri.port)
121
- @connection.do_capture ||= true
122
- @connection.interleave ||= false
123
- @capturer.rtp_port ||= 9000
124
- @capturer.transport_protocol ||= :UDP
125
- @capturer.broadcast_type ||= :unicast
126
- @capturer.rtp_file ||= Tempfile.new(RTP::Receiver::DEFAULT_CAPFILE_NAME)
127
-
128
- @play_thread = nil
129
- @cseq = 1
130
- reset_state
131
- end
132
-
133
- # The URL for the RTSP server to talk to can change if multiple servers are
134
- # involved in delivering content. This method can be used to change the
135
- # server to talk to on the fly.
136
- #
137
- # @param [String] new_url The new server URL to use to communicate over.
138
- def server_url=(new_url)
139
- @server_uri = build_resource_uri_from new_url
140
- end
141
-
142
- # Sends the message over the socket.
143
- #
144
- # @param [RTSP::Message] message
145
- # @return [RTSP::Response]
146
- # @raise [RTSP::Error] If the timeout value is reached and the server hasn't
147
- # responded.
148
- def send_message message
149
- RTSP::Client.log "Sending #{message.method_type.upcase} to #{message.request_uri}"
150
- message.to_s.each_line { |line| RTSP::Client.log line.strip }
151
-
152
- begin
153
- response = Timeout::timeout(@connection.timeout) do
154
- @connection.socket.send(message.to_s, 0)
155
- socket_data = @connection.socket.recvfrom MAX_BYTES_TO_RECEIVE
156
- RTSP::Response.new socket_data.first
157
- end
158
- rescue Timeout::Error
159
- raise RTSP::Error, "Request took more than #{@connection.timeout} seconds to send."
160
- end
161
-
162
- RTSP::Client.log "Received response:"
163
-
164
- if response
165
- response.to_s.each_line { |line| RTSP::Client.log line.strip }
166
- end
167
-
168
- response
169
- end
170
-
171
- # Sends an OPTIONS message to the server specified by +@server_uri+. Sets
172
- # +@supported_methods+ based on the list of supported methods returned in
173
- # the Public headers.
174
- #
175
- # @param [Hash] additional_headers
176
- # @return [RTSP::Response]
177
- # @see http://tools.ietf.org/html/rfc2326#page-30 RFC 2326, Section 10.1.
178
- def options(additional_headers={})
179
- message = RTSP::Message.options(@server_uri.to_s).with_headers({
180
- cseq: @cseq })
181
- message.add_headers additional_headers
182
-
183
- request(message) do |response|
184
- @supported_methods = extract_supported_methods_from response.public
185
- end
186
- end
187
-
188
- # Sends the DESCRIBE request, then extracts the SDP description into
189
- # +@session_description+, extracts the session +@start_time+ and +@stop_time+,
190
- # +@content_base+, +@media_control_tracks+, and +@aggregate_control_track+.
191
- #
192
- # @todo get tracks, IP's, ports, multicast/unicast
193
- # @param [Hash] additional_headers
194
- # @return [RTSP::Response]
195
- # @see http://tools.ietf.org/html/rfc2326#page-31 RFC 2326, Section 10.2.
196
- # @see #media_control_tracks
197
- # @see #aggregate_control_track
198
- def describe additional_headers={}
199
- message = RTSP::Message.describe(@server_uri.to_s).with_headers({
200
- cseq: @cseq })
201
- message.add_headers additional_headers
202
-
203
- request(message) do |response|
204
- @session_description = response.body
205
- #@session_start_time = response.body.start_time
206
- #@session_stop_time = response.body.stop_time
207
- @content_base = build_resource_uri_from response.content_base
208
-
209
- @media_control_tracks = media_control_tracks
210
- @aggregate_control_track = aggregate_control_track
211
- end
212
- end
213
-
214
- # Sends an ANNOUNCE Request to the provided URL. This method also requires
215
- # an SDP description to send to the server.
216
- #
217
- # @param [String] request_url The URL to post the presentation or media
218
- # object to.
219
- # @param [SDP::Description] description The SDP description to send to the
220
- # server.
221
- # @param [Hash] additional_headers
222
- # @return [RTSP::Response]
223
- # @see http://tools.ietf.org/html/rfc2326#page-32 RFC 2326, Section 10.3.
224
- def announce(request_url, description, additional_headers={})
225
- message = RTSP::Message.announce(request_url).with_headers({ cseq: @cseq })
226
- message.add_headers additional_headers
227
- message.body = description.to_s
228
-
229
- request(message)
230
- end
231
-
232
- # Builds the Transport header fields string based on info used in setting up
233
- # the Client instance.
234
- #
235
- # @return [String] The String to use with the Transport header.
236
- # @see http://tools.ietf.org/html/rfc2326#page-58 RFC 2326, Section 12.39.
237
- def request_transport
238
- value = "RTP/AVP;#{@capturer.broadcast_type};client_port="
239
- value << "#{@capturer.rtp_port}-#{@capturer.rtp_port + 1}\r\n"
240
- end
241
-
242
- # Sends the SETUP request, then sets +@session+ to the value returned in the
243
- # Session header from the server, then sets the +@session_state+ to +:ready+.
244
- #
245
- # @todo +@session+ numbers are relevant to tracks, and a client must be able
246
- # to play multiple tracks at the same time.
247
- # @param [String] track
248
- # @param [Hash] additional_headers
249
- # @return [RTSP::Response] The response formatted as a Hash.
250
- # @see http://tools.ietf.org/html/rfc2326#page-33 RFC 2326, Section 10.4.
251
- def setup(track, additional_headers={})
252
- message = RTSP::Message.setup(track).with_headers({
253
- cseq: @cseq, transport: request_transport })
254
- message.add_headers additional_headers
255
-
256
- request(message) do |response|
257
- if @session_state == :init
258
- @session_state = :ready
259
- end
260
-
261
- @session = response.session
262
- parser = RTSP::TransportParser.new
263
- @transport = parser.parse response.transport
264
-
265
- unless @transport[:transport_protocol].nil?
266
- @capturer.transport_protocol = @transport[:transport_protocol]
267
- end
268
-
269
- @capturer.rtp_port = @transport[:client_port][:rtp].to_i
270
- @capturer.broadcast_type = @transport[:broadcast_type]
271
- end
272
- end
273
-
274
- # Sends the PLAY request and sets +@session_state+ to +:playing+.
275
- #
276
- # @param [String] track
277
- # @param [Hash] additional_headers
278
- # @return [RTSP::Response]
279
- # @todo If playback over UDP doesn't result in any data coming in on the
280
- # socket, re-setup with RTP/AVP/TCP;unicast;interleaved=0-1.
281
- # @raise [RTSP::Error] If +#play+ is called but the session hasn't yet been
282
- # set up via +#setup+.
283
- # @see http://tools.ietf.org/html/rfc2326#page-34 RFC 2326, Section 10.5.
284
- def play(track, additional_headers={})
285
- message = RTSP::Message.play(track).with_headers({
286
- cseq: @cseq, session: @session[:session_id] })
287
- message.add_headers additional_headers
288
-
289
- request(message) do
290
- unless @session_state == :ready
291
- raise RTSP::Error, "Session not set up yet. Run #setup first."
292
- end
293
-
294
- if @play_thread.nil?
295
- RTSP::Client.log "Capturing RTP data on port #{@transport[:client_port][:rtp]}"
296
-
297
- unless @capturer.running?
298
- @play_thread = Thread.new do
299
- @capturer.run
300
- end
301
- end
302
- end
303
-
304
- @session_state = :playing
305
- end
306
- end
307
-
308
- # Sends the PAUSE request and sets +@session_state+ to +:ready+.
309
- #
310
- # @param [String] track A track or presentation URL to pause.
311
- # @param [Hash] additional_headers
312
- # @return [RTSP::Response]
313
- # @see http://tools.ietf.org/html/rfc2326#page-36 RFC 2326, Section 10.6.
314
- def pause(track, additional_headers={})
315
- message = RTSP::Message.pause(track).with_headers({
316
- cseq: @cseq, session: @session[:session_id] })
317
- message.add_headers additional_headers
318
-
319
- request(message) do
320
- if [:playing, :recording].include? @session_state
321
- @session_state = :ready
322
- end
323
- end
324
- end
325
-
326
- # Sends the TEARDOWN request, then resets all state-related instance
327
- # variables.
328
- #
329
- # @param [String] track The presentation or media track to teardown.
330
- # @param [Hash] additional_headers
331
- # @return [RTSP::Response]
332
- # @see http://tools.ietf.org/html/rfc2326#page-37 RFC 2326, Section 10.7.
333
- def teardown(track, additional_headers={})
334
- message = RTSP::Message.teardown(track).with_headers({
335
- cseq: @cseq, session: @session[:session_id] })
336
- message.add_headers additional_headers
337
-
338
- request(message) do
339
- reset_state
340
-
341
- if @play_thread
342
- @capturer.stop
343
- @capturer.rtp_file.close
344
- @play_thread.exit
345
- end
346
- end
347
- end
348
-
349
- # Sets state related variables back to their starting values;
350
- # +@session_state+ is set to +:init+; +@session+ is set to 0.
351
- def reset_state
352
- @session_state = :init
353
- @session = {}
354
- end
355
-
356
- # Sends the GET_PARAMETERS request.
357
- #
358
- # @param [String] track The presentation or media track to ping.
359
- # @param [String] body The string containing the parameters to send.
360
- # @param [Hash] additional_headers
361
- # @return [RTSP::Response]
362
- # @see http://tools.ietf.org/html/rfc2326#page-37 RFC 2326, Section 10.8.
363
- def get_parameter(track, body="", additional_headers={})
364
- message = RTSP::Message.get_parameter(track).with_headers({
365
- cseq: @cseq })
366
- message.add_headers additional_headers
367
- message.body = body
368
-
369
- request(message)
370
- end
371
-
372
- # Sends the SET_PARAMETERS request.
373
- #
374
- # @param [String] track The presentation or media track to teardown.
375
- # @param [String] parameters The string containing the parameters to send.
376
- # @param [Hash] additional_headers
377
- # @return [RTSP::Response]
378
- # @see http://tools.ietf.org/html/rfc2326#page-38 RFC 2326, Section 10.9.
379
- def set_parameter(track, parameters, additional_headers={})
380
- message = RTSP::Message.set_parameter(track).with_headers({
381
- cseq: @cseq })
382
- message.add_headers additional_headers
383
- message.body = parameters
384
-
385
- request(message)
386
- end
387
-
388
- # Sends the RECORD request and sets +@session_state+ to +:recording+.
389
- #
390
- # @param [String] track
391
- # @param [Hash] additional_headers
392
- # @return [RTSP::Response]
393
- # @see http://tools.ietf.org/html/rfc2326#page-39 RFC 2326, Section 10.11.
394
- def record(track, additional_headers={})
395
- message = RTSP::Message.record(track).with_headers({
396
- cseq: @cseq, session: @session[:session_id] })
397
- message.add_headers additional_headers
398
-
399
- request(message) { @session_state = :recording }
400
- end
401
-
402
- # Executes the Request with the arguments passed in, yields the response to
403
- # the calling block, checks the CSeq response and the session response,
404
- # then increments +@cseq+ by 1. Handles any exceptions raised during the
405
- # Request.
406
- #
407
- # @param [Hash] new_args
408
- # @yield [RTSP::Response]
409
- # @return [RTSP::Response]
410
- # @raise [RTSP::Error] All 4xx & 5xx response codes & their messages.
411
- def request message
412
- response = send_message message
413
- #compare_sequence_number response.cseq
414
- @cseq += 1
415
-
416
- if response.code.to_s =~ /2../
417
- yield response if block_given?
418
- elsif response.code.to_s =~ /(4|5)../
419
- if (defined? response.connection) && response.connection == 'Close'
420
- reset_state
421
- end
422
-
423
- raise RTSP::Error, "#{response.code}: #{response.message}"
424
- else
425
- raise RTSP::Error, "Unknown Response code: #{response.code}"
426
- end
427
-
428
- dont_ensure_list = [:options, :describe, :teardown, :set_parameter,
429
- :get_parameter]
430
- unless dont_ensure_list.include? message.method_type
431
- ensure_session
432
- end
433
-
434
- response
435
- end
436
-
437
- # Ensures that +@session+ is set before continuing on.
438
- #
439
- # @raise [RTSP::Error] Raises if @session isn't set.
440
- def ensure_session
441
- if @session.empty? || @session[:session_id] <= 0
442
- raise RTSP::Error, "Session number not retrieved from server yet. Run SETUP first."
443
- end
444
- end
445
-
446
- # Extracts the URL associated with the "control" attribute from the main
447
- # section of the session description.
448
- #
449
- # @return [String] The URL as a String.
450
- def aggregate_control_track
451
- aggregate_control = @session_description.attributes.find_all do |a|
452
- a[:attribute] == "control"
453
- end
454
-
455
- "#{@content_base}#{aggregate_control.first[:value].gsub(/\*/, "")}"
456
- end
457
-
458
- # Extracts the value of the "control" attribute from all media sections of
459
- # the session description (SDP). You have to call the +#describe+ method in
460
- # order to get the session description info.
461
- #
462
- # @return [Array<String>] The tracks made up of the content base + control
463
- # track value.
464
- # @see #describe
465
- def media_control_tracks
466
- tracks = []
467
-
468
- if @session_description.nil?
469
- tracks << ""
470
- else
471
- @session_description.media_sections.each do |media_section|
472
- media_section[:attributes].each do |a|
473
- tracks << "#{@content_base}#{a[:value]}" if a[:attribute] == "control"
474
- end
475
- end
476
- end
477
-
478
- tracks
479
- end
480
-
481
- # Compares the sequence number passed in to the current client sequence
482
- # number ( +@cseq+ ) and raises if they're not equal. If that's the case, the
483
- # server responded to a different request.
484
- #
485
- # @param [Fixnum] server_cseq Sequence number returned by the server.
486
- # @raise [RTSP::Error] If the server returns a CSeq value that's different
487
- # from what the client sent.
488
- def compare_sequence_number server_cseq
489
- if @cseq != server_cseq
490
- message = "Sequence number mismatch. Client: #{@cseq}, Server: #{server_cseq}"
491
- raise RTSP::Error, message
492
- end
493
- end
494
-
495
- # Compares the session number passed in to the current client session
496
- # number ( +@session+ ) and raises if they're not equal. If that's the case,
497
- # the server responded to a different request.
498
- #
499
- # @param [Fixnum] server_session Session number returned by the server.
500
- # @raise [RTSP::Error] If the server returns a Session value that's different
501
- # from what the client sent.
502
- def compare_session_number server_session
503
- if @session[:session_id] != server_session
504
- message = "Session number mismatch. Client: #{@session[:session_id]}, Server: #{server_session}"
505
- raise RTSP::Error, message
506
- end
507
- end
508
-
509
- # Takes the methods returned from the Public header from an OPTIONS response
510
- # and puts them to an Array.
511
- #
512
- # @param [String] method_list The string returned from the server containing
513
- # the list of methods it supports.
514
- # @return [Array<Symbol>] The list of methods as symbols.
515
- # @see #options
516
- def extract_supported_methods_from method_list
517
- method_list.downcase.split(', ').map { |m| m.to_sym }
518
- end
519
- end
520
- end