1
Fork 0
mirror of https://github.com/jech/galene.git synced 2024-11-26 02:25:58 +01:00

Make upConnections generic.

This commit is contained in:
Juliusz Chroboczek 2020-06-08 22:14:28 +02:00
parent 0f96f94417
commit 208f023d9e
5 changed files with 1300 additions and 1187 deletions

336
conn.go
View file

@ -7,338 +7,38 @@ package main
import ( import (
"errors" "errors"
"sync"
"sync/atomic"
"sfu/estimator"
"sfu/jitter"
"sfu/packetcache"
"sfu/rtptime"
"github.com/pion/rtp" "github.com/pion/rtp"
"github.com/pion/webrtc/v2" "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") 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") var ErrKeyframeNeeded = errors.New("keyframe needed")
type downTrack interface { type upConnection interface {
WriteRTP(packat *rtp.Packet) error addLocal(downConnection) error
Accumulate(bytes uint32) delLocal(downConnection) bool
GetMaxBitrate(now uint64) uint64 Id() string
Label() string
} }
type rtpDownTrack struct { type upTrack interface {
track *webrtc.Track addLocal(downTrack) error
remote *upTrack delLocal(downTrack) bool
maxLossBitrate *bitrate Label() string
maxREMBBitrate *bitrate Codec() *webrtc.RTPCodec
rate *estimator.Estimator // get a recent packet. Returns 0 if the packet is not in cache.
stats *receiverStats getRTP(seqno uint16, result []byte) uint16
srTime uint64 // returns the last timestamp, if possible
srNTPTime uint64 getTimestamp() (uint32, bool)
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 downConnection interface { type downConnection interface {
Close() error Close() error
} }
type rtpDownConnection struct { type downTrack interface {
id string WriteRTP(packat *rtp.Packet) error
client *webClient Accumulate(bytes uint32)
pc *webrtc.PeerConnection GetMaxBitrate(now uint64) uint64
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
} }

28
disk.go
View file

@ -24,15 +24,15 @@ type diskClient struct {
closed bool closed bool
} }
func (client *diskClient) getGroup() *group { func (client *diskClient) Group() *group {
return client.group return client.group
} }
func (client *diskClient) getId() string { func (client *diskClient) Id() string {
return client.id return client.id
} }
func (client *diskClient) getUsername() string { func (client *diskClient) Username() string {
return "RECORDING" return "RECORDING"
} }
@ -52,7 +52,7 @@ func (client *diskClient) Close() error {
return nil 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() client.mu.Lock()
defer client.mu.Unlock() defer client.mu.Unlock()
@ -75,15 +75,13 @@ func (client *diskClient) pushConn(conn *upConnection, tracks []*upTrack, label
return nil return nil
} }
var _ client = &diskClient{}
type diskConn struct { type diskConn struct {
directory string directory string
label string label string
mu sync.Mutex mu sync.Mutex
file *os.File file *os.File
remote *upConnection remote upConnection
tracks []*diskTrack tracks []*diskTrack
width, height uint32 width, height uint32
} }
@ -154,7 +152,7 @@ func openDiskFile(directory, label string) (*os.File, error) {
} }
type diskTrack struct { type diskTrack struct {
remote *upTrack remote upTrack
conn *diskConn conn *diskConn
writer webm.BlockWriteCloser writer webm.BlockWriteCloser
@ -162,7 +160,7 @@ type diskTrack struct {
timestamp uint32 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{ conn := diskConn{
directory: directory, directory: directory,
label: label, label: label,
@ -172,7 +170,7 @@ func newDiskConn(directory, label string, up *upConnection, remoteTracks []*upTr
video := false video := false
for _, remote := range remoteTracks { for _, remote := range remoteTracks {
var builder *samplebuilder.SampleBuilder var builder *samplebuilder.SampleBuilder
switch remote.track.Codec().Name { switch remote.Codec().Name {
case webrtc.Opus: case webrtc.Opus:
builder = samplebuilder.New(16, &codecs.OpusPacket{}) builder = samplebuilder.New(16, &codecs.OpusPacket{})
case webrtc.VP8: case webrtc.VP8:
@ -245,7 +243,7 @@ func (t *diskTrack) WriteRTP(packet *rtp.Packet) error {
keyframe := true keyframe := true
switch t.remote.track.Codec().Name { switch t.remote.Codec().Name {
case webrtc.VP8: case webrtc.VP8:
if len(sample.Data) < 1 { if len(sample.Data) < 1 {
return nil return nil
@ -265,7 +263,7 @@ func (t *diskTrack) WriteRTP(packet *rtp.Packet) error {
return nil 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) _, err := t.writer.Write(keyframe, int64(tm), sample.Data)
if err != nil { if err != nil {
return err return err
@ -275,7 +273,7 @@ func (t *diskTrack) WriteRTP(packet *rtp.Packet) error {
// called locked // called locked
func (t *diskTrack) initWriter(data []byte) error { func (t *diskTrack) initWriter(data []byte) error {
switch t.remote.track.Codec().Name { switch t.remote.Codec().Name {
case webrtc.VP8: case webrtc.VP8:
if len(data) < 10 { if len(data) < 10 {
return nil return nil
@ -300,9 +298,9 @@ func (conn *diskConn) initWriter(width, height uint32) error {
} }
var entries []webm.TrackEntry var entries []webm.TrackEntry
for i, t := range conn.tracks { for i, t := range conn.tracks {
codec := t.remote.track.Codec() codec := t.remote.Codec()
var entry webm.TrackEntry var entry webm.TrackEntry
switch t.remote.track.Codec().Name { switch t.remote.Codec().Name {
case webrtc.Opus: case webrtc.Opus:
entry = webm.TrackEntry{ entry = webm.TrackEntry{
Name: "Audio", Name: "Audio",

View file

@ -22,10 +22,10 @@ import (
) )
type client interface { type client interface {
getGroup() *group Group() *group
getId() string Id() string
getUsername() string Username() string
pushConn(conn *upConnection, tracks []*upTrack, label string) error pushConn(conn upConnection, tracks []upTrack, label string) error
pushClient(id, username string, add bool) error pushClient(id, username string, add bool) error
} }
@ -58,8 +58,8 @@ type delConnAction struct {
} }
type addConnAction struct { type addConnAction struct {
conn *upConnection conn upConnection
tracks []*upTrack tracks []upTrack
} }
type addLabelAction struct { type addLabelAction struct {
@ -230,20 +230,20 @@ func addClient(name string, c client, user, pass string) (*group, error) {
return nil, userError("too many users") return nil, userError("too many users")
} }
} }
if g.clients[c.getId()] != nil { if g.clients[c.Id()] != nil {
return nil, protocolError("duplicate client id") return nil, protocolError("duplicate client id")
} }
g.clients[c.getId()] = c g.clients[c.Id()] = c
go func(clients []client) { go func(clients []client) {
c.pushClient(c.getId(), c.getUsername(), true) c.pushClient(c.Id(), c.Username(), true)
for _, cc := range clients { for _, cc := range clients {
err := c.pushClient(cc.getId(), cc.getUsername(), true) err := c.pushClient(cc.Id(), cc.Username(), true)
if err == ErrClientDead { if err == ErrClientDead {
return return
} }
cc.pushClient(c.getId(), c.getUsername(), true) cc.pushClient(c.Id(), c.Username(), true)
} }
}(g.getClientsUnlocked(c)) }(g.getClientsUnlocked(c))
@ -251,19 +251,19 @@ func addClient(name string, c client, user, pass string) (*group, error) {
} }
func delClient(c client) { func delClient(c client) {
g := c.getGroup() g := c.Group()
g.mu.Lock() g.mu.Lock()
defer g.mu.Unlock() defer g.mu.Unlock()
if g.clients[c.getId()] != c { if g.clients[c.Id()] != c {
log.Printf("Deleting unknown client") log.Printf("Deleting unknown client")
return return
} }
delete(g.clients, c.getId()) delete(g.clients, c.Id())
go func(clients []client) { go func(clients []client) {
for _, cc := range clients { for _, cc := range clients {
cc.pushClient(c.getId(), c.getUsername(), false) cc.pushClient(c.Id(), c.Username(), false)
} }
}(g.getClientsUnlocked(nil)) }(g.getClientsUnlocked(nil))
} }

1127
rtpconn.go Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff