mirror of
https://github.com/jech/galene.git
synced 2024-12-22 23:35:46 +01:00
Make upConnections generic.
This commit is contained in:
parent
0f96f94417
commit
208f023d9e
5 changed files with 1300 additions and 1187 deletions
336
conn.go
336
conn.go
|
@ -7,338 +7,38 @@ package main
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"sfu/estimator"
|
||||
"sfu/jitter"
|
||||
"sfu/packetcache"
|
||||
"sfu/rtptime"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
"github.com/pion/webrtc/v2"
|
||||
)
|
||||
|
||||
type localTrackAction struct {
|
||||
add bool
|
||||
track downTrack
|
||||
}
|
||||
|
||||
type upTrack struct {
|
||||
track *webrtc.Track
|
||||
label string
|
||||
rate *estimator.Estimator
|
||||
cache *packetcache.Cache
|
||||
jitter *jitter.Estimator
|
||||
maxBitrate uint64
|
||||
lastPLI uint64
|
||||
lastFIR uint64
|
||||
firSeqno uint32
|
||||
|
||||
localCh chan localTrackAction // signals that local has changed
|
||||
writerDone chan struct{} // closed when the loop dies
|
||||
|
||||
mu sync.Mutex
|
||||
local []downTrack
|
||||
srTime uint64
|
||||
srNTPTime uint64
|
||||
srRTPTime uint32
|
||||
}
|
||||
|
||||
func (up *upTrack) notifyLocal(add bool, track downTrack) {
|
||||
select {
|
||||
case up.localCh <- localTrackAction{add, track}:
|
||||
case <-up.writerDone:
|
||||
}
|
||||
}
|
||||
|
||||
func (up *upTrack) addLocal(local downTrack) error {
|
||||
up.mu.Lock()
|
||||
for _, t := range up.local {
|
||||
if t == local {
|
||||
up.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
up.local = append(up.local, local)
|
||||
up.mu.Unlock()
|
||||
|
||||
up.notifyLocal(true, local)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (up *upTrack) delLocal(local downTrack) bool {
|
||||
up.mu.Lock()
|
||||
for i, l := range up.local {
|
||||
if l == local {
|
||||
up.local = append(up.local[:i], up.local[i+1:]...)
|
||||
up.mu.Unlock()
|
||||
up.notifyLocal(false, l)
|
||||
return true
|
||||
}
|
||||
}
|
||||
up.mu.Unlock()
|
||||
return false
|
||||
}
|
||||
|
||||
func (up *upTrack) getLocal() []downTrack {
|
||||
up.mu.Lock()
|
||||
defer up.mu.Unlock()
|
||||
local := make([]downTrack, len(up.local))
|
||||
copy(local, up.local)
|
||||
return local
|
||||
}
|
||||
|
||||
func (up *upTrack) hasRtcpFb(tpe, parameter string) bool {
|
||||
for _, fb := range up.track.Codec().RTCPFeedback {
|
||||
if fb.Type == tpe && fb.Parameter == parameter {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type iceConnection interface {
|
||||
addICECandidate(candidate *webrtc.ICECandidateInit) error
|
||||
flushICECandidates() error
|
||||
}
|
||||
|
||||
type upConnection struct {
|
||||
id string
|
||||
label string
|
||||
pc *webrtc.PeerConnection
|
||||
labels map[string]string
|
||||
iceCandidates []*webrtc.ICECandidateInit
|
||||
|
||||
mu sync.Mutex
|
||||
closed bool
|
||||
tracks []*upTrack
|
||||
local []downConnection
|
||||
}
|
||||
|
||||
var ErrConnectionClosed = errors.New("connection is closed")
|
||||
|
||||
func (up *upConnection) getTracks() []*upTrack {
|
||||
up.mu.Lock()
|
||||
defer up.mu.Unlock()
|
||||
tracks := make([]*upTrack, len(up.tracks))
|
||||
copy(tracks, up.tracks)
|
||||
return tracks
|
||||
}
|
||||
|
||||
func (up *upConnection) addLocal(local downConnection) error {
|
||||
up.mu.Lock()
|
||||
defer up.mu.Unlock()
|
||||
if up.closed {
|
||||
return ErrConnectionClosed
|
||||
}
|
||||
for _, t := range up.local {
|
||||
if t == local {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
up.local = append(up.local, local)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (up *upConnection) delLocal(local downConnection) bool {
|
||||
up.mu.Lock()
|
||||
defer up.mu.Unlock()
|
||||
for i, l := range up.local {
|
||||
if l == local {
|
||||
up.local = append(up.local[:i], up.local[i+1:]...)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (up *upConnection) getLocal() []downConnection {
|
||||
up.mu.Lock()
|
||||
defer up.mu.Unlock()
|
||||
local := make([]downConnection, len(up.local))
|
||||
copy(local, up.local)
|
||||
return local
|
||||
}
|
||||
|
||||
func (up *upConnection) Close() error {
|
||||
up.mu.Lock()
|
||||
defer up.mu.Unlock()
|
||||
|
||||
go func(local []downConnection) {
|
||||
for _, l := range local {
|
||||
l.Close()
|
||||
}
|
||||
}(up.local)
|
||||
|
||||
up.local = nil
|
||||
up.closed = true
|
||||
return up.pc.Close()
|
||||
}
|
||||
|
||||
func (up *upConnection) addICECandidate(candidate *webrtc.ICECandidateInit) error {
|
||||
if up.pc.RemoteDescription() != nil {
|
||||
return up.pc.AddICECandidate(*candidate)
|
||||
}
|
||||
up.iceCandidates = append(up.iceCandidates, candidate)
|
||||
return nil
|
||||
}
|
||||
|
||||
func flushICECandidates(pc *webrtc.PeerConnection, candidates []*webrtc.ICECandidateInit) error {
|
||||
if pc.RemoteDescription() == nil {
|
||||
return errors.New("flushICECandidates called in bad state")
|
||||
}
|
||||
|
||||
var err error
|
||||
for _, candidate := range candidates {
|
||||
err2 := pc.AddICECandidate(*candidate)
|
||||
if err == nil {
|
||||
err = err2
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (up *upConnection) flushICECandidates() error {
|
||||
err := flushICECandidates(up.pc, up.iceCandidates)
|
||||
up.iceCandidates = nil
|
||||
return err
|
||||
}
|
||||
|
||||
func getUpMid(pc *webrtc.PeerConnection, track *webrtc.Track) string {
|
||||
for _, t := range pc.GetTransceivers() {
|
||||
if t.Receiver() != nil && t.Receiver().Track() == track {
|
||||
return t.Mid()
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// called locked
|
||||
func (up *upConnection) complete() bool {
|
||||
for mid, _ := range up.labels {
|
||||
found := false
|
||||
for _, t := range up.tracks {
|
||||
m := getUpMid(up.pc, t.track)
|
||||
if m == mid {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type bitrate struct {
|
||||
bitrate uint64
|
||||
jiffies uint64
|
||||
}
|
||||
|
||||
const receiverReportTimeout = 8 * rtptime.JiffiesPerSec
|
||||
|
||||
func (br *bitrate) Set(bitrate uint64, now uint64) {
|
||||
// this is racy -- a reader might read the
|
||||
// data between the two writes. This shouldn't
|
||||
// matter, we'll recover at the next sample.
|
||||
atomic.StoreUint64(&br.bitrate, bitrate)
|
||||
atomic.StoreUint64(&br.jiffies, now)
|
||||
}
|
||||
|
||||
func (br *bitrate) Get(now uint64) uint64 {
|
||||
ts := atomic.LoadUint64(&br.jiffies)
|
||||
if now < ts || now-ts > receiverReportTimeout {
|
||||
return ^uint64(0)
|
||||
}
|
||||
return atomic.LoadUint64(&br.bitrate)
|
||||
}
|
||||
|
||||
type receiverStats struct {
|
||||
loss uint32
|
||||
jitter uint32
|
||||
jiffies uint64
|
||||
}
|
||||
|
||||
func (s *receiverStats) Set(loss uint8, jitter uint32, now uint64) {
|
||||
atomic.StoreUint32(&s.loss, uint32(loss))
|
||||
atomic.StoreUint32(&s.jitter, jitter)
|
||||
atomic.StoreUint64(&s.jiffies, now)
|
||||
}
|
||||
|
||||
func (s *receiverStats) Get(now uint64) (uint8, uint32) {
|
||||
ts := atomic.LoadUint64(&s.jiffies)
|
||||
if now < ts || now > ts+receiverReportTimeout {
|
||||
return 0, 0
|
||||
}
|
||||
return uint8(atomic.LoadUint32(&s.loss)), atomic.LoadUint32(&s.jitter)
|
||||
}
|
||||
|
||||
var ErrKeyframeNeeded = errors.New("keyframe needed")
|
||||
|
||||
type downTrack interface {
|
||||
WriteRTP(packat *rtp.Packet) error
|
||||
Accumulate(bytes uint32)
|
||||
GetMaxBitrate(now uint64) uint64
|
||||
type upConnection interface {
|
||||
addLocal(downConnection) error
|
||||
delLocal(downConnection) bool
|
||||
Id() string
|
||||
Label() string
|
||||
}
|
||||
|
||||
type rtpDownTrack struct {
|
||||
track *webrtc.Track
|
||||
remote *upTrack
|
||||
maxLossBitrate *bitrate
|
||||
maxREMBBitrate *bitrate
|
||||
rate *estimator.Estimator
|
||||
stats *receiverStats
|
||||
srTime uint64
|
||||
srNTPTime uint64
|
||||
rtt uint64
|
||||
}
|
||||
|
||||
func (down *rtpDownTrack) WriteRTP(packet *rtp.Packet) error {
|
||||
return down.track.WriteRTP(packet)
|
||||
}
|
||||
|
||||
func (down *rtpDownTrack) Accumulate(bytes uint32) {
|
||||
down.rate.Accumulate(bytes)
|
||||
}
|
||||
|
||||
func (down *rtpDownTrack) GetMaxBitrate(now uint64) uint64 {
|
||||
br1 := down.maxLossBitrate.Get(now)
|
||||
br2 := down.maxREMBBitrate.Get(now)
|
||||
if br1 < br2 {
|
||||
return br1
|
||||
}
|
||||
return br2
|
||||
type upTrack interface {
|
||||
addLocal(downTrack) error
|
||||
delLocal(downTrack) bool
|
||||
Label() string
|
||||
Codec() *webrtc.RTPCodec
|
||||
// get a recent packet. Returns 0 if the packet is not in cache.
|
||||
getRTP(seqno uint16, result []byte) uint16
|
||||
// returns the last timestamp, if possible
|
||||
getTimestamp() (uint32, bool)
|
||||
}
|
||||
|
||||
type downConnection interface {
|
||||
Close() error
|
||||
}
|
||||
|
||||
type rtpDownConnection struct {
|
||||
id string
|
||||
client *webClient
|
||||
pc *webrtc.PeerConnection
|
||||
remote *upConnection
|
||||
tracks []*rtpDownTrack
|
||||
iceCandidates []*webrtc.ICECandidateInit
|
||||
}
|
||||
|
||||
func (down *rtpDownConnection) Close() error {
|
||||
return down.client.action(delConnAction{down.id})
|
||||
}
|
||||
|
||||
func (down *rtpDownConnection) addICECandidate(candidate *webrtc.ICECandidateInit) error {
|
||||
if down.pc.RemoteDescription() != nil {
|
||||
return down.pc.AddICECandidate(*candidate)
|
||||
}
|
||||
down.iceCandidates = append(down.iceCandidates, candidate)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (down *rtpDownConnection) flushICECandidates() error {
|
||||
err := flushICECandidates(down.pc, down.iceCandidates)
|
||||
down.iceCandidates = nil
|
||||
return err
|
||||
type downTrack interface {
|
||||
WriteRTP(packat *rtp.Packet) error
|
||||
Accumulate(bytes uint32)
|
||||
GetMaxBitrate(now uint64) uint64
|
||||
}
|
||||
|
|
28
disk.go
28
disk.go
|
@ -24,15 +24,15 @@ type diskClient struct {
|
|||
closed bool
|
||||
}
|
||||
|
||||
func (client *diskClient) getGroup() *group {
|
||||
func (client *diskClient) Group() *group {
|
||||
return client.group
|
||||
}
|
||||
|
||||
func (client *diskClient) getId() string {
|
||||
func (client *diskClient) Id() string {
|
||||
return client.id
|
||||
}
|
||||
|
||||
func (client *diskClient) getUsername() string {
|
||||
func (client *diskClient) Username() string {
|
||||
return "RECORDING"
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ func (client *diskClient) Close() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (client *diskClient) pushConn(conn *upConnection, tracks []*upTrack, label string) error {
|
||||
func (client *diskClient) pushConn(conn upConnection, tracks []upTrack, label string) error {
|
||||
client.mu.Lock()
|
||||
defer client.mu.Unlock()
|
||||
|
||||
|
@ -75,15 +75,13 @@ func (client *diskClient) pushConn(conn *upConnection, tracks []*upTrack, label
|
|||
return nil
|
||||
}
|
||||
|
||||
var _ client = &diskClient{}
|
||||
|
||||
type diskConn struct {
|
||||
directory string
|
||||
label string
|
||||
|
||||
mu sync.Mutex
|
||||
file *os.File
|
||||
remote *upConnection
|
||||
remote upConnection
|
||||
tracks []*diskTrack
|
||||
width, height uint32
|
||||
}
|
||||
|
@ -154,7 +152,7 @@ func openDiskFile(directory, label string) (*os.File, error) {
|
|||
}
|
||||
|
||||
type diskTrack struct {
|
||||
remote *upTrack
|
||||
remote upTrack
|
||||
conn *diskConn
|
||||
|
||||
writer webm.BlockWriteCloser
|
||||
|
@ -162,7 +160,7 @@ type diskTrack struct {
|
|||
timestamp uint32
|
||||
}
|
||||
|
||||
func newDiskConn(directory, label string, up *upConnection, remoteTracks []*upTrack) (*diskConn, error) {
|
||||
func newDiskConn(directory, label string, up upConnection, remoteTracks []upTrack) (*diskConn, error) {
|
||||
conn := diskConn{
|
||||
directory: directory,
|
||||
label: label,
|
||||
|
@ -172,7 +170,7 @@ func newDiskConn(directory, label string, up *upConnection, remoteTracks []*upTr
|
|||
video := false
|
||||
for _, remote := range remoteTracks {
|
||||
var builder *samplebuilder.SampleBuilder
|
||||
switch remote.track.Codec().Name {
|
||||
switch remote.Codec().Name {
|
||||
case webrtc.Opus:
|
||||
builder = samplebuilder.New(16, &codecs.OpusPacket{})
|
||||
case webrtc.VP8:
|
||||
|
@ -245,7 +243,7 @@ func (t *diskTrack) WriteRTP(packet *rtp.Packet) error {
|
|||
|
||||
keyframe := true
|
||||
|
||||
switch t.remote.track.Codec().Name {
|
||||
switch t.remote.Codec().Name {
|
||||
case webrtc.VP8:
|
||||
if len(sample.Data) < 1 {
|
||||
return nil
|
||||
|
@ -265,7 +263,7 @@ func (t *diskTrack) WriteRTP(packet *rtp.Packet) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
tm := t.timestamp / (t.remote.track.Codec().ClockRate / 1000)
|
||||
tm := t.timestamp / (t.remote.Codec().ClockRate / 1000)
|
||||
_, err := t.writer.Write(keyframe, int64(tm), sample.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -275,7 +273,7 @@ func (t *diskTrack) WriteRTP(packet *rtp.Packet) error {
|
|||
|
||||
// called locked
|
||||
func (t *diskTrack) initWriter(data []byte) error {
|
||||
switch t.remote.track.Codec().Name {
|
||||
switch t.remote.Codec().Name {
|
||||
case webrtc.VP8:
|
||||
if len(data) < 10 {
|
||||
return nil
|
||||
|
@ -300,9 +298,9 @@ func (conn *diskConn) initWriter(width, height uint32) error {
|
|||
}
|
||||
var entries []webm.TrackEntry
|
||||
for i, t := range conn.tracks {
|
||||
codec := t.remote.track.Codec()
|
||||
codec := t.remote.Codec()
|
||||
var entry webm.TrackEntry
|
||||
switch t.remote.track.Codec().Name {
|
||||
switch t.remote.Codec().Name {
|
||||
case webrtc.Opus:
|
||||
entry = webm.TrackEntry{
|
||||
Name: "Audio",
|
||||
|
|
30
group.go
30
group.go
|
@ -22,10 +22,10 @@ import (
|
|||
)
|
||||
|
||||
type client interface {
|
||||
getGroup() *group
|
||||
getId() string
|
||||
getUsername() string
|
||||
pushConn(conn *upConnection, tracks []*upTrack, label string) error
|
||||
Group() *group
|
||||
Id() string
|
||||
Username() string
|
||||
pushConn(conn upConnection, tracks []upTrack, label string) error
|
||||
pushClient(id, username string, add bool) error
|
||||
}
|
||||
|
||||
|
@ -58,8 +58,8 @@ type delConnAction struct {
|
|||
}
|
||||
|
||||
type addConnAction struct {
|
||||
conn *upConnection
|
||||
tracks []*upTrack
|
||||
conn upConnection
|
||||
tracks []upTrack
|
||||
}
|
||||
|
||||
type addLabelAction struct {
|
||||
|
@ -230,20 +230,20 @@ func addClient(name string, c client, user, pass string) (*group, error) {
|
|||
return nil, userError("too many users")
|
||||
}
|
||||
}
|
||||
if g.clients[c.getId()] != nil {
|
||||
if g.clients[c.Id()] != nil {
|
||||
return nil, protocolError("duplicate client id")
|
||||
}
|
||||
|
||||
g.clients[c.getId()] = c
|
||||
g.clients[c.Id()] = c
|
||||
|
||||
go func(clients []client) {
|
||||
c.pushClient(c.getId(), c.getUsername(), true)
|
||||
c.pushClient(c.Id(), c.Username(), true)
|
||||
for _, cc := range clients {
|
||||
err := c.pushClient(cc.getId(), cc.getUsername(), true)
|
||||
err := c.pushClient(cc.Id(), cc.Username(), true)
|
||||
if err == ErrClientDead {
|
||||
return
|
||||
}
|
||||
cc.pushClient(c.getId(), c.getUsername(), true)
|
||||
cc.pushClient(c.Id(), c.Username(), true)
|
||||
}
|
||||
}(g.getClientsUnlocked(c))
|
||||
|
||||
|
@ -251,19 +251,19 @@ func addClient(name string, c client, user, pass string) (*group, error) {
|
|||
}
|
||||
|
||||
func delClient(c client) {
|
||||
g := c.getGroup()
|
||||
g := c.Group()
|
||||
g.mu.Lock()
|
||||
defer g.mu.Unlock()
|
||||
|
||||
if g.clients[c.getId()] != c {
|
||||
if g.clients[c.Id()] != c {
|
||||
log.Printf("Deleting unknown client")
|
||||
return
|
||||
}
|
||||
delete(g.clients, c.getId())
|
||||
delete(g.clients, c.Id())
|
||||
|
||||
go func(clients []client) {
|
||||
for _, cc := range clients {
|
||||
cc.pushClient(c.getId(), c.getUsername(), false)
|
||||
cc.pushClient(c.Id(), c.Username(), false)
|
||||
}
|
||||
}(g.getClientsUnlocked(nil))
|
||||
}
|
||||
|
|
1127
rtpconn.go
Normal file
1127
rtpconn.go
Normal file
File diff suppressed because it is too large
Load diff
966
webclient.go
966
webclient.go
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue