added webRtcPlayer

This commit is contained in:
DmitriyB
2023-03-13 15:18:24 +05:00
parent 240f28935d
commit 61efa5611d
10 changed files with 4521 additions and 16 deletions
@@ -0,0 +1,221 @@
import './player.css'
export const Player: React.FC = () => {
return (
<>
<div id="overlay">
<div id="controls">
<button className="tooltip" id="fullscreen-btn">
<span className="tooltiptext">Fullscreen</span>
</button>
<button className="tooltip" id="settingsBtn">
<span className="tooltiptext">Settings</span>
</button>
<button className="tooltip" id="statsBtn">
<span className="tooltiptext">Information</span>
</button>
</div>
<div id="unrealengine">
</div>
<div id="connection" className="tooltip">
<span className="tooltiptext" id="qualityText">Not connected</span>
</div>
<div className="panel-wrap" id="settings-panel">
<div className="panel">
<div id="heading">Settings</div>
<div id="close"></div>
<div id="content">
<div id="fillWindow" className="setting">
<div className="settings-text">Enlarge display to fill window</div>
<label className="tgl-switch">
<input type="checkbox" id="enlarge-display-to-fill-window-tgl" className="tgl tgl-flat" checked />
<div className="tgl-slider"></div>
</label>
</div>
<div id="qualityControlOwnership" className="setting">
<div className="settings-text">Is quality controller?</div>
<label className="tgl-switch">
<input type="checkbox" id="quality-control-ownership-tgl" className="tgl tgl-flat" />
<div className="tgl-slider"></div>
</label>
</div>
<div id="matchViewportResolution" className="setting">
<div className="settings-text">Match viewport resolution</div>
<label className="tgl-switch">
<input type="checkbox" id="match-viewport-res-tgl" className="tgl tgl-flat" />
<div className="tgl-slider"></div>
</label>
</div>
<div id="offerToReceive" className="setting">
<div className="settings-text">Offer To Receive</div>
<label className="tgl-switch">
<input type="checkbox" id="offer-receive-tgl" className="tgl tgl-flat" />
<div className="tgl-slider"></div>
</label>
</div>
<div id="preferSFU" className="setting">
<div className="settings-text">Prefer SFU</div>
<label className="tgl-switch">
<input type="checkbox" id="prefer-sfu-tgl" className="tgl tgl-flat" />
<div className="tgl-slider"></div>
</label>
</div>
<div id="useMic" className="setting">
<div className="settings-text">Use microphone</div>
<label className="tgl-switch">
<input type="checkbox" id="use-mic-tgl" className="tgl tgl-flat" />
<div className="tgl-slider"></div>
</label>
</div>
<div id="forceMonoAudio" className="setting">
<div className="settings-text">Force mono audio</div>
<label className="tgl-switch">
<input type="checkbox" id="force-mono-tgl" className="tgl tgl-flat" />
<div className="tgl-slider"></div>
</label>
</div>
<div id="forceTURN" className="setting">
<div className="settings-text">Force TURN</div>
<label className="tgl-switch">
<input type="checkbox" id="force-turn-tgl" className="tgl tgl-flat" />
<div className="tgl-slider"></div>
</label>
</div>
<div id="toggleControl" className="setting">
<div className="settings-text" id="control-scheme-text">Control Scheme</div>
<label className="btn-overlay">
<label className="tgl-switch">
<input type="checkbox" id="control-tgl" className="tgl tgl-flat" />
<div className="tgl-slider"></div>
</label>
</label>
</div>
<div id="toggleCursor" className="setting">
<div className="settings-text" id="cursor-text">Hide Browser Cursor</div>
<label className="btn-overlay">
<label className="tgl-switch">
<input type="checkbox" id="cursor-tgl" className="tgl tgl-flat" />
<div className="tgl-slider"></div>
</label>
</label>
</div>
<div id="showFPS" className="setting">
<div className="settings-text">Show FPS</div>
<label className="btn-overlay">
<input type="button" id="show-fps-button" className="overlay-button btn-flat" value="Toggle" />
</label>
</div>
<div id="keyframeRequest" className="setting">
<div className="settings-text">Request KeyFrame</div>
<label className="btn-overlay">
<input type="button" id="request-keyframe-button" className="overlay-button btn-flat" value="Request" />
</label>
</div>
<section id="encoderSettings">
<div id="encoderSettingsHeader" className="settings-text">
<div>Encoder Settings</div>
</div>
<div id="encoderParamsContainer" className="collapse">
<div className="form-group">
<label htmlFor="encoder-min-qp-text">Min QP</label>
<input type="number" className="form-control" id="encoder-min-qp-text" value="0" min="0"
max="51" />
<label htmlFor="encoder-max-qp-text">Max QP</label>
<input type="number" className="form-control" id="encoder-max-qp-text" value="51" min="0"
max="51" />
<br></br>
<input id="encoder-params-submit" className="overlay-button btn-flat" type="button"
value="Apply" />
</div>
</div>
</section>
<section id="webRTCSettings">
<div id="webRTCSettingsHeader" className="settings-text">
<div>WebRTC Settings</div>
</div>
<div id="webrtcParamsContainer" className="collapse">
<div className="form-group">
<label htmlFor="webrtc-fps-text">FPS</label>
<input type="number" className="form-control" id="webrtc-fps-text" value="60" min="1"
max="999" />
<label htmlFor="webrtc-min-bitrate-text">Min bitrate (kbps)</label>
<input type="number" className="form-control" id="webrtc-min-bitrate-text" value="0" min="0"
max="100000" />
<label htmlFor="webrtc-max-bitrate-text">Max bitrate (kbps)</label>
<input type="number" className="form-control" id="webrtc-max-bitrate-text" value="0" min="0"
max="100000" />
<br></br>
<input id="webrtc-params-submit" className="overlay-button btn-flat" type="button"
value="Apply" />
</div>
</div>
</section>
<section id="streamSettings">
<div id="streamSettingsHeader" className="settings-text">
<div>Stream Settings</div>
</div>
<div id="streamSettingsContainer" className="collapse">
<div className="form-group">
<div className="settings-text">Player stream</div>
<select className="form-control" id="stream-select"></select>
<div className="settings-text">Player track</div>
<select className="form-control" id="track-select"></select>
</div>
</div>
</section>
<br></br>
<section id="connectionSettings">
<div id="connectionHeader" className="settings-text">
<div>Stream Settings</div>
</div>
<div id="connectionContainer" className="collapse">
<div className="setting">
<div className="settings-text"></div>
<label className="btn-overlay">
<input type="button" id="restart-stream-button" className="overlay-button btn-flat" value="Restart stream" />
</label>
</div>
</div>
</section>
</div>
</div>
</div>
<div className="panel-wrap" id="stats-panel">
<div className="panel">
<div id="heading">Information</div>
<div id="close"></div>
<div id="content">
<section id="statsPanel">
<div className="setting settings-text">
<div>Session Stats</div>
</div>
<div id="statsContainer" className="statsContainer">
<div id="stats" className="stats"></div>
</div>
</section>
<br></br>
<section id="latencyTest">
<div className="setting">
<div className="settings-text">
<div>Latency Report</div>
</div>
<label className="btn-overlay">
<input type="button" id="test-latency-button" className="overlay-button btn-flat"
value="Get Report" />
</label>
</div>
<div id="latencyStatsContainer" className="statsContainer">
<div className="stats">No report yet</div>
</div>
</section>
</div>
</div>
</div>
</div>
</>
)
}
@@ -0,0 +1,727 @@
#loader {
width: 106px;
height: 106px;
border-radius: 50%;
display: inline-block;
position: relative;
background: conic-gradient(from 135deg at 50% 50%,
rgba(255, 255, 255, 0) -6.26deg,
#ffffff 314.83deg,
rgba(255, 255, 255, 0) 353.74deg,
#ffffff 674.83deg);
box-sizing: border-box;
animation: rotation 1s linear infinite;
}
@keyframes rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
#loader::after {
content: "";
box-sizing: border-box;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 100px;
height: 100px;
border-radius: 50%;
background: #151619;
}
body {
margin: 0px;
background-color: #151619;
font-family: 'Montserrat', sans-serif;
}
h2 {
font-family: "GilroyWebRegular";
}
#playerUI {
width: 100%;
height: 100%;
}
canvas {
image-rendering: crisp-edges;
position: absolute;
}
video {
position: absolute;
width: 100%;
height: 100%;
}
#player {
width: 100%;
height: 100%;
position: absolute;
}
#videoPlayOverlay {
position: absolute;
font-size: 1.8em;
width: 100%;
height: 100%;
color: var(--colour2)
}
/* State for element to be clickable */
.clickableState {
align-items: center;
justify-content: center;
display: flex;
}
/* State for element to show text, this is for informational use*/
.textDisplayState {
align-items: center;
justify-content: center;
display: flex;
cursor: pointer;
}
/* State to hide overlay, WebRTC communication is in progress and or is playing */
.hiddenState {
display: none;
}
#playButton {
display: block;
width: 88px;
height: 88px;
z-index: 30;
backdrop-filter: blur(10px);
border-radius: 112px;
cursor: pointer;
}
#playButtonMob {
display: block;
width: 88px;
height: 88px;
z-index: 30;
backdrop-filter: blur(10px);
border-radius: 112px;
cursor: pointer;
}
#container {
width: 400px;
height: 100%;
justify-content: center;
/* Background */
background: transparent;
/* Button_1 */
border-width: 0px 2px;
border-style: solid;
border-color: #23242A;
display: flex;
flex-direction: column;
align-items: center;
gap: 40px;
padding: 40px 56px;
box-sizing: border-box;
}
@media screen and (max-width: 500px) {
#container {
width: 100%;
border: none;
}
}
#playButtonMob:hover {
background: linear-gradient(180deg, #BC75FF 0%, #798FFF 100%);
backdrop-filter: blur(10px)
}
#title {
font-style: normal;
font-weight: 400;
font-size: 38px;
line-height: 100%;
/* or 38px */
/* White */
color: #F2F2F2;
margin: 0 0 16px 0;
}
#caption {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 140%;
/* or 22px */
text-align: center;
/* Inactive */
color: #C5C7CE;
margin: 0;
text-align: left;
}
#link {
font-family: 'Inter';
cursor: pointer;
background: #1C1D21;
border-radius: 4px;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
text-decoration: none;
padding: 8px 16px;
box-sizing: border-box;
font-weight: 400;
font-size: 12px;
line-height: 130%;
/* identical to box height, or 16px */
/* Inactive */
color: #C5C7CE;
}
#link:hover {
background: #23242A;
}
#freezeFrameOverlay {
background-color: transparent;
}
.freezeframeBackground {
background-color: #000 !important;
}
#overlay {
width: 100%;
height: 100%;
z-index: 20;
position: absolute;
color: var(--colour2);
pointer-events: none;
overflow: hidden;
}
#overlay button {
background-color: var(--colour7);
border: 1px solid var(--colour7);
color: var(--colour2);
position: relative;
width: 3rem;
height: 3rem;
padding: 0.5rem;
text-align: center;
}
#fullscreen-btn {
padding: 0.6rem !important;
}
#overlay button:hover {
background-color: var(--colour3);
border: 3px solid var(--colour3);
transition: 0.25s ease;
padding-left: 0.55rem;
padding-top: 0.55rem;
}
#overlay button:active {
border: 3px solid var(--colour3);
background-color: var(--colour7);
padding-left: 0.55rem;
padding-top: 0.55rem;
}
#overlay img {
width: 100%;
height: 100%;
}
.tooltip .tooltiptext {
visibility: hidden;
width: auto;
color: var(--colour2);
text-align: center;
border-radius: 15px;
padding: 0px 10px;
font-family: 'Montserrat', sans-serif;
font-size: 0.75rem;
letter-spacing: 0.75px;
/* Position the tooltip */
position: absolute;
top: 0;
transform: translateY(25%);
left: 125%;
z-index: 20;
}
.tooltip:hover .tooltiptext {
visibility: visible;
background-color: var(--colour7);
}
#connection .tooltiptext {
top: 125%;
transform: translateX(-25%);
left: 0;
z-index: 20;
padding: 5px 10px;
}
#settings-panel .tooltiptext {
display: block;
top: 125%;
transform: translateX(-50%);
left: 0;
z-index: 20;
padding: 5px 10px;
border: 3px solid var(--colour5);
width: max-content;
}
#controls {
position: absolute;
top: 2%;
left: 1%;
font-family: 'Michroma', sans-serif;
pointer-events: all;
display: none;
}
#controls>* {
margin-bottom: 0.5rem;
border-radius: 50%;
display: block;
height: 2rem;
line-height: 1.75rem;
padding: 0.5rem;
}
#controls #additionalinfo {
text-align: center;
font-family: 'Montserrat', sans-serif;
}
#unrealengine {
position: absolute;
bottom: 5%;
right: 10%;
font-family: 'Michroma', sans-serif;
pointer-events: all;
visibility: hidden;
width: min-content;
}
#unrealengine p {
visibility: hidden;
width: 15rem;
}
#connection {
position: absolute;
bottom: 5%;
left: 10%;
font-family: 'Michroma', sans-serif;
height: 3rem;
width: 3rem;
pointer-events: none;
visibility: hidden;
}
.noselect {
-webkit-touch-callout: none;
/* iOS Safari */
-webkit-user-select: none;
/* Safari */
-khtml-user-select: none;
/* Konqueror HTML */
-moz-user-select: none;
/* Old versions of Firefox */
-ms-user-select: none;
/* Internet Explorer/Edge */
user-select: none;
/* Non-prefixed version, currently
supported by Chrome, Edge, Opera and Firefox */
}
.panel-wrap {
position: fixed;
top: 0;
bottom: 0;
right: 0;
height: 100%;
min-width: 20vw;
transform: translateX(100%);
transition: .3s ease-out;
pointer-events: all;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
overflow-y: auto;
overflow-x: hidden;
background-color: rgba(30, 29, 34, 0.5)
}
.panel-wrap-visible {
transform: translateX(0%);
}
.panel {
color: #eee;
overflow-y: auto;
padding: 1em;
}
#heading {
display: inline-block;
font-size: 2em;
margin-block-start: 0.67em;
margin-block-end: 0.67em;
margin-inline-start: 0px;
margin-inline-end: 0px;
position: relative;
padding: 0 0 0 2rem;
}
#close {
margin: 0.5rem;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
padding-right: 0.5rem;
font-size: 2em;
float: right;
}
#close:after {
padding-left: 0.5rem;
display: inline-block;
content: "\00d7";
/* This will render the 'X' */
}
#close:hover {
color: var(--colour3);
transition: ease 0.3s;
}
#content {
margin: 2rem;
}
.setting {
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 0;
margin: 0.5rem 0;
}
.settings-text {
margin-right: 2rem;
display: flex;
}
/*** Toggle Switch styles ***/
.tgl-switch {
vertical-align: middle;
display: inline-block;
}
.tgl-switch .tgl {
display: none;
}
.tgl,
.tgl:after,
.tgl:before,
.tgl *,
.tgl *:after,
.tgl *:before,
.tgl+.tgl-slider {
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.tgl::-moz-selection,
.tgl:after::-moz-selection,
.tgl:before::-moz-selection,
.tgl *::-moz-selection,
.tgl *:after::-moz-selection,
.tgl *:before::-moz-selection,
.tgl+.tgl-slider::-moz-selection {
background: none;
}
.tgl::selection,
.tgl:after::selection,
.tgl:before::selection,
.tgl *::selection,
.tgl *:after::selection,
.tgl *:before::selection,
.tgl+.tgl-slider::selection {
background: none;
}
.tgl+.tgl-slider {
outline: 0;
display: block;
width: 40px;
height: 18px;
position: relative;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.tgl+.tgl-slider:after,
.tgl+.tgl-slider:before {
position: relative;
display: block;
content: "";
width: 50%;
height: 100%;
}
.tgl+.tgl-slider:after {
left: 0;
}
.tgl+.tgl-slider:before {
display: none;
}
.tgl-flat+.tgl-slider {
padding: 2px;
-webkit-transition: all .2s ease;
transition: all .2s ease;
background: var(--colour6);
border: 3px solid var(--colour7);
border-radius: 2em;
}
.tgl-flat+.tgl-slider:after {
-webkit-transition: all .2s ease;
transition: all .2s ease;
background: var(--colour7);
content: "";
border-radius: 1em;
}
.tgl-flat:checked+.tgl-slider {
border: 3px solid var(--colour3);
}
.tgl-flat:checked+.tgl-slider:after {
left: 50%;
background: var(--colour3);
}
.subtitle-text {
margin: 0 0 0 1rem;
color: var(--colour5);
position: relative;
}
.form-group {
padding-top: 4px;
display: grid;
grid-template-columns: 50% 50%;
row-gap: 4px;
padding-right: 10px;
padding-left: 10px;
}
.form-group label {
color: var(--colour2);
vertical-align: middle;
font-weight: normal;
}
#stats {
margin-left: 1rem;
}
#LatencyStats {
margin-left: 1rem;
}
#hiddenInput {
position: absolute;
left: -10%;
/* Although invisible, push off-screen to prevent user interaction. */
width: 0px;
opacity: 0;
}
#editTextButton {
position: absolute;
height: 40px;
width: 40px;
}
.form-group label {
margin-right: 2rem;
min-width: 75%;
}
input {
text-align: right;
}
.warning {
box-sizing: border-box;
position: relative;
transform: scale(var(--ggs, 1));
width: 20px;
height: 20px;
border: 2px solid;
border-radius: 40px;
display: none;
}
.warning::after,
.warning::before {
content: "";
display: block;
box-sizing: border-box;
position: absolute;
border-radius: 3px;
width: 2px;
background: currentColor;
left: 7px
}
.warning::after {
top: 2px;
height: 8px
}
.warning::before {
height: 2px;
bottom: 2px
}
/* Flat buttons */
input[type="button"] {
background-color: transparent;
color: var(--colour2);
font-family: 'Montserrat';
border: 3px solid var(--colour3);
border-radius: 1rem;
font-size: 0.75rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
}
input[type="button"]:hover {
background-color: var(--colour3);
transition: ease 0.3s;
}
input[type="button"]:active {
background-color: transparent;
}
#encoder-params-submit,
#webrtc-params-submit {
text-align: center;
}
select,
input[type="number"] {
background-color: var(--colour7);
color: var(--colour2);
border: 1px solid var(--colour6);
padding: 0.25rem;
font-family: 'Montserrat';
border-radius: 0.25rem;
}
input[type=number]::-webkit-inner-spin-button {
margin-left: 0.5rem;
}
input[type="number"]:disabled {
padding-right: 0.5rem;
-moz-appearance: textfield;
}
input[type=number]:disabled::-webkit-inner-spin-button {
display: none;
}
#settingsBtn,
#statsBtn {
cursor: pointer;
}
#streamingVideo {
pointer-events: all;
}
embed {
border: none;
width: 100%;
height: 100%;
}
g {
fill: var(--colour2);
}
object {
pointer-events: none;
}
#connectionStrength {
fill: var(--colour7);
}
#minimize {
display: none;
}
#afkOverlay {
z-index: 999;
background-color: rgba(30, 29, 34, 0.5);
display: inline-block;
height: 100vh;
width: 100vw;
line-height: 100vh;
text-align: center;
overflow: hidden;
}
#afkOverlay center {
display: inline-block;
line-height: 1.5;
height: 100vh;
}
@@ -2,13 +2,14 @@ import "./PlayerStyles.css";
import React, { useEffect, useState, useRef } from "react";
import { useHistory, useParams } from "react-router-dom";
import useWindowDimensions from "hooks/useWindowDimensions";
import { load } from "utils/app";
import useMobile from "hooks/useMobile";
import { Sidebar } from "components/pages/Stream/Sidebar/Sidebar";
import { connectSession } from "store/reducers/ActionCreator";
import { useAppDispatch, useAppSelector } from "hooks/redux";
import { sessionSlice } from "store/reducers/sessionSlice";
import { Player } from "../Player/Player";
type link = {
id: string;
@@ -31,6 +32,7 @@ export const PlayerComponent: React.FC<any> = ({ closeStream }) => {
useEffect(() => {
dispatch(connectSession(id)).then((res: any) => {
load()
if (res.error) {
alert(res.payload);
}
@@ -68,17 +70,12 @@ export const PlayerComponent: React.FC<any> = ({ closeStream }) => {
<h2>Переверните устройство</h2>
</div>
)}
<iframe
ref={frameRef}
onLoad={(e: any) => e.target.focus()}
id="player"
onBlur={(e) => e.target.focus()} /// element loosing focus and keyboard input doesn't work
src={url}
className={"player playerOn"}
security={""}
allowFullScreen={true}
></iframe>
<div id="playerUI" className="noselect">
<div id="player"></div>
<div id="overlay">
</div>
</div>
<Player></Player>
<Sidebar
heightDevice={height}
isMobile={isMobile}