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:
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 (
|
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
28
disk.go
|
@ -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",
|
||||||
|
|
30
group.go
30
group.go
|
@ -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
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