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
@@ -0,0 +1,64 @@
|
|
1
|
+
/* Move down content because we have a fixed navbar that is 50px tall */
|
2
|
+
body {
|
3
|
+
padding-top: 50px;
|
4
|
+
padding-bottom: 20px;
|
5
|
+
background-color: #F2F2F2;
|
6
|
+
}
|
7
|
+
|
8
|
+
/* Responsive: Portrait tablets and up */
|
9
|
+
@media screen and (min-width: 768px) {
|
10
|
+
/* Remove padding from wrapping element since we kick in the grid classes here */
|
11
|
+
.body-content {
|
12
|
+
padding: 0;
|
13
|
+
}
|
14
|
+
}
|
15
|
+
|
16
|
+
#streams {
|
17
|
+
background-color: gray;
|
18
|
+
width: 320px;
|
19
|
+
height: 180px;
|
20
|
+
}
|
21
|
+
|
22
|
+
#streams > div {
|
23
|
+
width: 20%;
|
24
|
+
height: 20%;
|
25
|
+
float: left;
|
26
|
+
position: relative;
|
27
|
+
cursor: pointer;
|
28
|
+
}
|
29
|
+
|
30
|
+
#streams.vertical > div {
|
31
|
+
left: 0px;
|
32
|
+
clear: left;
|
33
|
+
padding: 0px;
|
34
|
+
}
|
35
|
+
|
36
|
+
#streams .focus {
|
37
|
+
position: relative;
|
38
|
+
top: 0;
|
39
|
+
left: 0;
|
40
|
+
margin-top: 0;
|
41
|
+
height: 100%;
|
42
|
+
width: 100%;
|
43
|
+
}
|
44
|
+
|
45
|
+
#streams.vertical .focus {
|
46
|
+
padding: 0;
|
47
|
+
left: 0;
|
48
|
+
margin: 0;
|
49
|
+
left: 20%;
|
50
|
+
height: 100%;
|
51
|
+
width: 80%;
|
52
|
+
}
|
53
|
+
|
54
|
+
.stop {
|
55
|
+
display: none;
|
56
|
+
}
|
57
|
+
|
58
|
+
.bump-me {
|
59
|
+
padding-top: 40px;
|
60
|
+
}
|
61
|
+
|
62
|
+
.help-block {
|
63
|
+
font-weight: bold;
|
64
|
+
}
|
@@ -0,0 +1,185 @@
|
|
1
|
+
/* global OT, apiKey, sessionId, initialBroadcastId, token, $, initialLayout, focusStreamId */
|
2
|
+
/* eslint-disable no-console */
|
3
|
+
|
4
|
+
var session = OT.initSession(apiKey, sessionId);
|
5
|
+
var publisher = OT.initPublisher('publisher', {
|
6
|
+
insertMode: 'append',
|
7
|
+
width: '100%',
|
8
|
+
height: '100%',
|
9
|
+
resolution: '1280x720'
|
10
|
+
});
|
11
|
+
var broadcastId = initialBroadcastId;
|
12
|
+
var layout = initialLayout;
|
13
|
+
|
14
|
+
function disableForm() {
|
15
|
+
$('.broadcast-options-fields').attr('disabled', 'disabled');
|
16
|
+
$('.start').hide();
|
17
|
+
$('.stop').show();
|
18
|
+
}
|
19
|
+
|
20
|
+
function enableForm() {
|
21
|
+
$('.broadcast-options-fields').removeAttr('disabled');
|
22
|
+
$('.start').show();
|
23
|
+
$('.stop').hide();
|
24
|
+
}
|
25
|
+
|
26
|
+
function positionStreams() {
|
27
|
+
var $focusElement;
|
28
|
+
$focusElement = $('.focus');
|
29
|
+
if ($('#streams').hasClass('vertical')) {
|
30
|
+
$('#streams').children().css('top', '0');
|
31
|
+
$focusElement.appendTo('#streams');
|
32
|
+
$focusElement.css('top', (-20 * ($('#streams').children().size() - 1)) + '%');
|
33
|
+
}
|
34
|
+
else {
|
35
|
+
$focusElement.prependTo('#streams');
|
36
|
+
$focusElement.css('top', '0');
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
function setFocus(focusStreamId) {
|
41
|
+
var $focusElement;
|
42
|
+
var otherStreams = $.map($('#streams').children(), function (element) {
|
43
|
+
var streamId = (element.id === 'publisher' && publisher.stream) ? publisher.stream.streamId
|
44
|
+
: element.id;
|
45
|
+
if (streamId !== focusStreamId) {
|
46
|
+
$('#' + element.id).removeClass('focus');
|
47
|
+
return streamId;
|
48
|
+
}
|
49
|
+
return null;
|
50
|
+
});
|
51
|
+
|
52
|
+
$.post('/focus', {
|
53
|
+
focus: focusStreamId,
|
54
|
+
otherStreams: otherStreams
|
55
|
+
}).done(function () {
|
56
|
+
console.log('Focus changed.');
|
57
|
+
}).fail(function (jqXHR) {
|
58
|
+
console.error('Stream class list error:', jqXHR.responseText);
|
59
|
+
});
|
60
|
+
|
61
|
+
$('.focus').removeClass('focus');
|
62
|
+
$focusElement = (publisher.stream && publisher.stream.streamId === focusStreamId) ?
|
63
|
+
$('#publisher') : $('#' + focusStreamId);
|
64
|
+
$focusElement.addClass('focus');
|
65
|
+
session.signal({
|
66
|
+
type: 'focusStream',
|
67
|
+
data: focusStreamId
|
68
|
+
});
|
69
|
+
positionStreams();
|
70
|
+
}
|
71
|
+
|
72
|
+
function createFocusClick(elementId, focusStreamId) {
|
73
|
+
$('#' + elementId).click(function () {
|
74
|
+
setFocus(focusStreamId);
|
75
|
+
});
|
76
|
+
}
|
77
|
+
|
78
|
+
if (initialLayout === 'verticalPresentation') {
|
79
|
+
$('#streams').addClass('vertical');
|
80
|
+
}
|
81
|
+
|
82
|
+
if (initialLayout === 'verticalPresentation') {
|
83
|
+
$('.start').hide();
|
84
|
+
$('.stop').show();
|
85
|
+
}
|
86
|
+
|
87
|
+
session.connect(token, function (err) {
|
88
|
+
if (err) {
|
89
|
+
alert(err.message || err); // eslint-disable-line no-alert
|
90
|
+
}
|
91
|
+
session.publish(publisher);
|
92
|
+
});
|
93
|
+
|
94
|
+
publisher.on('streamCreated', function () {
|
95
|
+
createFocusClick(publisher.id, publisher.stream.streamId);
|
96
|
+
positionStreams();
|
97
|
+
});
|
98
|
+
|
99
|
+
session.on('streamCreated', function (event) {
|
100
|
+
var subscriber;
|
101
|
+
var streamId = event.stream.streamId;
|
102
|
+
var $streamContainer = $('<div></div>');
|
103
|
+
$streamContainer.attr('id', event.stream.id);
|
104
|
+
$('#streams').append($streamContainer);
|
105
|
+
subscriber = session.subscribe(event.stream, streamId, {
|
106
|
+
insertMode: 'append',
|
107
|
+
width: '100%',
|
108
|
+
height: '100%'
|
109
|
+
});
|
110
|
+
|
111
|
+
if (streamId === focusStreamId) {
|
112
|
+
setFocus(streamId);
|
113
|
+
}
|
114
|
+
createFocusClick(subscriber.id, streamId);
|
115
|
+
positionStreams();
|
116
|
+
});
|
117
|
+
|
118
|
+
session.on('streamDestroyed', function (event) {
|
119
|
+
var $streamElem = $('#' + event.stream.id);
|
120
|
+
if ($streamElem.hasClass('focus')) {
|
121
|
+
setFocus(publisher.stream.streamId);
|
122
|
+
}
|
123
|
+
$streamElem.remove();
|
124
|
+
positionStreams();
|
125
|
+
});
|
126
|
+
|
127
|
+
$(document).ready(function () {
|
128
|
+
$('.start').click(function () {
|
129
|
+
var options = {
|
130
|
+
maxDuration: $('input[name=maxDuration]').val() || undefined,
|
131
|
+
resolution: $('input[name=resolution]:checked').val(),
|
132
|
+
layout: {
|
133
|
+
type: layout
|
134
|
+
}
|
135
|
+
};
|
136
|
+
disableForm();
|
137
|
+
$.post('/start', options)
|
138
|
+
.done(function (response) {
|
139
|
+
console.log('start success.');
|
140
|
+
broadcastId = response.id;
|
141
|
+
setFocus(publisher.stream.streamId);
|
142
|
+
})
|
143
|
+
.fail(function (jqXHR) {
|
144
|
+
console.error(jqXHR.responseText);
|
145
|
+
enableForm();
|
146
|
+
});
|
147
|
+
}).prop('disabled', false);
|
148
|
+
$('.stop').click(function () {
|
149
|
+
$.get('stop/' + broadcastId)
|
150
|
+
.done(function () {
|
151
|
+
console.log('stop success.');
|
152
|
+
broadcastId = null;
|
153
|
+
enableForm();
|
154
|
+
})
|
155
|
+
.fail(function (jqXHR) {
|
156
|
+
console.error(jqXHR.responseText);
|
157
|
+
});
|
158
|
+
});
|
159
|
+
$('.toggle-layout').click(function () {
|
160
|
+
if ($('#streams').hasClass('vertical')) {
|
161
|
+
$('#streams').removeClass('vertical');
|
162
|
+
}
|
163
|
+
else {
|
164
|
+
$('#streams').addClass('vertical');
|
165
|
+
}
|
166
|
+
|
167
|
+
positionStreams();
|
168
|
+
|
169
|
+
layout = $('#streams').hasClass('vertical') ? 'verticalPresentation'
|
170
|
+
: 'horizontalPresentation';
|
171
|
+
|
172
|
+
$.post('broadcast/' + broadcastId + '/layout', {
|
173
|
+
type: layout
|
174
|
+
}).done(function () {
|
175
|
+
console.log('Broadcast layout updated.');
|
176
|
+
}).fail(function (jqXHR) {
|
177
|
+
console.error('Broadcast layout error:', jqXHR.responseText);
|
178
|
+
});
|
179
|
+
|
180
|
+
session.signal({
|
181
|
+
type: 'layoutClass',
|
182
|
+
data: layout
|
183
|
+
});
|
184
|
+
});
|
185
|
+
});
|
@@ -0,0 +1,85 @@
|
|
1
|
+
/* global OT, apiKey, sessionId, token, $, layout, focusStreamId */
|
2
|
+
var session = OT.initSession(apiKey, sessionId);
|
3
|
+
var publisher;
|
4
|
+
|
5
|
+
var container = $('<div id = "publisher"></div>');
|
6
|
+
|
7
|
+
if (layout === 'verticalPresentation') {
|
8
|
+
$('#streams').addClass('vertical');
|
9
|
+
}
|
10
|
+
|
11
|
+
container.addClass('focus');
|
12
|
+
$('#streams').append(container);
|
13
|
+
|
14
|
+
publisher = OT.initPublisher('publisher', {
|
15
|
+
insertMode: 'append',
|
16
|
+
width: '100%',
|
17
|
+
height: '100%',
|
18
|
+
resolution: '1280x720'
|
19
|
+
});
|
20
|
+
|
21
|
+
function positionStreams() {
|
22
|
+
var $focusElement = $('.focus');
|
23
|
+
if ($('#streams').hasClass('vertical')) {
|
24
|
+
$focusElement.appendTo('#streams');
|
25
|
+
$('#streams').children().css('top', '0');
|
26
|
+
$focusElement.css('top', (-20 * ($('#streams').children().size() - 1)) + '%');
|
27
|
+
}
|
28
|
+
else {
|
29
|
+
$focusElement.prependTo('#streams');
|
30
|
+
$focusElement.css('top', '0');
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
function focusStream(streamId) {
|
35
|
+
var focusStreamId = streamId;
|
36
|
+
var $focusElement = (publisher.stream && publisher.stream.id === focusStreamId) ? $('#publisher')
|
37
|
+
: $('#' + focusStreamId);
|
38
|
+
$('.focus').removeClass('focus');
|
39
|
+
$focusElement.addClass('focus');
|
40
|
+
positionStreams();
|
41
|
+
}
|
42
|
+
|
43
|
+
session.connect(token, function (err) {
|
44
|
+
if (err) {
|
45
|
+
alert(err.message || err); // eslint-disable-line no-alert
|
46
|
+
}
|
47
|
+
session.publish(publisher);
|
48
|
+
});
|
49
|
+
|
50
|
+
session.on('streamCreated', function (event) {
|
51
|
+
var streamId = event.stream.id;
|
52
|
+
container = document.createElement('div');
|
53
|
+
container.id = streamId;
|
54
|
+
$('#streams').append(container);
|
55
|
+
session.subscribe(event.stream, streamId, {
|
56
|
+
insertMode: 'append',
|
57
|
+
width: '100%',
|
58
|
+
height: '100%'
|
59
|
+
});
|
60
|
+
if (streamId === focusStreamId) {
|
61
|
+
focusStream(streamId);
|
62
|
+
}
|
63
|
+
positionStreams();
|
64
|
+
});
|
65
|
+
|
66
|
+
session.on('streamDestroyed', function (event) {
|
67
|
+
$('#' + event.stream.id).remove();
|
68
|
+
positionStreams();
|
69
|
+
});
|
70
|
+
|
71
|
+
session.on('signal:layoutClass', function (event) {
|
72
|
+
if (event.data === 'horizontalPresentation') {
|
73
|
+
$('#streams').removeClass('vertical');
|
74
|
+
$('.focus').prependTo('#streams');
|
75
|
+
}
|
76
|
+
else {
|
77
|
+
$('#streams').addClass('vertical');
|
78
|
+
$('.focus').appendTo('#streams');
|
79
|
+
}
|
80
|
+
positionStreams();
|
81
|
+
});
|
82
|
+
|
83
|
+
session.on('signal:focusStream', function (event) {
|
84
|
+
focusStream(event.data);
|
85
|
+
});
|
@@ -0,0 +1,82 @@
|
|
1
|
+
|
2
|
+
<script src="https://static.opentok.com/v2/js/opentok.min.js"></script>
|
3
|
+
|
4
|
+
<div class="container bump-me">
|
5
|
+
|
6
|
+
<div class="body-content">
|
7
|
+
|
8
|
+
<div class="panel panel-default">
|
9
|
+
<div class="panel-heading">
|
10
|
+
<h3 class="panel-title">Host</h3>
|
11
|
+
</div>
|
12
|
+
<div class="panel-body">
|
13
|
+
<div id="streams">
|
14
|
+
<div id="publisher" class="focus"></div>
|
15
|
+
</div>
|
16
|
+
</div>
|
17
|
+
<div class="panel-footer">
|
18
|
+
<form class="broadcast-options">
|
19
|
+
<fieldset class="broadcast-options-fields">
|
20
|
+
<div class="form-group">
|
21
|
+
<h4>Broadcast Options:</h4>
|
22
|
+
<label>
|
23
|
+
Maximum duration (seconds, minimum 60, maximum 36000):
|
24
|
+
<input type="number" step="30" min="60" max="36000" name="maxDuration">
|
25
|
+
</label>
|
26
|
+
</div>
|
27
|
+
|
28
|
+
<div class="form-group">
|
29
|
+
<p>
|
30
|
+
<label>Resolution:</label>
|
31
|
+
<label class="help-block">
|
32
|
+
<input type="radio" name="resolution" value="640x480"> 640x480
|
33
|
+
</label>
|
34
|
+
<label class="help-block">
|
35
|
+
<input type="radio" name="resolution" value="1280x720" checked> 1280x720
|
36
|
+
</label>
|
37
|
+
</p>
|
38
|
+
</div>
|
39
|
+
</fieldset>
|
40
|
+
</form>
|
41
|
+
<button class="btn btn-danger start" disabled>Start broadcast</button>
|
42
|
+
<button class="btn btn-success stop">Stop broadcast</button>
|
43
|
+
<button class="btn toggle-layout">Toggle layout</button>
|
44
|
+
</div>
|
45
|
+
</div>
|
46
|
+
</div>
|
47
|
+
|
48
|
+
<div class="panel panel-default">
|
49
|
+
<div class="panel-heading">
|
50
|
+
<h3 class="panel-title">Instructions</h3>
|
51
|
+
</div>
|
52
|
+
<div class="panel-body">
|
53
|
+
<p>
|
54
|
+
Click <strong>Start broadcast</strong> to start broadcasting this session.
|
55
|
+
All publishers in the session will be included, and all publishers that
|
56
|
+
join the session will be included as well.
|
57
|
+
</p>
|
58
|
+
<p>
|
59
|
+
Click <strong>Stop broadcast</strong> to stop broadcasting this session.
|
60
|
+
</p>
|
61
|
+
<p>
|
62
|
+
Click <strong>Toggle layout</strong> to toggle the layout
|
63
|
+
between a vertical and horizontal presentation. The layout changes in all clients
|
64
|
+
and in the broadcast.
|
65
|
+
<p>
|
66
|
+
Click any stream to set it to be the focus stream in the broadcast layout.
|
67
|
+
</div>
|
68
|
+
</div>
|
69
|
+
</div>
|
70
|
+
|
71
|
+
<script>
|
72
|
+
var sessionId = "<%= sessionId %>";
|
73
|
+
var initialBroadcastId = "<%= initialBroadcastId || '' %>";
|
74
|
+
var apiKey = "<%= apiKey %>";
|
75
|
+
var token = "<%= token %>";
|
76
|
+
var focusStreamId = "<%= focusStreamId || '' %>";
|
77
|
+
var initialLayout = "<%= initialLayout %>";
|
78
|
+
</script>
|
79
|
+
<script src="/js/host.js"></script>
|
80
|
+
|
81
|
+
</div>
|
82
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<div class="container bump-me">
|
2
|
+
|
3
|
+
|
4
|
+
<div class="body-content">
|
5
|
+
|
6
|
+
|
7
|
+
<div class="row">
|
8
|
+
<div class="col-lg-6 col-offset-1">
|
9
|
+
|
10
|
+
<div class="panel panel-default">
|
11
|
+
<div class="panel-heading">Create a broadcast</div>
|
12
|
+
<div class="panel-body">
|
13
|
+
<p>
|
14
|
+
Everyone who joins either the Host View or Participant View
|
15
|
+
joins a single OpenTok session. The Host can click
|
16
|
+
Start Broadcast and Stop Broadcast to control the live streaming
|
17
|
+
broadcast of the entire session.
|
18
|
+
</p>
|
19
|
+
</div>
|
20
|
+
<div class="panel-footer">
|
21
|
+
<a class="btn btn-danger" href="host">Host View</a>
|
22
|
+
<a class="btn btn-danger" href="participant">Participant View</a>
|
23
|
+
<a class="btn btn-danger" href="broadcast">Broadcast URL</a>
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
|
27
|
+
</div>
|
28
|
+
|
29
|
+
</div>
|
30
|
+
|
31
|
+
</div>
|
32
|
+
</div>
|