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.
- checksums.yaml +7 -0
- data/.gemtest +0 -0
- data/ChangeLog.rdoc +74 -0
- data/Gemfile +3 -0
- data/LICENSE.rdoc +20 -0
- data/README.rdoc +152 -0
- data/Rakefile +23 -0
- data/bin/rtsp_client +133 -0
- data/features/client_changes_state.feature +58 -0
- data/features/client_requests.feature +27 -0
- data/features/control_streams_as_client.feature +26 -0
- data/features/step_definitions/client_changes_state_steps.rb +52 -0
- data/features/step_definitions/client_requests_steps.rb +68 -0
- data/features/step_definitions/control_streams_as_client_steps.rb +34 -0
- data/features/support/env.rb +50 -0
- data/features/support/hooks.rb +3 -0
- data/lib/ext/logger.rb +8 -0
- data/lib/rtsp/client.rb +520 -0
- data/lib/rtsp/common.rb +148 -0
- data/lib/rtsp/error.rb +6 -0
- data/lib/rtsp/global.rb +63 -0
- data/lib/rtsp/helpers.rb +28 -0
- data/lib/rtsp/message.rb +272 -0
- data/lib/rtsp/request.rb +39 -0
- data/lib/rtsp/response.rb +47 -0
- data/lib/rtsp/server.rb +311 -0
- data/lib/rtsp/socat_streaming.rb +320 -0
- data/lib/rtsp/stream_server.rb +37 -0
- data/lib/rtsp/transport_parser.rb +96 -0
- data/lib/rtsp/version.rb +4 -0
- data/lib/rtsp.rb +6 -0
- data/rtsp.gemspec +44 -0
- data/spec/rtsp/client_spec.rb +326 -0
- data/spec/rtsp/helpers_spec.rb +53 -0
- data/spec/rtsp/message_spec.rb +420 -0
- data/spec/rtsp/response_spec.rb +306 -0
- data/spec/rtsp/transport_parser_spec.rb +137 -0
- data/spec/rtsp_spec.rb +27 -0
- data/spec/spec_helper.rb +88 -0
- data/spec/support/fake_rtsp_server.rb +123 -0
- data/tasks/roodi.rake +9 -0
- data/tasks/roodi_config.yaml +14 -0
- data/tasks/stats.rake +12 -0
- metadata +280 -0
data/lib/rtsp/common.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
module RTSP
|
2
|
+
|
3
|
+
# Contains common methods belonging to Request and Response classes.
|
4
|
+
module Common
|
5
|
+
|
6
|
+
# @return [String] The unparsed request as a String.
|
7
|
+
def to_s
|
8
|
+
@raw_body
|
9
|
+
end
|
10
|
+
|
11
|
+
# Custom redefine to make sure all the dynamically created instance
|
12
|
+
# variables are displayed when this method is called.
|
13
|
+
#
|
14
|
+
# @return [String]
|
15
|
+
def inspect
|
16
|
+
me = "#<#{self.class.name}:#{self.__id__} "
|
17
|
+
|
18
|
+
self.instance_variables.each do |variable|
|
19
|
+
me << "#{variable}=#{instance_variable_get(variable).inspect}, "
|
20
|
+
end
|
21
|
+
|
22
|
+
me.sub!(/, $/, "")
|
23
|
+
me << ">"
|
24
|
+
|
25
|
+
me
|
26
|
+
end
|
27
|
+
|
28
|
+
# Takes the raw request text and splits it into a 2-element Array, where 0
|
29
|
+
# is the text containing the headers and 1 is the text containing the body.
|
30
|
+
#
|
31
|
+
# @param [String] raw_request
|
32
|
+
# @return [Array<String>] 2-element Array containing the head and body of
|
33
|
+
# the request. Body will be nil if there wasn't one in the request.
|
34
|
+
def split_head_and_body_from raw_request
|
35
|
+
head_and_body = raw_request.split("\r\n\r\n", 2)
|
36
|
+
head = head_and_body.first
|
37
|
+
body = head_and_body.last == head ? nil : head_and_body.last
|
38
|
+
|
39
|
+
[head, body]
|
40
|
+
end
|
41
|
+
|
42
|
+
# Pulls out the RTSP version, request code, and request message (AKA the
|
43
|
+
# status line info) into instance variables.
|
44
|
+
#
|
45
|
+
# @param [String] line The String containing the status line info.
|
46
|
+
def extract_status_line(line)
|
47
|
+
/RTSP\/(?<rtsp_version>\d\.\d)/ =~ line
|
48
|
+
/(?<url>rtsp:\/\/.*) RTSP/ =~ line
|
49
|
+
/rtsp:\/\/.*stream(?<stream_index>\d*)m?\/?.* RTSP/ =~ line
|
50
|
+
@url = url
|
51
|
+
@stream_index = stream_index.to_i
|
52
|
+
|
53
|
+
if rtsp_version.nil?
|
54
|
+
raise RTSP::Error, "Status line corrupted: #{line}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns the transport URL.
|
59
|
+
#
|
60
|
+
# @return [String] Transport URL associated with the request.
|
61
|
+
def transport_url
|
62
|
+
/client_port=(?<port>.*)-/ =~ transport
|
63
|
+
|
64
|
+
if port.nil?
|
65
|
+
log("Could not find client port associated with transport", :warn)
|
66
|
+
else
|
67
|
+
"#{@remote_host}:#{port}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Checks if the request is for a multicast stream.
|
72
|
+
#
|
73
|
+
# @return [Boolean] true if the request is for a multicast stream.
|
74
|
+
def multicast?
|
75
|
+
return false if @url.nil?
|
76
|
+
|
77
|
+
@url.end_with? "m"
|
78
|
+
end
|
79
|
+
|
80
|
+
# Reads through each header line of the RTSP request, extracts the
|
81
|
+
# request code, request message, request version, and creates a
|
82
|
+
# snake-case accessor with that value set.
|
83
|
+
#
|
84
|
+
# @param [String] head The section of headers from the request text.
|
85
|
+
def parse_head head
|
86
|
+
lines = head.split "\r\n"
|
87
|
+
|
88
|
+
lines.each_with_index do |line, i|
|
89
|
+
if i == 0
|
90
|
+
extract_status_line(line)
|
91
|
+
next
|
92
|
+
end
|
93
|
+
|
94
|
+
if line.include? "Session: "
|
95
|
+
value = {}
|
96
|
+
line =~ /Session: (\d+)/
|
97
|
+
value[:session_id] = $1.to_i
|
98
|
+
|
99
|
+
if line =~ /timeout=(.+)/
|
100
|
+
value[:timeout] = $1.to_i
|
101
|
+
end
|
102
|
+
|
103
|
+
create_reader("session", value)
|
104
|
+
elsif line.include? ": "
|
105
|
+
header_and_value = line.strip.split(":", 2)
|
106
|
+
header_name = header_and_value.first.downcase.gsub(/-/, "_")
|
107
|
+
create_reader(header_name, header_and_value[1].strip)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Reads through each line of the RTSP response body and parses it if
|
113
|
+
# needed. Returns a SDP::Description if the Content-Type is
|
114
|
+
# 'application/sdp', otherwise returns the String that was passed in.
|
115
|
+
#
|
116
|
+
# @param [String] body
|
117
|
+
# @return [SDP::Description,String]
|
118
|
+
def parse_body body
|
119
|
+
if body =~ /^(\r\n|\n)/
|
120
|
+
body.gsub!(/^(\r\n|\n)/, '')
|
121
|
+
end
|
122
|
+
|
123
|
+
if @content_type == "application/sdp"
|
124
|
+
SDP.parse body
|
125
|
+
else
|
126
|
+
body
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
# Creates an attr_reader with the name given and sets it to the value
|
133
|
+
# that's given.
|
134
|
+
#
|
135
|
+
# @param [String] name
|
136
|
+
# @param [String,Hash] value
|
137
|
+
def create_reader(name, value)
|
138
|
+
unless value.empty?
|
139
|
+
if value.is_a? String
|
140
|
+
value = value =~ /^[0-9]*$/ ? value.to_i : value
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
instance_variable_set("@#{name}", value)
|
145
|
+
self.instance_eval "def #{name}; @#{name}; end"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
data/lib/rtsp/error.rb
ADDED
data/lib/rtsp/global.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require_relative '../ext/logger'
|
2
|
+
|
3
|
+
module RTSP
|
4
|
+
module Global
|
5
|
+
DEFAULT_RTSP_PORT = 554
|
6
|
+
DEFAULT_VERSION = '1.0'
|
7
|
+
|
8
|
+
# Sets whether to log RTSP requests & responses.
|
9
|
+
attr_writer :log
|
10
|
+
|
11
|
+
# @return [Boolean] true if logging is enabled; false if it's turned off.
|
12
|
+
def log?
|
13
|
+
@log != false
|
14
|
+
end
|
15
|
+
|
16
|
+
# Sets the type logger to use.
|
17
|
+
attr_writer :logger
|
18
|
+
|
19
|
+
# By default, this creates a standard Ruby Logger. If a different type was
|
20
|
+
# passed in via +#logger=+, this returns that object.
|
21
|
+
#
|
22
|
+
# @return [Logger]
|
23
|
+
def logger
|
24
|
+
@logger ||= ::Logger.new STDOUT
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [Symbol] The Logger method to use for logging all messages.
|
28
|
+
attr_writer :log_level
|
29
|
+
|
30
|
+
# The Logger method to use for logging all messages.
|
31
|
+
#
|
32
|
+
# @return [Symbol] Defaults to +:debug+.
|
33
|
+
def log_level
|
34
|
+
@log_level ||= :debug
|
35
|
+
end
|
36
|
+
|
37
|
+
# @param [String] message The string to log.
|
38
|
+
def log(message, level=log_level)
|
39
|
+
logger.send(level, message) if log?
|
40
|
+
end
|
41
|
+
|
42
|
+
# Use to disable the raising of +RTSP::Error+s.
|
43
|
+
attr_writer :raise_errors
|
44
|
+
|
45
|
+
# @return [Boolean] true if set to raise errors; false if not.
|
46
|
+
def raise_errors?
|
47
|
+
@raise_errors != false
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [String] The RTSP version.
|
51
|
+
def rtsp_version
|
52
|
+
@version ||= DEFAULT_VERSION
|
53
|
+
end
|
54
|
+
|
55
|
+
# Resets class variables back to defaults.
|
56
|
+
def reset_config!
|
57
|
+
self.log = true
|
58
|
+
self.logger = ::Logger.new STDOUT
|
59
|
+
self.log_level = :debug
|
60
|
+
self.raise_errors = true
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/rtsp/helpers.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require_relative 'global'
|
3
|
+
require_relative 'error'
|
4
|
+
|
5
|
+
module RTSP
|
6
|
+
module Helpers
|
7
|
+
include RTSP::Global
|
8
|
+
|
9
|
+
# Takes the URL given and turns it into a URI. This allows for enforcing
|
10
|
+
# values for each part of the URI.
|
11
|
+
#
|
12
|
+
# @param [String] url The URL to turn in to a URI.
|
13
|
+
# @return [URI]
|
14
|
+
def build_resource_uri_from url
|
15
|
+
if url.is_a? String
|
16
|
+
url = "rtsp://#{url}" unless url =~ /^rtsp/
|
17
|
+
|
18
|
+
resource_uri = URI.parse url
|
19
|
+
resource_uri.port ||= DEFAULT_RTSP_PORT
|
20
|
+
|
21
|
+
resource_uri
|
22
|
+
|
23
|
+
else
|
24
|
+
raise RTSP::Error, "url must be a String."
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/rtsp/message.rb
ADDED
@@ -0,0 +1,272 @@
|
|
1
|
+
require_relative 'helpers'
|
2
|
+
require_relative 'error'
|
3
|
+
require_relative 'version'
|
4
|
+
|
5
|
+
module RTSP
|
6
|
+
|
7
|
+
# This class is responsible for building a single RTSP message that can be
|
8
|
+
# used by both clients and servers.
|
9
|
+
#
|
10
|
+
# Only message types defined in {RFC 2326}[http://tools.ietf.org/html/rfc2326]
|
11
|
+
# are implemented, however if you need to add a new message type (perhaps for
|
12
|
+
# some custom server implementation?), you can simply add to the supported
|
13
|
+
# list by:
|
14
|
+
# RTSP::Message.message_types << :barrel_roll
|
15
|
+
#
|
16
|
+
# You can then build it like a standard message:
|
17
|
+
# message = RTSP::Message.barrel_roll("192.168.1.10").with_headers({
|
18
|
+
# cseq: 123, content_type: "video/x-m4v" })
|
19
|
+
class Message
|
20
|
+
include RTSP::Helpers
|
21
|
+
|
22
|
+
RTSP_ACCEPT_TYPE = "application/sdp"
|
23
|
+
RTSP_DEFAULT_NPT = "0.000-"
|
24
|
+
RTSP_DEFAULT_SEQUENCE_NUMBER = 1
|
25
|
+
USER_AGENT =
|
26
|
+
"RubyRTSP/#{RTSP::VERSION} (Ruby #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL})"
|
27
|
+
|
28
|
+
@message_types = [
|
29
|
+
:announce,
|
30
|
+
:describe,
|
31
|
+
:get_parameter,
|
32
|
+
:options,
|
33
|
+
:play,
|
34
|
+
:pause,
|
35
|
+
:record,
|
36
|
+
:redirect,
|
37
|
+
:set_parameter,
|
38
|
+
:setup,
|
39
|
+
:teardown
|
40
|
+
]
|
41
|
+
|
42
|
+
# TODO: define #describe somewhere so I can actually test that method.
|
43
|
+
class << self
|
44
|
+
|
45
|
+
# Lists the method/message types this class can create.
|
46
|
+
# @return [Array<Symbol>]
|
47
|
+
attr_accessor :message_types
|
48
|
+
|
49
|
+
# Make sure the class responds to our message types.
|
50
|
+
#
|
51
|
+
# @param [Symbol] method
|
52
|
+
def respond_to?(method)
|
53
|
+
@message_types.include?(method) || super
|
54
|
+
end
|
55
|
+
|
56
|
+
# Creates a new message based on the given method type and URI.
|
57
|
+
#
|
58
|
+
# @param [Symbol] method
|
59
|
+
# @param [Array] args
|
60
|
+
# @return [RTSP::Message]
|
61
|
+
def method_missing(method, *args)
|
62
|
+
request_uri = args.first
|
63
|
+
|
64
|
+
if @message_types.include? method
|
65
|
+
self.new(method, request_uri)
|
66
|
+
else
|
67
|
+
super
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
attr_reader :method_type
|
73
|
+
attr_reader :request_uri
|
74
|
+
attr_reader :headers
|
75
|
+
attr_reader :body
|
76
|
+
attr_writer :rtsp_version
|
77
|
+
|
78
|
+
# @param [Symbol] :method_type The RTSP method to build and send.
|
79
|
+
# @param [String] request_uri The URL to communicate to.
|
80
|
+
def initialize(method_type, request_uri)
|
81
|
+
@method_type = method_type
|
82
|
+
@request_uri = build_resource_uri_from request_uri
|
83
|
+
@headers = default_headers
|
84
|
+
@body = ""
|
85
|
+
@version = DEFAULT_VERSION
|
86
|
+
end
|
87
|
+
|
88
|
+
# Adds the header and its value to the list of headers for the message.
|
89
|
+
#
|
90
|
+
# @param [Symbol] type The header type.
|
91
|
+
# @param [] value The value to set the header field to.
|
92
|
+
def header(type, value)
|
93
|
+
if type.is_a? Symbol
|
94
|
+
headers[type] = value
|
95
|
+
else
|
96
|
+
raise RTSP::Error, "Header type must be a Symbol (i.e. :cseq)."
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Use to message-chain with one of the method types; used when creating a
|
101
|
+
# new Message to add headers you want.
|
102
|
+
#
|
103
|
+
# @example Simple header
|
104
|
+
# RTSP::Message.options("192.168.1.10").with_headers({ cseq: @cseq })
|
105
|
+
# @example Multi-word header
|
106
|
+
# RTSP::Message.options("192.168.1.10").with_headers({ user_agent:
|
107
|
+
# 'My RTSP Client 1.0' }) # => "OPTIONS 192.168.1.10 RTSP 1.0\r\n
|
108
|
+
# # CSeq: 1\r\n
|
109
|
+
# # User-Agent: My RTSP Client 1.0\r\n"
|
110
|
+
# @param [Hash] new_headers The headers to add to the Request. The Hash
|
111
|
+
# key of each will be converted from snake_case to Rtsp-Style.
|
112
|
+
# @return [RTSP::Message]
|
113
|
+
def with_headers(new_headers)
|
114
|
+
add_headers new_headers
|
115
|
+
|
116
|
+
self
|
117
|
+
end
|
118
|
+
|
119
|
+
def add_headers(new_headers)
|
120
|
+
@headers.merge! new_headers
|
121
|
+
end
|
122
|
+
|
123
|
+
# Use when creating a new Message to add body you want.
|
124
|
+
#
|
125
|
+
# @example Simple header
|
126
|
+
# RTSP::Message.options("192.168.1.10").with_body("The body!")
|
127
|
+
# @param [Hash] new_headers The headers to add to the Request. The Hash
|
128
|
+
# key will be capitalized; if
|
129
|
+
def with_body(new_body)
|
130
|
+
add_body new_body
|
131
|
+
|
132
|
+
self
|
133
|
+
end
|
134
|
+
|
135
|
+
def add_body new_body
|
136
|
+
add_headers({ content_length: new_body.length })
|
137
|
+
@body = new_body
|
138
|
+
end
|
139
|
+
|
140
|
+
# @param [String] value Content to send as the body of the message.
|
141
|
+
# Generally this will be a String of some sort, but could be binary data as
|
142
|
+
# well. Also, this adds the Content-Length header to the header list.
|
143
|
+
def body= value
|
144
|
+
add_body value
|
145
|
+
end
|
146
|
+
|
147
|
+
# @return [String] The message as a String.
|
148
|
+
def to_s
|
149
|
+
message.to_s
|
150
|
+
end
|
151
|
+
|
152
|
+
###########################################################################
|
153
|
+
# PRIVATES
|
154
|
+
private
|
155
|
+
|
156
|
+
# Builds the request message to send to the server/client.
|
157
|
+
#
|
158
|
+
# @return [String]
|
159
|
+
def message
|
160
|
+
message = "#{@method_type.to_s.upcase} #{@request_uri} RTSP/#{@version}\r\n"
|
161
|
+
message << headers_to_s(@headers)
|
162
|
+
message << "\r\n"
|
163
|
+
message << "#{@body}" unless @body.nil?
|
164
|
+
|
165
|
+
#message.each_line { |line| RTSP::Client.log line.strip }
|
166
|
+
|
167
|
+
message
|
168
|
+
end
|
169
|
+
|
170
|
+
# Returns the required/default headers for the provided method.
|
171
|
+
#
|
172
|
+
# @return [Hash] The default headers for the given method.
|
173
|
+
def default_headers
|
174
|
+
headers = {}
|
175
|
+
|
176
|
+
headers[:cseq] ||= RTSP_DEFAULT_SEQUENCE_NUMBER
|
177
|
+
headers[:user_agent] ||= USER_AGENT
|
178
|
+
|
179
|
+
case @method_type
|
180
|
+
when :describe
|
181
|
+
headers[:accept] = RTSP_ACCEPT_TYPE
|
182
|
+
when :announce
|
183
|
+
headers[:content_type] = RTSP_ACCEPT_TYPE
|
184
|
+
when :play
|
185
|
+
headers[:range] = "npt=#{RTSP_DEFAULT_NPT}"
|
186
|
+
when :get_parameter
|
187
|
+
headers[:content_type] = 'text/parameters'
|
188
|
+
when :set_parameter
|
189
|
+
headers[:content_type] = 'text/parameters'
|
190
|
+
else
|
191
|
+
{}
|
192
|
+
end
|
193
|
+
|
194
|
+
headers
|
195
|
+
end
|
196
|
+
|
197
|
+
# Turns headers from Hash(es) into a String, where each element
|
198
|
+
# is a String in the form: [Header Type]: value(s)\r\n.
|
199
|
+
#
|
200
|
+
# @param [Hash] headers The headers to put to string.
|
201
|
+
# @return [String]
|
202
|
+
def headers_to_s headers
|
203
|
+
header_string = headers.inject("") do |result, (key, value)|
|
204
|
+
header_name = key.to_s.split(/_/).map do |header|
|
205
|
+
header.capitalize
|
206
|
+
end.join('-')
|
207
|
+
|
208
|
+
header_name = "CSeq" if header_name == "Cseq"
|
209
|
+
|
210
|
+
if value.is_a?(Hash) || value.is_a?(Array)
|
211
|
+
if header_name == "Content-Type"
|
212
|
+
values = values_to_s(value, ", ")
|
213
|
+
else
|
214
|
+
values = values_to_s(value)
|
215
|
+
end
|
216
|
+
|
217
|
+
result << "#{header_name}: #{values}\r\n"
|
218
|
+
else
|
219
|
+
result << "#{header_name}: #{value}\r\n"
|
220
|
+
end
|
221
|
+
|
222
|
+
result
|
223
|
+
end
|
224
|
+
|
225
|
+
arr = header_string.split "\r\n"
|
226
|
+
# Move the Session header to the top
|
227
|
+
session_index = arr.index { |a| a =~ /Session/ }
|
228
|
+
unless session_index.nil?
|
229
|
+
session = arr.delete_at(session_index)
|
230
|
+
arr.unshift(session)
|
231
|
+
end
|
232
|
+
|
233
|
+
# Move the User-Agent header to the top
|
234
|
+
user_agent_index = arr.index { |a| a =~ /User-Agent/ }
|
235
|
+
unless user_agent_index.nil?
|
236
|
+
user_agent = arr.delete_at(user_agent_index)
|
237
|
+
arr.unshift(user_agent)
|
238
|
+
end
|
239
|
+
|
240
|
+
# Move the CSeq header to the top
|
241
|
+
cseq_index = arr.index { |a| a =~ /CSeq/ }
|
242
|
+
cseq = arr.delete_at(cseq_index)
|
243
|
+
arr.unshift(cseq)
|
244
|
+
|
245
|
+
# Put it all back to a String
|
246
|
+
header_string = arr.join("\r\n")
|
247
|
+
header_string << "\r\n"
|
248
|
+
end
|
249
|
+
|
250
|
+
# Turns header values into a single string.
|
251
|
+
#
|
252
|
+
# @param [] values The header values to put to string.
|
253
|
+
# @param [String] separator The character to use to separate multiple
|
254
|
+
# values that define a header.
|
255
|
+
# @return [String] The header values as a single string.
|
256
|
+
def values_to_s(values, separator=";")
|
257
|
+
result = values.inject("") do |values_string, (header_field, header_field_value)|
|
258
|
+
if header_field.is_a? Symbol
|
259
|
+
values_string << "#{header_field}=#{header_field_value}"
|
260
|
+
elsif header_field.is_a? Hash
|
261
|
+
values_string << values_to_s(header_field)
|
262
|
+
else
|
263
|
+
values_string << header_field.to_s
|
264
|
+
end
|
265
|
+
|
266
|
+
values_string + separator
|
267
|
+
end
|
268
|
+
|
269
|
+
result.sub!(/#{separator}$/, '') if result.end_with? separator
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
data/lib/rtsp/request.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'sdp'
|
3
|
+
require_relative 'error'
|
4
|
+
require_relative 'global'
|
5
|
+
require_relative 'common'
|
6
|
+
|
7
|
+
module RTSP
|
8
|
+
|
9
|
+
# Parses raw request data from the server/client and turns it into
|
10
|
+
# attr_readers.
|
11
|
+
class Request
|
12
|
+
extend RTSP::Global
|
13
|
+
include RTSP::Common
|
14
|
+
|
15
|
+
attr_reader :rtsp_version
|
16
|
+
attr_reader :code
|
17
|
+
attr_reader :message
|
18
|
+
attr_reader :body
|
19
|
+
attr_reader :url
|
20
|
+
attr_reader :stream_index
|
21
|
+
attr_accessor :remote_host
|
22
|
+
|
23
|
+
# @param [String] raw_request The raw request string returned from the
|
24
|
+
# server/client.
|
25
|
+
# @param [String] remote_host The IP address of the remote host.
|
26
|
+
def initialize(raw_request, remote_host)
|
27
|
+
if raw_request.nil? || raw_request.empty?
|
28
|
+
raise RTSP::Error,
|
29
|
+
"#{self.class} received nil or empty string--this shouldn't happen."
|
30
|
+
end
|
31
|
+
|
32
|
+
@raw_body = raw_request
|
33
|
+
@remote_host = remote_host
|
34
|
+
|
35
|
+
head, body = split_head_and_body_from @raw_body
|
36
|
+
parse_head(head)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'sdp'
|
3
|
+
require_relative 'error'
|
4
|
+
require_relative 'common'
|
5
|
+
|
6
|
+
module RTSP
|
7
|
+
|
8
|
+
# Parses raw response data from the server/client and turns it into
|
9
|
+
# attr_readers.
|
10
|
+
class Response
|
11
|
+
include RTSP::Common
|
12
|
+
attr_reader :rtsp_version
|
13
|
+
attr_reader :code
|
14
|
+
attr_reader :message
|
15
|
+
attr_reader :body
|
16
|
+
|
17
|
+
# @param [String] raw_response The raw response string returned from the
|
18
|
+
# server/client.
|
19
|
+
def initialize(raw_response)
|
20
|
+
if raw_response.nil? || raw_response.empty?
|
21
|
+
raise RTSP::Error,
|
22
|
+
"#{self.class} received nil string--this shouldn't happen."
|
23
|
+
end
|
24
|
+
|
25
|
+
@raw_body = raw_response
|
26
|
+
|
27
|
+
head, body = split_head_and_body_from @raw_body
|
28
|
+
parse_head(head)
|
29
|
+
@body = parse_body(body)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Pulls out the RTSP version, response code, and response message (AKA the
|
33
|
+
# status line info) into instance variables.
|
34
|
+
#
|
35
|
+
# @param [String] line The String containing the status line info.
|
36
|
+
def extract_status_line(line)
|
37
|
+
line =~ /RTSP\/(\d\.\d) (\d\d\d) ([^\r\n]+)/
|
38
|
+
@rtsp_version = $1
|
39
|
+
@code = $2.to_i
|
40
|
+
@message = $3
|
41
|
+
|
42
|
+
if @rtsp_version.nil?
|
43
|
+
raise RTSP::Error, "Status line corrupted: #{line}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|