openBridgeChannel: 'websocket' degrades video quality

Did you actually have working WebSockets? Part of the communication used through the bridge channel is to select the received video quality for each participant. If they don’t work you may not “upgrade” to HD when the active specaker changes. You’ll get 240p for everyone, which is the thumbnail size, but stretched out, hence the perception of poor quality.

Why ddidd you change the bridge channel mode?

I thought I read somewhere on the forum that it was recommened, so I thought I would give it a try. As far as I could tell, websockets was working. its no big deal to me, I just thought Id report it here. If I’m the only one with the issue, than its likely a me issue only! :smiley:

In order to use WebSockets you need to also configure the JVB, since that’s where the connection will be established. Did you do that? Do you see any errors in the JS console about the bridge channel being closed?

I believe so. I uncommented the websocket: line, and enabled websockets on the prosody side. I was given the hint to try updated bosh: url to wss to see if that made any difference. is there something else I may be missing. I’ll reconfig today and see if I there are any errors in JS console

@saghul
Here are the only errors I’m seeing.

Logger.js:154 2020-04-05T14:54:53.579Z [JitsiConference.js] <e.sendMessage>:  Failed to send E2E ping request or response. undefined
o @ Logger.js:154
(anonymous) @ JitsiConference.js:337
value @ e2eping.js:92
setInterval (async)
e @ e2eping.js:62
value @ e2eping.js:298
c.emit @ events.js:151
oe.onMemberJoined @ JitsiConference.js:1487
c.emit @ events.js:146
value @ ChatRoom.js:545
value @ strophe.emuc.js:103
run @ strophe.umd.js:1875
(anonymous) @ strophe.umd.js:3157
forEachChild @ strophe.umd.js:830
_dataRecv @ strophe.umd.js:3146
_onMessage @ strophe.umd.js:5836

Logger.js:154 2020-04-05T14:55:10.607Z [modules/xmpp/XmppConnection.js] <t.value>:  Scheduling next WebSocket keep-alive in 95066.1905758738ms
Logger.js:154 2020-04-05T14:55:12.578Z [JitsiConference.js] <e.sendMessage>:  Failed to send E2E ping request or response. undefined

Hi speedy01,
Do you have enable the websocket proxy to videobridge in your nginx configuration like describe here : https://github.com/jitsi/jitsi-videobridge/blob/master/doc/web-sockets.md.
You talk about prosody configuration but openbridgechannel is not related to prosody.
There are 2 different communication channel which can be configured to use websocket :

  • client to prosody connexion (bosh or websocket)
  • client to videobridge connexion (stcp or websocket)

Which one have you configured ?

gotcha. this may be my problem. Is there a benefit to using websockets over stcp on the videobridge?

I don’t know what is the current recommendation with jvb2.
For the old bridge websocket was more stable than stcp (stcp could crash the v1 of videobridge in some corner case).

Hello guys,

I’m trying to figure out the websocket implementation.
I followed the guide and edited the videobridge2 sip-communicator-properties.
Also edited the nginx config (as you can see below) and the my.domain.config.js in meet.

Everything is on the same machine (installed the default jitsi-meet with apt-install).

When trying to connect to a room, i can see some errors in the Chrome’s console:
Error during WebSocket handshake: Unexpected response code: 200

Searched for this issue and found something here and setted everything like that.

Here is my nginx.conf (/etc/nginx/sites-enabled/my.domain.conf):

server_names_hash_bucket_size 64;

server {
    listen 80;
    listen [::]:80;
    server_name my.domain;

    location ^~ /.well-known/acme-challenge/ {
       default_type "text/plain";
       root         /usr/share/jitsi-meet;
    }
    location = /.well-known/acme-challenge/ {
       return 404;
    }
    location / {
       return 301 https://$host$request_uri;
    }
}
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name my.domain;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA256:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EDH+aRSA+AESGCM:EDH+aRSA+SHA256:EDH+aRSA:EECDH:!aNULL$

    add_header Strict-Transport-Security "max-age=31536000";

    ssl_certificate /etc/letsencrypt/live/my.domain/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/my.domain/privkey.pem;

    root /usr/share/jitsi-meet;

    # ssi on with javascript for multidomain variables in config.js
    ssi on;
    ssi_types application/x-javascript application/javascript;

    index index.html index.htm;
    error_page 404 /static/404.html;

    gzip on;
    gzip_types text/plain text/css application/javascript application/json;
    gzip_vary on;

    location = /config.js {
        alias /etc/jitsi/meet/my.domain-config.js;
    }

    location = /external_api.js {
        alias /usr/share/jitsi-meet/libs/external_api.min.js;
    }

    #ensure all static content can always be found first
    location ~ ^/(libs|css|static|images|fonts|lang|sounds|connection_optimization|.well-known)/(.*)$
    {
        add_header 'Access-Control-Allow-Origin' '*';
        alias /usr/share/jitsi-meet/$1/$2;
    }

    # BOSH
    location = /http-bind {
        proxy_pass      http://localhost:5280/http-bind;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host $http_host;
    }

    # xmpp websockets
    location = /xmpp-websocket {
        proxy_pass http://127.0.0.1:5280/xmpp-websocket?prefix=$prefix&$args;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host my.domain;
        tcp_nodelay on;
    }

    location ~ ^/([^/?&:'"]+)$ {
        try_files $uri @root_path;
    }
 location @root_path {
        rewrite ^/(.*)$ / break;
    }

    location ~ ^/([^/?&:'"]+)/config.js$
    {
       set $subdomain "$1.";
       set $subdir "$1/";

       alias /etc/jitsi/meet/my.domain-config.js;
    }

    #Anything that didn't match above, and isn't a real file, assume it's a room name and redirect to /
    location ~ ^/([^/?&:'"]+)/(.*)$ {
        set $subdomain "$1.";
        set $subdir "$1/";
        rewrite ^/([^/?&:'"]+)/(.*)$ /$2;
    }
    # BOSH for subdomains
    location ~ ^/([^/?&:'"]+)/http-bind {
        set $subdomain "$1.";
        set $subdir "$1/";
        set $prefix "$1";

        rewrite ^/(.*)$ /http-bind;
    }

    # websockets for subdomains
    location ~ ^/([^/?&:'"]+)/xmpp-websocket {
        set $subdomain "$1.";
        set $subdir "$1/";
        set $prefix "$1";

        rewrite ^/(.*)$ /xmpp-websocket;
    }

      # colibri (JVB) websockets for jvb1
   location ~ ^/colibri-ws/jvb1/(.*) {
       proxy_pass http://127.0.0.1:9090/colibri-ws/jvb1/$1$is_args$args;
       proxy_http_version 1.1;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection "upgrade";
       tcp_nodelay on;
       proxy_set_header Host my.domain;
   }

}

And here the JVB2 sip-communicator:

org.ice4j.ice.harvest.DISABLE_AWS_HARVESTER=true
org.ice4j.ice.harvest.STUN_MAPPING_HARVESTER_ADDRESSES=meet-jit-si-turnrelay.jitsi.net:443
org.jitsi.videobridge.ENABLE_STATISTICS=true
org.jitsi.videobridge.STATISTICS_TRANSPORT=muc
org.jitsi.videobridge.xmpp.user.shard.HOSTNAME=my.domain
org.jitsi.videobridge.xmpp.user.shard.DOMAIN=auth.my.domain
org.jitsi.videobridge.xmpp.user.shard.USERNAME=jvb
org.jitsi.videobridge.xmpp.user.shard.PASSWORD=5tSPZ0ar
org.jitsi.videobridge.xmpp.user.shard.MUC_JIDS=JvbBrewery@internal.auth.my.domain
org.jitsi.videobridge.xmpp.user.shard.MUC_NICKNAME=a08c909c-c358-4549-a966-a381bdfaa69a

org.jitsi.videobridge.rest.jetty.port=9090
org.jitsi.videobridge.rest.COLIBRI_WS_TLS=true
org.jitsi.videobridge.rest.COLIBRI_WS_DOMAIN=my.domain:443
1 Like

Not sure if this is the same thing but I also faced the same issue…
I solved it by moving the collibri-ws proxy configuration in nginx config from bottom to after xmpp-websocket. It seemed to work for me. So the new nginx conf in your case would be:

 server_names_hash_bucket_size 64;    
server {
        listen 80;
        listen [::]:80;
        server_name my.domain;

        location ^~ /.well-known/acme-challenge/ {
           default_type "text/plain";
           root         /usr/share/jitsi-meet;
        }
        location = /.well-known/acme-challenge/ {
           return 404;
        }
        location / {
           return 301 https://$host$request_uri;
        }
    }
    server {
        listen 443 ssl;
        listen [::]:443 ssl;
        server_name my.domain;

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;
        ssl_ciphers "EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA256:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EDH+aRSA+AESGCM:EDH+aRSA+SHA256:EDH+aRSA:EECDH:!aNULL$

        add_header Strict-Transport-Security "max-age=31536000";

        ssl_certificate /etc/letsencrypt/live/my.domain/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/my.domain/privkey.pem;

        root /usr/share/jitsi-meet;

        # ssi on with javascript for multidomain variables in config.js
        ssi on;
        ssi_types application/x-javascript application/javascript;

        index index.html index.htm;
        error_page 404 /static/404.html;

        gzip on;
        gzip_types text/plain text/css application/javascript application/json;
        gzip_vary on;

        location = /config.js {
            alias /etc/jitsi/meet/my.domain-config.js;
        }

        location = /external_api.js {
            alias /usr/share/jitsi-meet/libs/external_api.min.js;
        }

        #ensure all static content can always be found first
        location ~ ^/(libs|css|static|images|fonts|lang|sounds|connection_optimization|.well-known)/(.*)$
        {
            add_header 'Access-Control-Allow-Origin' '*';
            alias /usr/share/jitsi-meet/$1/$2;
        }

        # BOSH
        location = /http-bind {
            proxy_pass      http://localhost:5280/http-bind;
            proxy_set_header X-Forwarded-For $remote_addr;
            proxy_set_header Host $http_host;
        }

        # xmpp websockets
        location = /xmpp-websocket {
            proxy_pass http://127.0.0.1:5280/xmpp-websocket?prefix=$prefix&$args;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Host my.domain;
            tcp_nodelay on;
        }
    # colibri (JVB) websockets for jvb1
       location ~ ^/colibri-ws/jvb1/(.*) {
           proxy_pass http://127.0.0.1:9090/colibri-ws/jvb1/$1$is_args$args;
           proxy_http_version 1.1;
           proxy_set_header Upgrade $http_upgrade;
           proxy_set_header Connection "upgrade";
           tcp_nodelay on;
           proxy_set_header Host my.domain;
       }

        location ~ ^/([^/?&:'"]+)$ {
            try_files $uri @root_path;
        }
     location @root_path {
            rewrite ^/(.*)$ / break;
        }

        location ~ ^/([^/?&:'"]+)/config.js$
        {
           set $subdomain "$1.";
           set $subdir "$1/";

           alias /etc/jitsi/meet/my.domain-config.js;
        }

        #Anything that didn't match above, and isn't a real file, assume it's a room name and redirect to /
        location ~ ^/([^/?&:'"]+)/(.*)$ {
            set $subdomain "$1.";
            set $subdir "$1/";
            rewrite ^/([^/?&:'"]+)/(.*)$ /$2;
        }
        # BOSH for subdomains
        location ~ ^/([^/?&:'"]+)/http-bind {
            set $subdomain "$1.";
            set $subdir "$1/";
            set $prefix "$1";

            rewrite ^/(.*)$ /http-bind;
        }

        # websockets for subdomains
        location ~ ^/([^/?&:'"]+)/xmpp-websocket {
            set $subdomain "$1.";
            set $subdir "$1/";
            set $prefix "$1";

            rewrite ^/(.*)$ /xmpp-websocket;
        }

    }

because there’s an nginx rule in your configuration which is superceding the websocket configuration. Hopefully this helps. Thank you

2 Likes

@Damien_FETIS @saghul

to enable websocket we need to add each jvb in ngnix config so I think in this case we can’t do autoscaling of jvb correct?

What is a default value of openBridgeChannel and which value can provide better result?

I have noticed many times video becomes pixelated from chrome browser in desktop and mobile devices but video Which coming from mobile is looking very good! I understand there is difference between quality of camera in webcam and mobile phone but from the browser it usually pixelated even in mobile browser.

Same experience with meet.ji.si and self-hosted but I have seen some of the website which has Jitsi web app installed & in the config file it has resolution 480p & it’s works great!

So please if tell us any advice if you know regarding to settings so picture get less pixelated, I also don’t want to disable simulcast because it’s saves the server’s bandwidth.

Thanks

There is a solution for this. Use JVB’s IP as server-id and catch it on Nginx site config.
Check this Nginx location block

1 Like

So @emrah this way can use websocket and also take benifit of autoscaling right?

@jallamsetty do you know websocket can really helps in improving video quality?

What is the best settings for getting better quality in the browser just like mobile app.
Quality of mobile app is so good in custom installation and meet.jit.si why web browser is not performing that much good!!

I think disableSimulcast: false,
enableLayerSuspension: true,

Should give best result because in that case there should be minimum utilisation of bandwidth.

yes

Hi @pratik ,
The other way to do autoscaling with the colibri websocket is to not use the nginx has traffic proxy and let the user websocket connects directly to each JVB.
It can be done if you have a domain name per JVB and if you can generate an SLL certificate for each JVB (or use a wildcard certificate).
It reduces the cpu and trafic load on the nginx.
It seems this solution is the one used by meet.jit.si.
Regards,
Damien.

1 Like

Thanks for the response I think generating SSL for each JVB becomes more complicated because as server load decrease we also have to reduce the number of JVB automatically so we are generating SSL each time whenever a new JVB is created.

@jallamsetty @saghul @Damien_FETIS
The main thing is how much the Websocket is beneficial over the default setting? does it improve the video calling in the web browser?

@pratik, the type of bridgeChannel does not have any impact on quality. The client needs a proper working bridge channel to communicate quality requirements with the bridge, be it websockets or sctp datachannel. We did migrate to using websockets by default so I suggest you get this working properly as support for sctp datachannel will go away in the future.

If you are seeing poor quality, try these settings and see if that makes any difference. Also do you have resolution set to 480p in your config.js ?
enableLayerSuspension: false
disableSimulcast: false.

so If I do quick installation today then quick installation comes with enabled websockets, & I don’t need to do anything about it correct? and I can add multiple JVB by following this instructions

disableSimulcast : false means sending the highest possible 1 good quality video of all participants to the server and then to every participant.

Off-stage layer suppression lets the client only send the streams that are being viewed at a given time, reducing CPU and bandwidth consumption on both client-side and server-side while improving video quality
so if Simulcast disable then enableLayerSuspension: true or false in both cases it will take only one high-quality video from the client and sends it to the server. so why we need to enableLayerSuspension: false, because it doesn’t do anything?

if disableSimulcast : false
and enableLayerSuspension: true

is making sense to me but not this

enableLayerSuspension: false
disableSimulcast: false.

by default, both the settings are committed so what happens in that situation.

after I disableSimulcast:true I’m getting a little bit better quality but I think it will increase the bandwidth consumption so this is not a permanent solution. and that’s why I have decreased the fps value

maxFps:13,
resolution: 720,
constraints: {
video: {
frameRate: 13,
height: {
ideal: 720,
max: 720,
min: 720
}
}
},

I have another question. Is it possible to make the only P2P in full HD 1080p & if 2+ then it will again follow rules like 720p or 480p.

and enableOpusRed : true will improve the audio quality? supported by all browsers?

by the way, thank you for being that much active on Github, your contribution & efforts.

I am not sure if quick installation does websockets by default for bridge channel, maybe @damencho knows ?

I am trying to see if enabling layer suspension is what is causing the endpoints to receive lower resolution.
disableSimulcast : false means simulcast is enabled so multiple streams are sent up the bridge from the client and the bridge then decides what resolution to forward to the downstream clients based on resolution requested by those endpoints and their available downlink.
What layer suspension does is that based on the feedback received from the bridge, client suspends sending the higher resolution streams if the bridge does not request it. Like if everyone is in tile view, then there is no need to send 720p resolution stream.

We don’t have a way to accomplish this right now, the video constraints specified in config.js are applied to both p2p and jvb connections.

Yes, this will improve audio quality, it will be more resilient to network issues. The client code runs a check and enables it only when the browser supports it.

2 Likes

Websockets for the bridge are by default now.

1 Like