opentok 3.0.3 → 4.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/metrics.yml +17 -0
  3. data/.travis.yml +3 -1
  4. data/CODE_OF_CONDUCT.md +128 -0
  5. data/README.md +312 -28
  6. data/lib/opentok/archive.rb +45 -4
  7. data/lib/opentok/archives.rb +73 -5
  8. data/lib/opentok/broadcast.rb +118 -0
  9. data/lib/opentok/broadcasts.rb +149 -0
  10. data/lib/opentok/client.rb +212 -7
  11. data/lib/opentok/connections.rb +28 -0
  12. data/lib/opentok/constants.rb +1 -1
  13. data/lib/opentok/exceptions.rb +6 -0
  14. data/lib/opentok/opentok.rb +44 -10
  15. data/lib/opentok/session.rb +5 -0
  16. data/lib/opentok/signals.rb +47 -0
  17. data/lib/opentok/sip.rb +33 -0
  18. data/lib/opentok/stream.rb +46 -0
  19. data/lib/opentok/stream_list.rb +18 -0
  20. data/lib/opentok/streams.rb +79 -0
  21. data/lib/opentok/version.rb +1 -1
  22. data/opentok.gemspec +9 -8
  23. data/sample/Broadcast/Gemfile +4 -0
  24. data/sample/Broadcast/README.md +201 -0
  25. data/sample/Broadcast/broadcast_sample.rb +97 -0
  26. data/sample/Broadcast/public/css/sample.css +64 -0
  27. data/sample/Broadcast/public/js/host.js +185 -0
  28. data/sample/Broadcast/public/js/participant.js +85 -0
  29. data/sample/Broadcast/views/host.erb +82 -0
  30. data/sample/Broadcast/views/index.erb +32 -0
  31. data/sample/Broadcast/views/layout.erb +29 -0
  32. data/sample/Broadcast/views/participant.erb +27 -0
  33. data/sample/HelloWorld/public/js/helloworld.js +4 -10
  34. data/sample/HelloWorld/views/index.erb +1 -3
  35. data/spec/cassettes/OpenTok_Archives/calls_layout_on_archive_object.yml +47 -0
  36. data/spec/cassettes/OpenTok_Archives/changes_the_layout_of_an_archive.yml +38 -0
  37. data/spec/cassettes/OpenTok_Archives/http_client_errors/.yml +34 -0
  38. data/spec/cassettes/OpenTok_Archives/should_create_archives.yml +3 -1
  39. data/spec/cassettes/OpenTok_Archives/should_create_audio_only_archives.yml +3 -1
  40. data/spec/cassettes/OpenTok_Archives/should_create_custom_layout_archives.yml +50 -0
  41. data/spec/cassettes/OpenTok_Archives/should_create_hd_archives.yml +54 -0
  42. data/spec/cassettes/OpenTok_Archives/should_create_individual_archives.yml +3 -1
  43. data/spec/cassettes/OpenTok_Archives/should_create_named_archives.yml +3 -1
  44. data/spec/cassettes/OpenTok_Archives/should_delete_an_archive_by_id.yml +3 -1
  45. data/spec/cassettes/OpenTok_Archives/should_find_archives_by_id.yml +3 -1
  46. data/spec/cassettes/OpenTok_Archives/should_find_archives_with_unknown_properties.yml +3 -1
  47. data/spec/cassettes/OpenTok_Archives/should_find_expired_archives.yml +3 -1
  48. data/spec/cassettes/OpenTok_Archives/should_find_paused_archives_by_id.yml +3 -1
  49. data/spec/cassettes/OpenTok_Archives/should_stop_archives.yml +3 -1
  50. data/spec/cassettes/OpenTok_Archives/when_many_archives_are_created/should_return_all_archives.yml +3 -1
  51. data/spec/cassettes/OpenTok_Archives/when_many_archives_are_created/should_return_archives_with_an_offset.yml +3 -1
  52. data/spec/cassettes/OpenTok_Archives/when_many_archives_are_created/should_return_count_number_of_archives.yml +3 -1
  53. data/spec/cassettes/OpenTok_Archives/when_many_archives_are_created/should_return_part_of_the_archives_when_using_offset_and_count.yml +3 -1
  54. data/spec/cassettes/OpenTok_Archives/when_many_archives_are_created/should_return_session_archives.yml +3 -1
  55. data/spec/cassettes/OpenTok_Broadcasts/calls_layout_on_broadcast_object.yml +57 -0
  56. data/spec/cassettes/OpenTok_Broadcasts/changes_the_layout_of_a_broadcast.yml +38 -0
  57. data/spec/cassettes/OpenTok_Broadcasts/fetches_a_hls_broadcast_url.yml +52 -0
  58. data/spec/cassettes/OpenTok_Broadcasts/finds_a_broadcast.yml +57 -0
  59. data/spec/cassettes/OpenTok_Broadcasts/starts_a_rtmp_broadcast.yml +61 -0
  60. data/spec/cassettes/OpenTok_Broadcasts/stops_a_broadcast.yml +47 -0
  61. data/spec/cassettes/OpenTok_Connections/forces_a_connection_to_be_terminated.yml +38 -0
  62. data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_always_archived_sessions.yml +3 -1
  63. data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_default_sessions.yml +3 -1
  64. data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_relayed_media_sessions.yml +3 -1
  65. data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_relayed_media_sessions_for_invalid_media_modes.yml +3 -1
  66. data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_relayed_media_sessions_with_a_location_hint.yml +3 -1
  67. data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_routed_media_sessions.yml +3 -1
  68. data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_routed_media_sessions_with_a_location_hint.yml +3 -1
  69. data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_sessions_with_a_location_hint.yml +3 -1
  70. data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/with_an_addendum_to_the_user_agent_string/should_append_the_addendum_to_the_user_agent_header.yml +3 -1
  71. data/spec/cassettes/OpenTok_Signals/receives_a_valid_response_for_a_connection.yml +39 -0
  72. data/spec/cassettes/OpenTok_Signals/receives_a_valid_response_for_all_connections.yml +39 -0
  73. data/spec/cassettes/OpenTok_Sip/receives_a_valid_response.yml +3 -1
  74. data/spec/cassettes/OpenTok_Streams/get_all_streams_information.yml +55 -0
  75. data/spec/cassettes/OpenTok_Streams/get_specific_stream_information.yml +44 -0
  76. data/spec/cassettes/OpenTok_Streams/layout_working_on_two_stream_list.yml +38 -0
  77. data/spec/opentok/archives_spec.rb +84 -1
  78. data/spec/opentok/broadcasts_spec.rb +171 -0
  79. data/spec/opentok/client_spec.rb +51 -0
  80. data/spec/opentok/connection_spec.rb +38 -0
  81. data/spec/opentok/opentok_spec.rb +21 -0
  82. data/spec/opentok/signal_spec.rb +50 -0
  83. data/spec/opentok/streams_spec.rb +75 -0
  84. data/spec/spec_helper.rb +2 -0
  85. metadata +84 -22
@@ -1,7 +1,40 @@
1
1
  require "opentok/client"
2
2
 
3
+ # An object that lets you use the OpenTok SIP gateway.
3
4
  module OpenTok
4
5
  class Sip
6
+ # Dials a SIP gateway to input an audio-only stream into your OpenTok session.
7
+ # See the {https://tokbox.com/developer/guides/sip/ OpenTok SIP developer guide}.
8
+ #
9
+ # @example
10
+ # opts = { "from" => "14155550101@example.com",
11
+ # "auth" => { "username" => sip_username,
12
+ # "password" => sip_password },
13
+ # "headers" => { "X-KEY1" => "value1",
14
+ # "X-KEY1" => "value2" },
15
+ # "secure" => "true"
16
+ # }
17
+ # response = opentok.sip.dial(session_id, token, "sip:+15128675309@acme.pstn.example.com;transport=tls", opts)
18
+ # @param [String] session_id The session ID corresponding to the session to which
19
+ # the SIP gateway will connect.
20
+ # @param [String] token The token for the session ID with which the SIP user
21
+ # will use to connect.
22
+ # @param [String] sip_uri The SIP URI the OpenTok SIP gateway will dial.
23
+ # @param [Hash] opts A hash defining options for the SIP call. For example:
24
+ # @option opts [String] :from The number or string that will be sent to the final
25
+ # SIP number as the caller. It must be a string in the form of "from@example.com",
26
+ # where from can be a string or a number. If from is set to a number
27
+ # (for example, "14155550101@example.com"), it will show up as the incoming
28
+ # number on PSTN phones. If from is undefined or set to a string (for example,
29
+ # "joe@example.com"), +00000000 will show up as the incoming number on
30
+ # PSTN phones.
31
+ # @option opts [Hash] :headers This hash defines custom headers to be added
32
+ # to the SIP ​INVITE​ request initiated from OpenTok to the your SIP platform.
33
+ # @option opts [Hash] :auth This object contains the username and password
34
+ # to be used in the the SIP INVITE​ request for HTTP digest authentication,
35
+ # if it is required by your SIP platform.
36
+ # @option opts [true, false] :secure Wether the media must be transmitted
37
+ # encrypted (​true​) or not (​false​, the default).
5
38
  def dial(session_id, token, sip_uri, opts)
6
39
  response = @client.dial(session_id, token, sip_uri, opts)
7
40
  end
@@ -0,0 +1,46 @@
1
+ require "active_support/inflector"
2
+
3
+ module OpenTok
4
+ # Represents information about a stream in an OpenTok session.
5
+ #
6
+ # @attr [string] id
7
+ # The stream ID.
8
+ #
9
+ # @attr [string] name
10
+ # The name of the stream.
11
+
12
+ # @attr [string] videoType
13
+ # The videoType property is either "camera" or "screen".
14
+ #
15
+ # @attr [array] layoutClassList
16
+ # An array of the layout classes for the stream.
17
+ class Stream
18
+
19
+ # @private
20
+ def initialize(json)
21
+ # TODO: validate json fits schema
22
+ @json = json
23
+ end
24
+
25
+ # A JSON-encoded string representation of the stream.
26
+ def to_json
27
+ @json.to_json
28
+ end
29
+
30
+
31
+ # @private ignore
32
+ def method_missing(method, *args, &block)
33
+ camelized_method = method.to_s.camelize(:lower)
34
+ if @json.has_key? camelized_method and args.empty?
35
+ # TODO: convert create_time method call to a Time object
36
+ if camelized_method == 'outputMode'
37
+ @json[camelized_method].to_sym
38
+ else
39
+ @json[camelized_method]
40
+ end
41
+ else
42
+ super method, *args, &block
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,18 @@
1
+ require "opentok/stream"
2
+
3
+
4
+ module OpenTok
5
+ # A class for accessing a list of Stream objects.
6
+ class StreamList < Array
7
+
8
+ # The total number streams.
9
+ attr_reader :total
10
+
11
+ # @private
12
+ def initialize(json)
13
+ @total = json['count']
14
+ super json['items'].map { |item| Stream.new item }
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,79 @@
1
+ require 'opentok/client'
2
+ require 'opentok/stream'
3
+ require 'opentok/stream_list'
4
+
5
+ module OpenTok
6
+ # A class for working with OpenTok streams. It includes methods for getting info
7
+ # about OpenTok streams and for setting layout classes for streams.
8
+ class Streams
9
+ # @private
10
+ def initialize(client)
11
+ @client = client
12
+ end
13
+
14
+ # Use this method to get information on an OpenTok stream.
15
+ #
16
+ # For example, you can call this method to get information about layout classes used by an OpenTok stream.
17
+ # The layout classes define how the stream is displayed in the layout of a broadcast stream.
18
+ # For more information, see {https://tokbox.com/developer/guides/broadcast/live-streaming/#assign-layout-classes-to-streams Assigning layout classes to streams in live streaming broadcasts}
19
+ # and {https://tokbox.com/developer/guides/archiving/layout-control.html Customizing the video layout for composed archives}.
20
+ #
21
+ # @param [String] session_id The session ID of the OpenTok session.
22
+ # @param [String] stream_id The stream ID within the session.
23
+ # @return [Stream] The Stream object.
24
+ # @raise [ArgumentError] stream_id or session_id is invalid.
25
+ # @raise [OpenTokAuthenticationError] You are not authorized to fetch the stream information. Check your authentication credentials.
26
+ # @raise [OpenTokError] An OpenTok server error.
27
+ #
28
+ def find(session_id, stream_id)
29
+ raise ArgumentError, 'session_id not provided' if session_id.to_s.empty?
30
+ raise ArgumentError, 'stream_id not provided' if session_id.to_s.empty?
31
+ stream_json = @client.info_stream(session_id, stream_id)
32
+ Stream.new stream_json
33
+ end
34
+
35
+ # Use this method to get information on all OpenTok streams in a session.
36
+ #
37
+ # For example, you can call this method to get information about layout classes used by OpenTok streams.
38
+ # The layout classes define how the stream is displayed in the layout of a live streaming
39
+ # broadcast or a composed archive. For more information, see
40
+ # {https://tokbox.com/developer/guides/broadcast/live-streaming/#assign-layout-classes-to-streams Assigning layout classes to streams in live streaming broadcasts}
41
+ # and {https://tokbox.com/developer/guides/archiving/layout-control.html Customizing the video layout for composed archives}.
42
+ #
43
+ # @param [String] session_id The session ID of the OpenTok session.
44
+ # @return [StreamList] The StreamList of Stream objects.
45
+ # @raise [ArgumentError] The stream_id or session_id is invalid.
46
+ # @raise [OpenTokAuthenticationError] You are not authorized to fetch the stream information. Check your authentication credentials.
47
+ # @raise [OpenTokError] An OpenTok server error.
48
+ #
49
+ def all(session_id)
50
+ raise ArgumentError, 'session_id not provided' if session_id.to_s.empty?
51
+ response_json = @client.info_stream(session_id, '')
52
+ StreamList.new response_json
53
+ end
54
+
55
+ # Use this method to set the layout of a composed (archive or broadcast) OpenTok stream.
56
+ #
57
+ # For example, you can call this method to set the layout classes of an OpenTok stream.
58
+ # The layout classes define how the stream is displayed in the layout of a live streaming
59
+ # broadcast or a composed archive. For more information, see
60
+ # {https://tokbox.com/developer/guides/broadcast/live-streaming/#assign-layout-classes-to-streams Assigning layout classes to streams in live streaming broadcasts}
61
+ # and {https://tokbox.com/developer/guides/archiving/layout-control.html Customizing the video layout for composed archives}.
62
+ #
63
+ # @param [String] session_id The session ID of the OpenTok session.
64
+ # @param [Hash] opts A hash with one key <code>items</code> and value as array of objects having <code>stream_id</code> and <code>layoutClassList</code> properties.
65
+ # For more information, see Layout{https://tokbox.com/developer/rest/#change-stream-layout-classes-composed}
66
+ # @raise [ArgumentError] The session_id is invalid.
67
+ # @raise [OpenTokAuthenticationError] You are not authorized to fetch the stream information. Check your authentication credentials.
68
+ # @raise [OpenTokStreamLayoutError] The layout operation could not be performed due to incorrect layout values.
69
+ # @raise [OpenTokError] An OpenTok server error.
70
+ #
71
+ def layout(session_id, opts)
72
+ raise ArgumentError, 'session_id not provided' if session_id.to_s.empty?
73
+ raise ArgumentError, 'opts is empty' if opts.empty?
74
+ response = @client.layout_streams(session_id, opts)
75
+ (200..300).include? response.code
76
+ end
77
+
78
+ end
79
+ end
@@ -1,4 +1,4 @@
1
1
  module OpenTok
2
2
  # @private
3
- VERSION = '3.0.3'
3
+ VERSION = '4.1.1'
4
4
  end
@@ -16,17 +16,18 @@ Gem::Specification.new do |spec|
16
16
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
 
19
- spec.add_development_dependency "bundler", "~> 1.5"
20
- spec.add_development_dependency "rake", "~> 10.1.1"
21
- spec.add_development_dependency "rspec", "~> 2.14.1"
22
- spec.add_development_dependency "webmock", "~> 2.3.2"
23
- spec.add_development_dependency "vcr", "~> 2.8.0"
24
- spec.add_development_dependency "yard", "~> 0.9.11"
19
+ bundler_version = RUBY_VERSION < '2.1' ? '~> 1.5' : '>= 1.5'
20
+ spec.add_development_dependency "bundler", bundler_version
21
+ spec.add_development_dependency "rake", "~> 12.0.0"
22
+ spec.add_development_dependency "rspec", "~> 3.9.0"
23
+ spec.add_development_dependency "webmock", ">= 2.3.2"
24
+ spec.add_development_dependency "vcr", ">= 2.8.0"
25
+ spec.add_development_dependency "yard", ">= 0.9.11"
25
26
  # TODO: exclude this for compatibility with rbx
26
27
  # spec.add_development_dependency "debugger", "~> 1.6.6"
27
28
 
28
29
  spec.add_dependency "addressable", "~> 2.3" # 2.3.0 <= version < 3.0.0
29
- spec.add_dependency "httparty", "~> 0.15.5"
30
+ spec.add_dependency "httparty", ">= 0.18.0"
30
31
  spec.add_dependency "activesupport", ">= 2.0"
31
- spec.add_dependency "jwt", "~> 1.5.6"
32
+ spec.add_dependency "jwt", ">= 1.5.6"
32
33
  end
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "sinatra", "~> 1.4.4"
4
+ gem "opentok", :path => "../../"
@@ -0,0 +1,201 @@
1
+ # OpenTok Broadcasting Sample for Ruby
2
+
3
+ This is a simple demo app that shows how you can use the OpenTok Ruby SDK to broadcast
4
+ sessions and how to stop them, change the layout of the broadcast and/or the streams within.
5
+
6
+ ## Running the App
7
+
8
+ First, download the dependencies using [Bundler](http://bundler.io)
9
+
10
+ ```
11
+ $ bundle install
12
+ ```
13
+
14
+ Next, add your OpenTok API key and API secret to the environment variables. There are a few ways to do
15
+ this but the simplest would be to do it right in your shell.
16
+
17
+ ```
18
+ $ export API_KEY=0000000
19
+ $ export API_SECRET=abcdef1234567890abcdef01234567890abcdef
20
+ ```
21
+
22
+ Finally, start the server using Bundler to handle dependencies
23
+
24
+ ```
25
+ $ bundle exec ruby broadcast_sample.rb
26
+ ```
27
+
28
+ Visit <http://localhost:4567> in your browser. You can now create new broadcast (with a host and
29
+ as a participant) and also view those broadcasts.
30
+
31
+ ## Walkthrough
32
+
33
+ This demo application uses the same frameworks and libraries as the HelloWorld sample. If you have
34
+ not already gotten familiar with the code in that project, consider doing so before continuing.
35
+
36
+ The explanations below are separated by page. Each section will focus on a route handler within the
37
+ main application (broadcast_sample.rb).
38
+
39
+ ### Creating Broadcasts – Host View
40
+
41
+ The Host view manages the broadcasting process. Visit the host page at <http://localhost:4567/host>.
42
+ Your browser will first ask you to approve permission to use the camera and microphone.
43
+ Once you've accepted, your image will appear inside the section titled 'Host'. To start broadcasting
44
+ the video stream, press the 'Start Broadcast' button. You can specify the maximum duration,
45
+ resolution, and layout of the broadcast. Once broadcasting has begun the button will turn
46
+ green and change to 'Stop Broadcast'. Click this button when you are done broadcasting.
47
+
48
+ The host page basically sets up the OpenTok session with the API key and secret you provided.
49
+ If a previously started broadcast exists, it defaults to it, along with the layout and the stream
50
+ that has the focus:
51
+
52
+ ```ruby
53
+ get '/host' do
54
+ api_key = settings.api_key
55
+ session_id = settings.session.session_id
56
+ token = settings.opentok.generate_token(session_id, role: :publisher, initialLayoutClassList: ['focus'])
57
+
58
+ erb :host, locals: {
59
+ apiKey: api_key,
60
+ sessionId: session_id,
61
+ token: token,
62
+ initialBroadcastId: settings.broadcast_id,
63
+ focusStreamId: settings.focus_stream_id,
64
+ initialLayout: settings.broadcast_layout
65
+ }
66
+ end
67
+ ```
68
+
69
+ This handler generates the three strings that the client (JavaScript) needs to connect
70
+ to the session: `apiKey`, `sessionId`, and `token`. The `initialBroadcastId` is the broadcast ID,
71
+ `focusStreamId` is the stream ID that has the current focus (if there is one), and
72
+ `initialLayout` is the initial layout for the current broadcast in progress (if there is one).
73
+ (We will discuss focus stream and broadcast layout below.)
74
+
75
+ In the host page, the user presses the 'Start Broadcast' button, which sends an XHR (or Ajax)
76
+ request to the <http://localhost:4567/start> URL. The route handler for this URL is shown below:
77
+
78
+ ```ruby
79
+ post '/start' do
80
+ opts = {
81
+ :maxDuration => params.key?("maxDuration") ? params[:maxDuration] : 7200,
82
+ :resolution => params[:resolution],
83
+ :layout => params[:layout],
84
+ :outputs => {
85
+ :hls => {}
86
+ }
87
+ }
88
+ broadcast = settings.opentok.broadcasts.create(settings.session.session_id, opts)
89
+ settings.broadcast_id = broadcast.id
90
+ body broadcast.to_json
91
+ end
92
+ ```
93
+
94
+ In this handler, `opentok.broadcasts.create` is called with the `session_id` for
95
+ the OpenTok session to broadcast. The optional second argument is a hash which defines
96
+ optional properties for the broadcast. It consists of `maxDuration` of the broadcast,
97
+ `resolution`, and broadcast `layout`. This sample app starts an HLS broadcast (not RTMP),
98
+ so it only specifies an `hls` property of the `outputs` property. See the
99
+ [Ruby SDK documentation](https://github.com/opentok/OpenTok-Ruby-SDK) for information
100
+ on adding RTMP broadcast streams. In this case, as in the HelloWorld sample app, there is
101
+ only one session created and it is used here and for the participant view.
102
+ This will trigger the broadcasting to begin. The response sent back to the client’s XHR request
103
+ will be the JSON representation of the broadcast, which is returned from the `to_json()` method.
104
+
105
+ You can view the HLS broadcast by opening the root URL (<http://localhost:4567/>) in
106
+ a different tab and clicking the `Broadcast URL` button. The code for handling this is as follows:
107
+
108
+ ```ruby
109
+ get '/broadcast' do
110
+ return 'No broadcast id exists' if settings.broadcast_id.nil? || settings.broadcast_id.empty?
111
+ broadcast = settings.opentok.broadcasts.find settings.broadcast_id
112
+ redirect broadcast.broadcastUrls['hls'] if broadcast.status == 'started'
113
+ end
114
+ ```
115
+
116
+ The route for Stop Broadcast has the following code:
117
+
118
+ ```ruby
119
+ get '/stop/:broadcastId' do
120
+ broadcast = settings.opentok.broadcasts.stop settings.broadcast_id
121
+ settings.broadcast_id = nil
122
+ settings.focus_stream_id = ''
123
+ settings.broadcast_layout = 'horizontalPresentation'
124
+ body broadcast.to_json
125
+ end
126
+ ```
127
+
128
+ The settings revert backs to the settings when you start the app.
129
+
130
+ The host page includes a `Toggle Layout` button, which toggles between
131
+ `verticalPresentation` and `horizontalPresentation`.
132
+
133
+ The route for `Toggle Layout` has the following code:
134
+
135
+ ```ruby
136
+ post '/broadcast/:broadcastId/layout' do
137
+ layoutType = params[:type]
138
+ settings.opentok.broadcasts.layout(settings.broadcast_id, type: layoutType)
139
+ settings.broadcast_layout = layoutType
140
+ end
141
+ ```
142
+
143
+ This calls the `opentok.broadcasts.layout()` method, setting the broadcast layout to
144
+ the layout type defined in the POST request's body. In this app, the layout type is
145
+ set to `horizontalPresentation` or `verticalPresentation`, two of the [predefined layout
146
+ types](https://tokbox.com/developer/guides/broadcast/live-streaming/#predefined-layout-types)
147
+ available to live streaming broadcasts.
148
+
149
+ ### Creating Broadcast - Participant View
150
+
151
+ With the host view still open and publishing, open an additional tab and navigate to
152
+ <http://localhost:4567/participant> and allow the browser to use your camera and microphone.
153
+ You will now see the participant in the broadcast.
154
+
155
+ ```ruby
156
+ get '/participant' do
157
+ api_key = settings.api_key
158
+ session_id = settings.session.session_id
159
+ token = settings.opentok.generate_token(session_id, role: :publisher)
160
+
161
+ erb :participant, locals: {
162
+ apiKey: api_key,
163
+ sessionId: session_id,
164
+ token: token,
165
+ focusStreamId: settings.focus_stream_id,
166
+ layout: settings.broadcast_layout
167
+ }
168
+ end
169
+ ```
170
+
171
+ ### Changing the layout classes for streams
172
+
173
+ In the host page, if you click on either the host or a participant video, that video gets
174
+ the `focus` layout in the broadcast. The host page sends the `focus` stream ID and
175
+ the other streams' layout class lists can be cleared, as shown below:
176
+
177
+ ```ruby
178
+ post '/focus' do
179
+ hash = { items: [] }
180
+ hash[:items] << { id: params[:focus], layoutClassList: ['focus'] }
181
+ settings.focus_stream_id = params[:focus]
182
+ if params.key?('otherStreams')
183
+ params[:otherStreams].each do |stream|
184
+ hash[:items] << { id: stream, layoutClassList: [] }
185
+ end
186
+ end
187
+ settings.opentok.streams.layout(settings.session.session_id, hash)
188
+ end
189
+ ```
190
+
191
+ The host client page also uses OpenTok signaling to notify other clients when the layout type and
192
+ focus stream changes, and they then update the local display of streams in the HTML DOM accordingly.
193
+ However, this is not necessary. The layout of the broadcast is unrelated to the layout of
194
+ streams in the web clients.
195
+
196
+ When you view the broadcast stream, the layout type and focus stream changes, based on calls
197
+ to the `OpenTok.setBroadcastLayout()` and `OpenTok.setStreamClassLists()` methods during
198
+ the broadcast.
199
+
200
+ For more information, see [Configuring video layout for OpenTok live streaming
201
+ broadcasts](https://tokbox.com/developer/guides/broadcast/live-streaming/#configuring-video-layout-for-opentok-live-streaming-broadcasts).
@@ -0,0 +1,97 @@
1
+ require 'sinatra/base'
2
+ require 'opentok'
3
+
4
+ raise "You must define API_KEY and API_SECRET environment variables" unless ENV.has_key?("API_KEY") && ENV.has_key?("API_SECRET")
5
+
6
+ class BroadcastSample < Sinatra::Base
7
+
8
+ set :api_key, ENV['API_KEY']
9
+ set :opentok, OpenTok::OpenTok.new(api_key, ENV['API_SECRET'])
10
+ set :session, opentok.create_session(:media_mode => :routed)
11
+ set :erb, :layout => :layout
12
+ set :broadcast_id, nil
13
+ set :focus_stream_id, ''
14
+ set :broadcast_layout, 'horizontalPresentation'
15
+
16
+ get '/' do
17
+ erb :index
18
+ end
19
+
20
+ get '/host' do
21
+ api_key = settings.api_key
22
+ session_id = settings.session.session_id
23
+ token = settings.opentok.generate_token(session_id, role: :publisher, initialLayoutClassList: ['focus'])
24
+
25
+ erb :host, locals: {
26
+ apiKey: api_key,
27
+ sessionId: session_id,
28
+ token: token,
29
+ initialBroadcastId: settings.broadcast_id,
30
+ focusStreamId: settings.focus_stream_id,
31
+ initialLayout: settings.broadcast_layout
32
+ }
33
+ end
34
+
35
+ get '/participant' do
36
+ api_key = settings.api_key
37
+ session_id = settings.session.session_id
38
+ token = settings.opentok.generate_token(session_id, role: :publisher)
39
+
40
+ erb :participant, locals: {
41
+ apiKey: api_key,
42
+ sessionId: session_id,
43
+ token: token,
44
+ focusStreamId: settings.focus_stream_id,
45
+ layout: settings.broadcast_layout
46
+ }
47
+ end
48
+
49
+ post '/start' do
50
+ opts = {
51
+ :maxDuration => params.key?("maxDuration") ? params[:maxDuration] : 7200,
52
+ :resolution => params[:resolution],
53
+ :layout => params[:layout],
54
+ :outputs => {
55
+ :hls => {}
56
+ }
57
+ }
58
+ broadcast = settings.opentok.broadcasts.create(settings.session.session_id, opts)
59
+ settings.broadcast_id = broadcast.id
60
+ body broadcast.to_json
61
+ end
62
+
63
+ get '/broadcast' do
64
+ return 'No broadcast id exists' if settings.broadcast_id.nil? || settings.broadcast_id.empty?
65
+ broadcast = settings.opentok.broadcasts.find settings.broadcast_id
66
+ redirect broadcast.broadcastUrls['hls'] if broadcast.status == 'started'
67
+ end
68
+
69
+ get '/stop/:broadcastId' do
70
+ broadcast = settings.opentok.broadcasts.stop settings.broadcast_id
71
+ settings.broadcast_id = nil
72
+ settings.focus_stream_id = ''
73
+ settings.broadcast_layout = 'horizontalPresentation'
74
+ body broadcast.to_json
75
+ end
76
+
77
+ post '/broadcast/:broadcastId/layout' do
78
+ layoutType = params[:type]
79
+ settings.opentok.broadcasts.layout(settings.broadcast_id, type: layoutType)
80
+ settings.broadcast_layout = layoutType
81
+ end
82
+
83
+ post '/focus' do
84
+ hash = { items: [] }
85
+ hash[:items] << { id: params[:focus], layoutClassList: ['focus'] }
86
+ settings.focus_stream_id = params[:focus]
87
+ if params.key?('otherStreams')
88
+ params[:otherStreams].each do |stream|
89
+ hash[:items] << { id: stream, layoutClassList: [] }
90
+ end
91
+ end
92
+ settings.opentok.streams.layout(settings.session.session_id, hash)
93
+ end
94
+
95
+ # start the server if ruby file executed directly
96
+ run! if app_file == $0
97
+ end