opentok 3.1.0 → 4.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/workflows/metrics.yml +17 -0
- data/.travis.yml +3 -1
- data/CODE_OF_CONDUCT.md +128 -0
- data/README.md +98 -49
- data/lib/opentok/archives.rb +13 -2
- data/lib/opentok/broadcasts.rb +1 -1
- data/lib/opentok/client.rb +6 -4
- data/lib/opentok/constants.rb +1 -1
- data/lib/opentok/opentok.rb +4 -2
- data/lib/opentok/sip.rb +0 -2
- data/lib/opentok/version.rb +1 -1
- data/opentok.gemspec +9 -8
- data/sample/Broadcast/Gemfile +4 -0
- data/sample/Broadcast/README.md +201 -0
- data/sample/Broadcast/broadcast_sample.rb +97 -0
- data/sample/Broadcast/public/css/sample.css +64 -0
- data/sample/Broadcast/public/js/host.js +185 -0
- data/sample/Broadcast/public/js/participant.js +85 -0
- data/sample/Broadcast/views/host.erb +82 -0
- data/sample/Broadcast/views/index.erb +32 -0
- data/sample/Broadcast/views/layout.erb +29 -0
- data/sample/Broadcast/views/participant.erb +27 -0
- data/sample/HelloWorld/public/js/helloworld.js +4 -10
- data/sample/HelloWorld/views/index.erb +1 -3
- data/spec/cassettes/OpenTok_Archives/calls_layout_on_archive_object.yml +3 -1
- data/spec/cassettes/OpenTok_Archives/changes_the_layout_of_an_archive.yml +3 -1
- data/spec/cassettes/OpenTok_Archives/http_client_errors/.yml +34 -0
- data/spec/cassettes/OpenTok_Archives/should_create_archives.yml +3 -1
- data/spec/cassettes/OpenTok_Archives/should_create_audio_only_archives.yml +3 -1
- data/spec/cassettes/OpenTok_Archives/should_create_custom_layout_archives.yml +50 -0
- data/spec/cassettes/OpenTok_Archives/should_create_hd_archives.yml +3 -1
- data/spec/cassettes/OpenTok_Archives/should_create_individual_archives.yml +3 -1
- data/spec/cassettes/OpenTok_Archives/should_create_named_archives.yml +3 -1
- data/spec/cassettes/OpenTok_Archives/should_delete_an_archive_by_id.yml +3 -1
- data/spec/cassettes/OpenTok_Archives/should_find_archives_by_id.yml +3 -1
- data/spec/cassettes/OpenTok_Archives/should_find_archives_with_unknown_properties.yml +3 -1
- data/spec/cassettes/OpenTok_Archives/should_find_expired_archives.yml +3 -1
- data/spec/cassettes/OpenTok_Archives/should_find_paused_archives_by_id.yml +3 -1
- data/spec/cassettes/OpenTok_Archives/should_stop_archives.yml +3 -1
- data/spec/cassettes/OpenTok_Archives/when_many_archives_are_created/should_return_all_archives.yml +3 -1
- data/spec/cassettes/OpenTok_Archives/when_many_archives_are_created/should_return_archives_with_an_offset.yml +3 -1
- data/spec/cassettes/OpenTok_Archives/when_many_archives_are_created/should_return_count_number_of_archives.yml +3 -1
- data/spec/cassettes/OpenTok_Archives/when_many_archives_are_created/should_return_part_of_the_archives_when_using_offset_and_count.yml +3 -1
- data/spec/cassettes/OpenTok_Archives/when_many_archives_are_created/should_return_session_archives.yml +3 -1
- data/spec/cassettes/OpenTok_Broadcasts/calls_layout_on_broadcast_object.yml +3 -1
- data/spec/cassettes/OpenTok_Broadcasts/changes_the_layout_of_a_broadcast.yml +3 -1
- data/spec/cassettes/OpenTok_Broadcasts/fetches_a_hls_broadcast_url.yml +3 -1
- data/spec/cassettes/OpenTok_Broadcasts/finds_a_broadcast.yml +3 -1
- data/spec/cassettes/OpenTok_Broadcasts/starts_a_rtmp_broadcast.yml +3 -1
- data/spec/cassettes/OpenTok_Broadcasts/stops_a_broadcast.yml +3 -1
- data/spec/cassettes/OpenTok_Connections/forces_a_connection_to_be_terminated.yml +3 -1
- data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_always_archived_sessions.yml +3 -1
- data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_default_sessions.yml +3 -1
- data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_relayed_media_sessions.yml +3 -1
- data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_relayed_media_sessions_for_invalid_media_modes.yml +3 -1
- data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_relayed_media_sessions_with_a_location_hint.yml +3 -1
- data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_routed_media_sessions.yml +3 -1
- data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_routed_media_sessions_with_a_location_hint.yml +3 -1
- data/spec/cassettes/OpenTok_OpenTok/when_initialized_properly/_create_session/creates_sessions_with_a_location_hint.yml +3 -1
- 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
- data/spec/cassettes/OpenTok_Signals/receives_a_valid_response_for_a_connection.yml +3 -1
- data/spec/cassettes/OpenTok_Signals/receives_a_valid_response_for_all_connections.yml +3 -1
- data/spec/cassettes/OpenTok_Sip/receives_a_valid_response.yml +3 -1
- data/spec/cassettes/OpenTok_Streams/get_all_streams_information.yml +3 -1
- data/spec/cassettes/OpenTok_Streams/get_specific_stream_information.yml +3 -1
- data/spec/cassettes/OpenTok_Streams/layout_working_on_two_stream_list.yml +3 -1
- data/spec/opentok/archives_spec.rb +11 -1
- data/spec/opentok/broadcasts_spec.rb +25 -1
- data/spec/opentok/client_spec.rb +51 -0
- data/spec/opentok/opentok_spec.rb +21 -0
- data/spec/spec_helper.rb +2 -0
- metadata +39 -22
data/lib/opentok/archives.rb
CHANGED
@@ -48,6 +48,17 @@ module OpenTok
|
|
48
48
|
# default) or "1280x720" (HD). This property only applies to composed archives. If you set
|
49
49
|
# this property and set the outputMode property to "individual", the call the method
|
50
50
|
# results in an error.
|
51
|
+
# @option options [Hash] :layout Specify this to assign the initial layout type for
|
52
|
+
# the archive. This applies only to composed archives. This is a hash containing two keys:
|
53
|
+
# <code>:type</code> and <code>:stylesheet<code>. Valid values for <code>:type</code> are
|
54
|
+
# "bestFit" (best fit), "custom" (custom), "horizontalPresentation" (horizontal presentation),
|
55
|
+
# "pip" (picture-in-picture), and "verticalPresentation" (vertical presentation)).
|
56
|
+
# If you specify a "custom" layout type, set the <code>:stylesheet</code> key to the
|
57
|
+
# stylesheet (CSS). (For other layout types, do not set the <code>:stylesheet</code> key.)
|
58
|
+
# If you do not specify an initial layout type, the archive uses the best fit
|
59
|
+
# layout type. For more information, see
|
60
|
+
# {https://tokbox.com/developer/guides/archiving/layout-control.html Customizing
|
61
|
+
# the video layout for composed archives}.
|
51
62
|
#
|
52
63
|
# @return [Archive] The Archive object, which includes properties defining the archive,
|
53
64
|
# including the archive ID.
|
@@ -66,7 +77,7 @@ module OpenTok
|
|
66
77
|
"Resolution cannot be supplied for individual output mode" if options.key?(:resolution) and options[:output_mode] == :individual
|
67
78
|
|
68
79
|
# normalize opts so all keys are symbols and only include valid_opts
|
69
|
-
valid_opts = [ :name, :has_audio, :has_video, :output_mode, :resolution ]
|
80
|
+
valid_opts = [ :name, :has_audio, :has_video, :output_mode, :resolution, :layout ]
|
70
81
|
opts = options.inject({}) do |m,(k,v)|
|
71
82
|
if valid_opts.include? k.to_sym
|
72
83
|
m[k.to_sym] = v
|
@@ -198,7 +209,7 @@ module OpenTok
|
|
198
209
|
type = options[:type]
|
199
210
|
raise ArgumentError, "custom type must have a stylesheet" if (type.eql? "custom") && (!options.key? :stylesheet)
|
200
211
|
valid_non_custom_type = ["bestFit","horizontalPresentation","pip", "verticalPresentation", ""].include? type
|
201
|
-
raise ArgumentError, "type is not valid
|
212
|
+
raise ArgumentError, "type is not valid" if !valid_non_custom_type && !(type.eql? "custom")
|
202
213
|
raise ArgumentError, "type is not valid or stylesheet not needed" if valid_non_custom_type and options.key? :stylesheet
|
203
214
|
response = @client.layout_archive(archive_id, options)
|
204
215
|
(200..300).include? response.code
|
data/lib/opentok/broadcasts.rb
CHANGED
@@ -138,7 +138,7 @@ module OpenTok
|
|
138
138
|
type = options[:type]
|
139
139
|
raise ArgumentError, "custom type must have a stylesheet" if (type.eql? "custom") && (!options.key? :stylesheet)
|
140
140
|
valid_non_custom_type = ["bestFit","horizontalPresentation","pip", "verticalPresentation", ""].include? type
|
141
|
-
raise ArgumentError, "type is not valid" if !valid_non_custom_type
|
141
|
+
raise ArgumentError, "type is not valid" if !valid_non_custom_type && !(type.eql? "custom")
|
142
142
|
raise ArgumentError, "stylesheet not needed" if valid_non_custom_type and options.key? :stylesheet
|
143
143
|
response = @client.layout_broadcast(broadcast_id, options)
|
144
144
|
(200..300).include? response.code
|
data/lib/opentok/client.rb
CHANGED
@@ -12,18 +12,20 @@ module OpenTok
|
|
12
12
|
class Client
|
13
13
|
include HTTParty
|
14
14
|
|
15
|
-
open_timeout 2 # Set HTTParty default timeout (open/read) to 2 seconds
|
16
|
-
|
17
15
|
# TODO: expose a setting for http debugging for developers
|
18
16
|
# debug_output $stdout
|
19
17
|
|
20
|
-
|
18
|
+
attr_accessor :api_key, :api_secret, :api_url, :ua_addendum, :timeout_length
|
19
|
+
|
20
|
+
def initialize(api_key, api_secret, api_url, ua_addendum='', opts={})
|
21
21
|
self.class.base_uri api_url
|
22
22
|
self.class.headers({
|
23
|
-
"User-Agent" => "OpenTok-Ruby-SDK/#{VERSION}" + "-Ruby-Version-#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}" + (ua_addendum ? " #{ua_addendum}" : "")
|
23
|
+
"User-Agent" => "OpenTok-Ruby-SDK/#{VERSION}" + "-Ruby-Version-#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}" + (ua_addendum ? " #{ua_addendum}" : "")
|
24
24
|
})
|
25
25
|
@api_key = api_key
|
26
26
|
@api_secret = api_secret
|
27
|
+
@timeout_length = opts[:timeout_length] || 2
|
28
|
+
self.class.open_timeout @timeout_length
|
27
29
|
end
|
28
30
|
|
29
31
|
def generate_jwt(api_key, api_secret)
|
data/lib/opentok/constants.rb
CHANGED
@@ -2,6 +2,6 @@ module OpenTok
|
|
2
2
|
API_URL = "https://api.opentok.com"
|
3
3
|
TOKEN_SENTINEL = "T1=="
|
4
4
|
ROLES = { subscriber: "subscriber", publisher: "publisher", moderator: "moderator" }
|
5
|
-
ARCHIVE_MODES = Set.new([:manual, :always])
|
5
|
+
ARCHIVE_MODES = ::Set.new([:manual, :always])
|
6
6
|
AUTH_EXPIRE = 300
|
7
7
|
end
|
data/lib/opentok/opentok.rb
CHANGED
@@ -69,7 +69,7 @@ module OpenTok
|
|
69
69
|
# @private
|
70
70
|
# don't want these to be mutable, may cause bugs related to inconsistency since these values are
|
71
71
|
# cached in objects that this can create
|
72
|
-
attr_reader :api_key, :api_secret, :api_url, :ua_addendum
|
72
|
+
attr_reader :api_key, :api_secret, :timeout_length, :api_url, :ua_addendum
|
73
73
|
|
74
74
|
##
|
75
75
|
# Create a new OpenTok object.
|
@@ -79,9 +79,11 @@ module OpenTok
|
|
79
79
|
# @param [String] api_secret Your OpenTok API key.
|
80
80
|
# @option opts [Symbol] :api_url Do not set this parameter. It is for internal use by TokBox.
|
81
81
|
# @option opts [Symbol] :ua_addendum Do not set this parameter. It is for internal use by TokBox.
|
82
|
+
# @option opts [Symbol] :timeout_length Custom timeout in seconds. If not provided, defaults to 2 seconds.
|
82
83
|
def initialize(api_key, api_secret, opts={})
|
83
84
|
@api_key = api_key.to_s()
|
84
85
|
@api_secret = api_secret
|
86
|
+
@timeout_length = opts[:timeout_length] || 2
|
85
87
|
@api_url = opts[:api_url] || API_URL
|
86
88
|
@ua_addendum = opts[:ua_addendum]
|
87
89
|
end
|
@@ -212,7 +214,7 @@ module OpenTok
|
|
212
214
|
|
213
215
|
protected
|
214
216
|
def client
|
215
|
-
@client ||= Client.new api_key, api_secret, api_url, ua_addendum
|
217
|
+
@client ||= Client.new api_key, api_secret, api_url, ua_addendum, timeout_length: @timeout_length
|
216
218
|
end
|
217
219
|
|
218
220
|
end
|
data/lib/opentok/sip.rb
CHANGED
@@ -30,8 +30,6 @@ module OpenTok
|
|
30
30
|
# PSTN phones.
|
31
31
|
# @option opts [Hash] :headers This hash defines custom headers to be added
|
32
32
|
# to the SIP INVITE request initiated from OpenTok to the your SIP platform.
|
33
|
-
# Each of the custom headers must start with the "X-" prefix, or the call
|
34
|
-
# will result in a Bad Request (400) response.
|
35
33
|
# @option opts [Hash] :auth This object contains the username and password
|
36
34
|
# to be used in the the SIP INVITE request for HTTP digest authentication,
|
37
35
|
# if it is required by your SIP platform.
|
data/lib/opentok/version.rb
CHANGED
data/opentok.gemspec
CHANGED
@@ -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
|
-
|
20
|
-
spec.add_development_dependency "
|
21
|
-
spec.add_development_dependency "
|
22
|
-
spec.add_development_dependency "
|
23
|
-
spec.add_development_dependency "
|
24
|
-
spec.add_development_dependency "
|
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", "
|
30
|
+
spec.add_dependency "httparty", ">= 0.18.0"
|
30
31
|
spec.add_dependency "activesupport", ">= 2.0"
|
31
|
-
spec.add_dependency "jwt", "
|
32
|
+
spec.add_dependency "jwt", ">= 1.5.6"
|
32
33
|
end
|
@@ -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
|