mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-01-06 14:52:28 +00:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01deadf078 | ||
|
|
6eaa3cd45d | ||
|
|
b0d2a79873 | ||
|
|
d94f001f25 | ||
|
|
afb85e2fd9 | ||
|
|
fffb5801c5 | ||
|
|
d81cd20ee6 | ||
|
|
3e7a9228bc | ||
|
|
835e199135 | ||
|
|
1d660e1883 | ||
|
|
5746261961 | ||
|
|
cbeae8eb30 | ||
|
|
95b2752d2a | ||
|
|
e3da472e7a | ||
|
|
43f60ca336 | ||
|
|
118a61c416 | ||
|
|
bf99a129bd | ||
|
|
fb6ad8cffd | ||
|
|
21fef57bc4 | ||
|
|
777422c87d | ||
|
|
ee6fd63c25 | ||
|
|
b9f00b71b2 | ||
|
|
099e3340bc | ||
|
|
172c2d3d71 | ||
|
|
854c8e5f2f | ||
|
|
b2cff193a9 | ||
|
|
ad1772178d | ||
|
|
0959b3d5b8 | ||
|
|
36f91f7f1e | ||
|
|
2c9d0606c3 | ||
|
|
1ce22fb8c9 | ||
|
|
e0cba855a6 | ||
|
|
8af3a65d37 | ||
|
|
667f67376e | ||
|
|
ce7b6be024 | ||
|
|
57cd2647f3 | ||
|
|
efcfe99707 | ||
|
|
cc1ad1bc13 | ||
|
|
29f06829e7 | ||
|
|
0fdf5e0102 |
2
app.js
2
app.js
@@ -37,6 +37,8 @@ function init() {
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
var URLPRocessor = require("./modules/URLProcessor/URLProcessor");
|
||||
URLPRocessor.setConfigParametersFromUrl();
|
||||
APP.init();
|
||||
|
||||
APP.translation.init();
|
||||
|
||||
@@ -47,3 +47,21 @@
|
||||
#languages_selectbox{
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
|
||||
#startMutedOptions {
|
||||
padding-left: 10%;
|
||||
text-indent: -10%;
|
||||
}
|
||||
|
||||
#startAudioMuted {
|
||||
width: 13px !important;
|
||||
}
|
||||
|
||||
#startVideoMuted {
|
||||
width: 13px !important;
|
||||
}
|
||||
|
||||
.startMutedLabel {
|
||||
float: left;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Server Installation for Jitsi Meet
|
||||
|
||||
This describes configuring a server `jitsi.example.com`. You will need to
|
||||
This describes configuring a server `jitsi.example.com` running Debian or a Debian Derivative. You will need to
|
||||
change references to that to match your host, and generate some passwords for
|
||||
`YOURSECRET1`, `YOURSECRET2`, `YOURSECRET3` and `YOURSECRET4`.
|
||||
|
||||
There are also some complete [example config files](https://github.com/jitsi/jitsi-meet/tree/master/doc/example-config-files/) available, mentioned in each section.
|
||||
|
||||
## Install prosody and otalk modules
|
||||
## Install prosody
|
||||
```sh
|
||||
apt-get install lsb-release
|
||||
echo deb http://packages.prosody.im/debian $(lsb_release -sc) main | sudo tee -a /etc/apt/sources.list
|
||||
@@ -14,9 +14,6 @@ wget --no-check-certificate https://prosody.im/files/prosody-debian-packages.key
|
||||
apt-get update
|
||||
apt-get install prosody-trunk
|
||||
apt-get install git lua-zlib lua-sec-prosody lua-dbi-sqlite3 liblua5.1-bitop-dev liblua5.1-bitop0
|
||||
git clone https://github.com/andyet/otalk-server.git
|
||||
cd otalk-server
|
||||
cp -r mod* /usr/lib/prosody/modules
|
||||
```
|
||||
|
||||
## Configure prosody
|
||||
@@ -84,7 +81,7 @@ prosodyctl restart
|
||||
apt-get install nginx
|
||||
```
|
||||
|
||||
Add nginx config for domain in `/etc/nginx/nginx.conf`:
|
||||
Optionally, add nginx config for domain in `/etc/nginx/nginx.conf`:
|
||||
```
|
||||
tcp_nopush on;
|
||||
types_hash_max_size 2048;
|
||||
@@ -143,6 +140,8 @@ Install JRE if missing:
|
||||
apt-get install default-jre
|
||||
```
|
||||
|
||||
_NOTE: When installing on older Debian releases keep in mind that you need JRE >= 1.7._
|
||||
|
||||
In the user home that will be starting Jitsi Videobridge create `.sip-communicator` folder and add the file `sip-communicator.properties` with one line in it:
|
||||
```
|
||||
org.jitsi.impl.neomedia.transform.srtp.SRTPCryptoContext.checkReplay=false
|
||||
@@ -157,7 +156,15 @@ Or autostart it by adding the line in `/etc/rc.local`:
|
||||
/bin/bash /root/jitsi-videobridge-linux-{arch-buildnum}/jvb.sh --host=localhost --domain=jitsi.example.com --port=5347 --secret=YOURSECRET1 </dev/null >> /var/log/jvb.log 2>&1
|
||||
```
|
||||
|
||||
## Install Jitsi Conference Focus(jicofo)
|
||||
## Install Jitsi Conference Focus (jicofo)
|
||||
|
||||
Install JDK and Ant if missing:
|
||||
```
|
||||
apt-get install default-jdk ant
|
||||
```
|
||||
|
||||
_NOTE: When installing on older Debian releases keep in mind that you need JDK >= 1.7._
|
||||
|
||||
Clone source from Github repo:
|
||||
```sh
|
||||
git clone https://github.com/jitsi/jicofo.git
|
||||
@@ -202,54 +209,6 @@ Restart nginx to get the new configuration:
|
||||
invoke-rc.d nginx restart
|
||||
```
|
||||
|
||||
|
||||
## Install [Turn server](https://github.com/andyet/otalk-server/tree/master/restund)
|
||||
```sh
|
||||
apt-get install make gcc
|
||||
wget http://creytiv.com/pub/re-0.4.7.tar.gz
|
||||
tar zxvf re-0.4.7.tar.gz
|
||||
ln -s re-0.4.7 re
|
||||
cd re-0.4.7
|
||||
sudo make install PREFIX=/usr
|
||||
cd ..
|
||||
wget http://creytiv.com/pub/restund-0.4.2.tar.gz
|
||||
wget https://raw.github.com/andyet/otalk-server/master/restund/restund-auth.patch
|
||||
tar zxvf restund-0.4.2.tar.gz
|
||||
cd restund-0.4.2/
|
||||
patch -p1 < ../restund-auth.patch
|
||||
sudo make install PREFIX=/usr
|
||||
cp debian/restund.init /etc/init.d/restund
|
||||
chmod +x /etc/init.d/restund
|
||||
cd /etc
|
||||
wget https://raw.github.com/andyet/otalk-server/master/restund/restund.conf
|
||||
```
|
||||
|
||||
Configure addresses and ports as desired, and the password to be configured in prosody:
|
||||
```
|
||||
realm jitsi.example.com
|
||||
# share this with your prosody server
|
||||
auth_shared YOURSECRET4
|
||||
|
||||
# modules
|
||||
module_path /usr/lib/restund/modules
|
||||
turn_relay_addr [turn ip address]
|
||||
```
|
||||
|
||||
Configure prosody to use it in `/etc/prosody/prosody.cfg.lua`. Add to your virtual host:
|
||||
```
|
||||
turncredentials_secret = "YOURSECRET4";
|
||||
turncredentials = {
|
||||
{ type = "turn", host = "turn.address.ip.configured", port = 3478, transport = "tcp" }
|
||||
}
|
||||
```
|
||||
|
||||
Add turncredentials module in the "modules_enabled" section
|
||||
|
||||
Reload prosody if needed
|
||||
```
|
||||
prosodyctl restart
|
||||
```
|
||||
|
||||
## Running behind NAT
|
||||
In case of videobridge being installed on a machine behind NAT, add the following extra lines to the file `~/.sip-communicator/sip-communicator.properties` (in the home of user running the videobridge):
|
||||
```
|
||||
|
||||
14
index.html
14
index.html
@@ -10,7 +10,7 @@
|
||||
<meta itemprop="description" content="Join a WebRTC video conference powered by the Jitsi Videobridge"/>
|
||||
<meta itemprop="image" content="/images/jitsilogo.png"/>
|
||||
<script src="libs/jquery-2.1.1.min.js"></script>
|
||||
<script src="config.js?v=6"></script><!-- adapt to your needs, i.e. set hosts and bosh path -->
|
||||
<script src="config.js?v=8"></script><!-- adapt to your needs, i.e. set hosts and bosh path -->
|
||||
<script src="libs/strophe/strophe.min.js?v=1"></script>
|
||||
<script src="libs/strophe/strophe.disco.min.js?v=1"></script>
|
||||
<script src="libs/strophe/strophe.caps.jsonly.min.js?v=1"></script>
|
||||
@@ -19,7 +19,7 @@
|
||||
<script src="libs/popover.js?v=1"></script><!-- bootstrap tooltip lib -->
|
||||
<script src="libs/toastr.js?v=1"></script><!-- notifications lib -->
|
||||
<script src="interface_config.js?v=5"></script>
|
||||
<script src="libs/app.bundle.js?v=57"></script>
|
||||
<script src="libs/app.bundle.js?v=77"></script>
|
||||
<script src="analytics.js?v=1"></script><!-- google analytics plugin -->
|
||||
<link rel="stylesheet" href="css/font.css?v=7"/>
|
||||
<link rel="stylesheet" href="css/toastr.css?v=1">
|
||||
@@ -299,6 +299,16 @@
|
||||
<div class="arrow-up"></div>
|
||||
<input type="text" id="setDisplayName" data-i18n="[placeholder]settings.name" placeholder="Name">
|
||||
<input type="text" id="setEmail" placeholder="E-Mail">
|
||||
<div id = "startMutedOptions">
|
||||
<label class = "startMutedLabel">
|
||||
<input type="checkbox" id="startAudioMuted">
|
||||
<span data-i18n="settings.startAudioMuted"></span>
|
||||
</label>
|
||||
<label class = "startMutedLabel">
|
||||
<input type="checkbox" id="startVideoMuted">
|
||||
<span data-i18n="settings.startVideoMuted"></span>
|
||||
</label>
|
||||
</div>
|
||||
<button id="updateSettings" data-i18n="settings.update"></button>
|
||||
</div>
|
||||
<a id="downloadlog" data-container="body" data-toggle="popover" data-placement="right" data-i18n="[data-content]downloadlogs" ><i class="fa fa-cloud-download"></i></a>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"en": "",
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"tr": "",
|
||||
"it": ""
|
||||
"en": "Anglais",
|
||||
"bg": "Bulgare",
|
||||
"de": "Allemand",
|
||||
"tr": "Turc",
|
||||
"it": "Italien"
|
||||
}
|
||||
@@ -1,205 +1,227 @@
|
||||
{
|
||||
"contactlist": "",
|
||||
"connectionsettings": "",
|
||||
"poweredby": "",
|
||||
"downloadlogs": "",
|
||||
"roomUrlDefaultMsg": "",
|
||||
"participant": "",
|
||||
"me": "",
|
||||
"speaker": "",
|
||||
"defaultNickname": "",
|
||||
"defaultPreziLink": "",
|
||||
"contactlist": "Liste de contacts",
|
||||
"connectionsettings": "Paramètres de connexion",
|
||||
"poweredby": "propulsé par",
|
||||
"downloadlogs": "Téléchargement des logs",
|
||||
"roomUrlDefaultMsg": "Votre conférence est en cours de création...",
|
||||
"participant": "Participant",
|
||||
"me": "moi",
|
||||
"speaker": "Haut-parleur",
|
||||
"defaultNickname": "ex: __name__",
|
||||
"defaultPreziLink": "e.g. __url__",
|
||||
"welcomepage": {
|
||||
"go": "",
|
||||
"roomname": "",
|
||||
"disable": "",
|
||||
"go": "Créer",
|
||||
"roomname": "Saisissez un nom de salle",
|
||||
"disable": "Ne pas afficher cette page lors de ma prochaine visite",
|
||||
"feature1": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
"title": "Simple à utiliser",
|
||||
"content": "Aucun téléchargement requis. __app__ s'utilise directement depuis votre navigateur. Partager simplement l'URL de votre conférence avec les autres pour commencer."
|
||||
},
|
||||
"feature2": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
"title": "Faible bande passante",
|
||||
"content": "Les vidéo conférences à plusieurs participants nécessitent moins de 128 kbps. Le partage d'écran et les conférences avec seulement de l'audio sont possibles avec beaucoup moins de débit."
|
||||
},
|
||||
"feature3": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
"title": "Open source",
|
||||
"content": "__app__ est sous licence MIT. Vous êtes libre de le télécharger, l'utiliser, le modifier et le partager sous cette licence."
|
||||
},
|
||||
"feature4": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
"title": "Nombre d'utilisateurs illimité",
|
||||
"content": "Il n'y a pas de restrictions artificielles concernant le nombre d'utilisateurs ou de participants à une conférence. La puissance du serveur et la bande passante sont les seuls facteurs limitants."
|
||||
},
|
||||
"feature5": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
"title": "Partage d'écan",
|
||||
"content": "C'est facile de partager votre écran avec d'autres personnes. __app__ est idéal pour les présentations en ligne, les cours, et les sessions de support technique."
|
||||
},
|
||||
"feature6": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
"title": "Salles sécurisées",
|
||||
"content": "Besoin de confidentialité? Les salles de conférence __app__ peuvent être sécurisées par un mot de passe pour exclure des invités non désirées, et prévenir des interruptions. "
|
||||
},
|
||||
"feature7": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
"title": "Notes partagées",
|
||||
"content": "__app__ propose Etherpad, un éditeur de texte collaboratif en temps réel qui est parfait pour les procès-verbaux, l'édition d'articles et plus encore."
|
||||
},
|
||||
"feature8": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
"title": "Statistiques d'utilisation",
|
||||
"content": "Connaissez mieux vos utilisateurs avec une intégration facile de Piwik, Google Analytics et d'autres systèmes de statistiques et supervision d'utilisation."
|
||||
}
|
||||
},
|
||||
"toolbar": {
|
||||
"mute": "",
|
||||
"videomute": "",
|
||||
"mute": "Muet / Actif",
|
||||
"videomute": "Démarrer / Arrêter la caméra",
|
||||
"authenticate": "",
|
||||
"record": "",
|
||||
"lock": "",
|
||||
"invite": "",
|
||||
"record": "Enregistrer",
|
||||
"lock": "Verrouiller / déverrouiller la salle",
|
||||
"invite": "Inviter des participants",
|
||||
"chat": "",
|
||||
"prezi": "",
|
||||
"etherpad": "",
|
||||
"sharescreen": "",
|
||||
"fullscreen": "",
|
||||
"sip": "",
|
||||
"Settings": "",
|
||||
"hangup": "",
|
||||
"login": "",
|
||||
"prezi": "Partager une présentation Prezi",
|
||||
"etherpad": "Partager un document",
|
||||
"sharescreen": "Partager mon écran",
|
||||
"fullscreen": "Activer / Désactiver le plein écran",
|
||||
"sip": "Appeler un numéro SIP",
|
||||
"Settings": "Paramètres",
|
||||
"hangup": "Raccrocher",
|
||||
"login": "Connexion",
|
||||
"logout": ""
|
||||
},
|
||||
"bottomtoolbar": {
|
||||
"chat": "",
|
||||
"filmstrip": "",
|
||||
"contactlist": ""
|
||||
"chat": "Ouvrir / fermer le chat",
|
||||
"filmstrip": "Montrer / cacher ma vidéo miniature",
|
||||
"contactlist": "Ouvrir / fermer ma liste de contacts"
|
||||
},
|
||||
"chat": {
|
||||
"nickname": {
|
||||
"title": "",
|
||||
"popover": ""
|
||||
"title": "Saisissez un pseudonyme dans le champ ci-dessous",
|
||||
"popover": "Choisissez un pseudonyme"
|
||||
},
|
||||
"messagebox": ""
|
||||
"messagebox": "Saisissez votre texte..."
|
||||
},
|
||||
"settings": {
|
||||
"title": "",
|
||||
"update": "",
|
||||
"name": ""
|
||||
"title": "PARAMÈTRES",
|
||||
"update": "Mise à jour",
|
||||
"name": "Nom"
|
||||
},
|
||||
"videothumbnail": {
|
||||
"editnickname": "",
|
||||
"moderator": "",
|
||||
"videomute": "",
|
||||
"mute": "",
|
||||
"kick": "",
|
||||
"muted": "",
|
||||
"domute": ""
|
||||
"editnickname": "Cliquez pour modifier<br/>votre nom",
|
||||
"moderator": "Le propriétaire de<br/>cette conférence",
|
||||
"videomute": "Un participant a<br/>arrêté sa caméra.",
|
||||
"mute": "Un participant a coupé son micro",
|
||||
"kick": "Exclure",
|
||||
"muted": "Coupé",
|
||||
"domute": "Couper le son"
|
||||
},
|
||||
"connectionindicator": {
|
||||
"bitrate": "",
|
||||
"packetloss": "",
|
||||
"resolution": "",
|
||||
"less": "",
|
||||
"more": "",
|
||||
"address": "",
|
||||
"remoteport": "",
|
||||
"remoteport_plural": "",
|
||||
"localport": "",
|
||||
"localport_plural": "",
|
||||
"localaddress": "",
|
||||
"localaddress_plural": "",
|
||||
"remoteaddress": "",
|
||||
"remoteaddress_plural": "",
|
||||
"transport": "",
|
||||
"bandwidth": "",
|
||||
"na": ""
|
||||
"bitrate": "Débit",
|
||||
"packetloss": "Perte de paquets:",
|
||||
"resolution": "Résolution:",
|
||||
"less": "Cacher le détail",
|
||||
"more": "Montrer le détail",
|
||||
"address": "Adresse:",
|
||||
"remoteport": "Port distant:",
|
||||
"remoteport_plural": "Ports distants:",
|
||||
"localport": "Port local:",
|
||||
"localport_plural": "Ports locaux:",
|
||||
"localaddress": "Adresse locale:",
|
||||
"localaddress_plural": "Adresses locales:",
|
||||
"remoteaddress": "Adresse distante:",
|
||||
"remoteaddress_plural": "Adresses distantes:",
|
||||
"transport": "Transport:",
|
||||
"bandwidth": "Bande passante estimée:",
|
||||
"na": "Revenez ici pour afficher les informations de connexion une fois la conférence démarrée"
|
||||
},
|
||||
"notify": {
|
||||
"disconnected": "",
|
||||
"moderator": "",
|
||||
"connected": "",
|
||||
"somebody": "",
|
||||
"me": "",
|
||||
"disconnected": "Déconnecté",
|
||||
"moderator": "Droits modérateur accordés!",
|
||||
"connected": "Connecté",
|
||||
"somebody": "Quelqu'un",
|
||||
"me": "Moi",
|
||||
"focus": "",
|
||||
"focusFail": "",
|
||||
"grantedTo": "",
|
||||
"grantedToUnknown": ""
|
||||
"focusFail": "__component__ n'est pas disponible - réessayez dans __ms__ sec",
|
||||
"grantedTo": "Droits modérateur accordés à __to__!",
|
||||
"grantedToUnknown": "Droits modérateur accordés à $t(somebody)!"
|
||||
},
|
||||
"dialog": {
|
||||
"kickMessage": "",
|
||||
"popupError": "",
|
||||
"passwordError": "",
|
||||
"passwordError2": "",
|
||||
"joinError": "",
|
||||
"connectError": "",
|
||||
"kickMessage": "Oups! Vous avez été renvoyé de la réunion!",
|
||||
"popupError": "Votre navigateur bloque les popups pour ce site. Activez les popups pour ce site dans vos paramètres de sécurité et réessayez.",
|
||||
"passwordError": "Cette conversation est protégée par un mot de passe. Seul le propriétaire de cette conférence peut paramétrer un mot de passe.",
|
||||
"passwordError2": "Cette conversation n'est pas protégée par un mot de passe. Seul le propriétaire de cette conférence peut paramétrer un mot de passe.",
|
||||
"joinError": "Oups! La conférence ne peut être rejointe. Il y a peut-être un souci avec les paramètres de sécurité. Contactez l'administrateur.",
|
||||
"connectError": "Oups! Un problème est survenu et la connexion à la conférence est impossible.",
|
||||
"connecting": "",
|
||||
"error": "",
|
||||
"detectext": "",
|
||||
"failtoinstall": "",
|
||||
"failedpermissions": "",
|
||||
"bridgeUnavailable": "",
|
||||
"lockTitle": "",
|
||||
"lockMessage": "",
|
||||
"warning": "",
|
||||
"passwordNotSupported": "",
|
||||
"sorry": "",
|
||||
"internalError": "",
|
||||
"unableToSwitch": "",
|
||||
"SLDFailure": "",
|
||||
"SRDFailure": "",
|
||||
"oops": "",
|
||||
"defaultError": "",
|
||||
"passwordRequired": "",
|
||||
"Ok": "",
|
||||
"removePreziTitle": "",
|
||||
"removePreziMsg": "",
|
||||
"sharePreziTitle": "",
|
||||
"sharePreziMsg": "",
|
||||
"Remove": "",
|
||||
"Stop": "",
|
||||
"AuthMsg": "",
|
||||
"Authenticate": "",
|
||||
"Cancel": "",
|
||||
"retry": "",
|
||||
"logoutTitle": "",
|
||||
"logoutQuestion": "",
|
||||
"sessTerminated": "",
|
||||
"hungUp": "",
|
||||
"joinAgain": "",
|
||||
"Share": "",
|
||||
"preziLinkError": "",
|
||||
"Save": "",
|
||||
"recordingToken": "",
|
||||
"Dial": "",
|
||||
"sipMsg": "",
|
||||
"passwordCheck": "",
|
||||
"passwordMsg": "",
|
||||
"Invite": "",
|
||||
"shareLink": "",
|
||||
"settings1": "",
|
||||
"settings2": "",
|
||||
"settings3": "",
|
||||
"yourPassword": "",
|
||||
"Back": "",
|
||||
"serviceUnavailable": "",
|
||||
"gracefulShutdown": "",
|
||||
"Yes": "",
|
||||
"reservationError": "",
|
||||
"reservationErrorMsg": "",
|
||||
"password": "",
|
||||
"userPassword": "",
|
||||
"token": ""
|
||||
"detectext": "Une erreur est survenue pendant la détection de l'extension de partage d'écran.",
|
||||
"failtoinstall": "Échec de l'installation de l'extension de partage d'écran",
|
||||
"failedpermissions": "Échec d'obtention des permissions pour utiliser le micro et/ou la caméra local(e)",
|
||||
"bridgeUnavailable": "Le pont de visioconférence Jitsi est indisponible pour le moment. Réessayez plus tard!",
|
||||
"lockTitle": "Échec du verrouillage",
|
||||
"lockMessage": "Impossible de verrouiller la conférence.",
|
||||
"warning": "Avertissement",
|
||||
"passwordNotSupported": "Les mots de passe de conférence ne sont pas supportés.",
|
||||
"sorry": "Désolé",
|
||||
"internalError": "Une erreur interne de l'application est survenue [setRemoteDescription]",
|
||||
"unableToSwitch": "Impossible de passer le flux vidéo.",
|
||||
"SLDFailure": "Oups! Un problème est survenu et le micro n'a pas été coupé! (Échec SLD)",
|
||||
"SRDFailure": "Oups! Un problème est survenu et la caméra n'a pas été coupée! (Échec SRD)",
|
||||
"oops": "Oups!",
|
||||
"defaultError": "Une erreur est survenue",
|
||||
"passwordRequired": "Mot de passe requis",
|
||||
"Ok": "Ok",
|
||||
"removePreziTitle": "Supprimer la présentation Prezi",
|
||||
"removePreziMsg": "Voulez-vous vraiment supprimer votre présentation Prezi?",
|
||||
"sharePreziTitle": "Partager une présentation Prezi",
|
||||
"sharePreziMsg": "Un autre participant partage déjà une présentation Prezi. Cette conférence autorise une seule présentation Prezi à la fois.",
|
||||
"Remove": "Supprimer",
|
||||
"Stop": "Arrêter",
|
||||
"AuthMsg": "L'authentification est requise pour créer la conférence:<br/><b>__room__ </b></br> Vous pouvez vous authentifier pour créer la conférence ou attendre que quelqu'un le fasse pour vous.",
|
||||
"Authenticate": "Authentifiez-vous",
|
||||
"Cancel": "Annuler",
|
||||
"retry": "Réessayer",
|
||||
"logoutTitle": "Déconnexion",
|
||||
"logoutQuestion": "Voulez-vous vraiment vous déconnecter et arrêter la conférence?",
|
||||
"sessTerminated": "Session terminée",
|
||||
"hungUp": "Vous avez raccroché et quitté la conférence",
|
||||
"joinAgain": "Rejoignez à nouveau la conférence",
|
||||
"Share": "Partager",
|
||||
"preziLinkError": "Fournissez s'il vous plaît un lien prezi fonctionnel.",
|
||||
"Save": "Sauvegarder",
|
||||
"recordingToken": "Saisissez un jeton d'enregistrement",
|
||||
"Dial": "Composer",
|
||||
"sipMsg": "Saisissez un numéro SIP",
|
||||
"passwordCheck": "Voulez-vous vraiment supprimer votre mot de passe?",
|
||||
"passwordMsg": "Saisissez un mot de passe pour verrouiller la conférence",
|
||||
"Invite": "Inviter",
|
||||
"shareLink": "Partagez ce lien avec toutes les personnes que vous voulez inviter",
|
||||
"settings1": "Configurez votre conférence",
|
||||
"settings2": "Les participants rejoignent la conférence en étant muets.",
|
||||
"settings3": "Pseudonymes requis<br/><br/>Saisissez un mot de passe pour verrouiller la conférence:",
|
||||
"yourPassword": "Votre mot de passe",
|
||||
"Back": "Retour",
|
||||
"serviceUnavailable": "Service indisponible",
|
||||
"gracefulShutdown": "Le service est actuellement en maintenance. Réessayez plus tard.",
|
||||
"Yes": "Oui",
|
||||
"reservationError": "Erreur du système de réservation",
|
||||
"reservationErrorMsg": "Code d'erreur: __code__, message: __msg__",
|
||||
"password": "mot de passe",
|
||||
"userPassword": "mot de passe utilisateur",
|
||||
"token": "jeton"
|
||||
},
|
||||
"email": {
|
||||
"sharedKey": "",
|
||||
"subject": "",
|
||||
"body": "",
|
||||
"and": ""
|
||||
"sharedKey": [
|
||||
"Cette conférence est protégée par un mot de passe. Utilisez le code suivant pour la rejoindre:",
|
||||
"",
|
||||
"",
|
||||
"__sharedKey__",
|
||||
"",
|
||||
""
|
||||
],
|
||||
"subject": "Invitation à la conférence __appName__ : __conferenceName__",
|
||||
"body": [
|
||||
"Bonjour, je vous invite à la conférence __appName__ que je viens de créer.",
|
||||
"",
|
||||
"",
|
||||
"Cliquez sur le lien suivant pour rejoindre la conférence.",
|
||||
"",
|
||||
"",
|
||||
"__roomUrl__",
|
||||
"",
|
||||
"",
|
||||
"__sharedKeyText__",
|
||||
" Notez que __appName__ est actuellement seulement supporté par __supportedBrowsers__, vous devez donc utiliser un de ces navigateurs.",
|
||||
"",
|
||||
"",
|
||||
"À tout de suite dans la conférence!"
|
||||
],
|
||||
"and": "et"
|
||||
},
|
||||
"connection": {
|
||||
"ERROR": "",
|
||||
"CONNECTING": "",
|
||||
"CONNFAIL": "",
|
||||
"AUTHENTICATING": "",
|
||||
"AUTHFAIL": "",
|
||||
"CONNECTED": "",
|
||||
"DISCONNECTED": "",
|
||||
"DISCONNECTING": "",
|
||||
"ATTACHED": ""
|
||||
"ERROR": "Erreur",
|
||||
"CONNECTING": "Connexion en cours...",
|
||||
"CONNFAIL": "Échec de la Connexion",
|
||||
"AUTHENTICATING": "Authentification en cours...",
|
||||
"AUTHFAIL": "Échec de l'authentification",
|
||||
"CONNECTED": "Connecté",
|
||||
"DISCONNECTED": "Déconnecté",
|
||||
"DISCONNECTING": "Déconnexion en cours...",
|
||||
"ATTACHED": "Attachée"
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,9 @@
|
||||
{
|
||||
"title": "SETTINGS",
|
||||
"update": "Update",
|
||||
"name": "Name"
|
||||
"name": "Name",
|
||||
"startAudioMuted": "everyone join audio muted",
|
||||
"startVideoMuted": "everyone join video muted"
|
||||
},
|
||||
"videothumbnail":
|
||||
{
|
||||
@@ -123,8 +125,9 @@
|
||||
"focus": "Conference focus",
|
||||
"focusFail": "__component__ not available - retry in __ms__ sec",
|
||||
"grantedTo": "Moderator rights granted to __to__!",
|
||||
"grantedToUnknown": "Moderator rights granted to $t(somebody)!"
|
||||
|
||||
"grantedToUnknown": "Moderator rights granted to $t(somebody)!",
|
||||
"muted": "You have started the conversation muted.",
|
||||
"mutedTitle": "You're muted!"
|
||||
},
|
||||
"dialog": {
|
||||
"kickMessage": "Ouch! You have been kicked out of the meet!",
|
||||
@@ -133,6 +136,7 @@
|
||||
"passwordError2": "This conversation isn't currently protected by a password. Only the owner of the conference could set a password.",
|
||||
"joinError": "Oops! We couldn't join the conference. There might be some problem with security configuration. Please contact service administrator.",
|
||||
"connectError": "Oops! Something went wrong and we couldn't connect to the conference.",
|
||||
"connectErrorWithMsg": "Oops! Something went wrong and we couldn't connect to the conference: __msg__",
|
||||
"connecting": "Connecting",
|
||||
"error": "Error",
|
||||
"detectext": "Error when trying to detect desktopsharing extension.",
|
||||
|
||||
3012
libs/app.bundle.js
3012
libs/app.bundle.js
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,15 @@
|
||||
var StreamEventTypes = require("../../service/RTC/StreamEventTypes.js");
|
||||
|
||||
|
||||
function LocalStream(stream, type, eventEmitter, videoType)
|
||||
function LocalStream(stream, type, eventEmitter, videoType, isGUMStream)
|
||||
{
|
||||
this.stream = stream;
|
||||
this.eventEmitter = eventEmitter;
|
||||
this.type = type;
|
||||
this.videoType = videoType;
|
||||
this.isGUMStream = true;
|
||||
if(isGUMStream === false)
|
||||
this.isGUMStream = isGUMStream;
|
||||
var self = this;
|
||||
if(type == "audio")
|
||||
{
|
||||
@@ -37,26 +40,14 @@ LocalStream.prototype.getOriginalStream = function()
|
||||
}
|
||||
|
||||
LocalStream.prototype.isAudioStream = function () {
|
||||
return (this.stream.getAudioTracks() && this.stream.getAudioTracks().length > 0);
|
||||
};
|
||||
|
||||
LocalStream.prototype.mute = function()
|
||||
{
|
||||
var ismuted = false;
|
||||
var tracks = this.getTracks();
|
||||
|
||||
for (var idx = 0; idx < tracks.length; idx++) {
|
||||
ismuted = !tracks[idx].enabled;
|
||||
tracks[idx].enabled = ismuted;
|
||||
}
|
||||
return ismuted;
|
||||
return this.type === "audio";
|
||||
};
|
||||
|
||||
LocalStream.prototype.setMute = function(mute)
|
||||
{
|
||||
|
||||
if(window.location.protocol != "https:" ||
|
||||
this.isAudioStream() || this.videoType === "screen")
|
||||
if((window.location.protocol != "https:" && this.isGUMStream) ||
|
||||
(this.isAudioStream() && this.isGUMStream) || this.videoType === "screen")
|
||||
{
|
||||
var tracks = this.getTracks();
|
||||
|
||||
@@ -72,9 +63,18 @@ LocalStream.prototype.setMute = function(mute)
|
||||
}
|
||||
else
|
||||
{
|
||||
APP.RTC.rtcUtils.obtainAudioAndVideoPermissions(["video"],
|
||||
var self = this;
|
||||
APP.RTC.rtcUtils.obtainAudioAndVideoPermissions(
|
||||
(this.isAudioStream() ? ["audio"] : ["video"]),
|
||||
function (stream) {
|
||||
APP.RTC.changeLocalVideo(stream, false, function () {});
|
||||
if(self.isAudioStream())
|
||||
{
|
||||
APP.RTC.changeLocalAudio(stream, function () {});
|
||||
}
|
||||
else
|
||||
{
|
||||
APP.RTC.changeLocalVideo(stream, false, function () {});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,11 +13,39 @@ var UIEvents = require("../../service/UI/UIEvents");
|
||||
|
||||
var eventEmitter = new EventEmitter();
|
||||
|
||||
|
||||
function getMediaStreamUsage()
|
||||
{
|
||||
var result = {
|
||||
audio: true,
|
||||
video: true
|
||||
};
|
||||
|
||||
/** There are some issues with the desktop sharing
|
||||
* when this property is enabled.
|
||||
* WARNING: We must change the implementation to start video/audio if we
|
||||
* receive from the focus that the peer is not muted.
|
||||
|
||||
var isSecureConnection = window.location.protocol == "https:";
|
||||
|
||||
if(config.disableEarlyMediaPermissionRequests || !isSecureConnection)
|
||||
{
|
||||
result = {
|
||||
audio: false,
|
||||
video: false
|
||||
};
|
||||
|
||||
}
|
||||
**/
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var RTC = {
|
||||
rtcUtils: null,
|
||||
devices: {
|
||||
audio: false,
|
||||
video: false
|
||||
audio: true,
|
||||
video: true
|
||||
},
|
||||
localStreams: [],
|
||||
remoteStreams: {},
|
||||
@@ -35,13 +63,15 @@ var RTC = {
|
||||
|
||||
eventEmitter.removeListener(eventType, listener);
|
||||
},
|
||||
createLocalStream: function (stream, type, change, videoType) {
|
||||
createLocalStream: function (stream, type, change, videoType, isMuted, isGUMStream) {
|
||||
|
||||
var localStream = new LocalStream(stream, type, eventEmitter, videoType);
|
||||
var localStream = new LocalStream(stream, type, eventEmitter, videoType, isGUMStream);
|
||||
//in firefox we have only one stream object
|
||||
if(this.localStreams.length == 0 ||
|
||||
this.localStreams[0].getOriginalStream() != stream)
|
||||
this.localStreams.push(localStream);
|
||||
if(isMuted === true)
|
||||
localStream.setMute(false);
|
||||
|
||||
if(type == "audio")
|
||||
{
|
||||
@@ -55,7 +85,7 @@ var RTC = {
|
||||
if(change)
|
||||
eventType = StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED;
|
||||
|
||||
eventEmitter.emit(eventType, localStream);
|
||||
eventEmitter.emit(eventType, localStream, isMuted);
|
||||
return localStream;
|
||||
},
|
||||
removeLocalStream: function (stream) {
|
||||
@@ -139,7 +169,8 @@ var RTC = {
|
||||
APP.UI.addListener(UIEvents.PINNED_ENDPOINT,
|
||||
DataChannels.handlePinnedEndpointEvent);
|
||||
this.rtcUtils = new RTCUtils(this);
|
||||
this.rtcUtils.obtainAudioAndVideoPermissions();
|
||||
this.rtcUtils.obtainAudioAndVideoPermissions(
|
||||
null, null, getMediaStreamUsage());
|
||||
},
|
||||
muteRemoteVideoStream: function (jid, value) {
|
||||
var stream;
|
||||
@@ -180,12 +211,20 @@ var RTC = {
|
||||
callback();
|
||||
};
|
||||
}
|
||||
var videoStream = this.rtcUtils.createVideoStream(stream);
|
||||
var videoStream = this.rtcUtils.createStream(stream, true);
|
||||
this.localVideo = this.createLocalStream(videoStream, "video", true, type);
|
||||
// Stop the stream to trigger onended event for old stream
|
||||
oldStream.stop();
|
||||
APP.xmpp.switchStreams(videoStream, oldStream,localCallback);
|
||||
},
|
||||
changeLocalAudio: function (stream, callback) {
|
||||
var oldStream = this.localAudio.getOriginalStream();
|
||||
var newStream = this.rtcUtils.createStream(stream);
|
||||
this.localAudio = this.createLocalStream(newStream, "audio", true);
|
||||
// Stop the stream to trigger onended event for old stream
|
||||
oldStream.stop();
|
||||
APP.xmpp.switchStreams(newStream, oldStream, callback, true);
|
||||
},
|
||||
/**
|
||||
* Checks if video identified by given src is desktop stream.
|
||||
* @param videoSrc eg.
|
||||
@@ -220,7 +259,7 @@ var RTC = {
|
||||
{
|
||||
APP.xmpp.sendVideoInfoPresence(mute);
|
||||
if(callback)
|
||||
callback();
|
||||
callback(mute);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -130,7 +130,7 @@ function RTCUtils(RTCService)
|
||||
if (navigator.mozGetUserMedia) {
|
||||
console.log('This appears to be Firefox');
|
||||
var version = parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
|
||||
if (version >= 38
|
||||
if (version >= 40
|
||||
&& !config.enableSimulcast && config.useBundle && config.useRtcpMux) {
|
||||
this.peerconnection = mozRTCPeerConnection;
|
||||
this.browser = RTCBrowserType.RTC_BROWSER_FIREFOX;
|
||||
@@ -144,6 +144,8 @@ function RTCUtils(RTCService)
|
||||
//
|
||||
// https://groups.google.com/forum/#!topic/mozilla.dev.media/pKOiioXonJg
|
||||
// https://github.com/webrtc/samples/issues/302
|
||||
if(!element[0])
|
||||
return;
|
||||
element[0].mozSrcObject = stream;
|
||||
element[0].play();
|
||||
};
|
||||
@@ -156,10 +158,13 @@ function RTCUtils(RTCService)
|
||||
return tracks[0].id.replace(/[\{,\}]/g,"");
|
||||
};
|
||||
this.getVideoSrc = function (element) {
|
||||
if(!element)
|
||||
return null;
|
||||
return element.mozSrcObject;
|
||||
};
|
||||
this.setVideoSrc = function (element, src) {
|
||||
element.mozSrcObject = src;
|
||||
if(element)
|
||||
element.mozSrcObject = src;
|
||||
};
|
||||
RTCSessionDescription = mozRTCSessionDescription;
|
||||
RTCIceCandidate = mozRTCIceCandidate;
|
||||
@@ -182,10 +187,13 @@ function RTCUtils(RTCService)
|
||||
return stream.id.replace(/[\{,\}]/g,"");
|
||||
};
|
||||
this.getVideoSrc = function (element) {
|
||||
if(!element)
|
||||
return null;
|
||||
return element.getAttribute("src");
|
||||
};
|
||||
this.setVideoSrc = function (element, src) {
|
||||
element.setAttribute("src", src);
|
||||
if(element)
|
||||
element.setAttribute("src", src);
|
||||
};
|
||||
// DTLS should now be enabled by default but..
|
||||
this.pc_constraints = {'optional': [{'DtlsSrtpKeyAgreement': 'true'}]};
|
||||
@@ -292,32 +300,97 @@ RTCUtils.prototype.setAvailableDevices = function (um, available) {
|
||||
* We ask for audio and video combined stream in order to get permissions and
|
||||
* not to ask twice.
|
||||
*/
|
||||
RTCUtils.prototype.obtainAudioAndVideoPermissions = function(devices, callback) {
|
||||
RTCUtils.prototype.obtainAudioAndVideoPermissions =
|
||||
function(devices, callback, usageOptions)
|
||||
{
|
||||
var self = this;
|
||||
// Get AV
|
||||
|
||||
var successCallback = function (stream) {
|
||||
if(callback)
|
||||
callback(stream, usageOptions);
|
||||
else
|
||||
self.successCallback(stream, usageOptions);
|
||||
};
|
||||
|
||||
if(!devices)
|
||||
devices = ['audio', 'video'];
|
||||
|
||||
this.getUserMediaWithConstraints(
|
||||
devices,
|
||||
var newDevices = [];
|
||||
|
||||
|
||||
if(usageOptions)
|
||||
for(var i = 0; i < devices.length; i++)
|
||||
{
|
||||
var device = devices[i];
|
||||
if(usageOptions[device] === true)
|
||||
newDevices.push(device);
|
||||
}
|
||||
else
|
||||
newDevices = devices;
|
||||
|
||||
if(newDevices.length === 0)
|
||||
{
|
||||
successCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
if (navigator.mozGetUserMedia) {
|
||||
|
||||
// With FF we can't split the stream into audio and video because FF
|
||||
// doesn't support media stream constructors. So, we need to get the
|
||||
// audio stream separately from the video stream using two distinct GUM
|
||||
// calls. Not very user friendly :-( but we don't have many other
|
||||
// options neither.
|
||||
//
|
||||
// Note that we pack those 2 streams in a single object and pass it to
|
||||
// the successCallback method.
|
||||
|
||||
self.getUserMediaWithConstraints(
|
||||
['audio'],
|
||||
function (audioStream) {
|
||||
self.getUserMediaWithConstraints(
|
||||
['video'],
|
||||
function (videoStream) {
|
||||
return self.successCallback({
|
||||
audioStream: audioStream,
|
||||
videoStream: videoStream
|
||||
});
|
||||
},
|
||||
function (error) {
|
||||
console.error('failed to obtain video stream - stop',
|
||||
error);
|
||||
return self.successCallback(null);
|
||||
},
|
||||
config.resolution || '360');
|
||||
},
|
||||
function (error) {
|
||||
console.error('failed to obtain audio stream - stop',
|
||||
error);
|
||||
return self.successCallback(null);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.getUserMediaWithConstraints(
|
||||
newDevices,
|
||||
function (stream) {
|
||||
if(callback)
|
||||
callback(stream);
|
||||
else
|
||||
self.successCallback(stream);
|
||||
successCallback(stream);
|
||||
},
|
||||
function (error) {
|
||||
self.errorCallback(error);
|
||||
},
|
||||
config.resolution || '360');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RTCUtils.prototype.successCallback = function (stream) {
|
||||
if(stream)
|
||||
RTCUtils.prototype.successCallback = function (stream, usageOptions) {
|
||||
// If this is FF, the stream parameter is *not* a MediaStream object, it's
|
||||
// an object with two properties: audioStream, videoStream.
|
||||
if(stream && !navigator.mozGetUserMedia)
|
||||
console.log('got', stream, stream.getAudioTracks().length,
|
||||
stream.getVideoTracks().length);
|
||||
this.handleLocalStream(stream);
|
||||
this.handleLocalStream(stream, usageOptions);
|
||||
};
|
||||
|
||||
RTCUtils.prototype.errorCallback = function (error) {
|
||||
@@ -355,12 +428,15 @@ RTCUtils.prototype.errorCallback = function (error) {
|
||||
|
||||
}
|
||||
|
||||
RTCUtils.prototype.handleLocalStream = function(stream)
|
||||
RTCUtils.prototype.handleLocalStream = function(stream, usageOptions)
|
||||
{
|
||||
// If this is FF, the stream parameter is *not* a MediaStream object, it's
|
||||
// an object with two properties: audioStream, videoStream.
|
||||
var audioStream, videoStream;
|
||||
if(window.webkitMediaStream)
|
||||
{
|
||||
var audioStream = new webkitMediaStream();
|
||||
var videoStream = new webkitMediaStream();
|
||||
audioStream = new webkitMediaStream();
|
||||
videoStream = new webkitMediaStream();
|
||||
if(stream) {
|
||||
var audioTracks = stream.getAudioTracks();
|
||||
|
||||
@@ -374,38 +450,47 @@ RTCUtils.prototype.handleLocalStream = function(stream)
|
||||
videoStream.addTrack(videoTracks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
this.service.createLocalStream(audioStream, "audio");
|
||||
|
||||
this.service.createLocalStream(videoStream, "video");
|
||||
}
|
||||
else
|
||||
{//firefox
|
||||
this.service.createLocalStream(stream, "stream");
|
||||
audioStream = stream.audioStream;
|
||||
videoStream = stream.videoStream;
|
||||
}
|
||||
|
||||
var audioMuted = (usageOptions && usageOptions.audio === false),
|
||||
videoMuted = (usageOptions && usageOptions.video === false);
|
||||
|
||||
var audioGUM = (!usageOptions || usageOptions.audio !== false),
|
||||
videoGUM = (!usageOptions || usageOptions.video !== false);
|
||||
|
||||
|
||||
this.service.createLocalStream(audioStream, "audio", null, null,
|
||||
audioMuted, audioGUM);
|
||||
|
||||
this.service.createLocalStream(videoStream, "video", null, null,
|
||||
videoMuted, videoGUM);
|
||||
};
|
||||
|
||||
RTCUtils.prototype.createVideoStream = function(stream)
|
||||
RTCUtils.prototype.createStream = function(stream, isVideo)
|
||||
{
|
||||
var videoStream = null;
|
||||
var newStream = null;
|
||||
if(window.webkitMediaStream)
|
||||
{
|
||||
videoStream = new webkitMediaStream();
|
||||
if(stream)
|
||||
newStream = new webkitMediaStream();
|
||||
if(newStream)
|
||||
{
|
||||
var videoTracks = stream.getVideoTracks();
|
||||
var tracks = (isVideo? stream.getVideoTracks() : stream.getAudioTracks());
|
||||
|
||||
for (i = 0; i < videoTracks.length; i++) {
|
||||
videoStream.addTrack(videoTracks[i]);
|
||||
for (i = 0; i < tracks.length; i++) {
|
||||
newStream.addTrack(tracks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
videoStream = stream;
|
||||
newStream = stream;
|
||||
|
||||
return videoStream;
|
||||
return newStream;
|
||||
};
|
||||
|
||||
module.exports = RTCUtils;
|
||||
|
||||
@@ -32,6 +32,12 @@ var eventEmitter = new EventEmitter();
|
||||
var roomName = null;
|
||||
|
||||
|
||||
function notifyForInitialMute()
|
||||
{
|
||||
messageHandler.notify(null, "notify.mutedTitle", "connected",
|
||||
"notify.muted", null, {timeOut: 120000});
|
||||
}
|
||||
|
||||
function setupPrezi()
|
||||
{
|
||||
$("#reloadPresentationLink").click(function()
|
||||
@@ -54,24 +60,42 @@ function setupToolbars() {
|
||||
BottomToolbar.init();
|
||||
}
|
||||
|
||||
function streamHandler(stream) {
|
||||
function streamHandler(stream, isMuted) {
|
||||
switch (stream.type)
|
||||
{
|
||||
case "audio":
|
||||
VideoLayout.changeLocalAudio(stream);
|
||||
VideoLayout.changeLocalAudio(stream, isMuted);
|
||||
break;
|
||||
case "video":
|
||||
VideoLayout.changeLocalVideo(stream);
|
||||
VideoLayout.changeLocalVideo(stream, isMuted);
|
||||
break;
|
||||
case "stream":
|
||||
VideoLayout.changeLocalStream(stream);
|
||||
VideoLayout.changeLocalStream(stream, isMuted);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function onXmppConnectionFailed(stropheErrorMsg) {
|
||||
|
||||
var title = APP.translation.generateTranslatonHTML(
|
||||
"dialog.error");
|
||||
|
||||
var message;
|
||||
if (stropheErrorMsg) {
|
||||
message = APP.translation.generateTranslatonHTML(
|
||||
"dialog.connectErrorWithMsg", {msg: stropheErrorMsg});
|
||||
} else {
|
||||
message = APP.translation.generateTranslatonHTML(
|
||||
"dialog.connectError");
|
||||
}
|
||||
|
||||
messageHandler.openDialog(
|
||||
title, message, true, {}, function (e, v, m, f) { return false; });
|
||||
}
|
||||
|
||||
function onDisposeConference(unload) {
|
||||
Toolbar.showAuthenticateButton(false);
|
||||
};
|
||||
}
|
||||
|
||||
function onDisplayNameChanged(jid, displayName) {
|
||||
ContactList.onDisplayNameChange(jid, displayName);
|
||||
@@ -141,6 +165,7 @@ function registerListeners() {
|
||||
VideoLayout.updateConnectionStats);
|
||||
APP.connectionquality.addListener(CQEvents.STOP,
|
||||
VideoLayout.onStatsStop);
|
||||
APP.xmpp.addListener(XMPPEvents.CONNECTION_FAILED, onXmppConnectionFailed);
|
||||
APP.xmpp.addListener(XMPPEvents.DISPOSE_CONFERENCE, onDisposeConference);
|
||||
APP.xmpp.addListener(XMPPEvents.GRACEFUL_SHUTDOWN, function () {
|
||||
messageHandler.openMessageDialog(
|
||||
@@ -227,6 +252,9 @@ function registerListeners() {
|
||||
|
||||
APP.members.addListener(MemberEvents.DTMF_SUPPORT_CHANGED,
|
||||
onDtmfSupportChanged);
|
||||
APP.xmpp.addListener(XMPPEvents.START_MUTED, function (audio, video) {
|
||||
SettingsMenu.setStartMuted(audio, video);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -414,6 +442,9 @@ function onMucJoined(jid, info) {
|
||||
|
||||
if (displayName)
|
||||
onDisplayNameChanged('localVideoContainer', displayName);
|
||||
|
||||
|
||||
VideoLayout.mucJoined();
|
||||
}
|
||||
|
||||
function initEtherpad(name) {
|
||||
@@ -427,6 +458,9 @@ function onMucMemberLeft(jid) {
|
||||
messageHandler.notify(displayName,'notify.somebody',
|
||||
'disconnected',
|
||||
'notify.disconnected');
|
||||
if(!config.startAudioMuted ||
|
||||
config.startAudioMuted > APP.members.size())
|
||||
UIUtil.playSoundNotification('userLeft');
|
||||
// Need to call this with a slight delay, otherwise the element couldn't be
|
||||
// found for some reason.
|
||||
// XXX(gp) it works fine without the timeout for me (with Chrome 38).
|
||||
@@ -453,6 +487,7 @@ function onLocalRoleChanged(jid, info, pres, isModerator)
|
||||
console.info("My role changed, new role: " + info.role);
|
||||
onModeratorStatusChanged(isModerator);
|
||||
VideoLayout.showModeratorIndicator();
|
||||
SettingsMenu.onRoleChanged();
|
||||
|
||||
if (isModerator) {
|
||||
Authentication.closeAuthenticationWindow();
|
||||
@@ -520,6 +555,9 @@ function onMucMemberJoined(jid, id, displayName) {
|
||||
'connected',
|
||||
'notify.connected');
|
||||
|
||||
if(!config.startAudioMuted ||
|
||||
config.startAudioMuted > APP.members.size())
|
||||
UIUtil.playSoundNotification('userJoined');
|
||||
// Add Peer's container
|
||||
VideoLayout.ensurePeerContainerExists(jid,id);
|
||||
}
|
||||
@@ -697,6 +735,12 @@ UI.getRoomName = function () {
|
||||
return roomName;
|
||||
};
|
||||
|
||||
UI.setInitialMuteFromFocus = function (muteAudio, muteVideo) {
|
||||
if(muteAudio || muteVideo) notifyForInitialMute();
|
||||
if(muteAudio) UI.setAudioMuted(true);
|
||||
if(muteVideo) UI.setVideoMute(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutes/unmutes the local video.
|
||||
*/
|
||||
@@ -714,9 +758,17 @@ UI.toggleAudio = function() {
|
||||
/**
|
||||
* Sets muted audio state for the local participant.
|
||||
*/
|
||||
UI.setAudioMuted = function (mute) {
|
||||
|
||||
if(!APP.xmpp.setAudioMute(mute, function () {
|
||||
UI.setAudioMuted = function (mute, earlyMute) {
|
||||
var audioMute = null;
|
||||
if(earlyMute)
|
||||
audioMute = function (mute, cb) {
|
||||
return APP.xmpp.sendAudioInfoPresence(mute, cb);
|
||||
};
|
||||
else
|
||||
audioMute = function (mute, cb) {
|
||||
return APP.xmpp.setAudioMute(mute, cb);
|
||||
}
|
||||
if(!audioMute(mute, function () {
|
||||
VideoLayout.showLocalAudioIndicator(mute);
|
||||
|
||||
UIUtil.buttonClick("#mute", "icon-microphone icon-mic-disabled");
|
||||
@@ -764,5 +816,8 @@ UI.setVideoMuteButtonsState = function (mute) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
UI.setVideoMute = setVideoMute;
|
||||
|
||||
module.exports = UI;
|
||||
|
||||
|
||||
@@ -20,6 +20,12 @@ function isUserMuted(jid) {
|
||||
}
|
||||
}
|
||||
|
||||
if(jid && jid == APP.xmpp.myJid())
|
||||
{
|
||||
var localVideo = APP.RTC.localVideo;
|
||||
return (!localVideo || localVideo.isMuted());
|
||||
}
|
||||
|
||||
if (!APP.RTC.remoteStreams[jid] || !APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE]) {
|
||||
return null;
|
||||
}
|
||||
@@ -119,8 +125,9 @@ var Avatar = {
|
||||
} else {
|
||||
if (video && video.length > 0) {
|
||||
setVisibility(video, !show);
|
||||
setVisibility(avatar, show);
|
||||
}
|
||||
setVisibility(avatar, show);
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -26,7 +26,7 @@ function generateLanguagesSelectBox()
|
||||
var SettingsMenu = {
|
||||
|
||||
init: function () {
|
||||
$("#updateSettings").before(generateLanguagesSelectBox());
|
||||
$("#startMutedOptions").before(generateLanguagesSelectBox());
|
||||
APP.translation.translateElement($("#languages_selectbox"));
|
||||
$('#settingsmenu>input').keyup(function(event){
|
||||
if(event.keyCode === 13) {//enter
|
||||
@@ -34,11 +34,36 @@ var SettingsMenu = {
|
||||
}
|
||||
});
|
||||
|
||||
if(APP.xmpp.isModerator())
|
||||
{
|
||||
$("#startMutedOptions").css("display", "block");
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#startMutedOptions").css("display", "none");
|
||||
}
|
||||
|
||||
$("#updateSettings").click(function () {
|
||||
SettingsMenu.update();
|
||||
});
|
||||
},
|
||||
|
||||
onRoleChanged: function () {
|
||||
if(APP.xmpp.isModerator())
|
||||
{
|
||||
$("#startMutedOptions").css("display", "block");
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#startMutedOptions").css("display", "none");
|
||||
}
|
||||
},
|
||||
|
||||
setStartMuted: function (audio, video) {
|
||||
$("#startAudioMuted").attr("checked", audio);
|
||||
$("#startVideoMuted").attr("checked", video);
|
||||
},
|
||||
|
||||
update: function() {
|
||||
var newDisplayName = UIUtil.escapeHtml($('#setDisplayName').get(0).value);
|
||||
var newEmail = UIUtil.escapeHtml($('#setEmail').get(0).value);
|
||||
@@ -55,6 +80,10 @@ var SettingsMenu = {
|
||||
APP.xmpp.addToPresence("email", newEmail);
|
||||
var email = Settings.setEmail(newEmail);
|
||||
|
||||
var startAudioMuted = ($("#startAudioMuted").is(":checked"));
|
||||
var startVideoMuted = ($("#startVideoMuted").is(":checked"));
|
||||
APP.xmpp.addToPresence("startMuted",
|
||||
[startAudioMuted, startVideoMuted]);
|
||||
|
||||
Avatar.setUserAvatar(APP.xmpp.myJid(), email);
|
||||
},
|
||||
|
||||
@@ -333,7 +333,8 @@ var Toolbar = (function (my) {
|
||||
if (inviteLink) {
|
||||
inviteLink.value = roomUrl;
|
||||
inviteLink.select();
|
||||
document.getElementById('jqi_state0_buttonInvite').disabled = false;
|
||||
$('#inviteLinkRef').parent()
|
||||
.find('button[value=true]').prop('disabled', false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -426,12 +427,13 @@ var Toolbar = (function (my) {
|
||||
}
|
||||
}
|
||||
},
|
||||
function () {
|
||||
function (event) {
|
||||
if (roomUrl) {
|
||||
document.getElementById('inviteLinkRef').select();
|
||||
} else {
|
||||
document.getElementById('jqi_state0_buttonInvite')
|
||||
.disabled = true;
|
||||
if (event && event.target)
|
||||
$(event.target)
|
||||
.find('button[value=true]').prop('disabled', true);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -15,6 +15,9 @@ function showDesktopSharingButton() {
|
||||
* Hides the toolbar.
|
||||
*/
|
||||
function hideToolbar() {
|
||||
if(config.alwaysVisibleToolbar)
|
||||
return;
|
||||
|
||||
var header = $("#header"),
|
||||
bottomToolbar = $("#bottomToolbar");
|
||||
var isToolbarHover = false;
|
||||
|
||||
@@ -175,7 +175,7 @@ var messageHandler = (function(my) {
|
||||
};
|
||||
|
||||
my.notify = function(displayName, displayNameKey,
|
||||
cls, messageKey, messageArguments) {
|
||||
cls, messageKey, messageArguments, options) {
|
||||
var displayNameSpan = '<span class="nickname" ';
|
||||
if(displayName)
|
||||
{
|
||||
@@ -195,7 +195,7 @@ var messageHandler = (function(my) {
|
||||
: "") + ">" +
|
||||
APP.translation.translateString(messageKey,
|
||||
messageArguments) +
|
||||
'</span>');
|
||||
'</span>', null, options);
|
||||
};
|
||||
|
||||
return my;
|
||||
|
||||
@@ -27,12 +27,6 @@ var eventEmitter = null;
|
||||
*/
|
||||
var focusedVideoInfo = null;
|
||||
|
||||
/**
|
||||
* Indicates if we have muted our audio before the conference has started.
|
||||
* @type {boolean}
|
||||
*/
|
||||
var preMuted = false;
|
||||
|
||||
var mutedAudios = {};
|
||||
|
||||
var flipXLocalVideo = true;
|
||||
@@ -550,24 +544,58 @@ var VideoLayout = (function (my) {
|
||||
|| (lastNEndpointsCache && lastNEndpointsCache.indexOf(resource) !== -1);
|
||||
};
|
||||
|
||||
my.changeLocalStream = function (stream) {
|
||||
VideoLayout.changeLocalVideo(stream);
|
||||
my.changeLocalStream = function (stream, isMuted) {
|
||||
VideoLayout.changeLocalVideo(stream, isMuted);
|
||||
};
|
||||
|
||||
my.changeLocalAudio = function(stream) {
|
||||
my.changeLocalAudio = function(stream, isMuted) {
|
||||
if(isMuted)
|
||||
APP.UI.setAudioMuted(true, true);
|
||||
APP.RTC.attachMediaStream($('#localAudio'), stream.getOriginalStream());
|
||||
document.getElementById('localAudio').autoplay = true;
|
||||
document.getElementById('localAudio').volume = 0;
|
||||
if (preMuted) {
|
||||
if(!APP.UI.setAudioMuted(true))
|
||||
{
|
||||
preMuted = mute;
|
||||
}
|
||||
preMuted = false;
|
||||
}
|
||||
};
|
||||
|
||||
my.changeLocalVideo = function(stream) {
|
||||
my.changeLocalVideo = function(stream, isMuted) {
|
||||
// Set default display name.
|
||||
setDisplayName('localVideoContainer');
|
||||
|
||||
if(!VideoLayout.connectionIndicators["localVideoContainer"]) {
|
||||
VideoLayout.connectionIndicators["localVideoContainer"]
|
||||
= new ConnectionIndicator($("#localVideoContainer")[0], null, VideoLayout);
|
||||
}
|
||||
|
||||
AudioLevels.updateAudioLevelCanvas(null, VideoLayout);
|
||||
|
||||
var localVideo = null;
|
||||
|
||||
function localVideoClick(event) {
|
||||
event.stopPropagation();
|
||||
VideoLayout.handleVideoThumbClicked(
|
||||
APP.RTC.getVideoSrc(localVideo),
|
||||
false,
|
||||
APP.xmpp.myResource());
|
||||
}
|
||||
|
||||
$('#localVideoContainer').click(localVideoClick);
|
||||
|
||||
// Add hover handler
|
||||
$('#localVideoContainer').hover(
|
||||
function() {
|
||||
VideoLayout.showDisplayName('localVideoContainer', true);
|
||||
},
|
||||
function() {
|
||||
if (!VideoLayout.isLargeVideoVisible()
|
||||
|| APP.RTC.getVideoSrc(localVideo) !== APP.RTC.getVideoSrc($('#largeVideo')[0]))
|
||||
VideoLayout.showDisplayName('localVideoContainer', false);
|
||||
}
|
||||
);
|
||||
|
||||
if(isMuted)
|
||||
{
|
||||
APP.UI.setVideoMute(true);
|
||||
return;
|
||||
}
|
||||
var flipX = true;
|
||||
if(stream.videoType == "screen")
|
||||
flipX = false;
|
||||
@@ -581,55 +609,29 @@ var VideoLayout = (function (my) {
|
||||
var localVideoContainer = document.getElementById('localVideoWrapper');
|
||||
localVideoContainer.appendChild(localVideo);
|
||||
|
||||
// Set default display name.
|
||||
setDisplayName('localVideoContainer');
|
||||
|
||||
if(!VideoLayout.connectionIndicators["localVideoContainer"]) {
|
||||
VideoLayout.connectionIndicators["localVideoContainer"]
|
||||
= new ConnectionIndicator($("#localVideoContainer")[0], null, VideoLayout);
|
||||
}
|
||||
|
||||
AudioLevels.updateAudioLevelCanvas(null, VideoLayout);
|
||||
|
||||
var localVideoSelector = $('#' + localVideo.id);
|
||||
|
||||
function localVideoClick(event) {
|
||||
event.stopPropagation();
|
||||
VideoLayout.handleVideoThumbClicked(
|
||||
APP.RTC.getVideoSrc(localVideo),
|
||||
false,
|
||||
APP.xmpp.myResource());
|
||||
}
|
||||
// Add click handler to both video and video wrapper elements in case
|
||||
// there's no video.
|
||||
localVideoSelector.click(localVideoClick);
|
||||
$('#localVideoContainer').click(localVideoClick);
|
||||
|
||||
// Add hover handler
|
||||
$('#localVideoContainer').hover(
|
||||
function() {
|
||||
VideoLayout.showDisplayName('localVideoContainer', true);
|
||||
},
|
||||
function() {
|
||||
if (!VideoLayout.isLargeVideoVisible()
|
||||
|| APP.RTC.getVideoSrc(localVideo) !== APP.RTC.getVideoSrc($('#largeVideo')[0]))
|
||||
VideoLayout.showDisplayName('localVideoContainer', false);
|
||||
}
|
||||
);
|
||||
// Add stream ended handler
|
||||
stream.getOriginalStream().onended = function () {
|
||||
localVideoContainer.removeChild(localVideo);
|
||||
VideoLayout.updateRemovedVideo(APP.RTC.getVideoSrc(localVideo));
|
||||
};
|
||||
// Flip video x axis if needed
|
||||
flipXLocalVideo = flipX;
|
||||
if (flipX) {
|
||||
localVideoSelector.addClass("flipVideoX");
|
||||
}
|
||||
|
||||
// Attach WebRTC stream
|
||||
var videoStream = APP.simulcast.getLocalVideoStream();
|
||||
APP.RTC.attachMediaStream(localVideoSelector, videoStream);
|
||||
|
||||
// Add stream ended handler
|
||||
stream.getOriginalStream().onended = function () {
|
||||
localVideoContainer.removeChild(localVideo);
|
||||
VideoLayout.updateRemovedVideo(APP.RTC.getVideoSrc(localVideo));
|
||||
};
|
||||
|
||||
|
||||
localVideoSrc = APP.RTC.getVideoSrc(localVideo);
|
||||
|
||||
var myResourceJid = APP.xmpp.myResource();
|
||||
@@ -639,6 +641,14 @@ var VideoLayout = (function (my) {
|
||||
|
||||
};
|
||||
|
||||
my.mucJoined = function () {
|
||||
var myResourceJid = APP.xmpp.myResource();
|
||||
|
||||
if(!largeVideoState.userResourceJid)
|
||||
VideoLayout.updateLargeVideo(localVideoSrc, 0,
|
||||
myResourceJid, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds or removes icons for not available camera and microphone.
|
||||
* @param resourceJid the jid of user
|
||||
@@ -706,26 +716,34 @@ var VideoLayout = (function (my) {
|
||||
}
|
||||
}
|
||||
|
||||
var src = null, volume = null;
|
||||
// mute if localvideo
|
||||
if (pick) {
|
||||
var container = pick.parentNode;
|
||||
var jid = null;
|
||||
if(container)
|
||||
{
|
||||
if(container.id == "localVideoWrapper")
|
||||
{
|
||||
jid = APP.xmpp.myResource();
|
||||
}
|
||||
else
|
||||
{
|
||||
jid = VideoLayout.getPeerContainerResourceJid(container);
|
||||
}
|
||||
}
|
||||
|
||||
VideoLayout.updateLargeVideo(APP.RTC.getVideoSrc(pick), pick.volume, jid);
|
||||
src = APP.RTC.getVideoSrc(pick);
|
||||
volume = pick.volume;
|
||||
} else {
|
||||
console.warn("Failed to elect large video");
|
||||
container = $('#remoteVideos>span[id!="mixedstream"]:visible:last').get(0);
|
||||
|
||||
}
|
||||
|
||||
var jid = null;
|
||||
if(container)
|
||||
{
|
||||
if(container.id == "localVideoWrapper")
|
||||
{
|
||||
jid = APP.xmpp.myResource();
|
||||
}
|
||||
else
|
||||
{
|
||||
jid = VideoLayout.getPeerContainerResourceJid(container);
|
||||
}
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
VideoLayout.updateLargeVideo(src, volume, jid);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -755,7 +773,6 @@ var VideoLayout = (function (my) {
|
||||
container.id = 'mixedstream';
|
||||
container.className = 'videocontainer';
|
||||
remotes.appendChild(container);
|
||||
UIUtil.playSoundNotification('userJoined');
|
||||
}
|
||||
|
||||
if (container) {
|
||||
@@ -774,11 +791,10 @@ var VideoLayout = (function (my) {
|
||||
/**
|
||||
* Updates the large video with the given new video source.
|
||||
*/
|
||||
my.updateLargeVideo = function(newSrc, vol, resourceJid) {
|
||||
console.log('hover in', newSrc);
|
||||
|
||||
if (APP.RTC.getVideoSrc($('#largeVideo')[0]) !== newSrc) {
|
||||
my.updateLargeVideo = function(newSrc, vol, resourceJid, forceUpdate) {
|
||||
console.log('hover in', newSrc, resourceJid);
|
||||
|
||||
if (APP.RTC.getVideoSrc($('#largeVideo')[0]) !== newSrc || forceUpdate) {
|
||||
$('#activeSpeaker').css('visibility', 'hidden');
|
||||
// Due to the simulcast the localVideoSrc may have changed when the
|
||||
// fadeOut event triggers. In that case the getJidFromVideoSrc and
|
||||
@@ -816,7 +832,6 @@ var VideoLayout = (function (my) {
|
||||
largeVideoState.updateInProgress = true;
|
||||
|
||||
var doUpdate = function () {
|
||||
|
||||
Avatar.updateActiveSpeakerAvatarSrc(
|
||||
APP.xmpp.findJidFromResource(
|
||||
largeVideoState.userResourceJid));
|
||||
@@ -1278,7 +1293,6 @@ var VideoLayout = (function (my) {
|
||||
// Remove whole container
|
||||
container.remove();
|
||||
|
||||
UIUtil.playSoundNotification('userLeft');
|
||||
VideoLayout.resizeThumbnails();
|
||||
}
|
||||
|
||||
@@ -1661,10 +1675,9 @@ var VideoLayout = (function (my) {
|
||||
if (videoSpan.classList.contains("dominantspeaker"))
|
||||
videoSpan.classList.remove("dominantspeaker");
|
||||
}
|
||||
|
||||
Avatar.showUserAvatar(
|
||||
APP.xmpp.findJidFromResource(resourceJid));
|
||||
}
|
||||
Avatar.showUserAvatar(
|
||||
APP.xmpp.findJidFromResource(resourceJid));
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
40
modules/URLProcessor/URLProcessor.js
Normal file
40
modules/URLProcessor/URLProcessor.js
Normal file
@@ -0,0 +1,40 @@
|
||||
var params = {};
|
||||
function getConfigParamsFromUrl() {
|
||||
if(!location.hash)
|
||||
return {};
|
||||
var hash = location.hash.substr(1);
|
||||
var result = {};
|
||||
hash.split("&").forEach(function(part) {
|
||||
var item = part.split("=");
|
||||
result[item[0]] = JSON.parse(
|
||||
decodeURIComponent(item[1]).replace(/\\&/, "&"));
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
params = getConfigParamsFromUrl();
|
||||
|
||||
var URLProcessor = {
|
||||
setConfigParametersFromUrl: function () {
|
||||
for(var k in params)
|
||||
{
|
||||
if(typeof k !== "string" || k.indexOf("config.") === -1)
|
||||
continue;
|
||||
|
||||
var v = params[k];
|
||||
var confKey = k.substr(7);
|
||||
if(config[confKey] && typeof config[confKey] !== typeof v)
|
||||
{
|
||||
console.warn("The type of " + k +
|
||||
" is wrong. That parameter won't be updated in config.js.");
|
||||
continue;
|
||||
}
|
||||
|
||||
config[confKey] = v;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = URLProcessor;
|
||||
@@ -117,6 +117,12 @@ var Members = {
|
||||
},
|
||||
removeListener: function (type, listener) {
|
||||
eventEmitter.removeListener(type, listener);
|
||||
},
|
||||
size: function () {
|
||||
return Object.keys(members).length;
|
||||
},
|
||||
getMembers: function () {
|
||||
return members;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -260,7 +260,7 @@ StatsCollector.prototype.start = function ()
|
||||
);
|
||||
}
|
||||
|
||||
if(!config.disableStats) {
|
||||
if(!config.disableStats && !navigator.mozGetUserMedia) {
|
||||
this.statsIntervalId = setInterval(
|
||||
function () {
|
||||
// Interval updates
|
||||
@@ -294,7 +294,7 @@ StatsCollector.prototype.start = function ()
|
||||
);
|
||||
}
|
||||
|
||||
if (config.logStats) {
|
||||
if (config.logStats && !navigator.mozGetUserMedia) {
|
||||
this.gatherStatsIntervalId = setInterval(
|
||||
function () {
|
||||
self.peerconnection.getStats(
|
||||
@@ -696,9 +696,10 @@ StatsCollector.prototype.processAudioLevelReport = function ()
|
||||
|
||||
var ssrc = getStatValue(now, 'ssrc');
|
||||
var jid = APP.xmpp.getJidFromSSRC(ssrc);
|
||||
if (!jid && (Date.now() - now.timestamp) < 3000)
|
||||
if (!jid)
|
||||
{
|
||||
console.warn("No jid for ssrc: " + ssrc);
|
||||
if((Date.now() - now.timestamp) < 3000)
|
||||
console.warn("No jid for ssrc: " + ssrc);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ var SDPDiffer = require("./SDPDiffer");
|
||||
var SDPUtil = require("./SDPUtil");
|
||||
var SDP = require("./SDP");
|
||||
var RTCBrowserType = require("../../service/RTC/RTCBrowserType");
|
||||
var async = require("async");
|
||||
|
||||
// Jingle stuff
|
||||
function JingleSession(me, sid, connection, service) {
|
||||
@@ -52,10 +53,21 @@ function JingleSession(me, sid, connection, service) {
|
||||
* by the application logic.
|
||||
*/
|
||||
this.videoMuteByUser = false;
|
||||
this.modifySourcesQueue = async.queue(this._modifySources.bind(this), 1);
|
||||
// We start with the queue paused. We resume it when the signaling state is
|
||||
// stable and the ice connection state is connected.
|
||||
this.modifySourcesQueue.pause();
|
||||
}
|
||||
|
||||
//TODO: this array must be removed when firefox implement multistream support
|
||||
JingleSession.notReceivedSSRCs = [];
|
||||
JingleSession.prototype.updateModifySourcesQueue = function() {
|
||||
var signalingState = this.peerconnection.signalingState;
|
||||
var iceConnectionState = this.peerconnection.iceConnectionState;
|
||||
if (signalingState === 'stable' && iceConnectionState === 'connected') {
|
||||
this.modifySourcesQueue.resume();
|
||||
} else {
|
||||
this.modifySourcesQueue.pause();
|
||||
}
|
||||
};
|
||||
|
||||
JingleSession.prototype.initiate = function (peerjid, isInitiator) {
|
||||
var self = this;
|
||||
@@ -82,8 +94,16 @@ JingleSession.prototype.initiate = function (peerjid, isInitiator) {
|
||||
self.sendIceCandidate(event.candidate);
|
||||
};
|
||||
this.peerconnection.onaddstream = function (event) {
|
||||
console.log("REMOTE STREAM ADDED: " + event.stream + " - " + event.stream.id);
|
||||
self.remoteStreamAdded(event);
|
||||
if (event.stream.id !== 'default') {
|
||||
console.log("REMOTE STREAM ADDED: " + event.stream + " - " + event.stream.id);
|
||||
self.remoteStreamAdded(event);
|
||||
} else {
|
||||
// This is a recvonly stream. Clients that implement Unified Plan,
|
||||
// such as Firefox use recvonly "streams/channels/tracks" for
|
||||
// receiving remote stream/tracks, as opposed to Plan B where there
|
||||
// are only 3 channels: audio, video and data.
|
||||
console.log("RECVONLY REMOTE STREAM IGNORED: " + event.stream + " - " + event.stream.id);
|
||||
}
|
||||
};
|
||||
this.peerconnection.onremovestream = function (event) {
|
||||
// Remove the stream from remoteStreams
|
||||
@@ -92,9 +112,11 @@ JingleSession.prototype.initiate = function (peerjid, isInitiator) {
|
||||
};
|
||||
this.peerconnection.onsignalingstatechange = function (event) {
|
||||
if (!(self && self.peerconnection)) return;
|
||||
self.updateModifySourcesQueue();
|
||||
};
|
||||
this.peerconnection.oniceconnectionstatechange = function (event) {
|
||||
if (!(self && self.peerconnection)) return;
|
||||
self.updateModifySourcesQueue();
|
||||
switch (self.peerconnection.iceConnectionState) {
|
||||
case 'connected':
|
||||
this.startTime = new Date();
|
||||
@@ -774,7 +796,17 @@ JingleSession.prototype.addSource = function (elem, fromJid) {
|
||||
});
|
||||
sdp.raw = sdp.session + sdp.media.join('');
|
||||
});
|
||||
this.modifySources();
|
||||
|
||||
this.modifySourcesQueue.push(function() {
|
||||
// When a source is added and if this is FF, a new channel is allocated
|
||||
// for receiving the added source. We need to diffuse the SSRC of this
|
||||
// new recvonly channel to the rest of the peers.
|
||||
console.log('modify sources done');
|
||||
|
||||
var newSdp = new SDP(self.peerconnection.localDescription.sdp);
|
||||
console.log("SDPs", mySdp, newSdp);
|
||||
self.notifyMySSRCUpdate(mySdp, newSdp);
|
||||
});
|
||||
};
|
||||
|
||||
JingleSession.prototype.removeSource = function (elem, fromJid) {
|
||||
@@ -835,11 +867,22 @@ JingleSession.prototype.removeSource = function (elem, fromJid) {
|
||||
});
|
||||
sdp.raw = sdp.session + sdp.media.join('');
|
||||
});
|
||||
this.modifySources();
|
||||
|
||||
this.modifySourcesQueue.push(function() {
|
||||
// When a source is removed and if this is FF, the recvonly channel that
|
||||
// receives the remote stream is deactivated . We need to diffuse the
|
||||
// recvonly SSRC removal to the rest of the peers.
|
||||
console.log('modify sources done');
|
||||
|
||||
var newSdp = new SDP(self.peerconnection.localDescription.sdp);
|
||||
console.log("SDPs", mySdp, newSdp);
|
||||
self.notifyMySSRCUpdate(mySdp, newSdp);
|
||||
});
|
||||
};
|
||||
|
||||
JingleSession.prototype.modifySources = function (successCallback) {
|
||||
JingleSession.prototype._modifySources = function (successCallback, queueCallback) {
|
||||
var self = this;
|
||||
|
||||
if (this.peerconnection.signalingState == 'closed') return;
|
||||
if (!(this.addssrc.length || this.removessrc.length || this.pendingop !== null || this.switchstreams)){
|
||||
// There is nothing to do since scheduled job might have been executed by another succeeding call
|
||||
@@ -847,21 +890,7 @@ JingleSession.prototype.modifySources = function (successCallback) {
|
||||
if(successCallback){
|
||||
successCallback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: this is a big hack
|
||||
// https://code.google.com/p/webrtc/issues/detail?id=2688
|
||||
// ^ has been fixed.
|
||||
if (!(this.peerconnection.signalingState == 'stable' && this.peerconnection.iceConnectionState == 'connected')) {
|
||||
console.warn('modifySources not yet', this.peerconnection.signalingState, this.peerconnection.iceConnectionState);
|
||||
this.wait = true;
|
||||
window.setTimeout(function() { self.modifySources(successCallback); }, 250);
|
||||
return;
|
||||
}
|
||||
if (this.wait) {
|
||||
window.setTimeout(function() { self.modifySources(successCallback); }, 2500);
|
||||
this.wait = false;
|
||||
queueCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -901,6 +930,7 @@ JingleSession.prototype.modifySources = function (successCallback) {
|
||||
|
||||
if(self.signalingState == 'closed') {
|
||||
console.error("createAnswer attempt on closed state");
|
||||
queueCallback("createAnswer attempt on closed state");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -937,30 +967,35 @@ JingleSession.prototype.modifySources = function (successCallback) {
|
||||
if(successCallback){
|
||||
successCallback();
|
||||
}
|
||||
queueCallback();
|
||||
},
|
||||
function(error) {
|
||||
console.error('modified setLocalDescription failed', error);
|
||||
queueCallback(error);
|
||||
}
|
||||
);
|
||||
},
|
||||
function(error) {
|
||||
console.error('modified answer failed', error);
|
||||
queueCallback(error);
|
||||
}
|
||||
);
|
||||
},
|
||||
function(error) {
|
||||
console.error('modify failed', error);
|
||||
queueCallback(error);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Switches video streams.
|
||||
* @param new_stream new stream that will be used as video of this session.
|
||||
* @param oldStream old video stream of this session.
|
||||
* @param success_callback callback executed after successful stream switch.
|
||||
*/
|
||||
JingleSession.prototype.switchStreams = function (new_stream, oldStream, success_callback) {
|
||||
JingleSession.prototype.switchStreams = function (new_stream, oldStream, success_callback, isAudio) {
|
||||
|
||||
var self = this;
|
||||
|
||||
@@ -975,7 +1010,8 @@ JingleSession.prototype.switchStreams = function (new_stream, oldStream, success
|
||||
self.peerconnection.addStream(new_stream);
|
||||
}
|
||||
|
||||
APP.RTC.switchVideoStreams(new_stream, oldStream);
|
||||
if(!isAudio)
|
||||
APP.RTC.switchVideoStreams(new_stream, oldStream);
|
||||
|
||||
// Conference is not active
|
||||
if(!oldSdp || !self.peerconnection) {
|
||||
@@ -984,7 +1020,7 @@ JingleSession.prototype.switchStreams = function (new_stream, oldStream, success
|
||||
}
|
||||
|
||||
self.switchstreams = true;
|
||||
self.modifySources(function() {
|
||||
self.modifySourcesQueue.push(function() {
|
||||
console.log('modify sources done');
|
||||
|
||||
success_callback();
|
||||
@@ -1092,7 +1128,23 @@ JingleSession.prototype.setVideoMute = function (mute, callback, options) {
|
||||
|
||||
this.hardMuteVideo(mute);
|
||||
|
||||
this.modifySources(callback(mute));
|
||||
var self = this;
|
||||
var oldSdp = null;
|
||||
if(self.peerconnection) {
|
||||
if(self.peerconnection.localDescription) {
|
||||
oldSdp = new SDP(self.peerconnection.localDescription.sdp);
|
||||
}
|
||||
}
|
||||
|
||||
this.modifySourcesQueue.push(function() {
|
||||
console.log('modify sources done');
|
||||
|
||||
callback(mute);
|
||||
|
||||
var newSdp = new SDP(self.peerconnection.localDescription.sdp);
|
||||
console.log("SDPs", oldSdp, newSdp);
|
||||
self.notifyMySSRCUpdate(oldSdp, newSdp);
|
||||
});
|
||||
};
|
||||
|
||||
JingleSession.prototype.hardMuteVideo = function (muted) {
|
||||
|
||||
@@ -179,7 +179,7 @@ TraceablePeerConnection.prototype.setLocalDescription = function (description, s
|
||||
this.trace('setLocalDescription::preTransform (Plan B)', dumpSDP(description));
|
||||
// if we're running on FF, transform to Plan A first.
|
||||
if (navigator.mozGetUserMedia) {
|
||||
description = this.interop.toPlanA(description);
|
||||
description = this.interop.toUnifiedPlan(description);
|
||||
} else {
|
||||
description = APP.simulcast.transformLocalDescription(description);
|
||||
}
|
||||
@@ -206,7 +206,7 @@ TraceablePeerConnection.prototype.setRemoteDescription = function (description,
|
||||
this.trace('setRemoteDescription::preTransform (Plan B)', dumpSDP(description));
|
||||
// if we're running on FF, transform to Plan A first.
|
||||
if (navigator.mozGetUserMedia) {
|
||||
description = this.interop.toPlanA(description);
|
||||
description = this.interop.toUnifiedPlan(description);
|
||||
}
|
||||
else {
|
||||
description = APP.simulcast.transformRemoteDescription(description);
|
||||
|
||||
@@ -177,6 +177,20 @@ var Moderator = {
|
||||
{ name: 'openSctp', value: config.openSctp})
|
||||
.up();
|
||||
}
|
||||
if(config.startAudioMuted !== undefined)
|
||||
{
|
||||
elem.c(
|
||||
'property',
|
||||
{ name: 'startAudioMuted', value: config.startAudioMuted})
|
||||
.up();
|
||||
}
|
||||
if(config.startVideoMuted !== undefined)
|
||||
{
|
||||
elem.c(
|
||||
'property',
|
||||
{ name: 'startVideoMuted', value: config.startVideoMuted})
|
||||
.up();
|
||||
}
|
||||
elem.up();
|
||||
return elem;
|
||||
},
|
||||
|
||||
@@ -28,6 +28,15 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
initPresenceMap: function (myroomjid) {
|
||||
this.presMap['to'] = myroomjid;
|
||||
this.presMap['xns'] = 'http://jabber.org/protocol/muc';
|
||||
if(APP.RTC.localAudio.isMuted())
|
||||
{
|
||||
this.addAudioInfoToPresence(true);
|
||||
}
|
||||
|
||||
if(APP.RTC.localVideo.isMuted())
|
||||
{
|
||||
this.addVideoInfoToPresence(true);
|
||||
}
|
||||
},
|
||||
doJoin: function (jid, password) {
|
||||
this.myroomjid = jid;
|
||||
@@ -143,6 +152,13 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
$(document).trigger('videomuted.muc', [from, videoMuted.text()]);
|
||||
}
|
||||
|
||||
var startMuted = $(pres).find('>startmuted');
|
||||
if (startMuted.length)
|
||||
{
|
||||
eventEmitter.emit(XMPPEvents.START_MUTED,
|
||||
startMuted.attr("audio") === "true", startMuted.attr("video") === "true");
|
||||
}
|
||||
|
||||
var devices = $(pres).find('>devices');
|
||||
if(devices.length)
|
||||
{
|
||||
@@ -262,6 +278,15 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
eventEmitter.emit(XMPPEvents.MUC_DESTROYED, reason);
|
||||
return true;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
// Remove old ssrcs coming from the jid
|
||||
Object.keys(this.ssrc2jid).forEach(function (ssrc) {
|
||||
if (self.ssrc2jid[ssrc] == from) {
|
||||
delete self.ssrc2jid[ssrc];
|
||||
}
|
||||
});
|
||||
|
||||
// Status code 110 indicates that this notification is "self-presence".
|
||||
if (!$(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="110"]').length) {
|
||||
delete this.members[from];
|
||||
@@ -497,6 +522,15 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
|| 'sendrecv' }
|
||||
).up();
|
||||
}
|
||||
pres.up();
|
||||
}
|
||||
|
||||
if(this.presMap["startMuted"] !== undefined)
|
||||
{
|
||||
pres.c("startmuted", {audio: this.presMap["startMuted"].audio,
|
||||
video: this.presMap["startMuted"].video,
|
||||
xmlns: "http://jitsi.org/jitmeet/start-muted"});
|
||||
delete this.presMap["startMuted"];
|
||||
}
|
||||
|
||||
pres.up();
|
||||
@@ -577,6 +611,9 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
addUserIdToPresence: function (userId) {
|
||||
this.presMap['userId'] = userId;
|
||||
},
|
||||
addStartMutedToPresence: function (audio, video) {
|
||||
this.presMap["startMuted"] = {audio: audio, video: video};
|
||||
},
|
||||
isModerator: function () {
|
||||
return this.role === 'moderator';
|
||||
},
|
||||
@@ -621,8 +658,6 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
//console.log(jid, 'assoc ssrc', ssrc.getAttribute('type'), ssrc.getAttribute('ssrc'));
|
||||
var ssrcV = ssrc.getAttribute('ssrc');
|
||||
self.ssrc2jid[ssrcV] = from;
|
||||
JingleSession.notReceivedSSRCs.push(ssrcV);
|
||||
|
||||
|
||||
var type = ssrc.getAttribute('type');
|
||||
|
||||
|
||||
@@ -108,6 +108,14 @@ module.exports = function(XMPP, eventEmitter)
|
||||
// see http://xmpp.org/extensions/xep-0166.html#concepts-session
|
||||
switch (action) {
|
||||
case 'session-initiate':
|
||||
var startMuted = $(iq).find('jingle>startmuted');
|
||||
if(startMuted && startMuted.length > 0)
|
||||
{
|
||||
var audioMuted = startMuted.attr("audio");
|
||||
var videoMuted = startMuted.attr("video");
|
||||
APP.UI.setInitialMuteFromFocus((audioMuted === "true"),
|
||||
(videoMuted === "true"));
|
||||
}
|
||||
sess = new JingleSession(
|
||||
$(iq).attr('to'), $(iq).find('jingle').attr('sid'),
|
||||
this.connection, XMPP);
|
||||
|
||||
@@ -9,66 +9,145 @@ var StreamEventTypes = require("../../service/RTC/StreamEventTypes");
|
||||
var RTCEvents = require("../../service/RTC/RTCEvents");
|
||||
var UIEvents = require("../../service/UI/UIEvents");
|
||||
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
|
||||
var retry = require('retry');
|
||||
|
||||
var eventEmitter = new EventEmitter();
|
||||
var connection = null;
|
||||
var authenticatedUser = false;
|
||||
|
||||
function connect(jid, password) {
|
||||
connection = XMPP.createConnection();
|
||||
Moderator.setConnection(connection);
|
||||
|
||||
if (connection.disco) {
|
||||
// for chrome, add multistream cap
|
||||
}
|
||||
connection.jingle.pc_constraints = APP.RTC.getPCConstraints();
|
||||
if (config.useIPv6) {
|
||||
// https://code.google.com/p/webrtc/issues/detail?id=2828
|
||||
if (!connection.jingle.pc_constraints.optional)
|
||||
connection.jingle.pc_constraints.optional = [];
|
||||
connection.jingle.pc_constraints.optional.push({googIPv6: true});
|
||||
}
|
||||
var faultTolerantConnect = retry.operation({
|
||||
retries: 3
|
||||
});
|
||||
|
||||
// Include user info in MUC presence
|
||||
var settings = Settings.getSettings();
|
||||
if (settings.email) {
|
||||
connection.emuc.addEmailToPresence(settings.email);
|
||||
}
|
||||
if (settings.uid) {
|
||||
connection.emuc.addUserIdToPresence(settings.uid);
|
||||
}
|
||||
if (settings.displayName) {
|
||||
connection.emuc.addDisplayNameToPresence(settings.displayName);
|
||||
}
|
||||
// fault tolerant connect
|
||||
faultTolerantConnect.attempt(function () {
|
||||
|
||||
var anonymousConnectionFailed = false;
|
||||
connection.connect(jid, password, function (status, msg) {
|
||||
console.log('Strophe status changed to',
|
||||
Strophe.getStatusString(status));
|
||||
if (status === Strophe.Status.CONNECTED) {
|
||||
if (config.useStunTurn) {
|
||||
connection.jingle.getStunAndTurnCredentials();
|
||||
}
|
||||
|
||||
console.info("My Jabber ID: " + connection.jid);
|
||||
|
||||
if(password)
|
||||
authenticatedUser = true;
|
||||
maybeDoJoin();
|
||||
} else if (status === Strophe.Status.CONNFAIL) {
|
||||
if(msg === 'x-strophe-bad-non-anon-jid') {
|
||||
anonymousConnectionFailed = true;
|
||||
}
|
||||
} else if (status === Strophe.Status.DISCONNECTED) {
|
||||
if(anonymousConnectionFailed) {
|
||||
// prompt user for username and password
|
||||
XMPP.promptLogin();
|
||||
}
|
||||
} else if (status === Strophe.Status.AUTHFAIL) {
|
||||
// wrong password or username, prompt user
|
||||
XMPP.promptLogin();
|
||||
connection = XMPP.createConnection();
|
||||
Moderator.setConnection(connection);
|
||||
|
||||
if (connection.disco) {
|
||||
// for chrome, add multistream cap
|
||||
}
|
||||
connection.jingle.pc_constraints = APP.RTC.getPCConstraints();
|
||||
if (config.useIPv6) {
|
||||
// https://code.google.com/p/webrtc/issues/detail?id=2828
|
||||
if (!connection.jingle.pc_constraints.optional)
|
||||
connection.jingle.pc_constraints.optional = [];
|
||||
connection.jingle.pc_constraints.optional.push({googIPv6: true});
|
||||
}
|
||||
|
||||
// Include user info in MUC presence
|
||||
var settings = Settings.getSettings();
|
||||
if (settings.email) {
|
||||
connection.emuc.addEmailToPresence(settings.email);
|
||||
}
|
||||
if (settings.uid) {
|
||||
connection.emuc.addUserIdToPresence(settings.uid);
|
||||
}
|
||||
if (settings.displayName) {
|
||||
connection.emuc.addDisplayNameToPresence(settings.displayName);
|
||||
}
|
||||
|
||||
|
||||
// connection.connect() starts the connection process.
|
||||
//
|
||||
// As the connection process proceeds, the user supplied callback will
|
||||
// be triggered multiple times with status updates. The callback should
|
||||
// take two arguments - the status code and the error condition.
|
||||
//
|
||||
// The status code will be one of the values in the Strophe.Status
|
||||
// constants. The error condition will be one of the conditions defined
|
||||
// in RFC 3920 or the condition ‘strophe-parsererror’.
|
||||
//
|
||||
// The Parameters wait, hold and route are optional and only relevant
|
||||
// for BOSH connections. Please see XEP 124 for a more detailed
|
||||
// explanation of the optional parameters.
|
||||
//
|
||||
// Connection status constants for use by the connection handler
|
||||
// callback.
|
||||
//
|
||||
// Status.ERROR - An error has occurred (websockets specific)
|
||||
// Status.CONNECTING - The connection is currently being made
|
||||
// Status.CONNFAIL - The connection attempt failed
|
||||
// Status.AUTHENTICATING - The connection is authenticating
|
||||
// Status.AUTHFAIL - The authentication attempt failed
|
||||
// Status.CONNECTED - The connection has succeeded
|
||||
// Status.DISCONNECTED - The connection has been terminated
|
||||
// Status.DISCONNECTING - The connection is currently being terminated
|
||||
// Status.ATTACHED - The connection has been attached
|
||||
|
||||
var anonymousConnectionFailed = false;
|
||||
var connectionFailed = false;
|
||||
var lastErrorMsg;
|
||||
connection.connect(jid, password, function (status, msg) {
|
||||
console.log('Strophe status changed to',
|
||||
Strophe.getStatusString(status), msg);
|
||||
if (status === Strophe.Status.CONNECTED) {
|
||||
if (config.useStunTurn) {
|
||||
connection.jingle.getStunAndTurnCredentials();
|
||||
}
|
||||
|
||||
console.info("My Jabber ID: " + connection.jid);
|
||||
|
||||
if (password)
|
||||
authenticatedUser = true;
|
||||
maybeDoJoin();
|
||||
} else if (status === Strophe.Status.CONNFAIL) {
|
||||
if (msg === 'x-strophe-bad-non-anon-jid') {
|
||||
anonymousConnectionFailed = true;
|
||||
} else {
|
||||
connectionFailed = true;
|
||||
}
|
||||
lastErrorMsg = msg;
|
||||
} else if (status === Strophe.Status.DISCONNECTED) {
|
||||
if (anonymousConnectionFailed) {
|
||||
// prompt user for username and password
|
||||
XMPP.promptLogin();
|
||||
} else {
|
||||
|
||||
// Strophe already has built-in HTTP/BOSH error handling and
|
||||
// request retry logic. Requests are resent automatically
|
||||
// until their error count reaches 5. Strophe.js disconnects
|
||||
// if the error count is > 5. We are not replicating this
|
||||
// here.
|
||||
//
|
||||
// The "problem" is that failed HTTP/BOSH requests don't
|
||||
// trigger a callback with a status update, so when a
|
||||
// callback with status Strophe.Status.DISCONNECTED arrives,
|
||||
// we can't be sure if it's a graceful disconnect or if it's
|
||||
// triggered by some HTTP/BOSH error.
|
||||
//
|
||||
// But that's a minor issue in Jitsi Meet as we never
|
||||
// disconnect anyway, not even when the user closes the
|
||||
// browser window (which is kind of wrong, but the point is
|
||||
// that we should never ever get disconnected).
|
||||
//
|
||||
// On the other hand, failed connections due to XMPP layer
|
||||
// errors, trigger a callback with status Strophe.Status.CONNFAIL.
|
||||
//
|
||||
// Here we implement retry logic for failed connections due
|
||||
// to XMPP layer errors and we display an error to the user
|
||||
// if we get disconnected from the XMPP server permanently.
|
||||
|
||||
// If the connection failed, retry.
|
||||
if (connectionFailed
|
||||
&& faultTolerantConnect.retry("connection-failed")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we failed to connect to the XMPP server, fire an event
|
||||
// to let all the interested module now about it.
|
||||
eventEmitter.emit(XMPPEvents.CONNECTION_FAILED,
|
||||
msg ? msg : lastErrorMsg);
|
||||
}
|
||||
} else if (status === Strophe.Status.AUTHFAIL) {
|
||||
// wrong password or username, prompt user
|
||||
XMPP.promptLogin();
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -111,8 +190,13 @@ function registerListeners() {
|
||||
});
|
||||
}
|
||||
|
||||
function setupEvents() {
|
||||
$(window).bind('beforeunload', function () {
|
||||
var unload = (function () {
|
||||
var unloaded = false;
|
||||
|
||||
return function () {
|
||||
if (unloaded) { return; }
|
||||
unloaded = true;
|
||||
|
||||
if (connection && connection.connected) {
|
||||
// ensure signout
|
||||
$.ajax({
|
||||
@@ -121,24 +205,41 @@ function setupEvents() {
|
||||
async: false,
|
||||
cache: false,
|
||||
contentType: 'application/xml',
|
||||
data: "<body rid='" + (connection.rid || connection._proto.rid)
|
||||
+ "' xmlns='http://jabber.org/protocol/httpbind' sid='"
|
||||
+ (connection.sid || connection._proto.sid)
|
||||
+ "' type='terminate'>" +
|
||||
"<presence xmlns='jabber:client' type='unavailable'/>" +
|
||||
"</body>",
|
||||
data: "<body rid='" + (connection.rid || connection._proto.rid) +
|
||||
"' xmlns='http://jabber.org/protocol/httpbind' sid='" +
|
||||
(connection.sid || connection._proto.sid) +
|
||||
"' type='terminate'>" +
|
||||
"<presence xmlns='jabber:client' type='unavailable'/>" +
|
||||
"</body>",
|
||||
success: function (data) {
|
||||
console.log('signed out');
|
||||
console.log(data);
|
||||
},
|
||||
error: function (XMLHttpRequest, textStatus, errorThrown) {
|
||||
console.log('signout error',
|
||||
textStatus + ' (' + errorThrown + ')');
|
||||
textStatus + ' (' + errorThrown + ')');
|
||||
}
|
||||
});
|
||||
}
|
||||
XMPP.disposeConference(true);
|
||||
});
|
||||
};
|
||||
})();
|
||||
|
||||
function setupEvents() {
|
||||
// In recent versions of FF the 'beforeunload' event is not fired when the
|
||||
// window or the tab is closed. It is only fired when we leave the page
|
||||
// (change URL). If this participant doesn't unload properly, then it
|
||||
// becomes a ghost for the rest of the participants that stay in the
|
||||
// conference. Thankfully handling the 'unload' event in addition to the
|
||||
// 'beforeunload' event seems to garante the execution of the 'unload'
|
||||
// method at least once.
|
||||
//
|
||||
// The 'unload' method can safely be run multiple times, it will actually do
|
||||
// something only the first time that it's run, so we're don't have to worry
|
||||
// about browsers that fire both events.
|
||||
|
||||
$(window).bind('beforeunload', unload);
|
||||
$(window).bind('unload', unload);
|
||||
}
|
||||
|
||||
var XMPP = {
|
||||
@@ -261,10 +362,10 @@ var XMPP = {
|
||||
isExternalAuthEnabled: function () {
|
||||
return Moderator.isExternalAuthEnabled();
|
||||
},
|
||||
switchStreams: function (stream, oldStream, callback) {
|
||||
switchStreams: function (stream, oldStream, callback, isAudio) {
|
||||
if (connection && connection.jingle.activecall) {
|
||||
// FIXME: will block switchInProgress on true value in case of exception
|
||||
connection.jingle.activecall.switchStreams(stream, oldStream, callback);
|
||||
connection.jingle.activecall.switchStreams(stream, oldStream, callback, isAudio);
|
||||
} else {
|
||||
// We are done immediately
|
||||
console.warn("No conference handler or conference not started yet");
|
||||
@@ -272,6 +373,8 @@ var XMPP = {
|
||||
}
|
||||
},
|
||||
sendVideoInfoPresence: function (mute) {
|
||||
if(!connection)
|
||||
return;
|
||||
connection.emuc.addVideoInfoToPresence(mute);
|
||||
connection.emuc.sendPresence();
|
||||
},
|
||||
@@ -315,10 +418,17 @@ var XMPP = {
|
||||
// It is not clear what is the right way to handle multiple tracks.
|
||||
// So at least make sure that they are all muted or all unmuted and
|
||||
// that we send presence just once.
|
||||
APP.RTC.localAudio.mute();
|
||||
APP.RTC.localAudio.setMute(!mute);
|
||||
// isMuted is the opposite of audioEnabled
|
||||
connection.emuc.addAudioInfoToPresence(mute);
|
||||
connection.emuc.sendPresence();
|
||||
this.sendAudioInfoPresence(mute, callback);
|
||||
return true;
|
||||
},
|
||||
sendAudioInfoPresence: function(mute, callback)
|
||||
{
|
||||
if(connection) {
|
||||
connection.emuc.addAudioInfoToPresence(mute);
|
||||
connection.emuc.sendPresence();
|
||||
}
|
||||
callback();
|
||||
return true;
|
||||
},
|
||||
@@ -393,6 +503,12 @@ var XMPP = {
|
||||
case "devices":
|
||||
connection.emuc.addDevicesToPresence(value);
|
||||
break;
|
||||
case "startMuted":
|
||||
if(!Moderator.isModerator())
|
||||
return;
|
||||
connection.emuc.addStartMutedToPresence(value[0],
|
||||
value[1]);
|
||||
break;
|
||||
default :
|
||||
console.log("Unknown tag for presence: " + name);
|
||||
return;
|
||||
|
||||
@@ -18,8 +18,10 @@
|
||||
"events": "*",
|
||||
"pako": "*",
|
||||
"i18next-client": "1.7.7",
|
||||
"sdp-interop": "jitsi/sdp-interop#f65fedfe57a",
|
||||
"sdp-transform": "1.3.0"
|
||||
"sdp-interop": "0.1.4",
|
||||
"sdp-transform": "1.4.0",
|
||||
"async": "0.9.0",
|
||||
"retry": "0.6.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
var XMPPEvents = {
|
||||
CONNECTION_FAILED: "xmpp.connection.failed",
|
||||
CONFERENCE_CREATED: "xmpp.conferenceCreated.jingle",
|
||||
CALL_TERMINATED: "xmpp.callterminated.jingle",
|
||||
CALL_INCOMING: "xmpp.callincoming.jingle",
|
||||
@@ -28,6 +29,7 @@ var XMPPEvents = {
|
||||
AUTHENTICATION_REQUIRED: "xmpp.authentication_required",
|
||||
CHAT_ERROR_RECEIVED: "xmpp.chat_error_received",
|
||||
ETHERPAD: "xmpp.etherpad",
|
||||
DEVICE_AVAILABLE: "xmpp.device_available"
|
||||
DEVICE_AVAILABLE: "xmpp.device_available",
|
||||
START_MUTED: "xmpp.start_muted"
|
||||
};
|
||||
module.exports = XMPPEvents;
|
||||
Reference in New Issue
Block a user