Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
sfu
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
Alain Takoudjou
sfu
Commits
a8a8ce6e
Commit
a8a8ce6e
authored
Jun 12, 2020
by
Juliusz Chroboczek
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
WIP: simulcast
parent
1e977213
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
204 additions
and
39 deletions
+204
-39
conn.go
conn.go
+17
-0
disk.go
disk.go
+24
-2
group.go
group.go
+0
-4
rtpconn.go
rtpconn.go
+64
-6
static/sfu.css
static/sfu.css
+5
-0
static/sfu.html
static/sfu.html
+10
-1
static/sfu.js
static/sfu.js
+72
-16
webclient.go
webclient.go
+12
-10
No files found.
conn.go
View file @
a8a8ce6e
...
...
@@ -7,6 +7,8 @@ package main
import
(
"errors"
"strconv"
"strings"
"github.com/pion/rtp"
"github.com/pion/webrtc/v2"
...
...
@@ -35,6 +37,7 @@ type upTrack interface {
type
downConnection
interface
{
GetMaxBitrate
(
now
uint64
)
uint64
GetVideoLayer
()
uint32
}
type
downTrack
interface
{
...
...
@@ -42,3 +45,17 @@ type downTrack interface {
Accumulate
(
bytes
uint32
)
setTimeOffset
(
ntp
uint64
,
rtp
uint32
)
}
func
splitLabel
(
label
string
)
(
string
,
uint32
)
{
split
:=
strings
.
SplitN
(
label
,
"-"
,
2
)
if
len
(
split
)
!=
2
{
return
label
,
^
uint32
(
0
)
}
n
,
err
:=
strconv
.
ParseUint
(
split
[
1
],
10
,
32
)
if
err
!=
nil
{
return
label
,
^
uint32
(
0
)
}
return
split
[
0
],
uint32
(
n
)
}
disk.go
View file @
a8a8ce6e
...
...
@@ -179,6 +179,24 @@ type diskTrack struct {
}
func
newDiskConn
(
directory
,
label
string
,
up
upConnection
,
remoteTracks
[]
upTrack
)
(
*
diskConn
,
error
)
{
// pick the best video track
var
video
upTrack
for
_
,
t
:=
range
remoteTracks
{
if
t
.
Codec
()
.
Name
!=
webrtc
.
VP8
{
continue
}
if
video
==
nil
{
video
=
t
}
else
{
_
,
s
:=
splitLabel
(
video
.
Label
())
_
,
r
:=
splitLabel
(
t
.
Label
())
if
s
<
r
{
video
=
t
}
}
}
conn
:=
diskConn
{
directory
:
directory
,
label
:
label
,
...
...
@@ -191,8 +209,8 @@ func newDiskConn(directory, label string, up upConnection, remoteTracks []upTrac
case
webrtc
.
Opus
:
builder
=
samplebuilder
.
New
(
16
,
&
codecs
.
OpusPacket
{})
case
webrtc
.
VP8
:
if
conn
.
hasV
ideo
{
return
nil
,
errors
.
New
(
"multiple video tracks not supported"
)
if
remote
!=
v
ideo
{
continue
}
builder
=
samplebuilder
.
New
(
32
,
&
codecs
.
VP8Packet
{})
conn
.
hasVideo
=
true
...
...
@@ -386,6 +404,10 @@ func (down *diskConn) GetMaxBitrate(now uint64) uint64 {
return
^
uint64
(
0
)
}
func
(
down
*
diskConn
)
GetVideoLayer
()
uint32
{
return
^
uint32
(
0
)
}
func
(
t
*
diskTrack
)
Accumulate
(
bytes
uint32
)
{
return
}
group.go
View file @
a8a8ce6e
...
...
@@ -36,10 +36,6 @@ type chatHistoryEntry struct {
me
bool
}
const
(
minBitrate
=
200000
)
type
group
struct
{
name
string
dead
bool
...
...
rtpconn.go
View file @
a8a8ce6e
...
...
@@ -10,6 +10,7 @@ import (
"io"
"log"
"math/bits"
"sort"
"sync"
"sync/atomic"
"time"
...
...
@@ -851,15 +852,57 @@ func sendUpRTCP(conn *rtpUpConnection) error {
},
}
rate
:=
^
uint64
(
0
)
layers
:=
make
([]
uint32
,
0
)
for
_
,
t
:=
range
conn
.
tracks
{
l
,
r
:=
splitLabel
(
t
.
Label
())
if
l
==
"video"
{
found
:=
false
for
_
,
rr
:=
range
layers
{
if
rr
==
r
{
found
=
true
break
}
}
if
!
found
{
layers
=
append
(
layers
,
r
)
}
}
}
sort
.
Slice
(
layers
,
func
(
i
,
j
int
)
bool
{
return
layers
[
i
]
<
layers
[
j
]
})
rates
:=
make
(
map
[
uint32
]
uint64
)
for
i
,
layer
:=
range
layers
{
if
i
<
len
(
layers
)
-
1
{
rates
[
layer
]
=
uint64
(
layers
[
i
+
1
])
*
1024
}
else
{
rates
[
layer
]
=
uint64
(
2
*
layer
)
*
1024
}
}
minBitrate
:=
func
(
l
uint32
)
uint64
{
if
len
(
layers
)
==
0
{
return
9600
}
for
i
:=
len
(
layers
)
-
1
;
i
>
1
;
i
--
{
if
l
>=
layers
[
i
]
{
return
uint64
(
layers
[
i
-
1
])
*
1000
}
}
return
9600
}
for
_
,
l
:=
range
conn
.
local
{
layer
:=
l
.
GetVideoLayer
()
r
:=
l
.
GetMaxBitrate
(
now
)
if
r
<
rate
{
rate
=
r
min
:=
minBitrate
(
layer
)
if
r
<
min
{
r
=
min
}
rate
,
ok
:=
rates
[
layer
]
if
!
ok
||
r
<
rate
{
rates
[
layer
]
=
r
}
}
if
rate
<
minBitrate
{
rate
=
minBitrate
}
var
ssrcs
[]
uint32
...
...
@@ -870,6 +913,10 @@ func sendUpRTCP(conn *rtpUpConnection) error {
ssrcs
=
append
(
ssrcs
,
t
.
track
.
SSRC
())
}
var
rate
uint64
for
_
,
r
:=
range
rates
{
rate
+=
r
}
if
len
(
ssrcs
)
>
0
{
packets
=
append
(
packets
,
&
rtcp
.
ReceiverEstimatedMaximumBitrate
{
...
...
@@ -1142,3 +1189,14 @@ func updateUpTrack(track *rtpUpTrack) {
}
track
.
cache
.
ResizeCond
(
packets
)
}
func
(
down
*
rtpDownConnection
)
GetVideoLayer
()
uint32
{
for
_
,
t
:=
range
down
.
tracks
{
label
:=
t
.
remote
.
Label
()
l
,
r
:=
splitLabel
(
label
)
if
l
==
"video"
{
return
r
}
}
return
0
}
static/sfu.css
View file @
a8a8ce6e
...
...
@@ -84,6 +84,11 @@ h1 {
margin-right
:
0.4em
;
}
#sendselect
{
width
:
8em
;
text-align-last
:
center
;
}
#requestselect
{
width
:
8em
;
text-align-last
:
center
;
...
...
static/sfu.html
View file @
a8a8ce6e
...
...
@@ -50,11 +50,20 @@
<button
id=
"sharebutton"
class=
"invisible"
>
Share screen
</button>
<button
id=
"unsharebutton"
class=
"invisible"
>
Stop sharing
</button>
<label
for=
"sendselect"
>
Send:
</label>
<select
id=
"sendselect"
>
<option
value=
"200"
>
LQ video
</option>
<option
value=
"800"
selected
>
normal video
</option>
<option
value=
"2000"
>
HQ video
</option>
</select>
<label
for=
"requestselect"
>
Receive:
</label>
<select
id=
"requestselect"
>
<option
value=
"audio"
>
audio only
</option>
<option
value=
"screenshare"
>
screen share
</option>
<option
value=
"everything"
selected
>
everything
</option>
<option
value=
"low"
>
LQ video
</option>
<option
value=
"normal"
selected
>
normal video
</option>
<option
value=
"high"
>
HQ video
</option>
</select>
</div>
</div>
...
...
static/sfu.js
View file @
a8a8ce6e
...
...
@@ -212,6 +212,11 @@ document.getElementById('requestselect').onchange = function(e) {
sendRequest
(
this
.
value
);
};
document
.
getElementById
(
'
sendselect
'
).
onchange
=
function
(
e
)
{
e
.
preventDefault
();
changePresentation
();
};
async
function
updateStats
(
conn
,
sender
)
{
let
tid
=
sender
.
track
&&
sender
.
track
.
id
;
if
(
!
tid
)
...
...
@@ -337,6 +342,8 @@ async function setMediaChoices() {
mediaChoicesDone
=
true
;
}
const
videoRates
=
[
200
,
800
,
2000
];
async
function
addLocalMedia
(
id
)
{
if
(
!
getUserPass
())
return
;
...
...
@@ -369,17 +376,41 @@ async function addLocalMedia(id) {
id
=
await
newUpStream
(
id
);
let
c
=
up
[
id
];
let
maxRate
=
document
.
getElementById
(
'
sendselect
'
).
value
;
let
rates
=
[];
if
(
maxRate
>
0
)
videoRates
.
forEach
(
r
=>
{
if
(
r
<=
maxRate
)
rates
.
push
(
r
);
});
else
console
.
warn
(
"
Couldn't parse video rate
"
);
c
.
kind
=
'
local
'
;
c
.
stream
=
stream
;
stream
.
getTracks
().
forEach
(
t
=>
{
c
.
labels
[
t
.
id
]
=
t
.
kind
if
(
t
.
kind
==
'
audio
'
&&
localMute
)
let
tracks
=
stream
.
getTracks
();
for
(
let
i
=
0
;
i
<
tracks
.
length
;
i
++
)
{
let
t
=
tracks
[
i
];
if
(
t
.
kind
===
'
audio
'
&&
localMute
)
t
.
enabled
=
false
;
let
sender
=
c
.
pc
.
addTrack
(
t
,
stream
);
c
.
setInterval
(()
=>
{
updateStats
(
c
,
sender
);
},
2000
);
});
let
senders
=
[];
if
(
t
.
kind
===
'
video
'
&&
rates
.
length
>
0
)
{
for
(
let
j
=
0
;
j
<
rates
.
length
;
j
++
)
{
let
tt
=
(
j
<
rates
.
length
-
1
)
?
t
.
clone
()
:
t
;
senders
.
push
(
c
.
pc
.
addTrack
(
tt
,
stream
));
c
.
labels
[
tt
.
id
]
=
t
.
kind
+
'
-
'
+
rates
[
j
];
}
}
else
{
senders
.
push
(
c
.
pc
.
addTrack
(
t
,
stream
));
c
.
labels
[
t
.
id
]
=
t
.
kind
}
senders
.
forEach
(
sender
=>
{
c
.
setInterval
(()
=>
{
updateStats
(
c
,
sender
);
},
2000
);
});
}
c
.
setInterval
(()
=>
{
displayStats
(
id
);
},
2500
);
...
...
@@ -700,11 +731,18 @@ function sendRequest(value) {
case
'
screenshare
'
:
request
=
{
audio
:
true
,
screenshare
:
true
};
break
;
case
'
everything
'
:
case
'
low
'
:
request
=
{
audio
:
true
,
screenshare
:
true
,
video
:
200
};
break
;
case
'
normal
'
:
request
=
{
audio
:
true
,
screenshare
:
true
,
video
:
800
};
break
;
case
'
high
'
:
request
=
{
audio
:
true
,
screenshare
:
true
,
video
:
true
};
break
;
default
:
console
.
error
(
`Uknown value
${
value
}
in sendRequest`
);
console
.
warn
(
`Uknown value
${
value
}
in sendRequest`
);
request
=
{
audio
:
true
,
screenshare
:
true
,
video
:
500
};
break
;
}
...
...
@@ -1222,17 +1260,35 @@ async function negotiate(id) {
throw
(
new
Error
(
"
Didn't create offer
"
));
await
c
.
pc
.
setLocalDescription
(
offer
);
// mids are not known until this point
// mids a
nd encodings a
re not known until this point
if
(
typeof
(
c
.
pc
.
getTransceivers
)
===
'
function
'
)
{
c
.
pc
.
getTransceivers
().
forEach
(
t
=>
{
let
transceivers
=
c
.
pc
.
getTransceivers
();
for
(
let
i
=
0
;
i
<
transceivers
.
length
;
i
++
)
{
let
t
=
transceivers
[
i
];
if
(
t
.
sender
&&
t
.
sender
.
track
)
{
let
label
=
c
.
labels
[
t
.
sender
.
track
.
id
];
if
(
label
)
c
.
labelsByMid
[
t
.
mid
]
=
label
;
else
if
(
!
label
)
{
console
.
warn
(
"
Couldn't find label for track
"
);
continue
;
}
c
.
labelsByMid
[
t
.
mid
]
=
label
;
let
dash
=
label
.
indexOf
(
'
-
'
);
if
(
dash
>=
0
)
{
let
rate
=
parseInt
(
label
.
slice
(
dash
+
1
));
if
(
rate
>
0
)
{
let
p
=
t
.
sender
.
getParameters
();
p
.
encodings
.
forEach
(
e
=>
{
e
.
maxBitrate
=
rate
*
1000
;
});
try
{
await
t
.
sender
.
setParameters
(
p
);
}
catch
(
e
)
{
console
.
warn
(
e
);
}
}
}
}
}
);
}
}
else
{
console
.
warn
(
'
getTransceivers undefined
'
);
displayWarning
(
'
getTransceivers undefined, please upgrade your browser
'
);
...
...
webclient.go
View file @
a8a8ce6e
...
...
@@ -534,19 +534,20 @@ func pushConns(c client) {
}
}
func
(
c
*
webClient
)
isRequested
(
label
string
)
bool
{
return
c
.
requested
[
label
]
!=
0
}
func
addDownConnTracks
(
c
*
webClient
,
remote
upConnection
,
tracks
[]
upTrack
)
(
*
rtpDownConnection
,
error
)
{
requested
:=
false
requested
:=
make
(
map
[
string
]
uint32
)
for
_
,
t
:=
range
tracks
{
if
c
.
isRequested
(
t
.
Label
())
{
requested
=
true
break
l
,
r
:=
splitLabel
(
t
.
Label
())
rate
:=
c
.
requested
[
l
]
if
rate
!=
0
{
if
requested
[
l
]
==
0
||
(
r
<=
rate
&&
r
>
requested
[
l
])
||
(
requested
[
l
]
>
rate
&&
r
<
requested
[
l
])
{
requested
[
l
]
=
r
}
}
}
if
!
requested
{
if
len
(
requested
)
==
0
{
return
nil
,
nil
}
...
...
@@ -556,7 +557,8 @@ func addDownConnTracks(c *webClient, remote upConnection, tracks []upTrack) (*rt
}
for
_
,
t
:=
range
tracks
{
if
!
c
.
isRequested
(
t
.
Label
())
{
l
,
r
:=
splitLabel
(
t
.
Label
())
if
r
!=
requested
[
l
]
{
continue
}
_
,
err
=
addDownTrack
(
c
,
down
,
t
,
remote
)
...
...
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