Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
galene
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexedi
galene
Commits
cbff5067
Commit
cbff5067
authored
May 21, 2020
by
Juliusz Chroboczek
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement sharing of multiple application windows at a time.
parent
9fde0299
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
157 additions
and
119 deletions
+157
-119
static/sfu.css
static/sfu.css
+16
-5
static/sfu.html
static/sfu.html
+17
-15
static/sfu.js
static/sfu.js
+124
-99
No files found.
static/sfu.css
View file @
cbff5067
...
...
@@ -32,11 +32,7 @@ h1 {
display
:
inline
}
.userform-invisible
{
display
:
none
;
}
.disconnect-invisible
{
.invisible
{
display
:
none
;
}
...
...
@@ -53,6 +49,12 @@ h1 {
margin-bottom
:
4px
;
}
#presentbutton
,
#unpresentbutton
{
width
:
8em
;
white-space
:
nowrap
;
margin-right
:
0.4em
;
}
#videoselect
{
width
:
8em
;
text-align-last
:
center
;
...
...
@@ -65,6 +67,15 @@ h1 {
margin-right
:
0.4em
;
}
#sharebutton
,
#unsharebutton
{
width
:
8em
;
white-space
:
nowrap
;
}
#unsharebutton
{
margin-right
:
0.4em
;
}
#requestselect
{
width
:
8em
;
text-align-last
:
center
;
...
...
static/sfu.html
View file @
cbff5067
...
...
@@ -23,29 +23,31 @@
autocomplete=
"current-password"
/>
<input
id=
"connectbutton"
type=
"submit"
value=
"Connect"
disabled
/>
</form>
<input
id=
"disconnectbutton"
class=
"
disconnect-
invisible"
<input
id=
"disconnectbutton"
class=
"invisible"
type=
"submit"
value=
"Disconnect"
/>
<span
id=
"errspan"
></span>
</div>
<div
id=
"optionsdiv"
>
<
label
for=
"presenterbox"
>
Present:
</label
>
<
input
id=
"presenterbox"
type=
"checkbox"
disabled
/
>
<
button
id=
"presentbutton"
class=
"invisible"
>
Present
</button
>
<
button
id=
"unpresentbutton"
class=
"invisible"
>
Stop presenting
</button
>
<label
for=
"videoselect"
>
Camera:
</label>
<select
id=
"videoselect"
>
<option>
default
</option>
<option>
off
</option>
</select>
<span
id=
"mediaoptions"
>
<label
for=
"videoselect"
>
Camera:
</label>
<select
id=
"videoselect"
>
<option>
default
</option>
<option>
off
</option>
</select>
<label
for=
"audioselect"
>
Microphone:
</label>
<select
id=
"audioselect"
>
<option>
default
</option>
<option>
off
</option>
</select>
<label
for=
"audioselect"
>
Microphone:
</label>
<select
id=
"audioselect"
>
<option>
default
</option>
<option>
off
</option>
</select>
</span>
<
label
for=
"sharebox"
>
Share screen:
</label
>
<
input
id=
"sharebox"
type=
"checkbox"
disabled
/
>
<
button
id=
"sharebutton"
class=
"invisible"
>
Share screen
</button
>
<
button
id=
"unsharebutton"
class=
"invisible"
>
Stop sharing
</button
>
<label
for=
"requestselect"
>
Receive:
</label>
<select
id=
"requestselect"
>
...
...
static/sfu.js
View file @
cbff5067
...
...
@@ -36,6 +36,7 @@ function randomid() {
function
Connection
(
id
,
pc
)
{
this
.
id
=
id
;
this
.
kind
=
null
;
this
.
label
=
null
;
this
.
pc
=
pc
;
this
.
stream
=
null
;
...
...
@@ -100,9 +101,9 @@ function setConnected(connected) {
statspan
.
textContent
=
'
Connected
'
;
statspan
.
classList
.
remove
(
'
disconnected
'
);
statspan
.
classList
.
add
(
'
connected
'
);
userform
.
classList
.
add
(
'
userform-
invisible
'
);
userform
.
classList
.
add
(
'
invisible
'
);
userform
.
classList
.
remove
(
'
userform
'
);
disconnectbutton
.
classList
.
remove
(
'
disconnect-
invisible
'
);
disconnectbutton
.
classList
.
remove
(
'
invisible
'
);
displayUsername
();
}
else
{
let
userpass
=
getUserPass
();
...
...
@@ -114,33 +115,75 @@ function setConnected(connected) {
statspan
.
classList
.
remove
(
'
connected
'
);
statspan
.
classList
.
add
(
'
disconnected
'
);
userform
.
classList
.
add
(
'
userform
'
);
userform
.
classList
.
remove
(
'
userform-
invisible
'
);
disconnectbutton
.
classList
.
add
(
'
disconnect-
invisible
'
);
userform
.
classList
.
remove
(
'
invisible
'
);
disconnectbutton
.
classList
.
add
(
'
invisible
'
);
permissions
=
{};
clearUsername
(
false
);
}
}
document
.
getElementById
(
'
present
erbox
'
).
onchange
=
function
(
e
)
{
document
.
getElementById
(
'
present
button
'
).
onclick
=
function
(
e
)
{
e
.
preventDefault
();
setLocalMedia
(
this
.
checked
);
addLocalMedia
(
);
};
document
.
getElementById
(
'
unpresentbutton
'
).
onclick
=
function
(
e
)
{
e
.
preventDefault
();
delUpMediaKind
(
'
local
'
);
};
function
changePresentation
()
{
let
found
=
false
;
for
(
let
id
in
up
)
{
if
(
up
[
id
].
kind
===
'
local
'
)
found
=
true
;
}
delUpMediaKind
(
'
local
'
);
if
(
found
)
addLocalMedia
();
}
function
setVisibility
(
id
,
visible
)
{
let
elt
=
document
.
getElementById
(
id
);
if
(
visible
)
elt
.
classList
.
remove
(
'
invisible
'
);
else
elt
.
classList
.
add
(
'
invisible
'
);
}
function
setButtonsVisibility
()
{
let
local
=
findUpMedia
(
'
local
'
);
let
share
=
findUpMedia
(
'
screenshare
'
)
// don't allow multiple presentations
setVisibility
(
'
presentbutton
'
,
permissions
.
present
&&
!
local
);
setVisibility
(
'
unpresentbutton
'
,
local
);
// allow multiple shared documents
setVisibility
(
'
sharebutton
'
,
permissions
.
present
);
setVisibility
(
'
unsharebutton
'
,
share
);
setVisibility
(
'
mediaoptions
'
,
permissions
.
present
);
}
document
.
getElementById
(
'
audioselect
'
).
onchange
=
function
(
e
)
{
e
.
preventDefault
();
setLocalMedia
(
document
.
getElementById
(
'
presenterbox
'
).
checked
);
changePresentation
(
);
};
document
.
getElementById
(
'
videoselect
'
).
onchange
=
function
(
e
)
{
e
.
preventDefault
();
setLocalMedia
(
document
.
getElementById
(
'
presenterbox
'
).
checked
);
changePresentation
(
);
};
document
.
getElementById
(
'
shareb
ox
'
).
onchange
=
function
(
e
)
{
document
.
getElementById
(
'
shareb
utton
'
).
onclick
=
function
(
e
)
{
e
.
preventDefault
();
setShareMedia
(
this
.
checked
);
addShareMedia
(
);
};
document
.
getElementById
(
'
unsharebutton
'
).
onclick
=
function
(
e
)
{
e
.
preventDefault
();
delUpMediaKind
(
'
screenshare
'
);
}
document
.
getElementById
(
'
requestselect
'
).
onchange
=
function
(
e
)
{
e
.
preventDefault
();
sendRequest
(
this
.
value
);
...
...
@@ -273,26 +316,13 @@ async function setMediaChoices() {
mediaChoicesDone
=
true
;
}
let
localMediaId
=
null
;
async
function
setLocalMedia
(
setup
)
{
async
function
addLocalMedia
()
{
if
(
!
getUserPass
())
return
;
if
(
!
setup
)
{
if
(
localMediaId
)
{
up
[
localMediaId
].
close
(
true
);
delete
(
up
[
localMediaId
]);
delMedia
(
localMediaId
);
localMediaId
=
null
;
}
return
;
}
let
audio
=
mapMediaOption
(
document
.
getElementById
(
'
audioselect
'
).
value
);
let
video
=
mapMediaOption
(
document
.
getElementById
(
'
videoselect
'
).
value
);
setLocalMedia
(
false
);
if
(
!
audio
&&
!
video
)
return
;
...
...
@@ -302,15 +332,14 @@ async function setLocalMedia(setup) {
stream
=
await
navigator
.
mediaDevices
.
getUserMedia
(
constraints
);
}
catch
(
e
)
{
console
.
error
(
e
);
document
.
getElementById
(
'
presenterbox
'
).
checked
=
false
;
await
setLocalMedia
(
false
);
return
;
}
setMediaChoices
();
localMediaId
=
await
newUpStream
();
let
c
=
up
[
localMediaId
];
let
id
=
await
newUpStream
();
let
c
=
up
[
id
];
c
.
kind
=
'
local
'
;
c
.
stream
=
stream
;
stream
.
getTracks
().
forEach
(
t
=>
{
c
.
labels
[
t
.
id
]
=
t
.
kind
...
...
@@ -320,56 +349,77 @@ async function setLocalMedia(setup) {
},
2000
);
});
c
.
setInterval
(()
=>
{
displayStats
(
localMediaI
d
);
displayStats
(
i
d
);
},
2500
);
await
setMedia
(
localMediaId
);
await
setMedia
(
id
);
setButtonsVisibility
()
}
let
shareMediaId
=
null
;
async
function
setShareMedia
(
setup
)
{
async
function
addShareMedia
(
setup
)
{
if
(
!
getUserPass
())
return
;
if
(
!
setup
)
{
if
(
shareMediaId
)
{
up
[
shareMediaId
].
close
(
true
);
delete
(
up
[
shareMediaId
]);
delMedia
(
shareMediaId
)
shareMediaId
=
null
;
}
let
stream
=
null
;
try
{
stream
=
await
navigator
.
mediaDevices
.
getDisplayMedia
({});
}
catch
(
e
)
{
console
.
error
(
e
);
return
;
}
if
(
!
shareMediaId
)
{
let
stream
=
null
;
try
{
stream
=
await
navigator
.
mediaDevices
.
getDisplayMedia
({});
}
catch
(
e
)
{
console
.
error
(
e
);
document
.
getElementById
(
'
sharebox
'
).
checked
=
false
;
await
setShareMedia
(
false
);
return
;
}
shareMediaId
=
await
newUpStream
();
let
c
=
up
[
shareMediaId
];
c
.
stream
=
stream
;
stream
.
getTracks
().
forEach
(
t
=>
{
let
sender
=
c
.
pc
.
addTrack
(
t
,
stream
);
t
.
onended
=
e
=>
{
document
.
getElementById
(
'
sharebox
'
).
checked
=
false
;
setShareMedia
(
false
);
};
c
.
labels
[
t
.
id
]
=
'
screenshare
'
;
c
.
setInterval
(()
=>
{
updateStats
(
c
,
sender
);
},
2000
);
});
let
id
=
await
newUpStream
();
let
c
=
up
[
id
];
c
.
kind
=
'
screenshare
'
;
c
.
stream
=
stream
;
stream
.
getTracks
().
forEach
(
t
=>
{
let
sender
=
c
.
pc
.
addTrack
(
t
,
stream
);
t
.
onended
=
e
=>
{
delUpMedia
(
id
);
};
c
.
labels
[
t
.
id
]
=
'
screenshare
'
;
c
.
setInterval
(()
=>
{
displayStats
(
shareMediaId
);
},
2500
);
await
setMedia
(
shareMediaId
);
updateStats
(
c
,
sender
);
},
2000
);
});
c
.
setInterval
(()
=>
{
displayStats
(
id
);
},
2500
);
await
setMedia
(
id
);
setButtonsVisibility
()
}
function
delUpMedia
(
id
)
{
let
c
=
up
[
id
];
if
(
!
c
)
{
console
.
error
(
"
Deleting unknown up media
"
);
return
;
}
c
.
close
(
true
);
delMedia
(
id
);
delete
(
up
[
id
]);
setButtonsVisibility
()
}
function
delUpMediaKind
(
kind
)
{
for
(
let
id
in
up
)
{
let
c
=
up
[
id
];
if
(
c
.
kind
!=
kind
)
continue
c
.
close
(
true
);
delMedia
(
id
);
delete
(
up
[
id
]);
}
setButtonsVisibility
()
}
function
findUpMedia
(
kind
)
{
for
(
let
id
in
up
)
{
if
(
up
[
id
].
kind
===
kind
)
return
true
;
}
return
false
;
}
function
setMedia
(
id
)
{
...
...
@@ -493,12 +543,8 @@ function serverConnect() {
};
socket
.
onclose
=
function
(
e
)
{
setConnected
(
false
);
document
.
getElementById
(
'
presenterbox
'
).
checked
=
false
;
document
.
getElementById
(
'
presenterbox
'
).
disabled
=
true
;
setLocalMedia
(
false
);
document
.
getElementById
(
'
sharebox
'
).
checked
=
false
;
document
.
getElementById
(
'
sharebox
'
).
disabled
=
true
;
setShareMedia
(
false
);
delUpMediaKind
(
'
local
'
);
delUpMediaKind
(
'
screenshare
'
);
for
(
let
id
in
down
)
{
let
c
=
down
[
id
];
delete
(
down
[
id
]);
...
...
@@ -648,21 +694,7 @@ function gotClose(id) {
}
function
gotAbort
(
id
)
{
let
c
=
up
[
id
];
if
(
!
c
)
throw
new
Error
(
'
unknown up stream in abort
'
);
if
(
id
===
localMediaId
)
{
document
.
getElementById
(
'
presenterbox
'
).
checked
=
false
;
setLocalMedia
(
false
);
}
else
if
(
id
===
shareMediaId
)
{
document
.
getElementById
(
'
sharebox
'
).
checked
=
false
;
setShareMedia
(
false
);
}
else
{
console
.
error
(
'
Strange stream in abort
'
);
delMedia
(
id
);
c
.
pc
.
close
();
delete
(
up
[
id
]);
}
delUpMedia
(
id
);
}
async
function
gotICE
(
id
,
candidate
)
{
...
...
@@ -753,9 +785,8 @@ function clearUsername() {
function
gotPermissions
(
perm
)
{
permissions
=
perm
;
document
.
getElementById
(
'
presenterbox
'
).
disabled
=
!
perm
.
present
;
document
.
getElementById
(
'
sharebox
'
).
disabled
=
!
perm
.
present
;
displayUsername
();
setButtonsVisibility
();
}
const
urlRegexp
=
/https
?
:
\/\/[
-a-zA-Z0-9@:%
/
._
\\
+~#=?
]
+
[
-a-zA-Z0-9@:%
/
_
\\
+~#=
]
/g
;
...
...
@@ -1088,18 +1119,12 @@ async function getIceServers() {
iceServers
=
servers
;
}
async
function
doConnect
()
{
await
serverConnect
();
await
setLocalMedia
(
document
.
getElementById
(
'
presenterbox
'
).
checked
);
await
setShareMedia
(
document
.
getElementById
(
'
sharebox
'
).
checked
);
}
document
.
getElementById
(
'
userform
'
).
onsubmit
=
async
function
(
e
)
{
e
.
preventDefault
();
let
username
=
document
.
getElementById
(
'
username
'
).
value
.
trim
();
let
password
=
document
.
getElementById
(
'
password
'
).
value
;
setUserPass
(
username
,
password
);
await
do
Connect
();
await
server
Connect
();
};
document
.
getElementById
(
'
disconnectbutton
'
).
onclick
=
function
(
e
)
{
...
...
@@ -1121,7 +1146,7 @@ function start() {
}).
then
(
c
=>
{
let
userpass
=
getUserPass
();
if
(
userpass
)
do
Connect
();
return
server
Connect
();
});
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment