opentok 3.0.3 → 4.1.1

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.
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