Merge pull request #543 from damencho/shared-video
Shared video, synchronized play/pause/seek/muting/volume, initial commit.
This commit is contained in:
commit
32c2d912be
|
@ -27,7 +27,8 @@ let room, connection, localAudio, localVideo, roomLocker;
|
||||||
const Commands = {
|
const Commands = {
|
||||||
CONNECTION_QUALITY: "stats",
|
CONNECTION_QUALITY: "stats",
|
||||||
EMAIL: "email",
|
EMAIL: "email",
|
||||||
ETHERPAD: "etherpad"
|
ETHERPAD: "etherpad",
|
||||||
|
SHARED_VIDEO: "shared-video"
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -689,6 +690,7 @@ export default {
|
||||||
console.log('USER %s LEFT', id, user);
|
console.log('USER %s LEFT', id, user);
|
||||||
APP.API.notifyUserLeft(id);
|
APP.API.notifyUserLeft(id);
|
||||||
APP.UI.removeUser(id, user.getDisplayName());
|
APP.UI.removeUser(id, user.getDisplayName());
|
||||||
|
APP.UI.stopSharedVideo({from: id});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -1012,5 +1014,47 @@ export default {
|
||||||
APP.UI.addListener(
|
APP.UI.addListener(
|
||||||
UIEvents.TOGGLE_SCREENSHARING, this.toggleScreenSharing.bind(this)
|
UIEvents.TOGGLE_SCREENSHARING, this.toggleScreenSharing.bind(this)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
APP.UI.addListener(UIEvents.UPDATE_SHARED_VIDEO,
|
||||||
|
(url, state, time, volume) => {
|
||||||
|
// send start and stop commands once, and remove any updates
|
||||||
|
// that had left
|
||||||
|
if (state === 'stop' || state === 'start' || state === 'playing') {
|
||||||
|
room.removeCommand(Commands.SHARED_VIDEO);
|
||||||
|
room.sendCommandOnce(Commands.SHARED_VIDEO, {
|
||||||
|
value: url,
|
||||||
|
attributes: {
|
||||||
|
from: APP.conference.localId,
|
||||||
|
state: state,
|
||||||
|
time: time,
|
||||||
|
volume: volume
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// in case of paused, in order to allow late users to join
|
||||||
|
// paused
|
||||||
|
room.sendCommand(Commands.SHARED_VIDEO, {
|
||||||
|
value: url,
|
||||||
|
attributes: {
|
||||||
|
from: APP.conference.localId,
|
||||||
|
state: state,
|
||||||
|
time: time,
|
||||||
|
volume: volume
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
room.addCommandListener(
|
||||||
|
Commands.SHARED_VIDEO, ({value, attributes}) => {
|
||||||
|
if (attributes.state === 'stop') {
|
||||||
|
APP.UI.stopSharedVideo(attributes);
|
||||||
|
} else if (attributes.state === 'start') {
|
||||||
|
APP.UI.showSharedVideo(value, attributes);
|
||||||
|
} else if (attributes.state === 'playing'
|
||||||
|
|| attributes.state === 'pause') {
|
||||||
|
APP.UI.updateSharedVideo(value, attributes);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -63,6 +63,7 @@ html, body{
|
||||||
text-align: center;
|
text-align: center;
|
||||||
text-shadow: 0 1px 0 rgba(255,255,255,.3), 0 -1px 0 rgba(0,0,0,.6);
|
text-shadow: 0 1px 0 rgba(255,255,255,.3), 0 -1px 0 rgba(0,0,0,.6);
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
font-size: 1.22em !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar_span>span {
|
.toolbar_span>span {
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#remoteVideos .videocontainer {
|
#remoteVideos .videocontainer {
|
||||||
|
@ -112,6 +113,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#presentation,
|
#presentation,
|
||||||
|
#sharedVideo,
|
||||||
#etherpad,
|
#etherpad,
|
||||||
#localVideoWrapper>video,
|
#localVideoWrapper>video,
|
||||||
#localVideoWrapper>object,
|
#localVideoWrapper>object,
|
||||||
|
@ -436,6 +438,10 @@
|
||||||
border-radius: 200px;
|
border-radius: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sharedVideoAvatar {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.noMic {
|
.noMic {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|
|
@ -123,6 +123,7 @@
|
||||||
<span id="unreadMessages"></span>
|
<span id="unreadMessages"></span>
|
||||||
</a>
|
</a>
|
||||||
<a class="button icon-share-doc" id="toolbar_button_etherpad" data-container="body" data-toggle="popover" data-placement="bottom" content="Shared document" data-i18n="[content]toolbar.etherpad"></a>
|
<a class="button icon-share-doc" id="toolbar_button_etherpad" data-container="body" data-toggle="popover" data-placement="bottom" content="Shared document" data-i18n="[content]toolbar.etherpad"></a>
|
||||||
|
<a class="button fa fa-share-alt-square" id="toolbar_button_sharedvideo" data-container="body" data-toggle="popover" data-placement="bottom" content="Shared Video" data-i18n="[content]toolbar.sharedvideo" style="display: none"></a>
|
||||||
<a class="button icon-share-desktop" id="toolbar_button_desktopsharing" data-container="body" data-toggle="popover" data-placement="bottom" shortcut="toggleDesktopSharingPopover" content="Share screen" data-i18n="[content]toolbar.sharescreen" style="display: none"></a>
|
<a class="button icon-share-desktop" id="toolbar_button_desktopsharing" data-container="body" data-toggle="popover" data-placement="bottom" shortcut="toggleDesktopSharingPopover" content="Share screen" data-i18n="[content]toolbar.sharescreen" style="display: none"></a>
|
||||||
<a class="button icon-full-screen" id="toolbar_button_fullScreen" data-container="body" data-toggle="popover" data-placement="bottom" content="Enter / Exit Full Screen" data-i18n="[content]toolbar.fullscreen"></a>
|
<a class="button icon-full-screen" id="toolbar_button_fullScreen" data-container="body" data-toggle="popover" data-placement="bottom" content="Enter / Exit Full Screen" data-i18n="[content]toolbar.fullscreen"></a>
|
||||||
<a class="button icon-telephone" id="toolbar_button_sip" data-container="body" data-toggle="popover" data-placement="bottom" content="Call SIP number" data-i18n="[content]toolbar.sip" style="display: none"></a>
|
<a class="button icon-telephone" id="toolbar_button_sip" data-container="body" data-toggle="popover" data-placement="bottom" content="Call SIP number" data-i18n="[content]toolbar.sip" style="display: none"></a>
|
||||||
|
@ -137,6 +138,7 @@
|
||||||
|
|
||||||
<div id="largeVideoContainer" class="videocontainer">
|
<div id="largeVideoContainer" class="videocontainer">
|
||||||
<div id="presentation"></div>
|
<div id="presentation"></div>
|
||||||
|
<div id="sharedVideo"><div id="sharedVideoIFrame"></div></div>
|
||||||
<div id="etherpad"></div>
|
<div id="etherpad"></div>
|
||||||
<a target="_new"><div class="watermark leftwatermark"></div></a>
|
<a target="_new"><div class="watermark leftwatermark"></div></a>
|
||||||
<a target="_new"><div class="watermark rightwatermark"></div></a>
|
<a target="_new"><div class="watermark rightwatermark"></div></a>
|
||||||
|
|
|
@ -16,7 +16,7 @@ var interfaceConfig = {
|
||||||
INVITATION_POWERED_BY: true,
|
INVITATION_POWERED_BY: true,
|
||||||
DOMINANT_SPEAKER_AVATAR_SIZE: 100,
|
DOMINANT_SPEAKER_AVATAR_SIZE: 100,
|
||||||
TOOLBAR_BUTTONS: ['authentication', 'microphone', 'camera', 'desktop',
|
TOOLBAR_BUTTONS: ['authentication', 'microphone', 'camera', 'desktop',
|
||||||
'recording', 'security', 'invite', 'chat', 'etherpad',
|
'recording', 'security', 'invite', 'chat', 'etherpad', 'sharedvideo',
|
||||||
'fullscreen', 'sip', 'dialpad', 'settings', 'hangup', 'filmstrip',
|
'fullscreen', 'sip', 'dialpad', 'settings', 'hangup', 'filmstrip',
|
||||||
'contacts'],
|
'contacts'],
|
||||||
// Determines how the video would fit the screen. 'both' would fit the whole
|
// Determines how the video would fit the screen. 'both' would fit the whole
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
"me": "me",
|
"me": "me",
|
||||||
"speaker": "Speaker",
|
"speaker": "Speaker",
|
||||||
"defaultNickname": "ex. __name__",
|
"defaultNickname": "ex. __name__",
|
||||||
|
"defaultLink": "e.g. __url__",
|
||||||
"welcomepage":{
|
"welcomepage":{
|
||||||
"go": "GO",
|
"go": "GO",
|
||||||
"roomname": "Enter room name",
|
"roomname": "Enter room name",
|
||||||
|
@ -55,6 +56,7 @@
|
||||||
"invite": "Invite others",
|
"invite": "Invite others",
|
||||||
"chat": "Open / close chat",
|
"chat": "Open / close chat",
|
||||||
"etherpad": "Shared document",
|
"etherpad": "Shared document",
|
||||||
|
"sharedvideo": "Shared video",
|
||||||
"sharescreen": "Share screen",
|
"sharescreen": "Share screen",
|
||||||
"fullscreen": "Enter / Exit Full Screen",
|
"fullscreen": "Enter / Exit Full Screen",
|
||||||
"sip": "Call SIP number",
|
"sip": "Call SIP number",
|
||||||
|
@ -159,6 +161,10 @@
|
||||||
"passwordRequired": "Password required",
|
"passwordRequired": "Password required",
|
||||||
"Ok": "Ok",
|
"Ok": "Ok",
|
||||||
"Remove": "Remove",
|
"Remove": "Remove",
|
||||||
|
"shareVideoTitle": "Share a video",
|
||||||
|
"shareVideoLinkError": "Please provide a correct youtube link.",
|
||||||
|
"removeSharedVideoTitle": "Remove shared video",
|
||||||
|
"removeSharedVideoMsg": "Are you sure you would like to remove your shared video?",
|
||||||
"WaitingForHost": "Waiting for the host ...",
|
"WaitingForHost": "Waiting for the host ...",
|
||||||
"WaitForHostMsg": "The conference <b>__room__ </b> has not yet started. If you are the host then please authenticate. Otherwise, please wait for the host to arrive.",
|
"WaitForHostMsg": "The conference <b>__room__ </b> has not yet started. If you are the host then please authenticate. Otherwise, please wait for the host to arrive.",
|
||||||
"IamHost": "I am the host",
|
"IamHost": "I am the host",
|
||||||
|
|
|
@ -13,6 +13,7 @@ import UIUtil from "./util/UIUtil";
|
||||||
import UIEvents from "../../service/UI/UIEvents";
|
import UIEvents from "../../service/UI/UIEvents";
|
||||||
import CQEvents from '../../service/connectionquality/CQEvents';
|
import CQEvents from '../../service/connectionquality/CQEvents';
|
||||||
import EtherpadManager from './etherpad/Etherpad';
|
import EtherpadManager from './etherpad/Etherpad';
|
||||||
|
import SharedVideoManager from './shared_video/SharedVideo';
|
||||||
|
|
||||||
import VideoLayout from "./videolayout/VideoLayout";
|
import VideoLayout from "./videolayout/VideoLayout";
|
||||||
import FilmStrip from "./videolayout/FilmStrip";
|
import FilmStrip from "./videolayout/FilmStrip";
|
||||||
|
@ -30,6 +31,7 @@ var eventEmitter = new EventEmitter();
|
||||||
UI.eventEmitter = eventEmitter;
|
UI.eventEmitter = eventEmitter;
|
||||||
|
|
||||||
let etherpadManager;
|
let etherpadManager;
|
||||||
|
let sharedVideoManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prompt user for nickname.
|
* Prompt user for nickname.
|
||||||
|
@ -260,6 +262,12 @@ function registerListeners() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
UI.addListener(UIEvents.SHARED_VIDEO_CLICKED, function () {
|
||||||
|
if (sharedVideoManager) {
|
||||||
|
sharedVideoManager.toggleSharedVideo();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
UI.addListener(UIEvents.FULLSCREEN_TOGGLE, toggleFullScreen);
|
UI.addListener(UIEvents.FULLSCREEN_TOGGLE, toggleFullScreen);
|
||||||
|
|
||||||
UI.addListener(UIEvents.TOGGLE_CHAT, UI.toggleChat);
|
UI.addListener(UIEvents.TOGGLE_CHAT, UI.toggleChat);
|
||||||
|
@ -334,6 +342,7 @@ UI.start = function () {
|
||||||
ContactList.init(eventEmitter);
|
ContactList.init(eventEmitter);
|
||||||
|
|
||||||
bindEvents();
|
bindEvents();
|
||||||
|
sharedVideoManager = new SharedVideoManager(eventEmitter);
|
||||||
if (!interfaceConfig.filmStripOnly) {
|
if (!interfaceConfig.filmStripOnly) {
|
||||||
|
|
||||||
$("#videospace").mousemove(function () {
|
$("#videospace").mousemove(function () {
|
||||||
|
@ -481,11 +490,11 @@ UI.addUser = function (id, displayName) {
|
||||||
config.startAudioMuted > APP.conference.membersCount)
|
config.startAudioMuted > APP.conference.membersCount)
|
||||||
UIUtil.playSoundNotification('userJoined');
|
UIUtil.playSoundNotification('userJoined');
|
||||||
|
|
||||||
// Configure avatar
|
|
||||||
UI.setUserAvatar(id);
|
|
||||||
|
|
||||||
// Add Peer's container
|
// Add Peer's container
|
||||||
VideoLayout.addParticipantContainer(id);
|
VideoLayout.addParticipantContainer(id);
|
||||||
|
|
||||||
|
// Configure avatar
|
||||||
|
UI.setUserAvatar(id);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -530,6 +539,7 @@ UI.updateLocalRole = function (isModerator) {
|
||||||
|
|
||||||
Toolbar.showSipCallButton(isModerator);
|
Toolbar.showSipCallButton(isModerator);
|
||||||
Toolbar.showRecordingButton(isModerator);
|
Toolbar.showRecordingButton(isModerator);
|
||||||
|
Toolbar.showSharedVideoButton(isModerator);
|
||||||
SettingsMenu.showStartMutedOptions(isModerator);
|
SettingsMenu.showStartMutedOptions(isModerator);
|
||||||
|
|
||||||
if (isModerator) {
|
if (isModerator) {
|
||||||
|
@ -1030,6 +1040,14 @@ UI.getLargeVideoID = function () {
|
||||||
return VideoLayout.getLargeVideoID();
|
return VideoLayout.getLargeVideoID();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current video shown on large.
|
||||||
|
* Currently used by tests (torture).
|
||||||
|
*/
|
||||||
|
UI.getLargeVideo = function () {
|
||||||
|
return VideoLayout.getLargeVideo();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows dialog with a link to FF extension.
|
* Shows dialog with a link to FF extension.
|
||||||
*/
|
*/
|
||||||
|
@ -1046,4 +1064,33 @@ UI.updateDevicesAvailability = function (id, devices) {
|
||||||
VideoLayout.setDeviceAvailabilityIcons(id, devices);
|
VideoLayout.setDeviceAvailabilityIcons(id, devices);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show shared video.
|
||||||
|
* @param {string} url video url
|
||||||
|
* @param {string} attributes
|
||||||
|
*/
|
||||||
|
UI.showSharedVideo = function (url, attributes) {
|
||||||
|
if (sharedVideoManager)
|
||||||
|
sharedVideoManager.showSharedVideo(url, attributes);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update shared video.
|
||||||
|
* @param {string} url video url
|
||||||
|
* @param {string} attributes
|
||||||
|
*/
|
||||||
|
UI.updateSharedVideo = function (url, attributes) {
|
||||||
|
if (sharedVideoManager)
|
||||||
|
sharedVideoManager.updateSharedVideo(url, attributes);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop showing shared video.
|
||||||
|
* @param {string} attributes
|
||||||
|
*/
|
||||||
|
UI.stopSharedVideo = function (attributes) {
|
||||||
|
if (sharedVideoManager)
|
||||||
|
sharedVideoManager.stopSharedVideo(attributes);
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = UI;
|
module.exports = UI;
|
||||||
|
|
|
@ -110,9 +110,11 @@ class Etherpad extends LargeContainer {
|
||||||
show () {
|
show () {
|
||||||
const $iframe = $(this.iframe);
|
const $iframe = $(this.iframe);
|
||||||
const $container = $(this.container);
|
const $container = $(this.container);
|
||||||
|
let self = this;
|
||||||
|
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
$iframe.fadeIn(300, function () {
|
$iframe.fadeIn(300, function () {
|
||||||
|
self.bodyBackground = document.body.style.background;
|
||||||
document.body.style.background = '#eeeeee';
|
document.body.style.background = '#eeeeee';
|
||||||
$iframe.css({visibility: 'visible'});
|
$iframe.css({visibility: 'visible'});
|
||||||
$container.css({zIndex: 2});
|
$container.css({zIndex: 2});
|
||||||
|
@ -124,6 +126,7 @@ class Etherpad extends LargeContainer {
|
||||||
hide () {
|
hide () {
|
||||||
const $iframe = $(this.iframe);
|
const $iframe = $(this.iframe);
|
||||||
const $container = $(this.container);
|
const $container = $(this.container);
|
||||||
|
document.body.style.background = this.bodyBackground;
|
||||||
|
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
$iframe.fadeOut(300, function () {
|
$iframe.fadeOut(300, function () {
|
||||||
|
|
|
@ -0,0 +1,537 @@
|
||||||
|
/* global $, APP, YT, onPlayerReady, onPlayerStateChange, onPlayerError */
|
||||||
|
|
||||||
|
import messageHandler from '../util/MessageHandler';
|
||||||
|
import UIUtil from '../util/UIUtil';
|
||||||
|
import UIEvents from '../../../service/UI/UIEvents';
|
||||||
|
|
||||||
|
import VideoLayout from "../videolayout/VideoLayout";
|
||||||
|
import LargeContainer from '../videolayout/LargeContainer';
|
||||||
|
import SmallVideo from '../videolayout/SmallVideo';
|
||||||
|
import FilmStrip from '../videolayout/FilmStrip';
|
||||||
|
import ToolbarToggler from "../toolbars/ToolbarToggler";
|
||||||
|
|
||||||
|
export const SHARED_VIDEO_CONTAINER_TYPE = "sharedvideo";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example shared video link.
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
const defaultSharedVideoLink = "https://www.youtube.com/watch?v=xNXN7CZk8X0";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manager of shared video.
|
||||||
|
*/
|
||||||
|
export default class SharedVideoManager {
|
||||||
|
constructor (emitter) {
|
||||||
|
this.emitter = emitter;
|
||||||
|
this.isSharedVideoShown = false;
|
||||||
|
this.isPlayerAPILoaded = false;
|
||||||
|
this.updateInterval = 5000; // milliseconds
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts shared video by asking user for url, or if its already working
|
||||||
|
* asks whether the user wants to stop sharing the video.
|
||||||
|
*/
|
||||||
|
toggleSharedVideo () {
|
||||||
|
if(!this.isSharedVideoShown) {
|
||||||
|
requestVideoLink().then(
|
||||||
|
url => this.emitter.emit(
|
||||||
|
UIEvents.UPDATE_SHARED_VIDEO, url, 'start'),
|
||||||
|
err => console.error('SHARED VIDEO CANCELED', err)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showStopVideoPropmpt().then(() =>
|
||||||
|
this.emitter.emit(UIEvents.UPDATE_SHARED_VIDEO, null, 'stop'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the player component and starts the checking function
|
||||||
|
* that will be sending updates, if we are the one shared the video
|
||||||
|
* @param url the video url
|
||||||
|
* @param attributes
|
||||||
|
*/
|
||||||
|
showSharedVideo (url, attributes) {
|
||||||
|
if (this.isSharedVideoShown)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// the video url
|
||||||
|
this.url = url;
|
||||||
|
|
||||||
|
// the owner of the video
|
||||||
|
this.from = attributes.from;
|
||||||
|
|
||||||
|
// This code loads the IFrame Player API code asynchronously.
|
||||||
|
var tag = document.createElement('script');
|
||||||
|
|
||||||
|
tag.src = "https://www.youtube.com/iframe_api";
|
||||||
|
var firstScriptTag = document.getElementsByTagName('script')[0];
|
||||||
|
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
if(self.isPlayerAPILoaded)
|
||||||
|
window.onYouTubeIframeAPIReady();
|
||||||
|
else
|
||||||
|
window.onYouTubeIframeAPIReady = function() {
|
||||||
|
self.isPlayerAPILoaded = true;
|
||||||
|
let showControls = APP.conference.isLocalId(self.from) ? 1 : 0;
|
||||||
|
self.player = new YT.Player('sharedVideoIFrame', {
|
||||||
|
height: '100%',
|
||||||
|
width: '100%',
|
||||||
|
videoId: self.url,
|
||||||
|
playerVars: {
|
||||||
|
'origin': location.origin,
|
||||||
|
'fs': '0',
|
||||||
|
'autoplay': 1,
|
||||||
|
'controls': showControls,
|
||||||
|
'rel' : 0
|
||||||
|
},
|
||||||
|
events: {
|
||||||
|
'onReady': onPlayerReady,
|
||||||
|
'onStateChange': onPlayerStateChange,
|
||||||
|
'onError': onPlayerError
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
window.onPlayerStateChange = function(event) {
|
||||||
|
if (event.data == YT.PlayerState.PLAYING) {
|
||||||
|
self.playerPaused = false;
|
||||||
|
self.updateCheck();
|
||||||
|
} else if (event.data == YT.PlayerState.PAUSED) {
|
||||||
|
self.playerPaused = true;
|
||||||
|
self.updateCheck(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.onPlayerReady = function(event) {
|
||||||
|
let player = event.target;
|
||||||
|
player.playVideo();
|
||||||
|
|
||||||
|
let thumb = new SharedVideoThumb(self.url);
|
||||||
|
thumb.setDisplayName(player.getVideoData().title);
|
||||||
|
VideoLayout.addParticipantContainer(self.url, thumb);
|
||||||
|
|
||||||
|
let iframe = player.getIframe();
|
||||||
|
self.sharedVideo = new SharedVideoContainer(
|
||||||
|
{url, iframe, player});
|
||||||
|
|
||||||
|
VideoLayout.addLargeVideoContainer(
|
||||||
|
SHARED_VIDEO_CONTAINER_TYPE, self.sharedVideo);
|
||||||
|
VideoLayout.handleVideoThumbClicked(true, self.url);
|
||||||
|
|
||||||
|
self.isSharedVideoShown = true;
|
||||||
|
|
||||||
|
// If we are sending the command and we are starting the player
|
||||||
|
// we need to continuously send the player current time position
|
||||||
|
if(APP.conference.isLocalId(self.from)) {
|
||||||
|
self.intervalId = setInterval(
|
||||||
|
self.updateCheck.bind(self),
|
||||||
|
self.updateInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set initial state of the player if there is enough information
|
||||||
|
if(attributes.state === 'pause')
|
||||||
|
player.pauseVideo();
|
||||||
|
else if(attributes.time > 0) {
|
||||||
|
console.log("Player seekTo:", attributes.time);
|
||||||
|
player.seekTo(attributes.time);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.onPlayerError = function(event) {
|
||||||
|
console.error("Error in the player:" + event.data);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks current state of the player and fire an event with the values.
|
||||||
|
*/
|
||||||
|
updateCheck(sendPauseEvent)
|
||||||
|
{
|
||||||
|
// ignore update checks if we are not the owner of the video
|
||||||
|
if(!APP.conference.isLocalId(this.from))
|
||||||
|
return;
|
||||||
|
|
||||||
|
let state = this.player.getPlayerState();
|
||||||
|
// if its paused and haven't been pause - send paused
|
||||||
|
if (state === YT.PlayerState.PAUSED && sendPauseEvent) {
|
||||||
|
this.emitter.emit(UIEvents.UPDATE_SHARED_VIDEO,
|
||||||
|
this.url, 'pause');
|
||||||
|
}
|
||||||
|
// if its playing and it was paused - send update with time
|
||||||
|
// if its playing and was playing just send update with time
|
||||||
|
else if (state === YT.PlayerState.PLAYING) {
|
||||||
|
this.emitter.emit(UIEvents.UPDATE_SHARED_VIDEO,
|
||||||
|
this.url, 'playing',
|
||||||
|
this.player.getCurrentTime(),
|
||||||
|
this.player.isMuted() ? 0 : this.player.getVolume());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates video, if its not playing and needs starting or
|
||||||
|
* if its playing and needs to be paysed
|
||||||
|
* @param url the video url
|
||||||
|
* @param attributes
|
||||||
|
*/
|
||||||
|
updateSharedVideo (url, attributes) {
|
||||||
|
// if we are sending the event ignore
|
||||||
|
if(APP.conference.isLocalId(this.from)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attributes.state == 'playing') {
|
||||||
|
|
||||||
|
if(!this.isSharedVideoShown) {
|
||||||
|
this.showSharedVideo(url, attributes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ocasionally we get this.player.getCurrentTime is not a function
|
||||||
|
// it seems its that player hasn't really loaded
|
||||||
|
if(!this.player || !this.player.getCurrentTime
|
||||||
|
|| !this.player.pauseVideo
|
||||||
|
|| !this.player.playVideo
|
||||||
|
|| !this.player.getVolume
|
||||||
|
|| !this.player.seekTo
|
||||||
|
|| !this.player.getVolume)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// check received time and current time
|
||||||
|
let currentPosition = this.player.getCurrentTime();
|
||||||
|
let diff = Math.abs(attributes.time - currentPosition);
|
||||||
|
|
||||||
|
// if we drift more than two times of the interval for checking
|
||||||
|
// sync, the interval is in milliseconds
|
||||||
|
if(diff > this.updateInterval*2/1000) {
|
||||||
|
console.log("Player seekTo:", attributes.time,
|
||||||
|
" current time is:", currentPosition, " diff:", diff);
|
||||||
|
this.player.seekTo(attributes.time);
|
||||||
|
}
|
||||||
|
|
||||||
|
// lets check the volume
|
||||||
|
if (attributes.volume !== undefined &&
|
||||||
|
this.player.getVolume() != attributes.volume) {
|
||||||
|
this.player.setVolume(attributes.volume);
|
||||||
|
console.log("Player change of volume:" + attributes.volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.playerPaused)
|
||||||
|
this.player.playVideo();
|
||||||
|
} else if (attributes.state == 'pause') {
|
||||||
|
// if its not paused, pause it
|
||||||
|
if(this.isSharedVideoShown) {
|
||||||
|
this.player.pauseVideo();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// if not shown show it, passing attributes so it can
|
||||||
|
// be shown paused
|
||||||
|
this.showSharedVideo(url, attributes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop shared video if it is currently showed. If the user started the
|
||||||
|
* shared video is the one in the attributes.from (called when user
|
||||||
|
* left and we want to remove video if the user sharing it left).
|
||||||
|
* @param attributes
|
||||||
|
*/
|
||||||
|
stopSharedVideo (attributes) {
|
||||||
|
if (!this.isSharedVideoShown)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(this.from !== attributes.from)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(this.intervalId) {
|
||||||
|
clearInterval(this.intervalId);
|
||||||
|
this.intervalId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoLayout.removeParticipantContainer(this.url);
|
||||||
|
|
||||||
|
VideoLayout.showLargeVideoContainer(SHARED_VIDEO_CONTAINER_TYPE, false)
|
||||||
|
.then(() => {
|
||||||
|
VideoLayout.removeLargeVideoContainer(
|
||||||
|
SHARED_VIDEO_CONTAINER_TYPE);
|
||||||
|
|
||||||
|
this.player.destroy();
|
||||||
|
this.player = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.url = null;
|
||||||
|
this.isSharedVideoShown = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container for shared video iframe.
|
||||||
|
*/
|
||||||
|
class SharedVideoContainer extends LargeContainer {
|
||||||
|
|
||||||
|
constructor ({url, iframe, player}) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.$iframe = $(iframe);
|
||||||
|
this.url = url;
|
||||||
|
this.player = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
get $video () {
|
||||||
|
return this.$iframe;
|
||||||
|
}
|
||||||
|
|
||||||
|
show () {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
this.$iframe.fadeIn(300, () => {
|
||||||
|
this.$iframe.css({opacity: 1});
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
hide () {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
this.$iframe.fadeOut(300, () => {
|
||||||
|
this.$iframe.css({opacity: 0});
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onHoverIn () {
|
||||||
|
ToolbarToggler.showToolbar();
|
||||||
|
}
|
||||||
|
|
||||||
|
get id () {
|
||||||
|
return this.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
resize (containerWidth, containerHeight) {
|
||||||
|
let height = containerHeight - FilmStrip.getFilmStripHeight();
|
||||||
|
|
||||||
|
let width = containerWidth;
|
||||||
|
|
||||||
|
this.$iframe.width(width).height(height);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {boolean} do not switch on dominant speaker event if on stage.
|
||||||
|
*/
|
||||||
|
stayOnStage () {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function SharedVideoThumb (url)
|
||||||
|
{
|
||||||
|
this.id = url;
|
||||||
|
|
||||||
|
this.url = url;
|
||||||
|
this.setVideoType(SHARED_VIDEO_CONTAINER_TYPE);
|
||||||
|
this.videoSpanId = "sharedVideoContainer";
|
||||||
|
this.container = this.createContainer(this.videoSpanId);
|
||||||
|
this.container.onclick = this.videoClick.bind(this);
|
||||||
|
this.bindHoverHandler();
|
||||||
|
|
||||||
|
SmallVideo.call(this, VideoLayout);
|
||||||
|
this.isVideoMuted = true;
|
||||||
|
}
|
||||||
|
SharedVideoThumb.prototype = Object.create(SmallVideo.prototype);
|
||||||
|
SharedVideoThumb.prototype.constructor = SharedVideoThumb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hide display name
|
||||||
|
*/
|
||||||
|
|
||||||
|
SharedVideoThumb.prototype.setDeviceAvailabilityIcons = function () {};
|
||||||
|
|
||||||
|
SharedVideoThumb.prototype.avatarChanged = function () {};
|
||||||
|
|
||||||
|
SharedVideoThumb.prototype.createContainer = function (spanId) {
|
||||||
|
var container = document.createElement('span');
|
||||||
|
container.id = spanId;
|
||||||
|
container.className = 'videocontainer';
|
||||||
|
|
||||||
|
// add the avatar
|
||||||
|
var avatar = document.createElement('img');
|
||||||
|
avatar.id = 'avatar_' + this.id;
|
||||||
|
avatar.className = 'sharedVideoAvatar';
|
||||||
|
avatar.src = "https://img.youtube.com/vi/" + this.url + "/0.jpg";
|
||||||
|
container.appendChild(avatar);
|
||||||
|
|
||||||
|
var remotes = document.getElementById('remoteVideos');
|
||||||
|
return remotes.appendChild(container);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The thumb click handler.
|
||||||
|
*/
|
||||||
|
SharedVideoThumb.prototype.videoClick = function () {
|
||||||
|
VideoLayout.handleVideoThumbClicked(true, this.url);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes RemoteVideo from the page.
|
||||||
|
*/
|
||||||
|
SharedVideoThumb.prototype.remove = function () {
|
||||||
|
console.log("Remove shared video thumb", this.id);
|
||||||
|
|
||||||
|
// Make sure that the large video is updated if are removing its
|
||||||
|
// corresponding small video.
|
||||||
|
this.VideoLayout.updateRemovedVideo(this.id);
|
||||||
|
|
||||||
|
// Remove whole container
|
||||||
|
if (this.container.parentNode) {
|
||||||
|
this.container.parentNode.removeChild(this.container);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the display name for the thumb.
|
||||||
|
*/
|
||||||
|
SharedVideoThumb.prototype.setDisplayName = function(displayName) {
|
||||||
|
if (!this.container) {
|
||||||
|
console.warn( "Unable to set displayName - " + this.videoSpanId +
|
||||||
|
" does not exist");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nameSpan = $('#' + this.videoSpanId + '>span.displayname');
|
||||||
|
|
||||||
|
// If we already have a display name for this video.
|
||||||
|
if (nameSpan.length > 0) {
|
||||||
|
if (displayName && displayName.length > 0) {
|
||||||
|
$('#' + this.videoSpanId + '_name').text(displayName);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nameSpan = document.createElement('span');
|
||||||
|
nameSpan.className = 'displayname';
|
||||||
|
$('#' + this.videoSpanId)[0].appendChild(nameSpan);
|
||||||
|
|
||||||
|
if (displayName && displayName.length > 0)
|
||||||
|
$(nameSpan).text(displayName);
|
||||||
|
nameSpan.id = this.videoSpanId + '_name';
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if given string is youtube url.
|
||||||
|
* @param {string} url string to check.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function getYoutubeLink(url) {
|
||||||
|
let p = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/;//jshint ignore:line
|
||||||
|
return (url.match(p)) ? RegExp.$1 : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask user if he want to close shared video.
|
||||||
|
*/
|
||||||
|
function showStopVideoPropmpt() {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
messageHandler.openTwoButtonDialog(
|
||||||
|
"dialog.removeSharedVideoTitle",
|
||||||
|
null,
|
||||||
|
"dialog.removeSharedVideoMsg",
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
"dialog.Remove",
|
||||||
|
function(e,v,m,f) {
|
||||||
|
if (v) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask user for shared video url to share with others.
|
||||||
|
* Dialog validates client input to allow only youtube urls.
|
||||||
|
*/
|
||||||
|
function requestVideoLink() {
|
||||||
|
let i18n = APP.translation;
|
||||||
|
const title = i18n.generateTranslationHTML("dialog.shareVideoTitle");
|
||||||
|
const cancelButton = i18n.generateTranslationHTML("dialog.Cancel");
|
||||||
|
const shareButton = i18n.generateTranslationHTML("dialog.Share");
|
||||||
|
const backButton = i18n.generateTranslationHTML("dialog.Back");
|
||||||
|
const linkError
|
||||||
|
= i18n.generateTranslationHTML("dialog.shareVideoLinkError");
|
||||||
|
const i18nOptions = {url: defaultSharedVideoLink};
|
||||||
|
const defaultUrl = i18n.translateString("defaultLink", i18nOptions);
|
||||||
|
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
let dialog = messageHandler.openDialogWithStates({
|
||||||
|
state0: {
|
||||||
|
html: `
|
||||||
|
<h2>${title}</h2>
|
||||||
|
<input name="sharedVideoUrl" type="text"
|
||||||
|
data-i18n="[placeholder]defaultLink"
|
||||||
|
data-i18n-options="${JSON.stringify(i18nOptions)}"
|
||||||
|
placeholder="${defaultUrl}"
|
||||||
|
autofocus>`,
|
||||||
|
persistent: false,
|
||||||
|
buttons: [
|
||||||
|
{title: cancelButton, value: false},
|
||||||
|
{title: shareButton, value: true}
|
||||||
|
],
|
||||||
|
focus: ':input:first',
|
||||||
|
defaultButton: 1,
|
||||||
|
submit: function (e, v, m, f) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!v) {
|
||||||
|
reject('cancelled');
|
||||||
|
dialog.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sharedVideoUrl = f.sharedVideoUrl;
|
||||||
|
if (!sharedVideoUrl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let urlValue = encodeURI(UIUtil.escapeHtml(sharedVideoUrl));
|
||||||
|
let yVideoId = getYoutubeLink(urlValue);
|
||||||
|
if (!yVideoId) {
|
||||||
|
dialog.goToState('state1');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(yVideoId);
|
||||||
|
dialog.close();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
state1: {
|
||||||
|
html: `<h2>${title}</h2> ${linkError}`,
|
||||||
|
persistent: false,
|
||||||
|
buttons: [
|
||||||
|
{title: cancelButton, value: false},
|
||||||
|
{title: backButton, value: true}
|
||||||
|
],
|
||||||
|
focus: ':input:first',
|
||||||
|
defaultButton: 1,
|
||||||
|
submit: function (e, v, m, f) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (v === 0) {
|
||||||
|
reject();
|
||||||
|
dialog.close();
|
||||||
|
} else {
|
||||||
|
dialog.goToState('state0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -126,6 +126,10 @@ const buttonHandlers = {
|
||||||
AnalyticsAdapter.sendEvent('toolbar.etherpad.clicked');
|
AnalyticsAdapter.sendEvent('toolbar.etherpad.clicked');
|
||||||
emitter.emit(UIEvents.ETHERPAD_CLICKED);
|
emitter.emit(UIEvents.ETHERPAD_CLICKED);
|
||||||
},
|
},
|
||||||
|
"toolbar_button_sharedvideo": function () {
|
||||||
|
AnalyticsAdapter.sendEvent('toolbar.sharedvideo.clicked');
|
||||||
|
emitter.emit(UIEvents.SHARED_VIDEO_CLICKED);
|
||||||
|
},
|
||||||
"toolbar_button_desktopsharing": function () {
|
"toolbar_button_desktopsharing": function () {
|
||||||
if (APP.conference.isSharingScreen) {
|
if (APP.conference.isSharingScreen) {
|
||||||
AnalyticsAdapter.sendEvent('toolbar.screen.disabled');
|
AnalyticsAdapter.sendEvent('toolbar.screen.disabled');
|
||||||
|
@ -284,6 +288,15 @@ const Toolbar = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Shows or hides the 'shared video' button.
|
||||||
|
showSharedVideoButton (show) {
|
||||||
|
if (UIUtil.isButtonEnabled('sharedvideo') && show) {
|
||||||
|
$('#toolbar_button_sharedvideo').css({display: "inline-block"});
|
||||||
|
} else {
|
||||||
|
$('#toolbar_button_sharedvideo').css({display: "none"});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// checks whether recording is enabled and whether we have params
|
// checks whether recording is enabled and whether we have params
|
||||||
// to start automatically recording
|
// to start automatically recording
|
||||||
checkAutoRecord () {
|
checkAutoRecord () {
|
||||||
|
|
|
@ -11,6 +11,8 @@ import {createDeferred} from '../../util/helpers';
|
||||||
const avatarSize = interfaceConfig.DOMINANT_SPEAKER_AVATAR_SIZE;
|
const avatarSize = interfaceConfig.DOMINANT_SPEAKER_AVATAR_SIZE;
|
||||||
const FADE_DURATION_MS = 300;
|
const FADE_DURATION_MS = 300;
|
||||||
|
|
||||||
|
export const VIDEO_CONTAINER_TYPE = "camera";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get stream id.
|
* Get stream id.
|
||||||
* @param {JitsiTrack?} stream
|
* @param {JitsiTrack?} stream
|
||||||
|
@ -150,8 +152,6 @@ function getDesktopVideoPosition(videoWidth,
|
||||||
return { horizontalIndent, verticalIndent };
|
return { horizontalIndent, verticalIndent };
|
||||||
}
|
}
|
||||||
|
|
||||||
export const VideoContainerType = "camera";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Container for user video.
|
* Container for user video.
|
||||||
*/
|
*/
|
||||||
|
@ -365,9 +365,10 @@ export default class LargeVideoManager {
|
||||||
constructor () {
|
constructor () {
|
||||||
this.containers = {};
|
this.containers = {};
|
||||||
|
|
||||||
this.state = VideoContainerType;
|
this.state = VIDEO_CONTAINER_TYPE;
|
||||||
this.videoContainer = new VideoContainer(() => this.resizeContainer(VideoContainerType));
|
this.videoContainer = new VideoContainer(
|
||||||
this.addContainer(VideoContainerType, this.videoContainer);
|
() => this.resizeContainer(VIDEO_CONTAINER_TYPE));
|
||||||
|
this.addContainer(VIDEO_CONTAINER_TYPE, this.videoContainer);
|
||||||
// use the same video container to handle and desktop tracks
|
// use the same video container to handle and desktop tracks
|
||||||
this.addContainer("desktop", this.videoContainer);
|
this.addContainer("desktop", this.videoContainer);
|
||||||
|
|
||||||
|
@ -451,7 +452,12 @@ export default class LargeVideoManager {
|
||||||
// change the avatar url on large
|
// change the avatar url on large
|
||||||
this.updateAvatar(Avatar.getAvatarUrl(id));
|
this.updateAvatar(Avatar.getAvatarUrl(id));
|
||||||
|
|
||||||
let isVideoMuted = stream ? stream.isMuted() : true;
|
// If we the continer is VIDEO_CONTAINER_TYPE, we need to check
|
||||||
|
// its stream whether exist and is muted to set isVideoMuted
|
||||||
|
// in rest of the cases it is false
|
||||||
|
let isVideoMuted = false;
|
||||||
|
if (videoType == VIDEO_CONTAINER_TYPE)
|
||||||
|
isVideoMuted = stream ? stream.isMuted() : true;
|
||||||
|
|
||||||
// show the avatar on large if needed
|
// show the avatar on large if needed
|
||||||
container.showAvatar(isVideoMuted);
|
container.showAvatar(isVideoMuted);
|
||||||
|
@ -616,7 +622,7 @@ export default class LargeVideoManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
let oldContainer = this.containers[this.state];
|
let oldContainer = this.containers[this.state];
|
||||||
if (this.state === VideoContainerType) {
|
if (this.state === VIDEO_CONTAINER_TYPE) {
|
||||||
this.showWatermark(false);
|
this.showWatermark(false);
|
||||||
}
|
}
|
||||||
oldContainer.hide();
|
oldContainer.hide();
|
||||||
|
@ -625,7 +631,7 @@ export default class LargeVideoManager {
|
||||||
let container = this.getContainer(type);
|
let container = this.getContainer(type);
|
||||||
|
|
||||||
return container.show().then(() => {
|
return container.show().then(() => {
|
||||||
if (type === VideoContainerType) {
|
if (type === VIDEO_CONTAINER_TYPE) {
|
||||||
this.showWatermark(true);
|
this.showWatermark(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -363,14 +363,7 @@ SmallVideo.prototype.updateView = function () {
|
||||||
}
|
}
|
||||||
setVisibility(avatar, showAvatar);
|
setVisibility(avatar, showAvatar);
|
||||||
|
|
||||||
var showDisplayName = !showVideo && !showAvatar;
|
this.showDisplayName(!showVideo && !showAvatar);
|
||||||
|
|
||||||
if (showDisplayName) {
|
|
||||||
this.showDisplayName(this.VideoLayout.isLargeVideoVisible());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.showDisplayName(false);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
SmallVideo.prototype.avatarChanged = function (avatarUrl) {
|
SmallVideo.prototype.avatarChanged = function (avatarUrl) {
|
||||||
|
|
|
@ -9,7 +9,8 @@ import UIEvents from "../../../service/UI/UIEvents";
|
||||||
import UIUtil from "../util/UIUtil";
|
import UIUtil from "../util/UIUtil";
|
||||||
|
|
||||||
import RemoteVideo from "./RemoteVideo";
|
import RemoteVideo from "./RemoteVideo";
|
||||||
import LargeVideoManager, {VideoContainerType} from "./LargeVideo";
|
import LargeVideoManager, {VIDEO_CONTAINER_TYPE} from "./LargeVideo";
|
||||||
|
import {SHARED_VIDEO_CONTAINER_TYPE} from '../shared_video/SharedVideo';
|
||||||
import LocalVideo from "./LocalVideo";
|
import LocalVideo from "./LocalVideo";
|
||||||
import PanelToggler from "../side_pannels/SidePanelToggler";
|
import PanelToggler from "../side_pannels/SidePanelToggler";
|
||||||
|
|
||||||
|
@ -92,6 +93,11 @@ var VideoLayout = {
|
||||||
init (emitter) {
|
init (emitter) {
|
||||||
eventEmitter = emitter;
|
eventEmitter = emitter;
|
||||||
localVideoThumbnail = new LocalVideo(VideoLayout, emitter);
|
localVideoThumbnail = new LocalVideo(VideoLayout, emitter);
|
||||||
|
// sets default video type of local video
|
||||||
|
localVideoThumbnail.setVideoType(VIDEO_CONTAINER_TYPE);
|
||||||
|
// if we do not resize the thumbs here, if there is no video device
|
||||||
|
// the local video thumb maybe one pixel
|
||||||
|
this.resizeThumbnails(false, true, false);
|
||||||
|
|
||||||
emitter.addListener(UIEvents.CONTACT_CLICKED, onContactClicked);
|
emitter.addListener(UIEvents.CONTACT_CLICKED, onContactClicked);
|
||||||
this.lastNCount = config.channelLastN;
|
this.lastNCount = config.channelLastN;
|
||||||
|
@ -343,7 +349,7 @@ var VideoLayout = {
|
||||||
let videoType = VideoLayout.getRemoteVideoType(id);
|
let videoType = VideoLayout.getRemoteVideoType(id);
|
||||||
if (!videoType) {
|
if (!videoType) {
|
||||||
// make video type the default one (camera)
|
// make video type the default one (camera)
|
||||||
videoType = VideoContainerType;
|
videoType = VIDEO_CONTAINER_TYPE;
|
||||||
}
|
}
|
||||||
remoteVideo.setVideoType(videoType);
|
remoteVideo.setVideoType(videoType);
|
||||||
|
|
||||||
|
@ -367,7 +373,8 @@ var VideoLayout = {
|
||||||
// current dominant, focused speaker or update it to
|
// current dominant, focused speaker or update it to
|
||||||
// the current dominant speaker.
|
// the current dominant speaker.
|
||||||
if ((!focusedVideoResourceJid &&
|
if ((!focusedVideoResourceJid &&
|
||||||
!currentDominantSpeaker) ||
|
!currentDominantSpeaker &&
|
||||||
|
this.isLargeContainerTypeVisible(VIDEO_CONTAINER_TYPE)) ||
|
||||||
focusedVideoResourceJid === resourceJid ||
|
focusedVideoResourceJid === resourceJid ||
|
||||||
(resourceJid &&
|
(resourceJid &&
|
||||||
currentDominantSpeaker === resourceJid)) {
|
currentDominantSpeaker === resourceJid)) {
|
||||||
|
@ -888,7 +895,7 @@ var VideoLayout = {
|
||||||
},
|
},
|
||||||
|
|
||||||
isLargeVideoVisible () {
|
isLargeVideoVisible () {
|
||||||
return this.isLargeContainerTypeVisible(VideoContainerType);
|
return this.isLargeContainerTypeVisible(VIDEO_CONTAINER_TYPE);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -960,8 +967,17 @@ var VideoLayout = {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let currentId = largeVideo.id;
|
||||||
|
if(currentId) {
|
||||||
|
var oldSmallVideo = this.getSmallVideo(currentId);
|
||||||
|
}
|
||||||
|
|
||||||
// if !show then use default type - large video
|
// if !show then use default type - large video
|
||||||
return largeVideo.showContainer(show ? type : VideoContainerType);
|
return largeVideo.showContainer(show ? type : VIDEO_CONTAINER_TYPE)
|
||||||
|
.then(() => {
|
||||||
|
if(oldSmallVideo)
|
||||||
|
oldSmallVideo && oldSmallVideo.updateView();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
isLargeContainerTypeVisible (type) {
|
isLargeContainerTypeVisible (type) {
|
||||||
|
@ -970,10 +986,18 @@ var VideoLayout = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the id of the current video shown on large.
|
* Returns the id of the current video shown on large.
|
||||||
* Currently used by tests (troture).
|
* Currently used by tests (torture).
|
||||||
*/
|
*/
|
||||||
getLargeVideoID () {
|
getLargeVideoID () {
|
||||||
return largeVideo.id;
|
return largeVideo.id;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the the current video shown on large.
|
||||||
|
* Currently used by tests (torture).
|
||||||
|
*/
|
||||||
|
getLargeVideo () {
|
||||||
|
return largeVideo;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,13 @@ export default {
|
||||||
AUDIO_MUTED: "UI.audio_muted",
|
AUDIO_MUTED: "UI.audio_muted",
|
||||||
VIDEO_MUTED: "UI.video_muted",
|
VIDEO_MUTED: "UI.video_muted",
|
||||||
ETHERPAD_CLICKED: "UI.etherpad_clicked",
|
ETHERPAD_CLICKED: "UI.etherpad_clicked",
|
||||||
|
SHARED_VIDEO_CLICKED: "UI.start_shared_video",
|
||||||
|
/**
|
||||||
|
* Updates shared video with params: url, state, time(optional)
|
||||||
|
* Where url is the video link, state is stop/start/pause and time is the
|
||||||
|
* current video playing time.
|
||||||
|
*/
|
||||||
|
UPDATE_SHARED_VIDEO: "UI.update_shared_video",
|
||||||
ROOM_LOCK_CLICKED: "UI.room_lock_clicked",
|
ROOM_LOCK_CLICKED: "UI.room_lock_clicked",
|
||||||
USER_INVITED: "UI.user_invited",
|
USER_INVITED: "UI.user_invited",
|
||||||
USER_KICKED: "UI.user_kicked",
|
USER_KICKED: "UI.user_kicked",
|
||||||
|
|
Loading…
Reference in New Issue