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.



294
295
296
297
298
# File 'lib/rtsp/server.rb', line 294

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.



270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/rtsp/server.rb', line 270

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.



156
157
158
# File 'lib/rtsp/server.rb', line 156

def announce(request)
  []
end

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

Handles the describe request.

Parameters:

Returns:

  • (Array<Array<String>>)

    Response headers and body.



145
146
147
148
149
150
# File 'lib/rtsp/server.rb', line 145

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.



205
206
207
208
209
210
# File 'lib/rtsp/server.rb', line 205

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.



132
133
134
135
136
137
138
139
# File 'lib/rtsp/server.rb', line 132

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.



252
253
254
255
256
257
258
259
260
261
# File 'lib/rtsp/server.rb', line 252

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.



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/rtsp/server.rb', line 183

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.



118
119
120
121
122
123
124
125
126
# File 'lib/rtsp/server.rb', line 118

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.



227
228
229
230
231
232
# File 'lib/rtsp/server.rb', line 227

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
# 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 = []

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

 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.



216
217
218
219
220
221
# File 'lib/rtsp/server.rb', line 216

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.



164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/rtsp/server.rb', line 164

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.transport_url, 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.



238
239
240
241
242
243
244
245
246
# File 'lib/rtsp/server.rb', line 238

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