Class: RTSP::Server

Inherits:
Object
  • Object
show all
Extended by:
Global
Defined in:
lib/rtsp/server.rb

Overview

Instantiates an RTSP Server Streaming is performed using socat. All you need is the multicast source RTP host and port.

require ‘rtsp/server’ server = RTSP::Server.new “10.221.222.90”, 8554

This is for the stream at index 1 (rtsp://10.221.222.90:8554/stream1) RTSP::StreamServer.instance.source_ip << “239.221.222.241” RTSP::StreamServer.instance.source_port << 6780 RTSP::StreamServer.instance.fmtp << “96 packetization-mode=1…” RTSP::StreamServer.instance.rtp_map << “96 H264/90000”

This is for the stream at index 2 (rtsp://10.221.222.90:8554/stream2) RTSP::StreamServer.instance.source_ip << “239.221.222.141” RTSP::StreamServer.instance.source_port << 6740 RTSP::StreamServer.instance.fmtp << “96 packetization-mode=1…” RTSP::StreamServer.instance.rtp_map << “96 MP4/90000”

Now start the server server.start

Constant Summary collapse

OPTIONS_LIST =
%w(OPTIONS DESCRIBE SETUP TEARDOWN PLAY
PAUSE GET_PARAMETER SET_PARAMETER)

Constants included from Global

Global::DEFAULT_RTSP_PORT, Global::DEFAULT_VERSION

Instance Attribute Summary collapse

Attributes included from Global

#log, #log_level, #logger, #raise_errors

Instance Method Summary collapse

Methods included from Global

log?, raise_errors?, reset_config!, rtsp_version

Constructor Details

#initialize(host, port = 554) ⇒ Server

Initializes the the Stream Server.

Parameters:

  • host (Fixnum)

    IP interface to bind.

  • port (Fixnum) (defaults to: 554)

    RTSP port.



45
46
47
48
49
50
51
52
53
54
# File 'lib/rtsp/server.rb', line 45

def initialize(host, port=554)
  @session =  rand(99999999)
  @stream_server = RTSP::StreamServer.instance
  @interface_ip = host
  @stream_server.interface_ip = host
  @tcp_server = TCPServer.new(host, port)
  @udp_server = UDPSocket.new
  @udp_server.bind(host, port)
  @agent = {}
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args, &block) ⇒ Object

Handles unsupported RTSP requests.

Parameters:

  • method_name (Symbol)

    Method name to be called.

  • args (Array)

    Arguments to be passed in to the method.

  • block (Proc)

    A block of code to be passed to a method.



298
299
300
301
302
# File 'lib/rtsp/server.rb', line 298

def method_missing(method_name, *args, &block)
  RTSP::Server.log("Received request for #{method_name} (not implemented)", :warn)

  [[], "Not Implemented"]
end

Instance Attribute Details

#agentObject

Returns the value of attribute agent.



39
40
41
# File 'lib/rtsp/server.rb', line 39

def agent
  @agent
end

#options_listObject

Returns the value of attribute options_list.



36
37
38
# File 'lib/rtsp/server.rb', line 36

def options_list
  @options_list
end

#sessionObject

Returns the value of attribute session.



38
39
40
# File 'lib/rtsp/server.rb', line 38

def session
  @session
end

#versionObject

Returns the value of attribute version.



37
38
39
# File 'lib/rtsp/server.rb', line 37

def version
  @version
end

Instance Method Details

#add_headers(request, response, body, status = "200 OK") ⇒ Array<Array<String>>

Adds the headers to the response.

Parameters:

  • request (RTSP::Request)
  • response (Array<String>)

    Response headers

  • body (String)

    Response body

  • status (String) (defaults to: "200 OK")

    Response status

Returns:

  • (Array<Array<String>>)

    Response headers and body.



274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/rtsp/server.rb', line 274

def add_headers(request, response, body, status="200 OK")
  result = []
  version ||= SUPPORTED_VERSION
  result << "RTSP/#{version} #{status}"
  result << "CSeq: #{request.cseq}"

  unless body.nil?
    result << "Content-Type: #{request.accept}" rescue NoMethodError
    result << "Content-Base: #{request.url}/" rescue NoMethodError
    result << "Content-Length: #{body.size}"
  end

  result << "Date: #{Time.now.gmtime.strftime('%a, %b %d %Y %H:%M:%S GMT')}"
  result << response.join("\r\n") unless response.nil?
  result << body unless body.nil?

  result.flatten.join "\r\n"
end

#announce(request) ⇒ Array<Array<String>>

Handles the announce request.

Parameters:

Returns:

  • (Array<Array<String>>)

    Response headers and body.



160
161
162
# File 'lib/rtsp/server.rb', line 160

def announce(request)
  []
end

#describe(request) ⇒ Array<Array<String>>

Handles the describe request.

Parameters:

Returns:

  • (Array<Array<String>>)

    Response headers and body.



149
150
151
152
153
154
# File 'lib/rtsp/server.rb', line 149

def describe(request)
  RTSP::Server.log "Received DESCRIBE request from #{request.remote_host}"
  description = @stream_server.description(request.multicast?, request.stream_index)

  [[], description]
end

#get_parameter(request) ⇒ Array<Array<String>>

Handles the get_parameter request.

Parameters:

Returns:

  • (Array<Array<String>>)

    Response headers and body.



209
210
211
212
213
214
# File 'lib/rtsp/server.rb', line 209

def get_parameter(request)
  RTSP::Server.log "Received GET_PARAMETER request from #{request.remote_host}"
  " Pending Implementation"

  [[]]
end

#options(request) ⇒ Array<Array<String>>

Handles the options request.

Parameters:

Returns:

  • (Array<Array<String>>)

    Response headers and body.



136
137
138
139
140
141
142
143
# File 'lib/rtsp/server.rb', line 136

def options(request)
  RTSP::Server.log "Received OPTIONS request from #{request.remote_host}"
  response = []
  response << "Public: #{OPTIONS_LIST.join ','}"
  response << "\r\n"

  [response]
end

#pause(request) ⇒ Array<Array<String>>

Handles a pause request.

Parameters:

Returns:

  • (Array<Array<String>>)

    Response headers and body.



256
257
258
259
260
261
262
263
264
265
# File 'lib/rtsp/server.rb', line 256

def pause(request)
  RTSP::Server.log "Received PAUSE request from #{request.remote_host}"
  response = []
  sid = request.session[:session_id] rescue NoMethodError
  response << "Session: #{sid}"
  response << "\r\n"
  @stream_server.disconnect sid

  [response]
end

#play(request) ⇒ Array<Array<String>>

Handles the play request.

Parameters:

Returns:

  • (Array<Array<String>>)

    Response headers and body.



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/rtsp/server.rb', line 187

def play(request)
  RTSP::Server.log "Received PLAY request from #{request.remote_host}"
  sid = request.session[:session_id] rescue NoMethodError
  response = []
  response << "Session: #{sid}"
  response << "Range: #{request.range}" rescue NoMethodError
  index = request.stream_index - 1 rescue NoMethodError
  index = 1 unless index.is_a?(Fixnum)
  rtp_sequence, rtp_timestamp = @stream_server.parse_sequence_number(
    @stream_server.source_ip[index], @stream_server.source_port[index])
  @stream_server.start_streaming sid
  response << "RTP-Info: url=#{request.url}/track1;" +
    "seq=#{rtp_sequence + 6} ;rtptime=#{rtp_timestamp}"
  response << "\r\n"

  [response]
end

#process_request(request_str, io) ⇒ String

Process an RTSP request

Parameters:

  • request_str (String)

    RTSP request.

  • remote_address (String)

    IP address of sender.

Returns:

  • (String)

    Response.



122
123
124
125
126
127
128
129
130
# File 'lib/rtsp/server.rb', line 122

def process_request(request_str, io)
  remote_address = io.remote_address.ip_address
  /(?<action>.*) rtsp:\/\// =~ request_str
  request = RTSP::Request.new(request_str, remote_address)
  @agent[io] = request.user_agent rescue NoMethodError
  response, body = send(action.downcase.to_sym, request)

  add_headers(request, response, body)
end

#redirect(request) ⇒ Array<Array<String>>

Handles the redirect request.

Parameters:

Returns:

  • (Array<Array<String>>)

    Response headers and body.



231
232
233
234
235
236
# File 'lib/rtsp/server.rb', line 231

def redirect(request)
  RTSP::Server.log "Received REDIRECT request from #{request.remote_host}"
  " Pending Implementation"

  [[]]
end

#serve(io) ⇒ Object

Serves a client request.

Parameters:

  • io (IO)

    Request/response socket object.



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/rtsp/server.rb', line 87

def serve io
  request_str = ""
  count = 0

  begin
    request_str << io.read_nonblock(500)
  rescue IO::WaitReadable, EOFError => ex
    return(-1) if ex.is_a?(EOFError)
    count += 1
    sleep 0.001
    sleep 1 if count > 10
    count = 0 unless request_str == ""
    retry if count < 70
    return -1 if request_str == ""
  end
  
  responses = []
  puts "REQUEST       **********************************"
  p request_str

  request_str.split("\r\n\r\n").each do |r|
    responses << process_request(r, io)
  end

  puts "RESPONSE      **********************************"
  p responses
 io.send(responses.join, 0)
 return(-1) if request_str.include?("TEARDOWN")
end

#set_parameter(request) ⇒ Array<Array<String>>

Handles the set_parameter request.

Parameters:

Returns:

  • (Array<Array<String>>)

    Response headers and body.



220
221
222
223
224
225
# File 'lib/rtsp/server.rb', line 220

def set_parameter(request)
  RTSP::Server.log "Received SET_PARAMETER request from #{request.remote_host}"
  " Pending Implementation"

  [[]]
end

#setup(request) ⇒ Array<Array<String>>

Handles the setup request.

Parameters:

Returns:

  • (Array<Array<String>>)

    Response headers and body.



168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/rtsp/server.rb', line 168

def setup(request)
  RTSP::Server.log "Received SETUP request from #{request.remote_host}"
  @session = @session.next
  multicast_check = request.transport.include?('multicast')
  server_port = @stream_server.setup_streamer(@session,
    request, request.stream_index, multicast_check)
  response = []
  transport = generate_transport(request, server_port, request.stream_index)
  response << "Transport: #{transport.join}"
  response << "Session: #{@session}; timeout=60"
  response << "\r\n"

  [response]
end

#startObject

Starts accepting TCP connections



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/rtsp/server.rb', line 57

def start
  Thread.start { udp_listen }

  loop do
    client = @tcp_server.accept

    Thread.start do
      begin
        loop { break if serve(client) == -1 }
      rescue EOFError
        # do nothing
      ensure
        client.close unless @agent[client].include? "QuickTime"
      end
    end
  end
end

#teardown(request) ⇒ Array<Array<String>>

Handles the teardown request.

Parameters:

Returns:

  • (Array<Array<String>>)

    Response headers and body.



242
243
244
245
246
247
248
249
250
# File 'lib/rtsp/server.rb', line 242

def teardown(request)
  RTSP::Server.log "Received TEARDOWN request from #{request.remote_host}"
  sid = request.session[:session_id] rescue NoMethodError
  @stream_server.stop_streaming sid
  response = []
  response << "\r\n"

  [response]
end

#udp_listenObject

Listens on the UDP socket for RTSP requests.



76
77
78
79
80
81
82
# File 'lib/rtsp/server.rb', line 76

def udp_listen
  loop do
    data, sender = @udp_server.recvfrom(500)
    response = process_request(data, sender[3])
    @udp_server.send(response, 0, sender[3], sender[1])
  end
end