sdp 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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