Closed rbr-goodrich closed 2 years ago
This is what i've done to the browser client code.
I either am messing up by calling the "connect" function to many times, or this backend server simply cant handle 12 Simultaneous Feeds.
`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>WebRTSP</title>
<link rel="stylesheet" href="css/WebRTSP.css">
<script type="text/javascript" src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script type="text/javascript" src="Config.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/spin.js/4.1.0/spin.css">
<style>
@keyframes spinner-line-fade-more {
0%, 100% {
opacity: 0; /* minimum opacity */
}
1% {
opacity: 1;
}
}
@keyframes spinner-line-fade-quick {
0%, 39%, 100% {
opacity: 0.25; /* minimum opacity */
}
40% {
opacity: 1;
}
}
@keyframes spinner-line-fade-default {
0%, 100% {
opacity: 0.22; /* minimum opacity */
}
1% {
opacity: 1;
}
}
@keyframes spinner-line-shrink {
0%, 25%, 100% {
/* minimum scale and opacity */
transform: scale(0.5);
opacity: 0.25;
}
26% {
transform: scale(1);
opacity: 1;
}
}
video {
z-index: -1;
}
body{
overflow: hidden;
}
.fullScreenSwal{
height:720px;
}
.camDivs{
display:inline-block;
margin:0;
padding: 0 0;
}
.howdy > video{
float:left;
}
.banner {
display: none;
}
video:hover + .banner, .banner:hover {
display: inline-block;
}
video::-webkit-media-controls-timeline {display: none;}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="mainCont" class="howdy"></div>
<script>
entireScreenWidth = screen.width
d4Width = $("#mainCont").width()
var fswiwidth = d4Width;
var fswiheight = d4Width;
function float2int (value) {
return Math.abs(value | 0);
}
var cams = ["Bars","Price Center Plaza","Konský Grúň","Štrbské pleso", "Curacao", "Bedford Hills", "Norwich", "Montana", "Western Cape", "Nordland"] ;
var numCams = cams.length - 1;
var cam_expand = numCams;
let server = null;
if(window.location.protocol === 'http:'){
server = `ws://${window.location.hostname}:${WebRTSPPort}/`;
}else{
server = `wss://${window.location.hostname}:${WebRTSPPort}/`;
}
const stunServer =
(typeof STUNServer === "string") ? STUNServer : "stun:stun.l.google.com:19302"
let streamersList = undefined;
function isOdd(num) { return num % 2;}
function camPop(amt, gen_info){
var totals;
if (amt == 0){
totals = 2
}
else
{
totals = Math.sqrt(next_sq) *2;
}
if(amt > 0 && Math.sqrt(amt) % 1 === 0){
var next_sq = amt;
}
else{
var next_sq = Math.pow(Math.floor(Math.sqrt(amt))+1, 2);
}
totals = Math.sqrt(next_sq) *2;
for(var i =0; i< next_sq; i++)
{
container = document.getElementById('mainCont');
element = document.createElement('div');
element.className = 'camDivs';
var w = float2int(fswiwidth/Math.sqrt(next_sq));
var h = float2int(fswiheight/totals);
if(i < cams.length)
{
element.id = "videoContainer"+i;
element.innerHTML =
`<video controls controlsList="nodownload" id="video`+i+`" autoplay="true" muted="muted" style="width:`+w+`px; height:`+h+`px; object-fit: fill;" ></video>`;
container.appendChild(element);
camDivs = document.getElementsByClassName("camDivs");
camDivs[i].style = "width:calc(100%/"+Math.sqrt(next_sq)+"); height:calc(100%/"+Math.sqrt(next_sq)+");";
onLoad(i, gen_info);
let vid = document.getElementById('video'+i);
let vidc = document.getElementById('videoContainer'+i);
vid.setAttribute("height",vidc.getBoundingClientRect().height);
}
else
{
element.id = "videoContainer"+i;
element.innerHTML =
`<video controls controlsList="nodownload" id="video`+i+`" autoplay="true" muted="muted" style="width:`+w+`px; height:`+h+`px; object-fit: fill;" ></video>`;
container.appendChild(element);
camDivs = document.getElementsByClassName("camDivs");
camDivs[i].style = "width:calc(100%/"+Math.sqrt(next_sq)+"); height:calc(100%/"+Math.sqrt(next_sq)+");";
let vid = document.getElementById('video'+i);
let vidc = document.getElementById('videoContainer'+i);
vid.height = vidc.getBoundingClientRect().height;
}
}
}
function connect(streamerName, webrtsp) {
str = JSON.stringify(streamerName);
if(streamerName)
{
document.title = streamerName;
}
webrtsp.connect(server, streamerName);
}
async function onLoad(i, gen_info) {
let Spin = await import("https://cdnjs.cloudflare.com/ajax/libs/spin.js/4.1.0/spin.min.js");
let WebRTSP = await import("./WebRTSP.mjs");
var webrtsp ;
var videoContainer =
document.querySelector("#videoContainer"+i);
var videoElement =
document.querySelector("#video"+i);
contHeight = $(videoContainer).height();
var opts = {
lines: 6, // The number of lines to draw
length: 0, // The length of each line
width: 5, // The line thickness
radius: 15, // The radius of the inner circle
scale: 1, // Scales overall size of the spinner
corners: 1, // Corner roundness (0..1)
speed: 1.2, // Rounds per second
rotate: 0, // The rotation offset
animation: 'spinner-line-fade-quick', // The CSS animation name for the lines
direction: 1, // 1: clockwise, -1: counterclockwise
color: '#ffffff', // CSS color or array of colors
fadeColor: 'transparent', // CSS color or array of colors
top: String(contHeight/2) + "px", // Top position relative to parent
left: '50%', // Left position relative to parent
shadow: '0 0 1px transparent', // Box-shadow for the lines
zIndex: 2000000000, // The z-index (defaults to 2e9)
className: 'spinner', // The CSS class to assign to the spinner
position: 'relative', // Element positioning
};
var spinner = new Spin.Spinner(opts);
webrtsp = new WebRTSP.WebRTSP(
videoElement,
[{
urls: [stunServer]
}]);
webrtsp.events.addEventListener("list", (event) => {
console.log(Array.from(event.detail.list.keys()))
console.log(Array.from(event.detail.list.keys())[i], i)
webrtsp.streamerName = Array.from(event.detail.list.keys())[i];
});
webrtsp.events.addEventListener("disconnected", (event) => {
spinner.spin(videoContainer);
});
videoElement.addEventListener('playing', (event) => {
spinner.stop()
});
spinner.spin(videoContainer);
connect(null, webrtsp);
}
camPop(numCams, cam_expand);
</script>
</html>
`
I think the most possible reason is limited power of source you are trying to restream (i.e. source ip cam). The main problem of current implementation it creates separate connection to source for every restream session. I.e. if 10 users watch stream from the same ip cam - there will be 10 simulations connections to that ip cam. Right now I'm trying to solve this issue.
So, If a config file has 10 different RTSP feeds in it. Viewing all 10 simultaneously like the client code posted above, will result in 10 connections to each RTSP feed, instead of one to each ?
no, in this case there will be only one connection to each.
Okay, that's what i thought.
I believe there may be an issue with streaming each RTSP Feed simultaneously. As viewing them one at a time is fine, and smooth. However trying to view each one simultaneously like the client code posted above results in a long load time (some do not load), and stale feeds.
Unless you see a mistake in the way i request the feed on the client end, there must be some kind of limitation in the websocket server.
Can you please check CPU load on host with browser and on host with restreamer?
I've updated my example above, it is now plug and play replace index.html with it. You'll see what i mean.
I have 8 cpu cores ranging from 35-50% when serving and viewing on the same machine.
Your sources are fairing a bit better than my ip cams, but the effect still occurs.
Some do not load, and some are choppy, while others go in and out of service, but if you view them one by one like in your original index.html they all appear to be fine.
I'd like to add one more thing.
This error, also occurs when viewing the feeds with the above client code.
[2021-09-28 18:50:41.410] [libwebsockets] [error] accept: errno 24
Followed by the inevitable crash with the message:
(ReStreamer:3694): GLib-ERROR **: 18:50:41.447: Creating pipes for GWakeup: Too many open files
You're getting these errors too right ? Or is it just me ?
The program crashes in a similar manner to this
I didn't see you using gst_bus_remove_signal_watch()
to remove the bus signal anywhere.
Not sure, if this will improve the simultaneous live feeds. It should prevent the program from crashing though.
Thank you for testing! I'll investigate it soon.
@rbr-goodrich you was right. There was Unix socket leak from missing gst_bus_remove_watch
. Thank you for hint!
Also I've found mentions about possible limitations for amount of simultaneously playing videos on the same page: https://stackoverflow.com/a/26216249/3847832 Maybe it's your case.
Excellent ! Glad to have assisted.
As for the multiple live stream issue, after many sanity checks i've boiled it down to a corporate firewall issue. The ReStreamer Server i'm testing is behind a symmetrical NAT, and for whatever reason is blocking more than 4 STUN requests to a remote client. The weird thing is, your sources are now playing just fine, i can stream all of them at once.
Its only when i connect to my IP cameras that i cannot remotely stream more than 4 at once. When i'm on the local network everything is fine, and all the sources play simultaneously.
I don't know why there is a difference, but it must be down the origin point, of the source that ReStreamer is serving to the client, or maybe i misconfigured my turnserver.
Unless you have any tips, i'm going to close the issue.
Thank you for fixing the socket leak, and feel free to include that feed wall as an example along with index.html !
@rbr-goodrich could you please create PR to https://github.com/WebRTSP/www with your wall example? I just want save your copyright and confirm licence (WebRTSP/www defined as BSD 2-Clause). Thanks in advance!
Hello,
I was wondering what the Maximum Simultaneous Stream capacity is for this project ?
I've modified the browser client to produce a feed wall instead of showing just a single feed, but the performance is severely degraded after 4+ feeds being shown at the same time. (Meaning there is a long initial connection time, and choppy feeds once they finally show up)
I know its not a bandwidth issue as i have 400MB/s upload. I also have a custom stun/turn server, but using the default google one shows similar results.
Do you experience similar limitations ?