Class: RTSP::Client
- Inherits:
-
Object
- Object
- RTSP::Client
- Extended by:
- Global
- Includes:
- Helpers
- Defined in:
- lib/rtsp/client.rb
Overview
Break Stream out in to its own class.
This is the main interface to an RTSP server. A client object uses a couple main objects for configuration: an RTP::Receiver
and a Connection Struct. Use the capturer to configure how to capture the data which is the RTP stream provided by the RTSP server. Use the connection object to control the connection to the server.
You can initialize your client object using a block:
client = RTSP::Client.new("rtsp://192.168.1.10") do |connection, capturer|
connection.timeout = 5
capturer.rtp_file = File.open("my_file.rtp", "wb")
end
…or, without the block:
client = RTSP::Client.new("rtsp://192.168.1.10")
client.connection.timeout = 5
client.capturer.rtp_file = File.open("my_file.rtp", "wb")
After setting up the client object, call RTSP methods, Ruby style:
client.
Remember that, unlike HTTP, RTSP is state-based (and thus the ability to call certain methods depends on calling other methods first). Your client object tells you the current RTSP state that it’s in:
client.
client.session_state # => :init
client.describe
client.session_state # => :init
client.setup(client.media_control_tracks.first)
client.session_state # => :ready
client.play(client.aggregate_control_track)
client.session_state # => :playing
client.pause(client.aggregate_control_track)
client.session_state # => :ready
client.teardown(client.aggregate_control_track)
client.session_state # => :init
To enable/disable logging for clients, class methods:
RTSP::Client.log? # => true
RTSP::Client.log = false
Constant Summary collapse
- MAX_BYTES_TO_RECEIVE =
3000
Constants included from Global
Global::DEFAULT_RTSP_PORT, Global::DEFAULT_VERSION
Instance Attribute Summary collapse
-
#capturer ⇒ RTP::Receiver
Use to get/set an object for capturing received data.
- #connection ⇒ Struct::Connection
-
#cseq ⇒ Fixnum
readonly
Also known as the “sequence” number, this starts at 1 and increments after every request to the server.
-
#server_uri ⇒ URI
readonly
The URI that points to the RTSP server’s resource.
-
#session ⇒ Fixnum
readonly
A session is only established after calling #setup; otherwise returns nil.
-
#session_state ⇒ Symbol
readonly
See RFC section A.1..
-
#supported_methods ⇒ Array<Symbol>
readonly
Only populated after calling #options; otherwise returns nil.
Attributes included from Global
#log, #log_level, #logger, #raise_errors
Class Method Summary collapse
-
.configure {|_self| ... } ⇒ Object
Use to configure options for all clients.
Instance Method Summary collapse
-
#aggregate_control_track ⇒ String
Extracts the URL associated with the “control” attribute from the main section of the session description.
-
#announce(request_url, description, additional_headers = {}) ⇒ RTSP::Response
Sends an ANNOUNCE Request to the provided URL.
-
#compare_sequence_number(server_cseq) ⇒ Object
Compares the sequence number passed in to the current client sequence number ( @cseq ) and raises if they’re not equal.
-
#compare_session_number(server_session) ⇒ Object
Compares the session number passed in to the current client session number ( @session ) and raises if they’re not equal.
-
#describe(additional_headers = {}) ⇒ RTSP::Response
Sends the DESCRIBE request, then extracts the SDP description into @session_description, extracts the session @start_time and @stop_time, @content_base, @media_control_tracks, and @aggregate_control_track.
-
#ensure_session ⇒ Object
Ensures that @session is set before continuing on.
-
#extract_supported_methods_from(method_list) ⇒ Array<Symbol>
Takes the methods returned from the Public header from an OPTIONS response and puts them to an Array.
-
#get_parameter(track, body = "", additional_headers = {}) ⇒ RTSP::Response
Sends the GET_PARAMETERS request.
-
#initialize(server_url = nil) {|Struct::Connection, RTP::Receiver| ... } ⇒ Client
constructor
A new instance of Client.
-
#media_control_tracks ⇒ Array<String>
Extracts the value of the “control” attribute from all media sections of the session description (SDP).
-
#options(additional_headers = {}) ⇒ RTSP::Response
Sends an OPTIONS message to the server specified by @server_uri.
-
#pause(track, additional_headers = {}) ⇒ RTSP::Response
Sends the PAUSE request and sets @session_state to
:ready
. -
#play(track, additional_headers = {}) ⇒ RTSP::Response
Sends the PLAY request and sets @session_state to
:playing
. -
#record(track, additional_headers = {}) ⇒ RTSP::Response
Sends the RECORD request and sets @session_state to
:recording
. -
#request(message) {|RTSP::Response| ... } ⇒ RTSP::Response
Executes the Request with the arguments passed in, yields the response to the calling block, checks the CSeq response and the session response, then increments @cseq by 1.
-
#request_transport ⇒ String
Builds the Transport header fields string based on info used in setting up the Client instance.
-
#reset_state ⇒ Object
Sets state related variables back to their starting values; @session_state is set to
:init
; @session is set to 0. -
#send_message(message) ⇒ RTSP::Response
Sends the message over the socket.
-
#server_url=(new_url) ⇒ Object
The URL for the RTSP server to talk to can change if multiple servers are involved in delivering content.
-
#set_parameter(track, parameters, additional_headers = {}) ⇒ RTSP::Response
Sends the SET_PARAMETERS request.
-
#setup(track, additional_headers = {}) ⇒ RTSP::Response
Sends the SETUP request, then sets @session to the value returned in the Session header from the server, then sets the @session_state to
:ready
. -
#teardown(track, additional_headers = {}) ⇒ RTSP::Response
Sends the TEARDOWN request, then resets all state-related instance variables.
Methods included from Global
log?, raise_errors?, reset_config!, rtsp_version
Methods included from Helpers
Constructor Details
#initialize(server_url = nil) {|Struct::Connection, RTP::Receiver| ... } ⇒ Client
Use server_url everywhere; just use URI to ensure the port & rtspu.
Returns a new instance of Client.
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/rtsp/client.rb', line 104 def initialize(server_url=nil) Thread.abort_on_exception = true unless defined? Struct::Connection Struct.new("Connection", :server_url, :timeout, :socket, :do_capture, :interleave) end @connection = Struct::Connection.new @capturer = RTP::Receiver.new yield(connection, capturer) if block_given? @connection.server_url = server_url || @connection.server_url @server_uri = build_resource_uri_from(@connection.server_url) @connection.timeout ||= 30 @connection.socket ||= TCPSocket.new(@server_uri.host, @server_uri.port) @connection.do_capture ||= true @connection.interleave ||= false @capturer.rtp_port ||= 9000 @capturer.transport_protocol ||= :UDP @capturer.broadcast_type ||= :unicast @capturer.rtp_file ||= Tempfile.new(RTP::Receiver::DEFAULT_CAPFILE_NAME) @play_thread = nil @cseq = 1 reset_state end |
Instance Attribute Details
#capturer ⇒ RTP::Receiver
Use to get/set an object for capturing received data.
84 85 86 |
# File 'lib/rtsp/client.rb', line 84 def capturer @capturer end |
#connection ⇒ Struct::Connection
79 80 81 |
# File 'lib/rtsp/client.rb', line 79 def connection @connection end |
#cseq ⇒ Fixnum (readonly)
Returns Also known as the “sequence” number, this starts at 1 and increments after every request to the server. It is reset after calling #teardown.
67 68 69 |
# File 'lib/rtsp/client.rb', line 67 def cseq @cseq end |
#server_uri ⇒ URI (readonly)
Returns The URI that points to the RTSP server’s resource.
62 63 64 |
# File 'lib/rtsp/client.rb', line 62 def server_uri @server_uri end |
#session ⇒ Fixnum (readonly)
Returns A session is only established after calling #setup; otherwise returns nil.
71 72 73 |
# File 'lib/rtsp/client.rb', line 71 def session @session end |
#session_state ⇒ Symbol (readonly)
Returns See RFC section A.1..
87 88 89 |
# File 'lib/rtsp/client.rb', line 87 def session_state @session_state end |
#supported_methods ⇒ Array<Symbol> (readonly)
Only populated after calling #options; otherwise returns nil. There’s no sense in making any other requests than these since the server doesn’t support them.
76 77 78 |
# File 'lib/rtsp/client.rb', line 76 def supported_methods @supported_methods end |
Class Method Details
.configure {|_self| ... } ⇒ Object
Use to configure options for all clients.
91 92 93 |
# File 'lib/rtsp/client.rb', line 91 def self.configure yield self if block_given? end |
Instance Method Details
#aggregate_control_track ⇒ String
Extracts the URL associated with the “control” attribute from the main section of the session description.
450 451 452 453 454 455 456 |
# File 'lib/rtsp/client.rb', line 450 def aggregate_control_track aggregate_control = @session_description.attributes.find_all do |a| a[:attribute] == "control" end "#{@content_base}#{aggregate_control.first[:value].gsub(/\*/, "")}" end |
#announce(request_url, description, additional_headers = {}) ⇒ RTSP::Response
Sends an ANNOUNCE Request to the provided URL. This method also requires an SDP description to send to the server.
224 225 226 227 228 229 230 |
# File 'lib/rtsp/client.rb', line 224 def announce(request_url, description, additional_headers={}) = RTSP::Message.announce(request_url).with_headers({ cseq: @cseq }) .add_headers additional_headers .body = description.to_s request() end |
#compare_sequence_number(server_cseq) ⇒ Object
Compares the sequence number passed in to the current client sequence number ( @cseq ) and raises if they’re not equal. If that’s the case, the server responded to a different request.
488 489 490 491 492 493 |
# File 'lib/rtsp/client.rb', line 488 def compare_sequence_number server_cseq if @cseq != server_cseq = "Sequence number mismatch. Client: #{@cseq}, Server: #{server_cseq}" raise RTSP::Error, end end |
#compare_session_number(server_session) ⇒ Object
Compares the session number passed in to the current client session number ( @session ) and raises if they’re not equal. If that’s the case, the server responded to a different request.
502 503 504 505 506 507 |
# File 'lib/rtsp/client.rb', line 502 def compare_session_number server_session if @session[:session_id] != server_session = "Session number mismatch. Client: #{@session[:session_id]}, Server: #{server_session}" raise RTSP::Error, end end |
#describe(additional_headers = {}) ⇒ RTSP::Response
get tracks, IP’s, ports, multicast/unicast
Sends the DESCRIBE request, then extracts the SDP description into @session_description, extracts the session @start_time and @stop_time, @content_base, @media_control_tracks, and @aggregate_control_track.
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/rtsp/client.rb', line 198 def describe additional_headers={} = RTSP::Message.describe(@server_uri.to_s).with_headers({ cseq: @cseq }) .add_headers additional_headers request() do |response| @session_description = response.body #@session_start_time = response.body.start_time #@session_stop_time = response.body.stop_time @content_base = build_resource_uri_from response.content_base @media_control_tracks = media_control_tracks @aggregate_control_track = aggregate_control_track end end |
#ensure_session ⇒ Object
Ensures that @session is set before continuing on.
440 441 442 443 444 |
# File 'lib/rtsp/client.rb', line 440 def ensure_session if @session.empty? || @session[:session_id] <= 0 raise RTSP::Error, "Session number not retrieved from server yet. Run SETUP first." end end |
#extract_supported_methods_from(method_list) ⇒ Array<Symbol>
Takes the methods returned from the Public header from an OPTIONS response and puts them to an Array.
516 517 518 |
# File 'lib/rtsp/client.rb', line 516 def extract_supported_methods_from method_list method_list.downcase.split(', ').map { |m| m.to_sym } end |
#get_parameter(track, body = "", additional_headers = {}) ⇒ RTSP::Response
Sends the GET_PARAMETERS request.
363 364 365 366 367 368 369 370 |
# File 'lib/rtsp/client.rb', line 363 def get_parameter(track, body="", additional_headers={}) = RTSP::Message.get_parameter(track).with_headers({ cseq: @cseq }) .add_headers additional_headers .body = body request() end |
#media_control_tracks ⇒ Array<String>
Extracts the value of the “control” attribute from all media sections of the session description (SDP). You have to call the #describe
method in order to get the session description info.
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 |
# File 'lib/rtsp/client.rb', line 465 def media_control_tracks tracks = [] if @session_description.nil? tracks << "" else @session_description.media_sections.each do |media_section| media_section[:attributes].each do |a| tracks << "#{@content_base}#{a[:value]}" if a[:attribute] == "control" end end end tracks end |
#options(additional_headers = {}) ⇒ RTSP::Response
Sends an OPTIONS message to the server specified by @server_uri. Sets @supported_methods based on the list of supported methods returned in the Public headers.
178 179 180 181 182 183 184 185 186 |
# File 'lib/rtsp/client.rb', line 178 def (additional_headers={}) = RTSP::Message.(@server_uri.to_s).with_headers({ cseq: @cseq }) .add_headers additional_headers request() do |response| @supported_methods = extract_supported_methods_from response.public end end |
#pause(track, additional_headers = {}) ⇒ RTSP::Response
Sends the PAUSE request and sets @session_state to :ready
.
314 315 316 317 318 319 320 321 322 323 324 |
# File 'lib/rtsp/client.rb', line 314 def pause(track, additional_headers={}) = RTSP::Message.pause(track).with_headers({ cseq: @cseq, session: @session[:session_id] }) .add_headers additional_headers request() do if [:playing, :recording].include? @session_state @session_state = :ready end end end |
#play(track, additional_headers = {}) ⇒ RTSP::Response
If playback over UDP doesn’t result in any data coming in on the socket, re-setup with RTP/AVP/TCP;unicast;interleaved=0-1.
Sends the PLAY request and sets @session_state to :playing
.
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
# File 'lib/rtsp/client.rb', line 284 def play(track, additional_headers={}) = RTSP::Message.play(track).with_headers({ cseq: @cseq, session: @session[:session_id] }) .add_headers additional_headers request() do unless @session_state == :ready raise RTSP::Error, "Session not set up yet. Run #setup first." end if @play_thread.nil? RTSP::Client.log "Capturing RTP data on port #{@transport[:client_port][:rtp]}" unless @capturer.running? @play_thread = Thread.new do @capturer.run end end end @session_state = :playing end end |
#record(track, additional_headers = {}) ⇒ RTSP::Response
Sends the RECORD request and sets @session_state to :recording
.
394 395 396 397 398 399 400 |
# File 'lib/rtsp/client.rb', line 394 def record(track, additional_headers={}) = RTSP::Message.record(track).with_headers({ cseq: @cseq, session: @session[:session_id] }) .add_headers additional_headers request() { @session_state = :recording } end |
#request(message) {|RTSP::Response| ... } ⇒ RTSP::Response
Executes the Request with the arguments passed in, yields the response to the calling block, checks the CSeq response and the session response, then increments @cseq by 1. Handles any exceptions raised during the Request.
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 |
# File 'lib/rtsp/client.rb', line 411 def request response = #compare_sequence_number response.cseq @cseq += 1 if response.code.to_s =~ /2../ yield response if block_given? elsif response.code.to_s =~ /(4|5)../ if (defined? response.connection) && response.connection == 'Close' reset_state end raise RTSP::Error, "#{response.code}: #{response.}" else raise RTSP::Error, "Unknown Response code: #{response.code}" end dont_ensure_list = [:options, :describe, :teardown, :set_parameter, :get_parameter] unless dont_ensure_list.include? .method_type ensure_session end response end |
#request_transport ⇒ String
Builds the Transport header fields string based on info used in setting up the Client instance.
237 238 239 240 |
# File 'lib/rtsp/client.rb', line 237 def request_transport value = "RTP/AVP;#{@capturer.broadcast_type};client_port=" value << "#{@capturer.rtp_port}-#{@capturer.rtp_port + 1}\r\n" end |
#reset_state ⇒ Object
Sets state related variables back to their starting values; @session_state is set to :init
; @session is set to 0.
351 352 353 354 |
# File 'lib/rtsp/client.rb', line 351 def reset_state @session_state = :init @session = {} end |
#send_message(message) ⇒ RTSP::Response
Sends the message over the socket.
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/rtsp/client.rb', line 148 def RTSP::Client.log "Sending #{.method_type.upcase} to #{.request_uri}" .to_s.each_line { |line| RTSP::Client.log line.strip } begin response = Timeout::timeout(@connection.timeout) do @connection.socket.send(.to_s, 0) socket_data = @connection.socket.recvfrom MAX_BYTES_TO_RECEIVE RTSP::Response.new socket_data.first end rescue Timeout::Error raise RTSP::Error, "Request took more than #{@connection.timeout} seconds to send." end RTSP::Client.log "Received response:" if response response.to_s.each_line { |line| RTSP::Client.log line.strip } end response end |
#server_url=(new_url) ⇒ Object
The URL for the RTSP server to talk to can change if multiple servers are involved in delivering content. This method can be used to change the server to talk to on the fly.
138 139 140 |
# File 'lib/rtsp/client.rb', line 138 def server_url=(new_url) @server_uri = build_resource_uri_from new_url end |
#set_parameter(track, parameters, additional_headers = {}) ⇒ RTSP::Response
Sends the SET_PARAMETERS request.
379 380 381 382 383 384 385 386 |
# File 'lib/rtsp/client.rb', line 379 def set_parameter(track, parameters, additional_headers={}) = RTSP::Message.set_parameter(track).with_headers({ cseq: @cseq }) .add_headers additional_headers .body = parameters request() end |
#setup(track, additional_headers = {}) ⇒ RTSP::Response
@session numbers are relevant to tracks, and a client must be able to play multiple tracks at the same time.
Sends the SETUP request, then sets @session to the value returned in the Session header from the server, then sets the @session_state to :ready
.
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 |
# File 'lib/rtsp/client.rb', line 251 def setup(track, additional_headers={}) = RTSP::Message.setup(track).with_headers({ cseq: @cseq, transport: request_transport }) .add_headers additional_headers request() do |response| if @session_state == :init @session_state = :ready end @session = response.session parser = RTSP::TransportParser.new @transport = parser.parse response.transport unless @transport[:transport_protocol].nil? @capturer.transport_protocol = @transport[:transport_protocol] end @capturer.rtp_port = @transport[:client_port][:rtp].to_i @capturer.broadcast_type = @transport[:broadcast_type] end end |
#teardown(track, additional_headers = {}) ⇒ RTSP::Response
Sends the TEARDOWN request, then resets all state-related instance variables.
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 |
# File 'lib/rtsp/client.rb', line 333 def teardown(track, additional_headers={}) = RTSP::Message.teardown(track).with_headers({ cseq: @cseq, session: @session[:session_id] }) .add_headers additional_headers request() do reset_state if @play_thread @capturer.stop @capturer.rtp_file.close @play_thread.exit end end end |