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.
- 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 +312 -28
- data/lib/opentok/archive.rb +45 -4
- data/lib/opentok/archives.rb +73 -5
- data/lib/opentok/broadcast.rb +118 -0
- data/lib/opentok/broadcasts.rb +149 -0
- data/lib/opentok/client.rb +212 -7
- data/lib/opentok/connections.rb +28 -0
- data/lib/opentok/constants.rb +1 -1
- data/lib/opentok/exceptions.rb +6 -0
- data/lib/opentok/opentok.rb +44 -10
- data/lib/opentok/session.rb +5 -0
- data/lib/opentok/signals.rb +47 -0
- data/lib/opentok/sip.rb +33 -0
- data/lib/opentok/stream.rb +46 -0
- data/lib/opentok/stream_list.rb +18 -0
- data/lib/opentok/streams.rb +79 -0
- 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 +47 -0
- data/spec/cassettes/OpenTok_Archives/changes_the_layout_of_an_archive.yml +38 -0
- 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 +54 -0
- 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 +57 -0
- data/spec/cassettes/OpenTok_Broadcasts/changes_the_layout_of_a_broadcast.yml +38 -0
- data/spec/cassettes/OpenTok_Broadcasts/fetches_a_hls_broadcast_url.yml +52 -0
- data/spec/cassettes/OpenTok_Broadcasts/finds_a_broadcast.yml +57 -0
- data/spec/cassettes/OpenTok_Broadcasts/starts_a_rtmp_broadcast.yml +61 -0
- data/spec/cassettes/OpenTok_Broadcasts/stops_a_broadcast.yml +47 -0
- data/spec/cassettes/OpenTok_Connections/forces_a_connection_to_be_terminated.yml +38 -0
- 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 +39 -0
- data/spec/cassettes/OpenTok_Signals/receives_a_valid_response_for_all_connections.yml +39 -0
- data/spec/cassettes/OpenTok_Sip/receives_a_valid_response.yml +3 -1
- data/spec/cassettes/OpenTok_Streams/get_all_streams_information.yml +55 -0
- data/spec/cassettes/OpenTok_Streams/get_specific_stream_information.yml +44 -0
- data/spec/cassettes/OpenTok_Streams/layout_working_on_two_stream_list.yml +38 -0
- data/spec/opentok/archives_spec.rb +84 -1
- data/spec/opentok/broadcasts_spec.rb +171 -0
- data/spec/opentok/client_spec.rb +51 -0
- data/spec/opentok/connection_spec.rb +38 -0
- data/spec/opentok/opentok_spec.rb +21 -0
- data/spec/opentok/signal_spec.rb +50 -0
- data/spec/opentok/streams_spec.rb +75 -0
- data/spec/spec_helper.rb +2 -0
- metadata +84 -22
data/lib/opentok/sip.rb
CHANGED
@@ -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
|
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
|