Class: SDP::Description

Inherits:
Hash
  • Object
show all
Defined in:
lib/sdp/description.rb

Overview

Represents an SDP description as defined in RFC 4566. This class allows for creating an object so you can, in turn, create a String that represents an SDP description. The String, then can be used by other protocols that depend on an SDP description.

SDP::Description objects are initialized empty (i.e. no fields are defined), putting the onus on you to add fields in the proper order. After building the description up, call #to_s to render it. This will render the String with fields in order that they were added to the object, so be sure to add them according to spec!

Constant Summary collapse

FIELDS =
[
  :protocol_version,
  :username,
  :id,
  :version,
  :network_type,
  :address_type,
  :unicast_address,
  :name,
  :information,
  :uri,
  :email_address,
  :phone_number,
  :connection_network_type,
  :connection_address_type,
  :connection_address,
  :bandwidth_type,
  :bandwidth,
  :start_time,
  :stop_time,
  :repeat_interval,
  :active_duration,
  :offsets_from_start_time,
  :time_zones,
  :encryption_method,
  :encryption_key,
  :attributes,
  :media_sections
]

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Hash

#keys_to_s

Constructor Details

#initialize(session_as_hash = nil) ⇒ Description

Returns a new instance of Description.

Parameters:

  • session_as_hash (Hash) (defaults to: nil)

    Pass this in to use these values instead of building your own from scratch.



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/sdp/description.rb', line 105

def initialize(session_as_hash=nil)
  if session_as_hash.nil?
    self[:session_section] = {}
    self[:session_section][:time_zones] = []
    self[:session_section][:attributes] = []
    self[:media_sections] = []

    self.send :protocol_version=, SDP::PROTOCOL_VERSION
  else
    begin
      unless validate_init_value(session_as_hash)
        self.replace session_as_hash
      end
    rescue SDP::RuntimeError => ex
      puts ex.message
      raise
    end
  end

  super
end

Class Method Details

.define_read_field_method(field_type) ⇒ Object

Creates read accessor for the field type. This simply reads the correct Hash value and returns that.

Parameters:

  • field_type (Symbol)

Returns:

  • Returns whatever type the value is that’s stored

    in the Hash key.



37
38
39
40
41
42
43
44
45
# File 'lib/sdp/description.rb', line 37

def define_read_field_method(field_type)
  define_method field_type do
    if field_type == :media_sections
      self[:media_sections]
    else
      self[:session_section][field_type]
    end
  end
end

.define_write_field_method(field_type) ⇒ Object

Creates write accessor for the field type. This simply writes the correct Hash value and returns that.

Parameters:

  • field_type (Symbol)


51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/sdp/description.rb', line 51

def define_write_field_method(field_type)
  case field_type
  when :media_sections
    define_method ":media_sections<<" do |value|
      self[:media_sections] << value
    end
  when :time_zones || :attributes
    define_method "#{field_type}<<" do |value|
      self[:session_section][field_type] << value
    end
  else
    define_method "#{field_type}=" do |value|
      self[:session_section][field_type] = value
    end
  end
end

.field(field_type) ⇒ Object

Class macro to access the different fields that make up the description.

Parameters:

  • field_type (Symbol)


26
27
28
29
# File 'lib/sdp/description.rb', line 26

def field(field_type)
  define_read_field_method(field_type)
  define_write_field_method(field_type)
end

Instance Method Details

#connection_fieldsArray<String> (private)

Fields that make up the connection line.

Returns:

  • (Array<String>)


190
191
192
# File 'lib/sdp/description.rb', line 190

def connection_fields
  %w[connection_network_type connection_address_type connection_address]
end

#errorsArray

Checks to see if any required fields are not set.

Returns:

  • (Array)

    The list of unset fields that need to be set.



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/sdp/description.rb', line 146

def errors
  errors = []
  required_fields.each do |attrib|
    errors << attrib unless self.send(attrib)
  end

  unless has_session_connection_fields? || has_media_connection_fields?
    connection_errors = []

    connection_fields.each do |attrib|
      connection_errors << attrib unless self.send(attrib)
    end

    if connection_errors.empty?
      media_sections.each_with_index do |ms, i|
        connection_fields.each do |attrib|
          unless ms.has_key?(attrib.to_sym)
            connection_errors << "media_section[#{i}][#{attrib}]"
          end
        end
      end
    end

    errors += connection_errors
  end

  errors
end

#get_bindingBinding (private)

Returns Values for this object for ERB to use.

Returns:

  • (Binding)

    Values for this object for ERB to use.



213
214
215
# File 'lib/sdp/description.rb', line 213

def get_binding
  binding
end

#has_media_connection_fields?Boolean (private)

Returns:

  • (Boolean)


202
203
204
205
206
207
208
209
210
# File 'lib/sdp/description.rb', line 202

def has_media_connection_fields?
  return false if media_sections.empty?

  media_sections.any? do |ms|
    !!(ms.has_key?(:connection_network_type) &&
      ms.has_key?(:connection_address_type) &&
      ms.has_key?(:connection_address))
  end
end

#has_session_connection_fields?Boolean (private)

Checks to see if it has connection fields set in the session section.

Returns:

  • (Boolean)


197
198
199
200
# File 'lib/sdp/description.rb', line 197

def has_session_connection_fields?
  !!(connection_network_type && connection_address_type &&
    connection_address)
end

#required_fieldsArray<String> (private)

Fields required by the RFC.

Returns:

  • (Array<String>)


182
183
184
185
# File 'lib/sdp/description.rb', line 182

def required_fields
  %w[protocol_version username id version network_type address_type
    unicast_address name start_time stop_time media_sections]
end

#session_templateObject (private)



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/sdp/description.rb', line 236

def session_template
  session = <<-TMP
v=#{protocol_version}\r
o=#{username} #{id} #{version} #{network_type} #{address_type} #{unicast_address}\r
s=#{name}\r
  TMP

  session << "i=#{information}\r\n"                   if information
  session << "u=#{uri}\r\n"                           if uri
  session << "e=#{email_address}\r\n"                 if email_address
  session << "p=#{phone_number}\r\n"                  if phone_number

  if connection_network_type
    session << "c=#{connection_network_type} #{connection_address_type} #{connection_address}\r\n"
  end

  session << "b=#{bandwidth_type}:#{bandwidth}\r\n"   if bandwidth
  session << "t=#{start_time} #{stop_time}\r\n"

  if repeat_interval
    session <<
      "r=#{repeat_interval} #{active_duration} #{offsets_from_start_time}\r\n"
  end

  unless time_zones.nil? || time_zones.empty?
    session << "z=" << if time_zones.is_a? Array
      time_zones.map do |tz|
        "#{tz[:adjustment_time]} #{tz[:offset]}"
      end.join + "\r\n"
    else
      "#{time_zones[:adjustment_time]} #{time_zones[:offset]}"
    end
  end

  if encryption_method
    session << "k=#{encryption_method}"
    session << ":#{encryption_key}" if encryption_key
    session << "\r\n"
  end

  unless attributes.empty?
    attributes.each do |a|
      session << "a=#{a[:attribute]}"
      session << ":#{a[:value]}" if a[:value]
      session << "\r\n"
    end
  end

  media_sections.each do |m|
    session << "m=#{m[:media]} #{m[:port]} #{m[:protocol]} #{m[:format]}\r\n"

    if m[:attributes]
      m[:attributes].each do |a|
        session << "a=#{a[:attribute]}"
        session << ":#{a[:value]}" if a[:value]
        session << "\r\n"
      end
    end
  end

  session
end

#to_sString

Turns the current SDP::Description object into the SDP description, ready to be used.

Returns:

  • (String)

    The SDP description.



131
132
133
# File 'lib/sdp/description.rb', line 131

def to_s
  session_template
end

#valid?Boolean

Checks to see if the fields set in the current object will yield an SDP description that meets the RFC 4566 spec.

Returns:

  • (Boolean)

    true if the object will meet spec; false if not.



139
140
141
# File 'lib/sdp/description.rb', line 139

def valid?
  errors.empty?
end

#validate_init_value(value) ⇒ Object (private)

Raises:



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/sdp/description.rb', line 218

def validate_init_value value
  unless value.class == Hash
    message =
      "Must pass a Hash in on initialize.  You passed in a #{value.class}."
    raise SDP::RuntimeError, message
  end

  bad_keys = []
  value.each_key do |key|
    bad_keys << key unless (FIELDS.include?(key) || key == :session_section)
  end

  unless bad_keys.empty?
    message = "Invalid key value passed in on initialize: #{bad_keys}"
    raise SDP::RuntimeError, message
  end
end