sdp 0.1.0

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.
data/.document ADDED
@@ -0,0 +1,3 @@
1
+ -
2
+ ChangeLog.*
3
+ LICENSE.txt
data/.infinity_test ADDED
@@ -0,0 +1,4 @@
1
+ infinity_test do
2
+ use :rubies => %w(1.8.7 1.9.1 1.9.2), :test_framework => :rspec
3
+ notifications :growl
4
+ end
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format documentation
3
+ --tty
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --markup rdoc --title "sdp Documentation" --protected
data/ChangeLog.rdoc ADDED
@@ -0,0 +1,7 @@
1
+ === 0.1.0 / 2011-01-21
2
+
3
+ * Initial release:
4
+ * Parses SDP text to SDP::Description objects
5
+ * Create SDP::Description objects from scratch
6
+ * Not yet implemented:
7
+ * Parsing of media sections that contain fields other than "attribute"
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source :rubygems
2
+
3
+ #gemspec
4
+ gem 'parslet', '~> 1.0.0'
5
+
6
+ group :development do
7
+ gem 'rake', '~> 0.8.7'
8
+ gem 'ore-core', '~> 0.1.0'
9
+ gem 'jeweler', '~> 1.5.0'
10
+ gem 'ore-tasks', '~> 0.3.0'
11
+ gem 'rspec', '~> 2.3.0'
12
+ gem 'yard', '~> 0.6.0'
13
+ gem 'infinity_test'
14
+ gem 'metric_fu'
15
+ gem 'code_statistics', '~> 0.2.13'
16
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,111 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ Saikuro (1.1.0)
5
+ abstract (1.0.0)
6
+ activesupport (3.0.3)
7
+ arrayfields (4.7.4)
8
+ blankslate (2.1.2.3)
9
+ chronic (0.2.3)
10
+ hoe (>= 1.2.1)
11
+ churn (0.0.13)
12
+ chronic (>= 0.2.3)
13
+ hirb
14
+ json_pure
15
+ main
16
+ ruby_parser (~> 2.0.4)
17
+ sexp_processor (~> 3.0.3)
18
+ code_statistics (0.2.13)
19
+ colored (1.2)
20
+ diff-lcs (1.1.2)
21
+ erubis (2.6.6)
22
+ abstract (>= 1.0.0)
23
+ fattr (2.2.0)
24
+ flay (1.4.1)
25
+ ruby_parser (~> 2.0)
26
+ sexp_processor (~> 3.0)
27
+ flog (2.5.0)
28
+ ruby_parser (~> 2.0)
29
+ sexp_processor (~> 3.0)
30
+ git (1.2.5)
31
+ haml (3.0.25)
32
+ hirb (0.3.6)
33
+ hoe (2.8.0)
34
+ rake (>= 0.8.7)
35
+ i18n (0.5.0)
36
+ infinity_test (1.0.2)
37
+ notifiers (>= 1.1.0)
38
+ watchr (>= 0.7)
39
+ jeweler (1.5.2)
40
+ bundler (~> 1.0.0)
41
+ git (>= 1.2.5)
42
+ rake
43
+ json_pure (1.4.6)
44
+ main (4.4.0)
45
+ arrayfields (>= 4.7.4)
46
+ fattr (>= 2.1.0)
47
+ metric_fu (2.0.1)
48
+ Saikuro (>= 1.1.0)
49
+ activesupport (>= 2.0.0)
50
+ chronic (~> 0.2.3)
51
+ churn (>= 0.0.7)
52
+ flay (>= 1.2.1)
53
+ flog (>= 2.2.0)
54
+ rails_best_practices (>= 0.3.16)
55
+ rcov (>= 0.8.3.3)
56
+ reek (>= 1.2.6)
57
+ roodi (>= 2.1.0)
58
+ notifiers (1.1.0)
59
+ ore-core (0.1.1)
60
+ ore-tasks (0.3.0)
61
+ ore-core (~> 0.1.0)
62
+ parslet (1.0.1)
63
+ blankslate (~> 2.1.2.3)
64
+ rails_best_practices (0.6.5)
65
+ activesupport
66
+ colored (~> 1.2)
67
+ erubis (~> 2.6.6)
68
+ haml (~> 3.0.18)
69
+ i18n
70
+ ruby-progressbar (~> 0.0.9)
71
+ ruby_parser (~> 2.0.4)
72
+ rake (0.8.7)
73
+ rcov (0.9.9)
74
+ reek (1.2.8)
75
+ ruby2ruby (~> 1.2)
76
+ ruby_parser (~> 2.0)
77
+ sexp_processor (~> 3.0)
78
+ roodi (2.1.0)
79
+ ruby_parser
80
+ rspec (2.3.0)
81
+ rspec-core (~> 2.3.0)
82
+ rspec-expectations (~> 2.3.0)
83
+ rspec-mocks (~> 2.3.0)
84
+ rspec-core (2.3.1)
85
+ rspec-expectations (2.3.0)
86
+ diff-lcs (~> 1.1.2)
87
+ rspec-mocks (2.3.0)
88
+ ruby-progressbar (0.0.9)
89
+ ruby2ruby (1.2.5)
90
+ ruby_parser (~> 2.0)
91
+ sexp_processor (~> 3.0)
92
+ ruby_parser (2.0.5)
93
+ sexp_processor (~> 3.0)
94
+ sexp_processor (3.0.5)
95
+ watchr (0.7)
96
+ yard (0.6.4)
97
+
98
+ PLATFORMS
99
+ ruby
100
+
101
+ DEPENDENCIES
102
+ code_statistics (~> 0.2.13)
103
+ infinity_test
104
+ jeweler (~> 1.5.0)
105
+ metric_fu
106
+ ore-core (~> 0.1.0)
107
+ ore-tasks (~> 0.3.0)
108
+ parslet (~> 1.0.0)
109
+ rake (~> 0.8.7)
110
+ rspec (~> 2.3.0)
111
+ yard (~> 0.6.0)
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 sloveless
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,141 @@
1
+ = sdp
2
+
3
+ * {Homepage}[http://rubygems.org/gems/sdp]
4
+ * {Documentation}[http://rubydoc.info/gems/sdp]
5
+ * {Email}[steve.loveless at gmail.com]
6
+
7
+ == Description
8
+
9
+ SDP is used by a number of protocols for describing multimedia sessions; protocols
10
+ include SIP (Session Initiation Protocol), SAP (Session Announcement Protocol),
11
+ and RTSP (Real Time Streaming Protocol).
12
+
13
+ This gem has two purposes:
14
+ 1. To use as a client to parse SDP descriptions into useful objects
15
+ 2. To allow for creating an SDP description from scratch
16
+
17
+ Since SDP descriptions contain a lot of useful data pieces, but due to the
18
+ design of SDP descriptions, not only is it tough to know what's what when
19
+ you visually look at that description, but it's tough to extract out the piece of
20
+ data that you need to work with (ex. session ID). The SDP.#parse method takes
21
+ a description and parses it in to a Hash-like SDP::Description object, which has
22
+ accessor methods (that mimic the RFC 4566 document) for easily getting and setting
23
+ values.
24
+
25
+ Similarly, since it's difficult to remember all the ins and outs of building
26
+ an SDP description string that meets the requirements of RFC 4566, the
27
+ SDP::Description class has a #to_s method that takes the fields that you've
28
+ populated (from using the accessors) and turns those in to a string that's suitable
29
+ to use for an SDP description.
30
+
31
+ == Features
32
+
33
+ * Parse an SDP description from String to Ruby object
34
+ * Create new SDP description
35
+
36
+ == Examples
37
+
38
+ === Creating an SDP description
39
+
40
+ require 'sdp/description'
41
+
42
+ sdp = SDP::Description.new
43
+ sdp.inspect # => {:session_section=>{
44
+ # :time_zones=>[], :attributes=>[], :protocol_version=>0
45
+ # },
46
+ # :media_sections=>[]
47
+ # }
48
+ sdp.to_s # => "v=0\no= \ns=\nc= \nt= \n\n"
49
+ sdp.username = "elvis"
50
+ sdp.media_sections << { :media => "video", :port => 9000, :format => 0, :protocol => "RTP/AVP", :attributes => [{ :attribute => "recvonly" }] }
51
+ sdp.media_sections << { :media => "audio", :port => 9100, :format => 33, :protocol => "RTP/AVP", :attributes => [{ :attribute => "rtpmap", :value => "99 h263-1998/90000" }] }
52
+
53
+ # Fields are stored in a Hash, where the session information goes in :session_section,
54
+ # and each media section goes in an Array at :media_sections:
55
+ sdp.inspect # => {:session_section=>{:time_zones=>[], :attributes=>[], :protocol_version=>0, :username=>"elvis"}, :media_sections=>[{:media=>"video", :port=>9000, :format=>0, :protocol=>"RTP/AVP", :attributes=>[{:attribute=>"recvonly"}]}, {:media=>"audio", :port=>9100, :format=>33, :protocol=>"RTP/AVP", :attributes=>[{:attribute=>"rtpmap", :value=>"99 h263-1998/90000"}]}]}
56
+ sdp.valid? # => false (Require fields haven't yet been given)
57
+ sdp.id = 1
58
+ sdp.version = 123
59
+ sdp.network_type = :IN
60
+ sdp.address_type = :IP4
61
+ sdp.name = " "
62
+ sdp.start_time = 1
63
+ sdp.stop_time = 10
64
+ sdp.unicast_address = "127.0.0.1"
65
+ sdp.valid? # => true
66
+ sdp.to_s # => "v=0\no=elvis 1 123 IN IP4 127.0.0.1\ns= \nc=IN IP4 \nt=1 10\nm=video 9000 RTP/AVP 0\na=recvonlym=audio 9100 RTP/AVP 33\na=rtpmap:99 h263-1998/90000\n"
67
+
68
+
69
+ === Parsing an SDP description
70
+
71
+ sdp_string = <<-EOF
72
+ v=0
73
+ o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5
74
+ s=SDP Seminar
75
+ i=A Seminar on the session description protocol
76
+ u=http://www.example.com/seminars/sdp.pdf
77
+ e=j.doe@example.com (Jane Doe)
78
+ p=+1 617 555-6011
79
+ c=IN IP4 224.2.17.12/127
80
+ b=CT:1000
81
+ t=2873397496 2873404696
82
+ r=604800 3600 0 90000
83
+ z=2882844526 -1h
84
+ k=clear:password
85
+ a=recvonly
86
+ a=type:test
87
+ m=audio 49170 RTP/AVP 0
88
+ m=video 51372 RTP/AVP 99
89
+ a=rtpmap:99 h263-1998/90000
90
+ EOF
91
+
92
+ session = SDP.parse sdp_string
93
+
94
+ session.class # => SDP::Description
95
+ session.protocol_version # => 0
96
+ session.media_sections # => [{:media=>"audio", :port=>"49170", :protocol=>"RTP/AVP", :format=>"0", :attributes=>""}, {:media=>"video", :port=>"51372", :protocol=>"RTP/AVP", :format=>"99", :attributes=>[{:attribute=>"rtpmap", :value=>"99 h263-1998/90000"}]}]
97
+ session.username # => "jdoe"
98
+ session.id # => "2890844526"
99
+ session.version # => "2890842807"
100
+ session.network_type # => "IN"
101
+ session.address_type # => "IP4"
102
+ session.unicast_address # => "10.47.16.5"
103
+ session.name # => "SDP Seminar"
104
+ session.information # => "A Seminar on the session description protocol"
105
+ session.uri # => "http://www.example.com/seminars/sdp.pdf"
106
+ session.email_address # => "j.doe@example.com (Jane Doe)"
107
+ session.connection_address # => "224.2.17.12/127"
108
+ session.start_time # => 2873397496
109
+ session.stop_time # => 2873404696
110
+ session.attributes # => [{:attribute=>"recvonly"}, {:attribute=>"type", :value=>"test"}]
111
+
112
+ # Put it back to a string...
113
+ session.to_s # => "v=0\r\n
114
+ # o=elvis 2890844526 2890842807 IN IP4 10.47.16.5\r\n
115
+ # s=SDP Seminar\r\n
116
+ # i=A Seminar on the session description protocol\r\n
117
+ # u=http://www.example.com/seminars/sdp.pdf\r\n
118
+ # e=j.doe@example.com (Jane Doe)\r\n
119
+ # c=IN IP4 224.2.17.12/127\r\n
120
+ # t=2873397496 2873404696\r\n
121
+ # a=recvonly\r\n
122
+ # m=audio 49170 RTP/AVP 0\r\n
123
+ # m=video\r\n"
124
+ # a=rtpmap:99 h263-1998/90000\r\n"
125
+
126
+ == Requirements
127
+
128
+ * Rubies (tested, at least):
129
+ * 1.8.7
130
+ * 1.9.1
131
+ * 1.9.2
132
+
133
+ == Install
134
+
135
+ $ gem install sdp
136
+
137
+ == Copyright
138
+
139
+ Copyright (c) 2011 Steve Loveless
140
+
141
+ See LICENSE.txt for details.
data/Rakefile ADDED
@@ -0,0 +1,39 @@
1
+ require 'rubygems'
2
+
3
+ begin
4
+ require 'bundler'
5
+ rescue LoadError => e
6
+ STDERR.puts e.message
7
+ STDERR.puts "Run `gem install bundler` to install Bundler."
8
+ exit e.status_code
9
+ end
10
+
11
+ begin
12
+ Bundler.setup(:development)
13
+ rescue Bundler::BundlerError => e
14
+ STDERR.puts e.message
15
+ STDERR.puts "Run `bundle install` to install missing gems."
16
+ exit e.status_code
17
+ end
18
+
19
+ require 'rake'
20
+
21
+ require 'ore/specification'
22
+ require 'jeweler'
23
+ Jeweler::Tasks.new(Ore::Specification.new)
24
+
25
+ require 'ore/tasks'
26
+ Ore::Tasks.new
27
+
28
+ require 'rspec/core/rake_task'
29
+ RSpec::Core::RakeTask.new(:spec) do |t|
30
+ t.ruby_opts = "-w"
31
+ t.rspec_opts = ['--format', 'documentation', '--color']
32
+ end
33
+ task :default => :spec
34
+
35
+ require 'yard'
36
+ YARD::Rake::YardocTask.new
37
+
38
+ # Load all extra rake tasks
39
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].each { |ext| load ext }
@@ -0,0 +1,15 @@
1
+ Feature: Programmatically create an SDP file
2
+ As a utility using SDP to describe a multimedia session
3
+ I want to be able to turn Ruby code in to an SDP description,
4
+ as specified in RFC 4566
5
+ So that I can use Ruby to describe the multimedia session
6
+
7
+ Scenario: Create an SDP file from a Ruby object
8
+ Given I know what the SDP file should look like
9
+ When I build the Ruby object with the appropriate fields
10
+ Then the resulting file should look like the intended description
11
+
12
+ Scenario: Create a basic SDP object
13
+ Given I create an SDP object with no parameters
14
+ When I convert it to a String
15
+ Then it should have :version set to 0
@@ -0,0 +1,35 @@
1
+ Feature: Get SDP file fields and marshall into Ruby data types
2
+ As an RTSP consumer
3
+ I want to be able to be able to read SDP files into Ruby data types
4
+ So that it's easy to determine how to work with the RTSP stream
5
+
6
+ Scenario: Parse the RFC 4566 example
7
+ Given the RFC 4566 SDP example in a file
8
+ When I parse the file
9
+ Then the <value> for <field> is accessible via the SDP object
10
+ | field | value |
11
+ | protocol_version | 0 |
12
+ | username | jdoe |
13
+ | id | 2890844526 |
14
+ | version | 2890842807 |
15
+ | network_type | IN |
16
+ | address_type | IP4 |
17
+ | unicast_address | 10.47.16.5 |
18
+ | name | SDP Seminar |
19
+ | information | A Seminar on the session description protocol |
20
+ | uri | http://www.example.com/seminars/sdp.pdf |
21
+ | email_address | j.doe@example.com (Jane Doe) |
22
+ | connection_address | 224.2.17.12/127 |
23
+ | start_time | 2873397496 |
24
+ | stop_time | 2873404696 |
25
+ | attributes | recvonly |
26
+ # | m[0][1] | 49170 |
27
+ # | m[0][2] | "RTP/AVP" |
28
+ # | m[0][3] | 0 |
29
+ # | m[1][1] | 51372 |
30
+ # | m[1][2] | "RTP/AVP" |
31
+ # | m[1][3] | 99 |
32
+ # | a[1][1] | 99 |
33
+ # | a[1][2] | "h263-1998" |
34
+ # | a[1][3] | 90000 |
35
+
@@ -0,0 +1,46 @@
1
+ require 'sdp/description'
2
+
3
+ Given /^I know what the SDP file should look like$/ do
4
+ @example_sdp_file = File.read(File.dirname(__FILE__) + "/../support/sdp_file.txt")
5
+ end
6
+
7
+ When /^I build the Ruby object with the appropriate fields$/ do
8
+ @session = SDP::Description.new
9
+ @session.protocol_version = 0
10
+ @session.username = "jdoe"
11
+ @session.id = 2890844526
12
+ @session.version = 2890842807
13
+ @session.network_type = :IN
14
+ @session.address_type = :IP4
15
+ @session.unicast_address = "10.47.16.5"
16
+ @session.name = "SDP Seminar"
17
+ @session.information = "A Seminar on the session description protocol"
18
+ @session.uri = "http://www.example.com/seminars/sdp.pdf"
19
+ @session.email_address = "j.doe@example.com (Jane Doe)"
20
+ @session.connection_address = "224.2.17.12/127"
21
+ @session.start_time = 2873397496
22
+ @session.stop_time = 2873404696
23
+ @session.attributes << { :attribute => "recvonly" }
24
+ @session.media_sections <<
25
+ { :media => "audio", :port => 49170, :protocol => "RTP/AVP", :format => 0 }
26
+ @session.media_sections <<
27
+ { :media => "video", :port => 51372, :protocol => "RTP/AVP", :format => 99,
28
+ :attributes => [{ :attribute => "rtpmap", :value => "99 h263-1998/90000" }]
29
+ }
30
+ end
31
+
32
+ Then /^the resulting file should look like the intended description$/ do
33
+ @session.to_s.should == @example_sdp_file
34
+ end
35
+
36
+ Given /^I create an SDP object with no parameters$/ do
37
+ @session = SDP::Description.new
38
+ end
39
+
40
+ When /^I convert it to a String$/ do
41
+ @sdp_string = @session.to_s
42
+ end
43
+
44
+ Then /^it should have :version set to (\d+)$/ do |value|
45
+ @sdp_string.should match /v=#{value}/
46
+ end
@@ -0,0 +1,23 @@
1
+ Given /^the RFC 4566 SDP example in a file$/ do
2
+ @sdp_file = File.open(File.dirname(__FILE__) + '/../support/sdp_file.txt', 'r').read
3
+ end
4
+
5
+ When /^I parse the file$/ do
6
+ @sdp = SDP.parse @sdp_file
7
+ end
8
+
9
+ Then /^the <value> for <field> is accessible via the SDP object$/ do |table|
10
+ # table is a Cucumber::Ast::Table
11
+ table.hashes.each do |sdp_field|
12
+ field_type = sdp_field["field"].to_sym
13
+ value = sdp_field["value"]
14
+
15
+ actual_value = @sdp.send(field_type)
16
+
17
+ if field_type == :attributes
18
+ actual_value.first[:attribute].should == value
19
+ else
20
+ actual_value.to_s.should == value
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../../lib')
2
+ require 'sdp'
3
+ require 'sdp/description'
@@ -0,0 +1,12 @@
1
+ v=0
2
+ o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5
3
+ s=SDP Seminar
4
+ i=A Seminar on the session description protocol
5
+ u=http://www.example.com/seminars/sdp.pdf
6
+ e=j.doe@example.com (Jane Doe)
7
+ c=IN IP4 224.2.17.12/127
8
+ t=2873397496 2873404696
9
+ a=recvonly
10
+ m=audio 49170 RTP/AVP 0
11
+ m=video 51372 RTP/AVP 99
12
+ a=rtpmap:99 h263-1998/90000
data/gemspec.yml ADDED
@@ -0,0 +1,15 @@
1
+ name: sdp
2
+ summary: "Parse and create SDP (Session Description Protocol) text based on RFC4566."
3
+ description: "This gem allows for parsing SDP (Session Description Protocol) information in to a Ruby object, making it easy to read and work with that data. It also allows for easily creating SDP objects that can be converted to text using #to_s."
4
+ license: MIT
5
+ authors: sloveless
6
+ email: steve.loveless@gmail.com
7
+ homepage: http://rubygems.org/gems/sdp
8
+ has_yard: true
9
+
10
+ dependencies:
11
+ parslet: ~> 1.0.0
12
+
13
+ development_dependencies:
14
+ bundler: ~> 1.0.0
15
+ yard: ~> 0.6.0
@@ -0,0 +1,172 @@
1
+ require 'erb'
2
+
3
+ class SDP
4
+ PROTOCOL_VERSION = 0
5
+
6
+ # Represents an SDP description as defined in RFC 4566. This class allows
7
+ # for creating an object so you can, in turn, create a String that
8
+ # represents an SDP description. The String, then can be used by
9
+ # other protocols that depend on an SDP description.
10
+ #
11
+ # SDP::Description objects are initialized empty (i.e. no fields are
12
+ # defined), putting the onus on you to add fields in the proper order.
13
+ # After building the description up, call #to_s to render it. This
14
+ # will render the String with fields in order that they were added
15
+ # to the object, so be sure to add them according to spec!
16
+ class Description < Hash
17
+ class << self
18
+
19
+ # Class macro to access the different fields that make up the
20
+ # description.
21
+ #
22
+ # @param [Symbol] field_type
23
+ def field(field_type)
24
+ define_read_field_method(field_type)
25
+ define_write_field_method(field_type)
26
+ end
27
+
28
+ # Creates read accessor for the field type. This simply reads
29
+ # the correct Hash value and returns that.
30
+ #
31
+ # @param [Symbol] field_type
32
+ # @return [] Returns whatever type the value is that's stored
33
+ # in the Hash key.
34
+ def define_read_field_method(field_type)
35
+ define_method field_type do
36
+ if field_type == :media_sections
37
+ self[:media_sections]
38
+ else
39
+ self[:session_section][field_type]
40
+ end
41
+ end
42
+ end
43
+
44
+ # Creates write accessor for the field type. This simply writes
45
+ # the correct Hash value and returns that.
46
+ #
47
+ # @param [Symbol] field_type
48
+ def define_write_field_method(field_type)
49
+ case field_type
50
+ when :media_sections
51
+ define_method ":media_sections<<" do |value|
52
+ self[:media_sections] << value
53
+ end
54
+ when :time_zones || :attributes
55
+ define_method "#{field_type}<<" do |value|
56
+ self[:session_section][field_type] << value
57
+ end
58
+ else
59
+ define_method "#{field_type}=" do |value|
60
+ self[:session_section][field_type] = value
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ FIELDS = [
67
+ :protocol_version,
68
+ :username,
69
+ :id,
70
+ :version,
71
+ :network_type,
72
+ :address_type,
73
+ :unicast_address,
74
+ :name,
75
+ :information,
76
+ :uri,
77
+ :email_address,
78
+ :phone_number,
79
+ :connection_address,
80
+ :bandwidth_type,
81
+ :bandwidth,
82
+ :start_time,
83
+ :stop_time,
84
+ :repeat_interval,
85
+ :active_duration,
86
+ :offsets_from_start_time,
87
+ :time_zones,
88
+ :encryption_method,
89
+ :encryption_key,
90
+ :attributes,
91
+ :media_sections
92
+ ]
93
+
94
+ FIELDS.each do |field_type|
95
+ field field_type
96
+ end
97
+
98
+ # @param [Hash] session_as_hash Pass this in to use these values instead
99
+ # of building your own from scratch.
100
+ def initialize(session_as_hash=nil)
101
+ if session_as_hash.nil?
102
+ self[:session_section] = {}
103
+ self[:session_section][:time_zones] = []
104
+ self[:session_section][:attributes] = []
105
+ self[:media_sections] = []
106
+
107
+ self.send :protocol_version=, SDP::PROTOCOL_VERSION
108
+ else
109
+ begin
110
+ unless validate_init_value(session_as_hash)
111
+ self.replace session_as_hash
112
+ end
113
+ rescue SDP::RuntimeError => ex
114
+ puts ex.message
115
+ raise
116
+ end
117
+ end
118
+
119
+ super
120
+ end
121
+
122
+ # Turns the current SDP::Description object into the SDP description,
123
+ # ready to be used.
124
+ #
125
+ # @return [String] The SDP description.
126
+ def to_s
127
+ template = File.read(File.dirname(__FILE__) + "/session_template.erb")
128
+
129
+ sdp = ERB.new(template, 0, "%<>")
130
+ sdp.result(get_binding)
131
+ end
132
+
133
+ # Checks to see if the fields set in the current object will yield an SDP
134
+ # description that meets the RFC 4566 spec.
135
+ #
136
+ # @return [Boolean] true if the object will meet spec; false if not.
137
+ def valid?
138
+ return false unless protocol_version && username && id && version &&
139
+ network_type && address_type && unicast_address && name &&
140
+ start_time && stop_time && !media_sections.empty?
141
+
142
+ true
143
+ end
144
+
145
+ #--------------------------------------------------------------------------
146
+ # PRIVATES!
147
+ private
148
+
149
+ # @return [Binding] Values for this object for ERB to use.
150
+ def get_binding
151
+ binding
152
+ end
153
+
154
+ # @raise [SDP::RuntimeError] If not given a Hash.
155
+ def validate_init_value value
156
+ unless value.class == Hash
157
+ message = "Must pass a Hash in on initialize. You passed in a #{value.class}."
158
+ raise SDP::RuntimeError, message
159
+ end
160
+
161
+ bad_keys = []
162
+ value.each_key do |key|
163
+ bad_keys << key unless (FIELDS.include?(key) || key == :session_section)
164
+ end
165
+
166
+ unless bad_keys.empty?
167
+ message = "Invalid key value passed in on initialize: #{bad_keys}"
168
+ raise SDP::RuntimeError, message
169
+ end
170
+ end
171
+ end
172
+ end