OpenViduLayout: smart layout for recordings

pull/32/head
pabloFuente 2018-03-05 23:57:12 +01:00
parent 06e6f496ee
commit cdf786996c
8 changed files with 1348 additions and 156 deletions

View File

@ -3,39 +3,36 @@
"project": {
"name": "openvidu-server-frontend"
},
"apps": [
{
"root": "src",
"outDir": "dist",
"assets": [
"assets",
"favicon.ico"
],
"index": "index.html",
"main": "main.ts",
"polyfills": "polyfills.ts",
"test": "test.ts",
"tsconfig": "tsconfig.app.json",
"testTsconfig": "tsconfig.spec.json",
"prefix": "app",
"styles": [
"styles.css"
],
"scripts": [],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
"prod": "environments/environment.prod.ts"
}
"apps": [{
"root": "src",
"outDir": "dist",
"assets": [
"assets",
"favicon.ico"
],
"index": "index.html",
"main": "main.ts",
"polyfills": "polyfills.ts",
"test": "test.ts",
"tsconfig": "tsconfig.app.json",
"testTsconfig": "tsconfig.spec.json",
"prefix": "app",
"styles": [
"styles.css"
],
"scripts": [],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
"prod": "environments/environment.prod.ts"
}
],
}],
"e2e": {
"protractor": {
"config": "./protractor.conf.js"
}
},
"lint": [
{
"lint": [{
"project": "src/tsconfig.app.json"
},
{

View File

@ -1,52 +1,52 @@
{
"dependencies": {
"@angular/animations": "5.0.5",
"@angular/cdk": "5.0.0-rc.2",
"@angular/common": "5.0.5",
"@angular/compiler": "5.0.5",
"@angular/core": "5.0.5",
"@angular/flex-layout": "2.0.0-beta.10-4905443",
"@angular/forms": "5.0.5",
"@angular/http": "5.0.5",
"@angular/material": "5.0.0-rc.1",
"@angular/platform-browser": "5.0.5",
"@angular/platform-browser-dynamic": "5.0.5",
"@angular/router": "5.0.5",
"core-js": "2.5.1",
"hammerjs": "2.0.8",
"openvidu-browser": "1.7.0",
"rxjs": "5.5.3",
"zone.js": "0.8.18"
},
"devDependencies": {
"@angular/cli": "^1.6.0",
"@angular/compiler-cli": "5.0.5",
"@types/jasmine": "2.5.38",
"@types/node": "~6.0.60",
"codelyzer": "4.0.1",
"jasmine-core": "~2.5.2",
"jasmine-spec-reporter": "~3.2.0",
"karma": "~1.4.1",
"karma-chrome-launcher": "~2.0.0",
"karma-cli": "~1.0.1",
"karma-coverage-istanbul-reporter": "^0.2.0",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.1.0",
"ts-node": "3.3.0",
"tslint": "5.8.0",
"typescript": "2.4.2"
},
"license": "MIT",
"name": "frontend",
"private": true,
"scripts": {
"build": "ng build",
"e2e": "ng e2e",
"lint": "ng lint",
"ng": "ng",
"start": "ng serve",
"test": "ng test"
},
"version": "0.0.0"
}
"dependencies": {
"@angular/animations": "5.0.5",
"@angular/cdk": "5.0.0-rc.2",
"@angular/common": "5.0.5",
"@angular/compiler": "5.0.5",
"@angular/core": "5.0.5",
"@angular/flex-layout": "2.0.0-beta.10-4905443",
"@angular/forms": "5.0.5",
"@angular/http": "5.0.5",
"@angular/material": "5.0.0-rc.1",
"@angular/platform-browser": "5.0.5",
"@angular/platform-browser-dynamic": "5.0.5",
"@angular/router": "5.0.5",
"core-js": "2.5.1",
"hammerjs": "2.0.8",
"openvidu-browser": "1.7.0",
"rxjs": "5.5.3",
"zone.js": "0.8.18"
},
"devDependencies": {
"@angular/cli": "^1.6.0",
"@angular/compiler-cli": "5.0.5",
"@types/jasmine": "2.5.38",
"@types/node": "~6.0.60",
"codelyzer": "4.0.1",
"jasmine-core": "~2.5.2",
"jasmine-spec-reporter": "~3.2.0",
"karma": "~1.4.1",
"karma-chrome-launcher": "~2.0.0",
"karma-cli": "~1.0.1",
"karma-coverage-istanbul-reporter": "^0.2.0",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.1.0",
"ts-node": "3.3.0",
"tslint": "5.8.0",
"typescript": "2.4.2"
},
"license": "MIT",
"name": "frontend",
"private": true,
"scripts": {
"build": "ng build",
"e2e": "ng e2e",
"lint": "ng lint",
"ng": "ng",
"start": "ng serve",
"test": "ng test"
},
"version": "0.0.0"
}

View File

@ -1,10 +1,869 @@
.bounds {
background-color: black;
height: 100%;
overflow: hidden;
cursor: none !important;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
video {
-o-object-fit: cover;
object-fit: cover;
display: block;
position: absolute;
width: 100%;
height: 100%;
color: #ffffff;
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font-family: Arial, Helvetica, sans-serif;
}
/*!
* Copyright (c) 2017 TokBox, Inc.
* Released under the MIT license
* http://opensource.org/licenses/MIT
*/
.custom-class {
min-height: 0px !important;
}
/**
* OT Base styles
*/
/* Root OT object, this is where our CSS reset happens */
.OT_root,
.OT_root * {
color: #ffffff;
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font-family: Arial, Helvetica, sans-serif;
vertical-align: baseline;
}
.OT_dialog-centering {
display: table;
width: 100%;
height: 100%;
}
.OT_dialog-centering-child {
display: table-cell;
vertical-align: middle;
}
.OT_dialog {
position: relative;
-webkit-box-sizing: border-box;
box-sizing: border-box;
max-width: 576px;
margin-right: auto;
margin-left: auto;
padding: 36px;
text-align: center; /* centers all the inline content */
background-color: #363636;
color: #fff;
-webkit-box-shadow: 2px 4px 6px #999;
box-shadow: 2px 4px 6px #999;
font-family: 'Didact Gothic', sans-serif;
font-size: 13px;
line-height: 1.4;
}
.OT_dialog * {
font-family: inherit;
-webkit-box-sizing: inherit;
box-sizing: inherit;
}
.OT_closeButton {
color: #999999;
cursor: pointer;
font-size: 32px;
line-height: 36px;
position: absolute;
right: 18px;
top: 0;
}
.OT_dialog-messages {
text-align: center;
}
.OT_dialog-messages-main {
margin-bottom: 36px;
line-height: 36px;
font-weight: 300;
font-size: 24px;
}
.OT_dialog-messages-minor {
margin-bottom: 18px;
font-size: 13px;
line-height: 18px;
color: #A4A4A4;
}
.OT_dialog-messages-minor strong {
color: #ffffff;
}
.OT_dialog-actions-card {
display: inline-block;
}
.OT_dialog-button-title {
margin-bottom: 18px;
line-height: 18px;
font-weight: 300;
text-align: center;
font-size: 14px;
color: #999999;
}
.OT_dialog-button-title label {
color: #999999;
}
.OT_dialog-button-title a,
.OT_dialog-button-title a:link,
.OT_dialog-button-title a:active {
color: #02A1DE;
}
.OT_dialog-button-title strong {
color: #ffffff;
font-weight: 100;
display: block;
}
.OT_dialog-button {
display: inline-block;
margin-bottom: 18px;
padding: 0 1em;
background-color: #1CA3DC;
text-align: center;
cursor: pointer;
}
.OT_dialog-button:disabled {
cursor: not-allowed;
opacity: 0.5;
}
.OT_dialog-button-large {
line-height: 36px;
padding-top: 9px;
padding-bottom: 9px;
font-weight: 100;
font-size: 24px;
}
.OT_dialog-button-small {
line-height: 18px;
padding-top: 9px;
padding-bottom: 9px;
background-color: #444444;
color: #999999;
font-size: 16px;
}
.OT_dialog-progress-bar {
display: inline-block; /* prevents margin collapse */
width: 100%;
margin-top: 5px;
margin-bottom: 41px;
border: 1px solid #4E4E4E;
height: 8px;
}
.OT_dialog-progress-bar-fill {
height: 100%;
background-color: #29A4DA;
}
.OT_dialog-plugin-upgrading .OT_dialog-plugin-upgrade-percentage {
line-height: 54px;
font-size: 48px;
font-weight: 100;
}
/* Helpers */
.OT_centered {
position: fixed;
left: 50%;
top: 50%;
margin: 0;
}
.OT_dialog-hidden {
display: none;
}
.OT_dialog-button-block {
display: block;
}
.OT_dialog-no-natural-margin {
margin-bottom: 0;
}
/* Publisher and Subscriber styles */
.OT_publisher, .OT_subscriber {
position: relative;
min-width: 48px;
min-height: 48px;
}
.OT_publisher .OT_video-element,
.OT_subscriber .OT_video-element {
display: block;
position: absolute;
width: 100%;
height: 100%;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
}
/* Styles that are applied when the video element should be mirrored */
.OT_publisher.OT_mirrored .OT_video-element {
-webkit-transform: scale(-1, 1);
transform: scale(-1, 1);
-webkit-transform-origin: 50% 50%;
transform-origin: 50% 50%;
}
.OT_subscriber_error {
background-color: #000;
color: #fff;
text-align: center;
}
.OT_subscriber_error > p {
padding: 20px;
}
/* The publisher/subscriber name/mute background */
.OT_publisher .OT_bar,
.OT_subscriber .OT_bar,
.OT_publisher .OT_name,
.OT_subscriber .OT_name,
.OT_publisher .OT_archiving,
.OT_subscriber .OT_archiving,
.OT_publisher .OT_archiving-status,
.OT_subscriber .OT_archiving-status,
.OT_publisher .OT_archiving-light-box,
.OT_subscriber .OT_archiving-light-box {
-webkit-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
top: 0;
left: 0;
right: 0;
display: block;
height: 34px;
position: absolute;
}
.OT_publisher .OT_bar,
.OT_subscriber .OT_bar {
background: rgba(0, 0, 0, 0.4);
}
.OT_publisher .OT_edge-bar-item,
.OT_subscriber .OT_edge-bar-item {
z-index: 1; /* required to get audio level meter underneath */
}
/* The publisher/subscriber name panel/archiving status bar */
.OT_publisher .OT_name,
.OT_subscriber .OT_name {
background-color: transparent;
color: #ffffff;
font-size: 15px;
line-height: 34px;
font-weight: normal;
padding: 0 4px 0 36px;
}
.OT_publisher .OT_archiving-status,
.OT_subscriber .OT_archiving-status {
background: rgba(0, 0, 0, 0.4);
top: auto;
bottom: 0;
left: 34px;
padding: 0 4px;
color: rgba(255, 255, 255, 0.8);
font-size: 15px;
line-height: 34px;
font-weight: normal;
}
.OT_micro .OT_archiving-status,
.OT_micro:hover .OT_archiving-status,
.OT_mini .OT_archiving-status,
.OT_mini:hover .OT_archiving-status {
display: none;
}
.OT_publisher .OT_archiving-light-box,
.OT_subscriber .OT_archiving-light-box {
background: rgba(0, 0, 0, 0.4);
top: auto;
bottom: 0;
right: auto;
width: 34px;
height: 34px;
}
.OT_archiving-light {
width: 7px;
height: 7px;
border-radius: 30px;
position: absolute;
top: 14px;
left: 14px;
background-color: #575757;
-webkit-box-shadow: 0 0 5px 1px #575757;
box-shadow: 0 0 5px 1px #575757;
}
.OT_archiving-light.OT_active {
background-color: #970d13;
animation: OT_pulse 1.3s ease-in;
-webkit-animation: OT_pulse 1.3s ease-in;
-moz-animation: OT_pulse 1.3s ease-in;
-webkit-animation: OT_pulse 1.3s ease-in;
animation-iteration-count: infinite;
-webkit-animation-iteration-count: infinite;
-moz-animation-iteration-count: infinite;
-webkit-animation-iteration-count: infinite;
}
@-webkit-keyframes OT_pulse {
0% {
-webkit-box-shadow: 0 0 0px 0px #c70019;
box-shadow: 0 0 0px 0px #c70019;
}
30% {
-webkit-box-shadow: 0 0 5px 1px #c70019;
box-shadow: 0 0 5px 1px #c70019;
}
50% {
-webkit-box-shadow: 0 0 5px 1px #c70019;
box-shadow: 0 0 5px 1px #c70019;
}
80% {
-webkit-box-shadow: 0 0 0px 0px #c70019;
box-shadow: 0 0 0px 0px #c70019;
}
100% {
-webkit-box-shadow: 0 0 0px 0px #c70019;
box-shadow: 0 0 0px 0px #c70019;
}
}
@-webkit-keyframes OT_pulse {
0% {
-webkit-box-shadow: 0 0 0px 0px #c70019;
box-shadow: 0 0 0px 0px #c70019;
}
30% {
-webkit-box-shadow: 0 0 5px 1px #c70019;
box-shadow: 0 0 5px 1px #c70019;
}
50% {
-webkit-box-shadow: 0 0 5px 1px #c70019;
box-shadow: 0 0 5px 1px #c70019;
}
80% {
-webkit-box-shadow: 0 0 0px 0px #c70019;
box-shadow: 0 0 0px 0px #c70019;
}
100% {
-webkit-box-shadow: 0 0 0px 0px #c70019;
box-shadow: 0 0 0px 0px #c70019;
}
}
.OT_mini .OT_bar,
.OT_bar.OT_mode-mini,
.OT_bar.OT_mode-mini-auto {
bottom: 0;
height: auto;
}
.OT_mini .OT_name.OT_mode-off,
.OT_mini .OT_name.OT_mode-on,
.OT_mini .OT_name.OT_mode-auto,
.OT_mini:hover .OT_name.OT_mode-auto {
display: none;
}
.OT_publisher .OT_name,
.OT_subscriber .OT_name {
left: 10px;
right: 37px;
height: 34px;
padding-left: 0;
}
.OT_publisher .OT_mute,
.OT_subscriber .OT_mute {
border: none;
cursor: pointer;
display: block;
position: absolute;
text-align: center;
text-indent: -9999em;
background-color: transparent;
background-repeat: no-repeat;
}
.OT_publisher .OT_mute,
.OT_subscriber .OT_mute {
right: 0;
top: 0;
border-left: 1px solid rgba(255, 255, 255, 0.2);
height: 36px;
width: 37px;
}
.OT_mini .OT_mute,
.OT_publisher.OT_mini .OT_mute.OT_mode-auto.OT_mode-on-hold,
.OT_subscriber.OT_mini .OT_mute.OT_mode-auto.OT_mode-on-hold {
top: 50%;
left: 50%;
right: auto;
margin-top: -18px;
margin-left: -18.5px;
border-left: none;
}
.OT_publisher .OT_mute {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAAcCAMAAAC02HQrAAAA1VBMVEUAAAD3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pn3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pn3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj39/j3+Pj3+Pn4+Pk/JRMlAAAAQ3RSTlMABAUHCQoLDhAQERwdHiAjLjAxOD9ASFBRVl1mbnZ6fH2LjI+QkaWqrrC1uLzAwcXJycrL1NXj5Ofo6u3w9fr7/P3+d4M3+QAAAQBJREFUGBlVwYdCglAABdCLlr5Unijm3hMUtBzlBLSr//9JgUToOQgVJgceJgU8aHgMeA38K50ZOpcQmTPwcyXn+JM8M3JJIqQypiIkeXelTyIkGZPwKS1NMia1lgKTVkaE3oQQGYsmHNqSMWnTgUFbMiZtGlD2dpaxrL1XgM0i4ZK8MeAmFhsAs29MGZniawagS63oMOQUNXYB5D0D1RMDpyoMLw/fiE2og/V+PVDR5AiBl0/2Uwik+vx4xV3a5G5Ye68Nd1czjUjZckm6VhmPciRzeCZICjwTJAViQq+3e+St167rAoHK8sLYZVkBYPCZAZ/eGa+2R5LH7Wrc0YFf/O9J3yBDFaoAAAAASUVORK5CYII=);
background-position: 9px 5px;
}
.OT_publisher .OT_mute.OT_active {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAdCAYAAABFRCf7AAADcElEQVRIiaWVXWhcRRTHf7NNd2aDtUKMIjTpg4ufFIuiUOmDEWm0Vi3VYhXRqIggQh4sWJFSig9+oOhTKSpIRUWMIBIr2kptoTbgU6ooxCiIjR+14kcJmf9sNceHnd3ebnc3Uv9wuXfOzPzmnDMz5zozGwdWAbc65w5RUJQ8cC2wDJgFJioh/MJCMrNxq2vOzK4HmIvRRemxKP0RJWt53o7S+d2Yzsx6gQ+AIUDAnUqpBLzXZd4RYFUlhB/bdZacc3PAOmAcCMC7wfvFwLNdoAPAyx09bXyYWRl4E7gDmAdGlNKFwLYu8GolhO9O87RJd64GbMrgEvB68P4osMWdXLtVV7czlooNpVRWSs8DO7NpR/B+3rBHsvetCgtCMTxwQCm9BbyQrc8F7/uBex3uRCeXO0PrUZ4NfKyUPgWeyj3bg/crDNsIRGwBaJQGorQ3Svdn2wHgc2BUKb0DPJHtjwfvbwRucc7tz+N+i9LFUdoXpfVN36I0CVwBTFI/q9e1LPxT8P4qYEdu70q12mYzWw1MYQzjeJF6zq+shHC4B7jklOBPP/TzSunh4P0DwKvAfb5c9krpe+CcwsEoZdbhEvBM9wxRAl5RShcA9wAngE3B+8tLpdLuwrhp4MNmK0pfRWkySr7NXS8+L5nZbWZWy/Vin1IaitJnUTqvwevJ71lgSSWEFKUfHG7Q2m/xqFJaGry/GXgfGPLl8mJgrXPur2JoUC8Qy3OpG+sAbGhEKT0ErAWOA6uBPWbW1wr9BOgFbgKezot0kAPYqJQA1gC/A9cA+82svzksSn1R+jNKX0SpnM/e1x3yqig92JhrZivM7FjO8bSZLSuCR/Ok16K0KMNHojQWpYko7Y7S1igN5PE3ROl4lNaZ2UVmNpPBU01orvZvZPCeKFXbBR+lEKVtUapFaSZKg9njqpl9aWYTrmXCImA7sCWb9lK/jj9TrwkrgA1AH3AQuKsSwkzbrLfxpgpsBtYDxf/R3xm2ExirhNCuHHZXTsmRwiat+S/zSt06eysVA/4pmGr/G3qm6ik28v29FKgCg8BS6pvS0KNRGgZ+Bb4FpsxsOkfUlMuwDcBWYOUZOHYM2AU8WQmhBifDv70O7PjX7KZ+4G7g3FM8zd6uBIaBy4AqxnIcZwFLCovPAhE4Sj38b4BDwEeVEFKD9S94Khjn486v3QAAAABJRU5ErkJggg==);
background-position: 9px 4px;
}
.OT_subscriber .OT_mute {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAATCAYAAAB7u5a2AAABx0lEQVQ4jaWUv48NURiGn3ONmCs32ZBd28ht1gqyZAkF21ylQkEiSp2ehpDlD1BoFGqqVdJohYKI7MaPxMoVNghCWMF+7ybLUewnOXfcMWO9yeQ857zne8+XmZOBGjJpr0kvTIomvTZpS526UCO4DUwD64FjwCFgqZnnR+oc8LfgzKQ73vGsr42ZtGjSQFV9o8KfBCacZwCaef4YmAf2rzjcpN3A2WSpm/AssKcqPDNpDBjs410CViXzTwk/A7b1C4wxDgOngAsZcAXY2buDfp/6S4F3lDS8DjgBzDWAjX/Y/e/QgYS/AhsKHa+OMQ6GEJ4Cj4BOAxgq6aCowyZtdf4OtAr+FHDO+R4wWnVbihr3cQnICt4boO38GWj9a/icjwOACt4m4K3zEPA+AxaAtTWCnwN3lzHkEL8V/OPAGud9wK2GF9XR1Wae/1zG2AI+pGYI4VUIoRtjHAc2A9cz4LRPevYCZ+i9/4sJt4GXJU10gaPAzdI2TTro/5Tfz8XEe2LSZGmxq/SDNvP8BnA5WRrx4BwYBe6vONx1EnjovGvBLAAd4Adwuyq8UiaNmDTvr+a8SQ9MuvbfwckBHZPe+QEfTdpep+4XZmPBHiHgz74AAAAASUVORK5CYII=);
background-position: 8px 7px;
}
.OT_subscriber .OT_mute.OT_active {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAUCAYAAACXtf2DAAACtklEQVQ4jZ2VSYiURxTHf+/T9Nc9iRrBuYySmIsXUU9iFMEFERRBvAjJLUQi5ioiHvSScfTmgqC4XAT1ZIgLuJHkICaaQAgKI2hAUBT30bjUq7bbv4eukXK029F3+eqtv/fqK6qQdEnSNUmT6CDB/bvgfjO4N9zj2RD8007xg1IABkwEzkma0qb4PGAPMBZYLtSD8eNwAEjqTlNI0gNJM4YU7w7ut4O7gvuhZFsR3C8NC5BBLiTIY0mzM8AvqbiC++pk+zLpE95XuwAws3vAQuBPYDRwWtL84P4tsDSLv5oaug4EYOawAMF9jMdoLxqNZcDvQA04UVYqL4G/svj7AF21mhJscrvCksYBFO7xc2AAGGg2mrdjvf4rcAyomNn+slLZmUEGBgsYdh945xZJmgvckDSrEJpK6ySBgV6q12O8ABwGPjGzfWWlsjdN9rpjoSfA+DYDXARGAksK4Is3XC1Ub4z1f4CDQGFmu6tleQSYk0U+p7WVeefLJc00s4fAeWB6Qeunvj0m2ugx9gO7kmlrtSxvBfcy6fXUZS6rgG/S+jLQUwCVNmMC9HqM14EtSe+rluWazN8YEv8IqKZ1E1qnaIDO0ucx3gX6kv6TpM3AM+D/IbGjgP60/gq4WQA33gMA2OQxPgHWJX1ttSwL4FAeZGYLgB2SasBs4A8L7qOBf9M0uXQB3a+TMYSmVctyDrA9mfcBK82smSdKWgCcAaa1bTm4fxbc/8uuCQX3RanAD5Ka6Wo5IGnE0HxJPZ03pQX5Org3MsD3AO5xXLPZXJ9BjkrqdFg6QjZkgG3Jtsw93pG0VFI9QU5K6voYQBHcTydAfwheBI9HgvvPAJIWS3qeIL9JGvUxkO7gfi1BrqTvwkG/pPmSnibIqTzXPgAyEVgBjAEu1qrVPbk/PVTHgb/NbPGg/RVIzOQqzSTBaQAAAABJRU5ErkJggg==);
background-position: 7px 7px;
}
/**
* Styles for display modes
*
* Note: It's important that these completely control the display and opacity
* attributes, no other selectors should atempt to change them.
*/
/* Default display mode transitions for various chrome elements */
.OT_publisher .OT_edge-bar-item,
.OT_subscriber .OT_edge-bar-item {
-webkit-transition-property: top, bottom, opacity;
transition-property: top, bottom, opacity;
-webkit-transition-duration: 0.5s;
transition-duration: 0.5s;
-webkit-transition-timing-function: ease-in;
transition-timing-function: ease-in;
}
.OT_publisher .OT_edge-bar-item.OT_mode-off,
.OT_subscriber .OT_edge-bar-item.OT_mode-off,
.OT_publisher .OT_edge-bar-item.OT_mode-auto,
.OT_subscriber .OT_edge-bar-item.OT_mode-auto,
.OT_publisher .OT_edge-bar-item.OT_mode-mini-auto,
.OT_subscriber .OT_edge-bar-item.OT_mode-mini-auto {
top: -25px;
opacity: 0;
}
.OT_publisher .OT_edge-bar-item.OT_mode-off,
.OT_subscriber .OT_edge-bar-item.OT_mode-off {
display: none;
}
.OT_mini .OT_mute.OT_mode-auto,
.OT_publisher .OT_mute.OT_mode-mini-auto,
.OT_subscriber .OT_mute.OT_mode-mini-auto {
top: 50%;
}
.OT_publisher .OT_edge-bar-item.OT_edge-bottom.OT_mode-off,
.OT_subscriber .OT_edge-bar-item.OT_edge-bottom.OT_mode-off,
.OT_publisher .OT_edge-bar-item.OT_edge-bottom.OT_mode-auto,
.OT_subscriber .OT_edge-bar-item.OT_edge-bottom.OT_mode-auto,
.OT_publisher .OT_edge-bar-item.OT_edge-bottom.OT_mode-mini-auto,
.OT_subscriber .OT_edge-bar-item.OT_edge-bottom.OT_mode-mini-auto {
top: auto;
bottom: -25px;
}
.OT_publisher .OT_edge-bar-item.OT_mode-on,
.OT_subscriber .OT_edge-bar-item.OT_mode-on,
.OT_publisher .OT_edge-bar-item.OT_mode-auto.OT_mode-on-hold,
.OT_subscriber .OT_edge-bar-item.OT_mode-auto.OT_mode-on-hold,
.OT_publisher:hover .OT_edge-bar-item.OT_mode-auto,
.OT_subscriber:hover .OT_edge-bar-item.OT_mode-auto,
.OT_publisher:hover .OT_edge-bar-item.OT_mode-mini-auto,
.OT_subscriber:hover .OT_edge-bar-item.OT_mode-mini-auto {
top: 0;
opacity: 1;
}
.OT_mini .OT_mute.OT_mode-on,
.OT_mini:hover .OT_mute.OT_mode-auto,
.OT_mute.OT_mode-mini,
.OT_root:hover .OT_mute.OT_mode-mini-auto {
top: 50%;
}
.OT_publisher .OT_edge-bar-item.OT_edge-bottom.OT_mode-on,
.OT_subscriber .OT_edge-bar-item.OT_edge-bottom.OT_mode-on,
.OT_publisher:hover .OT_edge-bar-item.OT_edge-bottom.OT_mode-auto,
.OT_subscriber:hover .OT_edge-bar-item.OT_edge-bottom.OT_mode-auto {
top: auto;
bottom: 0;
opacity: 1;
}
/* Contains the video element, used to fix video letter-boxing */
.OT_widget-container {
position: absolute;
background-color: #000000;
overflow: hidden;
}
/* Load animation */
.OT_root .OT_video-loading {
position: absolute;
z-index: 1;
width: 100%;
height: 100%;
display: none;
background-color: rgba(0, 0, 0, .75);
}
.OT_root .OT_video-loading .OT_video-loading-spinner {
background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0yMCAtMjAgMjQwIDI0MCI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJhIiB4Mj0iMCIgeTI9IjEiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2ZmZiIgc3RvcC1vcGFjaXR5PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjZmZmIiBzdG9wLW9wYWNpdHk9IjAiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjEiIHgyPSIwIiB5Mj0iMSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZmZmIiBzdG9wLW9wYWNpdHk9IjAiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNmZmYiIHN0b3Atb3BhY2l0eT0iLjA4Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImMiIHgxPSIxIiB4Mj0iMCIgeTE9IjEiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2ZmZiIgc3RvcC1vcGFjaXR5PSIuMDgiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNmZmYiIHN0b3Atb3BhY2l0eT0iLjE2Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImQiIHgyPSIwIiB5MT0iMSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZmZmIiBzdG9wLW9wYWNpdHk9Ii4xNiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI2ZmZiIgc3RvcC1vcGFjaXR5PSIuMzMiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iZSIgeDI9IjEiIHkxPSIxIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNmZmYiIHN0b3Atb3BhY2l0eT0iLjMzIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjZmZmIiBzdG9wLW9wYWNpdHk9Ii42NiIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJmIiB4Mj0iMSIgeTI9IjEiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2ZmZiIgc3RvcC1vcGFjaXR5PSIuNjYiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNmZmYiLz48L2xpbmVhckdyYWRpZW50PjxtYXNrIGlkPSJnIj48ZyBmaWxsPSJub25lIiBzdHJva2Utd2lkdGg9IjQwIj48cGF0aCBzdHJva2U9InVybCgjYSkiIGQ9Ik04Ni42LTUwYTEwMCAxMDAgMCAwIDEgMCAxMDAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEwMCAxMDApIi8+PHBhdGggc3Ryb2tlPSJ1cmwoI2IpIiBkPSJNODYuNiA1MEExMDAgMTAwIDAgMCAxIDAgMTAwIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMDAgMTAwKSIvPjxwYXRoIHN0cm9rZT0idXJsKCNjKSIgZD0iTTAgMTAwYTEwMCAxMDAgMCAwIDEtODYuNi01MCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAwIDEwMCkiLz48cGF0aCBzdHJva2U9InVybCgjZCkiIGQ9Ik0tODYuNiA1MGExMDAgMTAwIDAgMCAxIDAtMTAwIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMDAgMTAwKSIvPjxwYXRoIHN0cm9rZT0idXJsKCNlKSIgZD0iTS04Ni42LTUwQTEwMCAxMDAgMCAwIDEgMC0xMDAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEwMCAxMDApIi8+PHBhdGggc3Ryb2tlPSJ1cmwoI2YpIiBkPSJNMC0xMDBhMTAwIDEwMCAwIDAgMSA4Ni42IDUwIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMDAgMTAwKSIvPjwvZz48L21hc2s+PC9kZWZzPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIHg9Ii0yMCIgeT0iLTIwIiBtYXNrPSJ1cmwoI2cpIiBmaWxsPSIjZmZmIi8+PC9zdmc+) no-repeat;
position: absolute;
width: 32px;
height: 32px;
left: 50%;
top: 50%;
margin-left: -16px;
margin-top: -16px;
-webkit-animation: OT_spin 2s linear infinite;
animation: OT_spin 2s linear infinite;
}
@-webkit-keyframes OT_spin {
100% {
-webkit-transform: rotate(360deg);
}
}
@keyframes OT_spin {
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
.OT_publisher.OT_loading .OT_video-loading,
.OT_subscriber.OT_loading .OT_video-loading {
display: block;
}
.OT_video-centering {
display: table;
width: 100%;
height: 100%;
}
.OT_video-container {
display: table-cell;
vertical-align: middle;
}
.OT_video-poster {
position: absolute;
z-index: 1;
width: 100%;
height: 100%;
display: none;
opacity: .25;
background-repeat: no-repeat;
background-image: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNDcxIDQ2NCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48bGluZWFyR3JhZGllbnQgaWQ9ImEiIHgyPSIwIiB5Mj0iMSI+PHN0b3Agb2Zmc2V0PSI2Ni42NiUiIHN0b3AtY29sb3I9IiNmZmYiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmZmYiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PHBhdGggZmlsbD0idXJsKCNhKSIgZD0iTTc5IDMwOGMxNC4yNS02LjUgNTQuMjUtMTkuNzUgNzEtMjkgOS0zLjI1IDI1LTIxIDI1LTIxczMuNzUtMTMgMy0yMmMtMS43NS02Ljc1LTE1LTQzLTE1LTQzLTIuNSAzLTQuNzQxIDMuMjU5LTcgMS0zLjI1LTcuNS0yMC41LTQ0LjUtMTYtNTcgMS4yNS03LjUgMTAtNiAxMC02LTExLjI1LTMzLjc1LTgtNjctOC02N3MuMDczLTcuMzQ2IDYtMTVjLTMuNDguNjM3LTkgNC05IDQgMi41NjMtMTEuNzI3IDE1LTIxIDE1LTIxIC4xNDgtLjMxMi0xLjMyMS0xLjQ1NC0xMCAxIDEuNS0yLjc4IDE2LjY3NS04LjY1NCAzMC0xMSAzLjc4Ny05LjM2MSAxMi43ODItMTcuMzk4IDIyLTIyLTIuMzY1IDMuMTMzLTMgNi0zIDZzMTUuNjQ3LTguMDg4IDQxLTZjLTE5Ljc1IDItMjQgNi0yNCA2czc0LjUtMTAuNzUgMTA0IDM3YzcuNSA5LjUgMjQuNzUgNTUuNzUgMTAgODkgMy43NS0xLjUgNC41LTQuNSA5IDEgLjI1IDE0Ljc1LTExLjUgNjMtMTkgNjItMi43NSAxLTQtMy00LTMtMTAuNzUgMjkuNS0xNCAzOC0xNCAzOC0yIDQuMjUtMy43NSAxOC41LTEgMjIgMS4yNSA0LjUgMjMgMjMgMjMgMjNsMTI3IDUzYzM3IDM1IDIzIDEzNSAyMyAxMzVMMCA0NjRzLTMtOTYuNzUgMTQtMTIwYzUuMjUtNi4yNSAyMS43NS0xOS43NSA2NS0zNnoiLz48L3N2Zz4=);
background-size: auto 76%;
}
.OT_fit-mode-cover .OT_video-element {
-o-object-fit: cover;
object-fit: cover;
}
/* Workaround for iOS freezing issue when cropping videos */
/* https://bugs.webkit.org/show_bug.cgi?id=176439 */
@media only screen
and (orientation: portrait) {
.OT_subscriber.OT_ForceContain.OT_fit-mode-cover .OT_video-element {
-o-object-fit: contain !important;
object-fit: contain !important;
}
}
.OT_fit-mode-contain .OT_video-element {
-o-object-fit: contain;
object-fit: contain;
}
.OT_fit-mode-cover .OT_video-poster {
background-position: center bottom;
}
.OT_fit-mode-contain .OT_video-poster {
background-position: center;
}
.OT_audio-level-meter {
position: absolute;
width: 25%;
max-width: 224px;
min-width: 21px;
top: 0;
right: 0;
overflow: hidden;
}
.OT_audio-level-meter:before {
/* makes the height of the container equals its width */
content: '';
display: block;
padding-top: 100%;
}
.OT_audio-level-meter__bar {
position: absolute;
width: 192%; /* meter value can overflow of 8% */
height: 192%;
top: -96% /* half of the size */;
right: -96%;
border-radius: 50%;
background-color: rgba(0, 0, 0, .8);
}
.OT_audio-level-meter__audio-only-img {
position: absolute;
top: 22%;
right: 15%;
width: 40%;
opacity: .7;
background: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNzkgODYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0iI2ZmZiI+PHBhdGggZD0iTTkuNzU3IDQwLjkyNGMzLjczOC01LjE5MSAxMi43MTEtNC4zMDggMTIuNzExLTQuMzA4IDIuMjIzIDMuMDE0IDUuMTI2IDI0LjU4NiAzLjYyNCAyOC43MTgtMS40MDEgMS4zMDEtMTEuNjExIDEuNjI5LTEzLjM4LTEuNDM2LTEuMjI2LTguODA0LTIuOTU1LTIyLjk3NS0yLjk1NS0yMi45NzV6bTU4Ljc4NSAwYy0zLjczNy01LjE5MS0xMi43MTEtNC4zMDgtMTIuNzExLTQuMzA4LTIuMjIzIDMuMDE0LTUuMTI2IDI0LjU4Ni0zLjYyNCAyOC43MTggMS40MDEgMS4zMDEgMTEuNjExIDEuNjI5IDEzLjM4LTEuNDM2IDEuMjI2LTguODA0IDIuOTU1LTIyLjk3NSAyLjk1NS0yMi45NzV6Ii8+PHBhdGggZD0iTTY4LjY0NyA1OC42Yy43MjktNC43NTMgMi4zOC05LjU2MSAyLjM4LTE0LjgwNCAwLTIxLjQxMi0xNC4xMTUtMzguNzctMzEuNTI4LTM4Ljc3LTE3LjQxMiAwLTMxLjUyNyAxNy4zNTgtMzEuNTI3IDM4Ljc3IDAgNC41NDEuNTE1IDguOTM2IDEuODAyIDEyLjk1IDEuNjk4IDUuMjk1LTUuNTQyIDYuOTkxLTYuNjE2IDIuMDczQzIuNDEgNTUuMzk0IDAgNTEuNzg3IDAgNDguMTAzIDAgMjEuNTM2IDE3LjY4NSAwIDM5LjUgMCA2MS4zMTYgMCA3OSAyMS41MzYgNzkgNDguMTAzYzAgLjcxOC0yLjg5OSA5LjY5My0zLjI5MiAxMS40MDgtLjc1NCAzLjI5My03Ljc1MSAzLjU4OS03LjA2MS0uOTEyeiIvPjxwYXRoIGQ9Ik01LjA4NCA1MS4zODVjLS44MDQtMy43ODIuNTY5LTcuMzM1IDMuMTM0LTcuOTIxIDIuNjM2LS42MDMgNS40ODUgMi4xNSA2LjI4OSA2LjEzMi43OTcgMy45NDgtLjc1MiA3LjQ1Ny0zLjM4OCA3Ljg1OS0yLjU2Ni4zOTEtNS4yMzctMi4zMTgtNi4wMzQtNi4wN3ptNjguODM0IDBjLjgwNC0zLjc4Mi0uNTY4LTcuMzM1LTMuMTMzLTcuOTIxLTIuNjM2LS42MDMtNS40ODUgMi4xNS02LjI4OSA2LjEzMi0uNzk3IDMuOTQ4Ljc1MiA3LjQ1NyAzLjM4OSA3Ljg1OSAyLjU2NS4zOTEgNS4yMzctMi4zMTggNi4wMzQtNi4wN3ptLTIuMDM4IDguMjg4Yy0uOTI2IDE5LjY1OS0xNS4xMTIgMjQuNzU5LTI1Ljg1OSAyMC40NzUtNS40MDUtLjYwNi0zLjAzNCAxLjI2Mi0zLjAzNCAxLjI2MiAxMy42NjEgMy41NjIgMjYuMTY4IDMuNDk3IDMxLjI3My0yMC41NDktLjU4NS00LjUxMS0yLjM3OS0xLjE4Ny0yLjM3OS0xLjE4N3oiLz48cGF0aCBkPSJNNDEuNjYyIDc4LjQyMmw3LjU1My41NWMxLjE5Mi4xMDcgMi4xMiAxLjE1MyAyLjA3MiAyLjMzNWwtLjEwOSAyLjczOGMtLjA0NyAxLjE4Mi0xLjA1MSAyLjA1NC0yLjI0MyAxLjk0NmwtNy41NTMtLjU1Yy0xLjE5MS0uMTA3LTIuMTE5LTEuMTUzLTIuMDcyLTIuMzM1bC4xMDktMi43MzdjLjA0Ny0xLjE4MiAxLjA1Mi0yLjA1NCAyLjI0My0xLjk0N3oiLz48L2c+PC9zdmc+) no-repeat center;
}
.OT_audio-level-meter__audio-only-img:before {
/* makes the height of the container equals its width */
content: '';
display: block;
padding-top: 100%;
}
.OT_audio-level-meter__value {
position: absolute;
border-radius: 50%;
background-image: radial-gradient(circle, rgba(151, 206, 0, 1) 0%, rgba(151, 206, 0, 0) 100%);
}
.OT_audio-level-meter.OT_mode-off {
display: none;
}
.OT_audio-level-meter.OT_mode-on,
.OT_audio-only .OT_audio-level-meter.OT_mode-auto {
display: block;
}
.OT_audio-only.OT_publisher .OT_video-element,
.OT_audio-only.OT_subscriber .OT_video-element {
display: none;
}
.OT_video-disabled-indicator {
opacity: 1;
border: none;
display: none;
position: absolute;
background-color: transparent;
background-repeat: no-repeat;
background-position: bottom right;
pointer-events: none;
top: 0;
left: 0;
bottom: 3px;
right: 3px;
}
.OT_video-disabled {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFIAAAAoCAYAAABtla08AAAINUlEQVR42u2aaUxUVxTHcRBmAAEBRVTK4sKwDIsg+wCK7CqIw1CN1YobbbS2qYlJ06Qx1UpdqMbYWq2pSzWmH6ytNbXWJY1Lq7VuqBERtW64V0XFLYae0/xvcp3MMAMzDz6IyT/ge2ce5/7ucpY3Ts3NzZ1ygF57AJ0gO0G2jyZPmdbFyclJSAV1EeoEaUUSLGdSV5KLLFxzFmA7QVqGqDqjixhWkxCVeyRVl38wM6bwj6yYItYK47BAuu9B0gCqs6Ng2r494KQtkj/Dz2jHraw6qw2fdSE4rNmcCPCvZONP8iF1I6kdBdMaQJWZLeJqRWa2kPJAxXY+GxE+zxLI03GRh8lGSwoi9WCY8FWlCEh+8JOnT7MfPGjMuXX7Tt61hoaCi/9cKmKdv3BxeEtim/UbNpnbQiqF4MmT7kqrbr4lkMcTo46TTSpJB5g+8NHuVWnWuaampvhmO/7duHmrGluoO4C6OsJZGRrkDIld43ZqUOTnlkDSmXmabAoBU0vqBf+6KgFSxQ9++uzZ8rZApM81TJ8xM5me0Z/UF7PuBmdVdkGEb5gYDeQmyZNW3SJLIP9Kj64lGyMpmxRN6sOfIbkoAhKOdnv2/PmB1kB88eLFo+olyyrps3rSINIAzLonnqlqK8R9w+L86vtrt5L2nhug3Vc3ULu/Liz8AOuXESlZZONH6kmr7gtLIA9lRNeRzVukAvj3BslLnJNKgfScO69K+/Lly0ZbQW7e8tNK+pwBjqaSIjDrXgJkW1ciAZvbQjQ+RDahpBBKd5ZZsqN758hmImk4KQHnpDd8UwSkCyJarx07d4+3BeKJmlMHyX4qaRxpBCmNFE4KENvHDpAutVERn1kCVBMfeRRgYvZnx62wZPdnZkw92VQA5GClQXYRBze2S+iJmpPVVoJLA9l9QKokjcWKTCT1R5rhLg70NuSsziT16diIKkuAjibrTpJNDkn/e17CahtAjlAWJAYkb29Sb1LE9Rs391kILk8mVkyuIpuZcLKUlEmKkra1WuSTNuesEPzwoEploSVAh9Oiz+BIyd9dOHhtx4OEpFpVg6gbNK3yXX1j48N6U5Dz5i/gc/FDrMY3sTLiSMEkXxGxzUEUAGnbxlPaksMlHUXWAlHS8URCPseSohZbCSLjSSU7ixLXdzhIWVKq4Y7t2a/2bN0qGeKly1fYsVmk6RgIDz4J0bonyUOcjeYqm/8hRoYbWkigV2NH9CHAS60EkUkkw47hSRs6FqT1LR5AVcsrueXlK1d5AO+RpmBrZZEiefByytPCanRGNLZY0uF52gNDYr9sCRB8MHY0SJu2OJWKS2WQV65e4y31DmkCImEi0hBfufRime0RIhpbKen0/Ny9OYNW2ghyYytABjNIaxNuKttAWk6HPLn0k0FevdZwFinPWFIuKZbUV16NVko6jbWSDoPO3pOf8K0jQWLSQ0S9bdpkYck+m7vfWpAiHfKgBsZiGSSt0FqcTeU8WETqAHE2CgcAVd3Gkm4MD3xXYeI6B4NMItvKbcUpQ9gP+KMWnSsW+TaYJtoo+avBWLoKoK0CCSDud+7eXWQGZAXqV3YoQjQCfixJ8+fzj9ta3JHhlUeJ8wJOY2ws6eRKpPS3oqTvHAESEz9ya0naXL5WH6pt3FqSOhTHkTcKEXc6k1POh4Q9YJu/03TT4a8PoGMFI4i2EqSbOZAYaBkpCyD92RkG6KCSbjI/H0HEISBnlOZPFdcEzI2GTO4KBZICGKyAKLTEmJOB2txf5MbgohBINCl4FTqmpJMB2W+HiRn1Q2l6lXyPmiEP6VVE2TfGoaMYrHyPdtAnyI0jEOn9RLWmNEhvBBE7SjpFQZaShtLK+1S+T12lRwxUvrZlVPp8jE1PikeO7C/nyEqBDCB1t7+kUx4kKUWclea0yZC5BIGpiJSNSD9QgFR0RQKkL6KxHSWdsiARHJNYewoGrzG1/bk4dTPSunL2EyDjcbb7MQ+lQfZmkKiN7SjpFAM5CWAyGcwyY84YsZ1lUcbRNNtQMAdtQWGvQ0DyVjzYAKQfQFodeAeC1C8vzymXIZqD+ZEh/2OyLSalS/3VbnJZ+VqDXGjMrTCFuK4s66vVZUNfqaDolcbjOcb899sLpEE+I20GifywXe2QR3KElu99PzqjGufhREqB1pjCnG3IL3fY1v733r2FMsiGhutn0LAoJWWIGbPxjKwgjUbF0m52mPhigrpdXOecEq9pR6MkHbu2LOtrcZ9y3d0ODTb15y9MePz48aF79+8fvXnr9sljx2u2I7KNxDuaMPGVECoRs7mC4eT7SIruFNfNHK15MKuM2evwNq+4qjxvGnd5CHwNNynawW4cOlUZdG8b55IIJHmkItwrZHH6QxB3OSL9kTtAGpIvZiQB3Z4SKBfXQtEE9sashWAW87Bt3sYZNR6zn4uzJwWDKUKXfaKCdqUoBpLxSjYe9nqGiwWRBGipuGZ3Qm76itYLbbJI/PEhUApfw73uOIy9xfse3M9F9BuFJHcYrseSouGkHtCVtkuGTTikI8XgZzhg9SeF4VqcvSWiaSvNHQ8JwkNjIfEHemCmNLD1RaEfLs18mlgNuN6PFALHo7CyU5W2g00gFAQF4ozvibH04muwDbWraSFAyt/AAMzewgGR8uCeWn77xzBxPxgzPRCDDMZ14bQ/3jqGKGoHf2Hjgx3kw5LbaJDYWb52t9FMgw4AuWNWukNeuOYqOsmQi2jgws4PA/DD/z0B2x0/veCs4naw0cgybezid7X9jV3rX2RSs0wfLkll4pBGcgifg+NYxe1kJ2ycTaRq66uG/wBOl0vjcw70xwAAAABJRU5ErkJggg==);
background-size: 33px auto;
}
.OT_video-disabled-warning {
background-image: url(data:image/png;base64,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);
background-size: 33px auto;
}
.OT_video-disabled-indicator.OT_active {
display: block;
}
.OT_audio-blocked-indicator {
opacity: 1;
border: none;
display: none;
position: absolute;
background-color: transparent;
background-repeat: no-repeat;
background-position: center;
pointer-events: none;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
.OT_audio-blocked {
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTUwIiBoZWlnaHQ9IjkwIj48ZGVmcz48cGF0aCBkPSJNNjcgMTJMNi40NDggNzIuNTUyIDAgMzFWMThMMjYgMGw0MSAxMnptMyA3bDYgNDctMjkgMTgtMzUuNTAyLTYuNDk4TDcwIDE5eiIgaWQ9ImEiLz48L2RlZnM+PHJlY3Qgd2lkdGg9IjE1MCIgaGVpZ2h0PSI5MCIgcng9IjM1IiByeT0iNDUiIG9wYWNpdHk9Ii41Ii8+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzNikiPjxtYXNrIGlkPSJiIiBmaWxsPSIjZmZmIj48dXNlIHhsaW5rOmhyZWY9IiNhIi8+PC9tYXNrPjxwYXRoIGQ9Ik0zOS4yNDkgNTEuMzEyYy42OTcgMTAuMzcgMi43ODUgMTcuODk3IDUuMjUxIDE3Ljg5NyAzLjAzOCAwIDUuNS0xMS40MTcgNS41LTI1LjVzLTIuNDYyLTI1LjUtNS41LTI1LjVjLTIuNTEgMC00LjYyOCA3Ljc5Ny01LjI4NyAxOC40NTNBOC45ODkgOC45ODkgMCAwIDEgNDMgNDRhOC45ODggOC45ODggMCAwIDEtMy43NTEgNy4zMTJ6TTIwLjk4NSAzMi4yMjRsMTUuNzQ2LTE2Ljg3N2E3LjM4NSA3LjM4NSAwIDAgMSAxMC4zNzQtLjQyQzUxLjcwMiAxOS4xMTQgNTQgMjkuMjA4IDU0IDQ1LjIwOGMwIDE0LjUyNy0yLjM0MyAyMy44OC03LjAzIDI4LjA1OGE3LjI4IDcuMjggMCAwIDEtMTAuMTY4LS40NjhMMjAuNDA1IDU1LjIyNEgxMmE1IDUgMCAwIDEtNS01di0xM2E1IDUgMCAwIDEgNS01aDguOTg1eiIgZmlsbD0iI0ZGRiIgbWFzaz0idXJsKCNiKSIvPjwvZz48cGF0aCBkPSJNMTA2LjUgMTMuNUw0NC45OTggNzUuMDAyIiBzdHJva2U9IiNGRkYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PC9nPjwvc3ZnPg==);
background-size: 90px auto;
}
.OT_container-audio-blocked {
cursor: pointer;
}
.OT_container-audio-blocked.OT_mini .OT_edge-bar-item {
display: none;
}
.OT_container-audio-blocked .OT_mute {
display: none;
}
.OT_audio-blocked-indicator.OT_active {
display: block;
}
.OT_video-unsupported {
opacity: 1;
border: none;
display: none;
position: absolute;
background-color: transparent;
background-repeat: no-repeat;
background-position: center;
background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOTciIGhlaWdodD0iOTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxkZWZzPjxwYXRoIGQ9Ik03MCAxMkw5LjQ0OCA3Mi41NTIgMCA2MmwzLTQ0TDI5IDBsNDEgMTJ6bTggMmwxIDUyLTI5IDE4LTM1LjUwMi02LjQ5OEw3OCAxNHoiIGlkPSJhIi8+PC9kZWZzPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOCAzKSI+PG1hc2sgaWQ9ImIiIGZpbGw9IiNmZmYiPjx1c2UgeGxpbms6aHJlZj0iI2EiLz48L21hc2s+PHBhdGggZD0iTTkuMTEgMjAuOTY4SDQ4LjFhNSA1IDAgMCAxIDUgNVY1OC4xOGE1IDUgMCAwIDEtNSA1SDkuMTFhNSA1IDAgMCAxLTUtNVYyNS45N2E1IDUgMCAwIDEgNS01em00Ny4wOCAxMy4zOTRjMC0uMzQ1IDUuNDcyLTMuMTU5IDE2LjQxNS04LjQ0M2EzIDMgMCAwIDEgNC4zMDQgMi43MDJ2MjYuODM1YTMgMyAwIDAgMS00LjMwNSAyLjcwMWMtMTAuOTQyLTUuMjg2LTE2LjQxMy04LjEtMTYuNDEzLTguNDQ2VjM0LjM2MnoiIGZpbGw9IiNGRkYiIG1hc2s9InVybCgjYikiLz48L2c+PHBhdGggZD0iTTgxLjUgMTYuNUwxOS45OTggNzguMDAyIiBzdHJva2U9IiNGRkYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PC9nPjwvc3ZnPg==);
background-size: 58px auto;
pointer-events: none;
top: 0;
left: 0;
bottom: 0;
right: 0;
margin-top: -30px;
}
.OT_video-unsupported-bar {
display: none;
position: absolute;
width: 192%; /* copy the size of the audio meter bar for symmetry */
height: 192%;
top: -96% /* half of the size */;
left: -96%;
border-radius: 50%;
background-color: rgba(0, 0, 0, .8);
}
.OT_video-unsupported-img {
display: none;
position: absolute;
top: 11%;
left: 15%;
width: 70%;
opacity: .7;
background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOTciIGhlaWdodD0iOTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxkZWZzPjxwYXRoIGQ9Ik03MCAxMkw5LjQ0OCA3Mi41NTIgMCA2MmwzLTQ0TDI5IDBsNDEgMTJ6bTggMmwxIDUyLTI5IDE4LTM1LjUwMi02LjQ5OEw3OCAxNHoiIGlkPSJhIi8+PC9kZWZzPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOCAzKSI+PG1hc2sgaWQ9ImIiIGZpbGw9IiNmZmYiPjx1c2UgeGxpbms6aHJlZj0iI2EiLz48L21hc2s+PHBhdGggZD0iTTkuMTEgMjAuOTY4SDQ4LjFhNSA1IDAgMCAxIDUgNVY1OC4xOGE1IDUgMCAwIDEtNSA1SDkuMTFhNSA1IDAgMCAxLTUtNVYyNS45N2E1IDUgMCAwIDEgNS01em00Ny4wOCAxMy4zOTRjMC0uMzQ1IDUuNDcyLTMuMTU5IDE2LjQxNS04LjQ0M2EzIDMgMCAwIDEgNC4zMDQgMi43MDJ2MjYuODM1YTMgMyAwIDAgMS00LjMwNSAyLjcwMWMtMTAuOTQyLTUuMjg2LTE2LjQxMy04LjEtMTYuNDEzLTguNDQ2VjM0LjM2MnoiIGZpbGw9IiNGRkYiIG1hc2s9InVybCgjYikiLz48L2c+PHBhdGggZD0iTTgxLjUgMTYuNUwxOS45OTggNzguMDAyIiBzdHJva2U9IiNGRkYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PC9nPjwvc3ZnPg==);
background-repeat: no-repeat;
background-position: center;
background-size: 100% auto;
}
.OT_video-unsupported-img:before {
/* makes the height of the container 93% of its width (90/97 px) */
content: '';
display: block;
padding-top: 93%;
}
.OT_video-unsupported-text {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
text-align: center;
height: 100%;
margin-top: 40px;
}

View File

@ -1,8 +1,7 @@
<div class="bounds">
<div *ngFor="let streams of remoteStreams" class="content" fxLayout="row" fxFlexFill [style.height]="100 / remoteStreams.length + '%'"
[style.min-height]="100 / remoteStreams.length + '%'">
<div *ngFor="let s of streams" [fxFlex]="100 / streams">
<video [id]="'native-video-' + s.streamId" autoplay="true" [srcObject]="s.getMediaStream()"></video>
<div id="layout" class="bounds">
<div *ngFor="let s of streams" class="OT_root OT_publisher custom-class">
<div class="OT_widget-container" style="width: 100%; height: 100%;">
<video [id]="'native-video-' + s.streamId" autoplay="true" [srcObject]="s.getMediaStream()" (playing)="onVideoPlaying($event)"></video>
</div>
</div>
</div>

View File

@ -1,22 +1,28 @@
import { Component, OnInit, OnDestroy, HostListener } from '@angular/core';
import { Component, OnInit, OnDestroy, HostListener, ViewEncapsulation, ApplicationRef } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { OpenVidu, Session, Stream } from 'openvidu-browser';
import { OpenVidu, Session, Stream, Subscriber } from 'openvidu-browser';
import { OpenViduLayout } from '../../../../assets/openvidu-layout';
@Component({
selector: 'app-layout-best-fit',
templateUrl: './layout-best-fit.component.html',
styleUrls: ['./layout-best-fit.component.css']
styleUrls: ['./layout-best-fit.component.css'],
encapsulation: ViewEncapsulation.None
})
export class LayoutBestFitComponent implements OnInit, OnDestroy {
openviduLayout: OpenViduLayout;
sessionId: string;
secret: string;
session: Session;
numberOfVideos = 0;
remoteStreams = [];
streams = [];
constructor(private route: ActivatedRoute) {
layout: any;
resizeTimeout;
constructor(private route: ActivatedRoute, private appRef: ApplicationRef) {
this.route.params.subscribe(params => {
this.sessionId = params.sessionId;
this.secret = params.secret;
@ -28,6 +34,14 @@ export class LayoutBestFitComponent implements OnInit, OnDestroy {
this.leaveSession();
}
@HostListener('window:resize', ['$event'])
sizeChange(event) {
clearTimeout(this.resizeTimeout);
this.resizeTimeout = setTimeout(() => {
this.openviduLayout.updateLayout();
}, 20);
}
ngOnDestroy() {
this.leaveSession();
}
@ -39,15 +53,14 @@ export class LayoutBestFitComponent implements OnInit, OnDestroy {
this.session = OV.initSession(fullSessionId);
this.session.on('streamCreated', (event) => {
this.numberOfVideos++;
const subscriber: Subscriber = this.session.subscribe(event.stream, '');
this.addRemoteStream(event.stream);
this.session.subscribe(event.stream, '');
});
this.session.on('streamDestroyed', (event) => {
this.numberOfVideos--;
event.preventDefault();
this.deleteRemoteStream(event.stream);
this.openviduLayout.updateLayout();
});
this.session.connect(null, (error) => {
@ -55,79 +68,46 @@ export class LayoutBestFitComponent implements OnInit, OnDestroy {
console.error(error);
}
});
this.openviduLayout = new OpenViduLayout();
this.openviduLayout.initLayoutContainer(document.getElementById('layout'), {
maxRatio: 3 / 2, // The narrowest ratio that will be used (default 2x3)
minRatio: 9 / 16, // The widest ratio that will be used (default 16x9)
fixedRatio: false, /* If this is true then the aspect ratio of the video is maintained
and minRatio and maxRatio are ignored (default false) */
bigClass: 'OV_big', // The class to add to elements that should be sized bigger
bigPercentage: 0.8, // The maximum percentage of space the big ones should take up
bigFixedRatio: false, // fixedRatio for the big ones
bigMaxRatio: 3 / 2, // The narrowest ratio to use for the big elements (default 2x3)
bigMinRatio: 9 / 16, // The widest ratio to use for the big elements (default 16x9)
bigFirst: true, // Whether to place the big one in the top left (true) or bottom right
animate: true // Whether you want to animate the transitions
});
}
private addRemoteStream(stream: Stream): void {
switch (true) {
case (this.numberOfVideos <= 2):
if (this.remoteStreams[0] == null) { this.remoteStreams[0] = []; }
this.remoteStreams[0].push(stream);
break;
case (this.numberOfVideos <= 4):
if (this.remoteStreams[1] == null) { this.remoteStreams[1] = []; }
this.remoteStreams[1].push(stream);
break;
case (this.numberOfVideos <= 5):
this.remoteStreams[0].push(stream);
break;
case (this.numberOfVideos <= 6):
this.remoteStreams[1].push(stream);
break;
default:
if (this.remoteStreams[2] == null) { this.remoteStreams[2] = []; }
this.remoteStreams[2].push(stream);
break;
}
this.streams.push(stream);
this.appRef.tick();
}
private deleteRemoteStream(stream: Stream): void {
for (let i = 0; i < this.remoteStreams.length; i++) {
const index = this.remoteStreams[i].indexOf(stream, 0);
if (index > -1) {
this.remoteStreams[i].splice(index, 1);
this.reArrangeVideos();
break;
}
const index = this.streams.indexOf(stream, 0);
if (index > -1) {
this.streams.splice(index, 1);
}
}
private reArrangeVideos(): void {
switch (true) {
case (this.numberOfVideos === 1):
if (this.remoteStreams[0].length === 0) {
this.remoteStreams[0].push(this.remoteStreams[1].pop());
}
break;
case (this.numberOfVideos === 2):
if (this.remoteStreams[0].length === 1) {
this.remoteStreams[0].push(this.remoteStreams[1].pop());
}
break;
case (this.numberOfVideos === 3):
if (this.remoteStreams[0].length === 1) {
this.remoteStreams[0].push(this.remoteStreams[1].pop());
}
break;
case (this.numberOfVideos === 4):
if (this.remoteStreams[0].length === 3) {
this.remoteStreams[1].unshift(this.remoteStreams[0].pop());
}
break;
case (this.numberOfVideos === 5):
if (this.remoteStreams[0].length === 2) {
this.remoteStreams[0].push(this.remoteStreams[1].shift());
}
break;
}
this.remoteStreams = this.remoteStreams.filter((array) => { return array.length > 0 });
this.appRef.tick();
}
leaveSession() {
if (this.session) { this.session.disconnect(); };
this.remoteStreams = [];
this.numberOfVideos = 0;
this.streams = [];
this.session = null;
}
onVideoPlaying(event) {
const video: HTMLVideoElement = event.target;
video.parentElement.parentElement.classList.remove('custom-class');
this.openviduLayout.updateLayout();
}
}

View File

@ -0,0 +1,357 @@
declare var $: any;
export interface OpenViduLayoutOptions {
maxRatio: number;
minRatio: number;
fixedRatio: boolean;
animate: any;
bigClass: string;
bigPercentage: any;
bigFixedRatio: any;
bigMaxRatio: any;
bigMinRatio: any;
bigFirst: any;
}
export class OpenViduLayout {
private layoutContainer: HTMLElement;
private opts: OpenViduLayoutOptions;
private fixAspectRatio(elem: HTMLVideoElement, width: number) {
const sub: HTMLVideoElement = <HTMLVideoElement>elem.querySelector('.OT_root');
if (sub) {
// If this is the parent of a subscriber or publisher then we need
// to force the mutation observer on the publisher or subscriber to
// trigger to get it to fix it's layout
const oldWidth = sub.style.width;
sub.style.width = width + 'px';
// sub.style.height = height + 'px';
sub.style.width = oldWidth || '';
}
}
private positionElement(elem: HTMLVideoElement, x: number, y: number, width: number, height: number, animate: any) {
const targetPosition = {
left: x + 'px',
top: y + 'px',
width: width + 'px',
height: height + 'px'
};
this.fixAspectRatio(elem, width);
if (animate && $) {
$(elem).stop();
$(elem).animate(targetPosition, animate.duration || 200, animate.easing || 'swing',
() => {
this.fixAspectRatio(elem, width);
if (animate.complete) { animate.complete.call(this); }
});
} else {
$(elem).css(targetPosition);
}
this.fixAspectRatio(elem, width);
}
private getVideoRatio(elem: HTMLVideoElement) {
if (!elem) {
return 3 / 4;
}
const video: HTMLVideoElement = <HTMLVideoElement>elem.querySelector('video');
if (video && video.videoHeight && video.videoWidth) {
return video.videoHeight / video.videoWidth;
} else if (elem.videoHeight && elem.videoWidth) {
return elem.videoHeight / elem.videoWidth;
}
return 3 / 4;
}
private getCSSNumber(elem: HTMLElement, prop: string) {
const cssStr = $(elem).css(prop);
return cssStr ? parseInt(cssStr, 10) : 0;
}
// Really cheap UUID function
private cheapUUID() {
return (Math.random() * 100000000).toFixed(0);
}
private getHeight(elem: HTMLElement) {
const heightStr = $(elem).css('height');
return heightStr ? parseInt(heightStr, 10) : 0;
}
private getWidth(elem: HTMLElement) {
const widthStr = $(elem).css('width');
return widthStr ? parseInt(widthStr, 10) : 0;
}
private getBestDimensions(minR: number, maxR: number, count: number, WIDTH: number, HEIGHT: number, targetHeight: number) {
let maxArea,
targetCols,
targetRows,
targetWidth,
tWidth,
tHeight,
tRatio;
// Iterate through every possible combination of rows and columns
// and see which one has the least amount of whitespace
for (let i = 1; i <= count; i++) {
const colsAux = i;
const rowsAux = Math.ceil(count / colsAux);
// Try taking up the whole height and width
tHeight = Math.floor(HEIGHT / rowsAux);
tWidth = Math.floor(WIDTH / colsAux);
tRatio = tHeight / tWidth;
if (tRatio > maxR) {
// We went over decrease the height
tRatio = maxR;
tHeight = tWidth * tRatio;
} else if (tRatio < minR) {
// We went under decrease the width
tRatio = minR;
tWidth = tHeight / tRatio;
}
const area = (tWidth * tHeight) * count;
// If this width and height takes up the most space then we're going with that
if (maxArea === undefined || (area > maxArea)) {
maxArea = area;
targetHeight = tHeight;
targetWidth = tWidth;
targetCols = colsAux;
targetRows = rowsAux;
}
}
return {
maxArea: maxArea,
targetCols: targetCols,
targetRows: targetRows,
targetHeight: targetHeight,
targetWidth: targetWidth,
ratio: targetHeight / targetWidth
};
};
private arrange(children: HTMLVideoElement[], WIDTH: number, HEIGHT: number, offsetLeft: number, offsetTop: number, fixedRatio: boolean,
minRatio: number, maxRatio: number, animate: any) {
let targetHeight;
const count = children.length;
let dimensions;
if (!fixedRatio) {
dimensions = this.getBestDimensions(minRatio, maxRatio, count, WIDTH, HEIGHT, targetHeight);
} else {
// Use the ratio of the first video element we find to approximate
const ratio = this.getVideoRatio(children.length > 0 ? children[0] : null);
dimensions = this.getBestDimensions(ratio, ratio, count, WIDTH, HEIGHT, targetHeight);
}
// Loop through each stream in the container and place it inside
let x = 0,
y = 0;
const rows = [];
let row;
// Iterate through the children and create an array with a new item for each row
// and calculate the width of each row so that we know if we go over the size and need
// to adjust
for (let i = 0; i < children.length; i++) {
if (i % dimensions.targetCols === 0) {
// This is a new row
row = {
children: [],
width: 0,
height: 0
};
rows.push(row);
}
const elem: HTMLVideoElement = children[i];
row.children.push(elem);
let targetWidth = dimensions.targetWidth;
targetHeight = dimensions.targetHeight;
// If we're using a fixedRatio then we need to set the correct ratio for this element
if (fixedRatio) {
targetWidth = targetHeight / this.getVideoRatio(elem);
}
row.width += targetWidth;
row.height = targetHeight;
}
// Calculate total row height adjusting if we go too wide
let totalRowHeight = 0;
let remainingShortRows = 0;
for (let i = 0; i < rows.length; i++) {
row = rows[i];
if (row.width > WIDTH) {
// Went over on the width, need to adjust the height proportionally
row.height = Math.floor(row.height * (WIDTH / row.width));
row.width = WIDTH;
} else if (row.width < WIDTH) {
remainingShortRows += 1;
}
totalRowHeight += row.height;
}
if (totalRowHeight < HEIGHT && remainingShortRows > 0) {
// We can grow some of the rows, we're not taking up the whole height
let remainingHeightDiff = HEIGHT - totalRowHeight;
totalRowHeight = 0;
for (let i = 0; i < rows.length; i++) {
row = rows[i];
if (row.width < WIDTH) {
// Evenly distribute the extra height between the short rows
let extraHeight = remainingHeightDiff / remainingShortRows;
if ((extraHeight / row.height) > ((WIDTH - row.width) / row.width)) {
// We can't go that big or we'll go too wide
extraHeight = Math.floor(((WIDTH - row.width) / row.width) * row.height);
}
row.width += Math.floor((extraHeight / row.height) * row.width);
row.height += extraHeight;
remainingHeightDiff -= extraHeight;
remainingShortRows -= 1;
}
totalRowHeight += row.height;
}
}
// vertical centering
y = ((HEIGHT - (totalRowHeight)) / 2);
// Iterate through each row and place each child
for (let i = 0; i < rows.length; i++) {
row = rows[i];
// center the row
const rowMarginLeft = ((WIDTH - row.width) / 2);
x = rowMarginLeft;
for (let j = 0; j < row.children.length; j++) {
const elem: HTMLVideoElement = row.children[j];
let targetWidth = dimensions.targetWidth;
targetHeight = row.height;
// If we're using a fixedRatio then we need to set the correct ratio for this element
if (fixedRatio) {
targetWidth = Math.floor(targetHeight / this.getVideoRatio(elem));
}
elem.style.position = 'absolute';
// $(elem).css('position', 'absolute');
const actualWidth = targetWidth - this.getCSSNumber(elem, 'paddingLeft') -
this.getCSSNumber(elem, 'paddingRight') -
this.getCSSNumber(elem, 'marginLeft') -
this.getCSSNumber(elem, 'marginRight') -
this.getCSSNumber(elem, 'borderLeft') -
this.getCSSNumber(elem, 'borderRight');
const actualHeight = targetHeight - this.getCSSNumber(elem, 'paddingTop') -
this.getCSSNumber(elem, 'paddingBottom') -
this.getCSSNumber(elem, 'marginTop') -
this.getCSSNumber(elem, 'marginBottom') -
this.getCSSNumber(elem, 'borderTop') -
this.getCSSNumber(elem, 'borderBottom');
this.positionElement(elem, x + offsetLeft, y + offsetTop, actualWidth, actualHeight, animate);
x += targetWidth;
}
y += targetHeight;
}
}
private filterDisplayNone(element: HTMLElement) {
return element.style.display !== 'none';
}
updateLayout() {
if (this.layoutContainer.style.display === 'none') {
return;
}
let id = this.layoutContainer.id;
if (!id) {
id = 'OT_' + this.cheapUUID();
this.layoutContainer.id = id;
}
const HEIGHT = this.getHeight(this.layoutContainer) -
this.getCSSNumber(this.layoutContainer, 'borderTop') -
this.getCSSNumber(this.layoutContainer, 'borderBottom');
const WIDTH = this.getWidth(this.layoutContainer) -
this.getCSSNumber(this.layoutContainer, 'borderLeft') -
this.getCSSNumber(this.layoutContainer, 'borderRight');
const availableRatio = HEIGHT / WIDTH;
let offsetLeft = 0;
let offsetTop = 0;
let bigOffsetTop = 0;
let bigOffsetLeft = 0;
const bigOnes = Array.prototype.filter.call(
this.layoutContainer.querySelectorAll('#' + id + '>.' + this.opts.bigClass),
this.filterDisplayNone);
const smallOnes = Array.prototype.filter.call(
this.layoutContainer.querySelectorAll('#' + id + '>*:not(.' + this.opts.bigClass + ')'),
this.filterDisplayNone);
if (bigOnes.length > 0 && smallOnes.length > 0) {
let bigWidth, bigHeight;
if (availableRatio > this.getVideoRatio(bigOnes[0])) {
// We are tall, going to take up the whole width and arrange small
// guys at the bottom
bigWidth = WIDTH;
bigHeight = Math.floor(HEIGHT * this.opts.bigPercentage);
offsetTop = bigHeight;
bigOffsetTop = HEIGHT - offsetTop;
} else {
// We are wide, going to take up the whole height and arrange the small
// guys on the right
bigHeight = HEIGHT;
bigWidth = Math.floor(WIDTH * this.opts.bigPercentage);
offsetLeft = bigWidth;
bigOffsetLeft = WIDTH - offsetLeft;
}
if (this.opts.bigFirst) {
this.arrange(bigOnes, bigWidth, bigHeight, 0, 0, this.opts.bigFixedRatio, this.opts.bigMinRatio,
this.opts.bigMaxRatio, this.opts.animate);
this.arrange(smallOnes, WIDTH - offsetLeft, HEIGHT - offsetTop, offsetLeft, offsetTop,
this.opts.fixedRatio, this.opts.minRatio, this.opts.maxRatio, this.opts.animate);
} else {
this.arrange(smallOnes, WIDTH - offsetLeft, HEIGHT - offsetTop, 0, 0, this.opts.fixedRatio,
this.opts.minRatio, this.opts.maxRatio, this.opts.animate);
this.arrange(bigOnes, bigWidth, bigHeight, bigOffsetLeft, bigOffsetTop,
this.opts.bigFixedRatio, this.opts.bigMinRatio, this.opts.bigMaxRatio, this.opts.animate);
}
} else if (bigOnes.length > 0 && smallOnes.length === 0) {
this.
// We only have one bigOne just center it
arrange(bigOnes, WIDTH, HEIGHT, 0, 0, this.opts.bigFixedRatio, this.opts.bigMinRatio,
this.opts.bigMaxRatio, this.opts.animate);
} else {
this.arrange(smallOnes, WIDTH - offsetLeft, HEIGHT - offsetTop, offsetLeft, offsetTop,
this.opts.fixedRatio, this.opts.minRatio, this.opts.maxRatio, this.opts.animate);
}
}
initLayoutContainer(container, opts) {
this.opts = {
maxRatio: (opts.maxRatio != null) ? opts.maxRatio : 3 / 2,
minRatio: (opts.minRatio != null) ? opts.minRatio : 9 / 16,
fixedRatio: (opts.fixedRatio != null) ? opts.fixedRatio : false,
animate: (opts.animate != null) ? opts.animate : false,
bigClass: (opts.bigClass != null) ? opts.bigClass : 'OT_big',
bigPercentage: (opts.bigPercentage != null) ? opts.bigPercentage : 0.8,
bigFixedRatio: (opts.bigFixedRatio != null) ? opts.bigFixedRatio : false,
bigMaxRatio: (opts.bigMaxRatio != null) ? opts.bigMaxRatio : 3 / 2,
bigMinRatio: (opts.bigMinRatio != null) ? opts.bigMinRatio : 9 / 16,
bigFirst: (opts.bigFirst != null) ? opts.bigFirst : true
};
this.layoutContainer = typeof (container) === 'string' ? $(container) : container;
}
setLayoutOptions(options: OpenViduLayoutOptions) {
this.opts = options;
}
}

View File

@ -9,7 +9,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha256-k2WSCIexGzOj3Euiig+TlR8gA0EmPjuc79OEeY5L45g="
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
</head>

View File

@ -16,7 +16,7 @@ li {
list-style: none;
}
video {
#mirrored-video video {
width: 100%;
}