直播:后台 JWT 推流、前台画中画;WebRTC 服务与 Nginx WebSocket 代理
Made-with: Cursor
This commit is contained in:
170
server/vendor/github.com/pion/transport/v2/udp/batchconn.go
generated
vendored
Normal file
170
server/vendor/github.com/pion/transport/v2/udp/batchconn.go
generated
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package udp
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
// BatchWriter represents conn can write messages in batch
|
||||
type BatchWriter interface {
|
||||
WriteBatch(ms []ipv4.Message, flags int) (int, error)
|
||||
}
|
||||
|
||||
// BatchReader represents conn can read messages in batch
|
||||
type BatchReader interface {
|
||||
ReadBatch(msg []ipv4.Message, flags int) (int, error)
|
||||
}
|
||||
|
||||
// BatchPacketConn represents conn can read/write messages in batch
|
||||
type BatchPacketConn interface {
|
||||
BatchWriter
|
||||
BatchReader
|
||||
io.Closer
|
||||
}
|
||||
|
||||
// BatchConn uses ipv4/v6.NewPacketConn to wrap a net.PacketConn to write/read messages in batch,
|
||||
// only available in linux. In other platform, it will use single Write/Read as same as net.Conn.
|
||||
type BatchConn struct {
|
||||
net.PacketConn
|
||||
|
||||
batchConn BatchPacketConn
|
||||
|
||||
batchWriteMutex sync.Mutex
|
||||
batchWriteMessages []ipv4.Message
|
||||
batchWritePos int
|
||||
batchWriteLast time.Time
|
||||
|
||||
batchWriteSize int
|
||||
batchWriteInterval time.Duration
|
||||
|
||||
closed int32
|
||||
}
|
||||
|
||||
// NewBatchConn creates a *BatchConn from net.PacketConn with batch configs.
|
||||
func NewBatchConn(conn net.PacketConn, batchWriteSize int, batchWriteInterval time.Duration) *BatchConn {
|
||||
bc := &BatchConn{
|
||||
PacketConn: conn,
|
||||
batchWriteLast: time.Now(),
|
||||
batchWriteInterval: batchWriteInterval,
|
||||
batchWriteSize: batchWriteSize,
|
||||
batchWriteMessages: make([]ipv4.Message, batchWriteSize),
|
||||
}
|
||||
for i := range bc.batchWriteMessages {
|
||||
bc.batchWriteMessages[i].Buffers = [][]byte{make([]byte, sendMTU)}
|
||||
}
|
||||
|
||||
// batch write only supports linux
|
||||
if runtime.GOOS == "linux" {
|
||||
if pc4 := ipv4.NewPacketConn(conn); pc4 != nil {
|
||||
bc.batchConn = pc4
|
||||
} else if pc6 := ipv6.NewPacketConn(conn); pc6 != nil {
|
||||
bc.batchConn = pc6
|
||||
}
|
||||
}
|
||||
|
||||
if bc.batchConn != nil {
|
||||
go func() {
|
||||
writeTicker := time.NewTicker(batchWriteInterval / 2)
|
||||
defer writeTicker.Stop()
|
||||
for atomic.LoadInt32(&bc.closed) != 1 {
|
||||
<-writeTicker.C
|
||||
bc.batchWriteMutex.Lock()
|
||||
if bc.batchWritePos > 0 && time.Since(bc.batchWriteLast) >= bc.batchWriteInterval {
|
||||
_ = bc.flush()
|
||||
}
|
||||
bc.batchWriteMutex.Unlock()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return bc
|
||||
}
|
||||
|
||||
// Close batchConn and the underlying PacketConn
|
||||
func (c *BatchConn) Close() error {
|
||||
atomic.StoreInt32(&c.closed, 1)
|
||||
c.batchWriteMutex.Lock()
|
||||
if c.batchWritePos > 0 {
|
||||
_ = c.flush()
|
||||
}
|
||||
c.batchWriteMutex.Unlock()
|
||||
if c.batchConn != nil {
|
||||
return c.batchConn.Close()
|
||||
}
|
||||
return c.PacketConn.Close()
|
||||
}
|
||||
|
||||
// WriteTo write message to an UDPAddr, addr should be nil if it is a connected socket.
|
||||
func (c *BatchConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
||||
if c.batchConn == nil {
|
||||
return c.PacketConn.WriteTo(b, addr)
|
||||
}
|
||||
return c.enqueueMessage(b, addr)
|
||||
}
|
||||
|
||||
func (c *BatchConn) enqueueMessage(buf []byte, raddr net.Addr) (int, error) {
|
||||
var err error
|
||||
c.batchWriteMutex.Lock()
|
||||
defer c.batchWriteMutex.Unlock()
|
||||
|
||||
msg := &c.batchWriteMessages[c.batchWritePos]
|
||||
// reset buffers
|
||||
msg.Buffers = msg.Buffers[:1]
|
||||
msg.Buffers[0] = msg.Buffers[0][:cap(msg.Buffers[0])]
|
||||
|
||||
c.batchWritePos++
|
||||
if raddr != nil {
|
||||
msg.Addr = raddr
|
||||
}
|
||||
if n := copy(msg.Buffers[0], buf); n < len(buf) {
|
||||
extraBuffer := make([]byte, len(buf)-n)
|
||||
copy(extraBuffer, buf[n:])
|
||||
msg.Buffers = append(msg.Buffers, extraBuffer)
|
||||
} else {
|
||||
msg.Buffers[0] = msg.Buffers[0][:n]
|
||||
}
|
||||
if c.batchWritePos == c.batchWriteSize {
|
||||
err = c.flush()
|
||||
}
|
||||
return len(buf), err
|
||||
}
|
||||
|
||||
// ReadBatch reads messages in batch, return length of message readed and error.
|
||||
func (c *BatchConn) ReadBatch(msgs []ipv4.Message, flags int) (int, error) {
|
||||
if c.batchConn == nil {
|
||||
n, addr, err := c.PacketConn.ReadFrom(msgs[0].Buffers[0])
|
||||
if err == nil {
|
||||
msgs[0].N = n
|
||||
msgs[0].Addr = addr
|
||||
return 1, nil
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
return c.batchConn.ReadBatch(msgs, flags)
|
||||
}
|
||||
|
||||
func (c *BatchConn) flush() error {
|
||||
var writeErr error
|
||||
var txN int
|
||||
for txN < c.batchWritePos {
|
||||
n, err := c.batchConn.WriteBatch(c.batchWriteMessages[txN:c.batchWritePos], 0)
|
||||
if err != nil {
|
||||
writeErr = err
|
||||
break
|
||||
}
|
||||
txN += n
|
||||
}
|
||||
c.batchWritePos = 0
|
||||
c.batchWriteLast = time.Now()
|
||||
return writeErr
|
||||
}
|
||||
406
server/vendor/github.com/pion/transport/v2/udp/conn.go
generated
vendored
Normal file
406
server/vendor/github.com/pion/transport/v2/udp/conn.go
generated
vendored
Normal file
@@ -0,0 +1,406 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package udp provides a connection-oriented listener over a UDP PacketConn
|
||||
package udp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/transport/v2/deadline"
|
||||
"github.com/pion/transport/v2/packetio"
|
||||
"golang.org/x/net/ipv4"
|
||||
)
|
||||
|
||||
const (
|
||||
receiveMTU = 8192
|
||||
sendMTU = 1500
|
||||
defaultListenBacklog = 128 // same as Linux default
|
||||
)
|
||||
|
||||
// Typed errors
|
||||
var (
|
||||
ErrClosedListener = errors.New("udp: listener closed")
|
||||
ErrListenQueueExceeded = errors.New("udp: listen queue exceeded")
|
||||
ErrInvalidBatchConfig = errors.New("udp: invalid batch config")
|
||||
)
|
||||
|
||||
// listener augments a connection-oriented Listener over a UDP PacketConn
|
||||
type listener struct {
|
||||
pConn net.PacketConn
|
||||
|
||||
readBatchSize int
|
||||
|
||||
accepting atomic.Value // bool
|
||||
acceptCh chan *Conn
|
||||
doneCh chan struct{}
|
||||
doneOnce sync.Once
|
||||
acceptFilter func([]byte) bool
|
||||
|
||||
connLock sync.Mutex
|
||||
conns map[string]*Conn
|
||||
connWG *sync.WaitGroup
|
||||
|
||||
readWG sync.WaitGroup
|
||||
errClose atomic.Value // error
|
||||
|
||||
readDoneCh chan struct{}
|
||||
errRead atomic.Value // error
|
||||
|
||||
logger logging.LeveledLogger
|
||||
}
|
||||
|
||||
// Accept waits for and returns the next connection to the listener.
|
||||
func (l *listener) Accept() (net.Conn, error) {
|
||||
select {
|
||||
case c := <-l.acceptCh:
|
||||
l.connWG.Add(1)
|
||||
return c, nil
|
||||
|
||||
case <-l.readDoneCh:
|
||||
err, _ := l.errRead.Load().(error)
|
||||
return nil, err
|
||||
|
||||
case <-l.doneCh:
|
||||
return nil, ErrClosedListener
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes the listener.
|
||||
// Any blocked Accept operations will be unblocked and return errors.
|
||||
func (l *listener) Close() error {
|
||||
var err error
|
||||
l.doneOnce.Do(func() {
|
||||
l.accepting.Store(false)
|
||||
close(l.doneCh)
|
||||
|
||||
l.connLock.Lock()
|
||||
// Close unaccepted connections
|
||||
lclose:
|
||||
for {
|
||||
select {
|
||||
case c := <-l.acceptCh:
|
||||
close(c.doneCh)
|
||||
delete(l.conns, c.rAddr.String())
|
||||
|
||||
default:
|
||||
break lclose
|
||||
}
|
||||
}
|
||||
nConns := len(l.conns)
|
||||
l.connLock.Unlock()
|
||||
|
||||
l.connWG.Done()
|
||||
|
||||
if nConns == 0 {
|
||||
// Wait if this is the final connection
|
||||
l.readWG.Wait()
|
||||
if errClose, ok := l.errClose.Load().(error); ok {
|
||||
err = errClose
|
||||
}
|
||||
} else {
|
||||
err = nil
|
||||
}
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Addr returns the listener's network address.
|
||||
func (l *listener) Addr() net.Addr {
|
||||
return l.pConn.LocalAddr()
|
||||
}
|
||||
|
||||
// BatchIOConfig indicates config to batch read/write packets,
|
||||
// it will use ReadBatch/WriteBatch to improve throughput for UDP.
|
||||
type BatchIOConfig struct {
|
||||
Enable bool
|
||||
// ReadBatchSize indicates the maximum number of packets to be read in one batch, a batch size less than 2 means
|
||||
// disable read batch.
|
||||
ReadBatchSize int
|
||||
// WriteBatchSize indicates the maximum number of packets to be written in one batch
|
||||
WriteBatchSize int
|
||||
// WriteBatchInterval indicates the maximum interval to wait before writing packets in one batch
|
||||
// small interval will reduce latency/jitter, but increase the io count.
|
||||
WriteBatchInterval time.Duration
|
||||
}
|
||||
|
||||
// ListenConfig stores options for listening to an address.
|
||||
type ListenConfig struct {
|
||||
// Backlog defines the maximum length of the queue of pending
|
||||
// connections. It is equivalent of the backlog argument of
|
||||
// POSIX listen function.
|
||||
// If a connection request arrives when the queue is full,
|
||||
// the request will be silently discarded, unlike TCP.
|
||||
// Set zero to use default value 128 which is same as Linux default.
|
||||
Backlog int
|
||||
|
||||
// AcceptFilter determines whether the new conn should be made for
|
||||
// the incoming packet. If not set, any packet creates new conn.
|
||||
AcceptFilter func([]byte) bool
|
||||
|
||||
// ReadBufferSize sets the size of the operating system's
|
||||
// receive buffer associated with the listener.
|
||||
ReadBufferSize int
|
||||
|
||||
// WriteBufferSize sets the size of the operating system's
|
||||
// send buffer associated with the connection.
|
||||
WriteBufferSize int
|
||||
|
||||
Batch BatchIOConfig
|
||||
|
||||
LoggerFactory logging.LoggerFactory
|
||||
}
|
||||
|
||||
// Listen creates a new listener based on the ListenConfig.
|
||||
func (lc *ListenConfig) Listen(network string, laddr *net.UDPAddr) (net.Listener, error) {
|
||||
if lc.Backlog == 0 {
|
||||
lc.Backlog = defaultListenBacklog
|
||||
}
|
||||
|
||||
if lc.Batch.Enable && (lc.Batch.WriteBatchSize <= 0 || lc.Batch.WriteBatchInterval <= 0) {
|
||||
return nil, ErrInvalidBatchConfig
|
||||
}
|
||||
|
||||
conn, err := net.ListenUDP(network, laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if lc.ReadBufferSize > 0 {
|
||||
_ = conn.SetReadBuffer(lc.ReadBufferSize)
|
||||
}
|
||||
if lc.WriteBufferSize > 0 {
|
||||
_ = conn.SetWriteBuffer(lc.WriteBufferSize)
|
||||
}
|
||||
|
||||
loggerFactory := lc.LoggerFactory
|
||||
if loggerFactory == nil {
|
||||
loggerFactory = logging.NewDefaultLoggerFactory()
|
||||
}
|
||||
|
||||
logger := loggerFactory.NewLogger("transport")
|
||||
|
||||
l := &listener{
|
||||
pConn: conn,
|
||||
acceptCh: make(chan *Conn, lc.Backlog),
|
||||
conns: make(map[string]*Conn),
|
||||
doneCh: make(chan struct{}),
|
||||
acceptFilter: lc.AcceptFilter,
|
||||
connWG: &sync.WaitGroup{},
|
||||
readDoneCh: make(chan struct{}),
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
if lc.Batch.Enable {
|
||||
l.pConn = NewBatchConn(conn, lc.Batch.WriteBatchSize, lc.Batch.WriteBatchInterval)
|
||||
l.readBatchSize = lc.Batch.ReadBatchSize
|
||||
}
|
||||
|
||||
l.accepting.Store(true)
|
||||
l.connWG.Add(1)
|
||||
l.readWG.Add(2) // wait readLoop and Close execution routine
|
||||
|
||||
go l.readLoop()
|
||||
go func() {
|
||||
l.connWG.Wait()
|
||||
if err := l.pConn.Close(); err != nil {
|
||||
l.errClose.Store(err)
|
||||
}
|
||||
l.readWG.Done()
|
||||
}()
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// Listen creates a new listener using default ListenConfig.
|
||||
func Listen(network string, laddr *net.UDPAddr) (net.Listener, error) {
|
||||
return (&ListenConfig{}).Listen(network, laddr)
|
||||
}
|
||||
|
||||
// readLoop has to tasks:
|
||||
// 1. Dispatching incoming packets to the correct Conn.
|
||||
// It can therefore not be ended until all Conns are closed.
|
||||
// 2. Creating a new Conn when receiving from a new remote.
|
||||
func (l *listener) readLoop() {
|
||||
defer l.readWG.Done()
|
||||
defer close(l.readDoneCh)
|
||||
|
||||
if br, ok := l.pConn.(BatchReader); ok && l.readBatchSize > 1 {
|
||||
l.readBatch(br)
|
||||
} else {
|
||||
l.read()
|
||||
}
|
||||
}
|
||||
|
||||
func (l *listener) readBatch(br BatchReader) {
|
||||
msgs := make([]ipv4.Message, l.readBatchSize)
|
||||
for i := range msgs {
|
||||
msg := &msgs[i]
|
||||
msg.Buffers = [][]byte{make([]byte, receiveMTU)}
|
||||
msg.OOB = make([]byte, 40)
|
||||
}
|
||||
for {
|
||||
n, err := br.ReadBatch(msgs, 0)
|
||||
if err != nil {
|
||||
l.errRead.Store(err)
|
||||
return
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
l.dispatchMsg(msgs[i].Addr, msgs[i].Buffers[0][:msgs[i].N])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *listener) read() {
|
||||
buf := make([]byte, receiveMTU)
|
||||
for {
|
||||
n, raddr, err := l.pConn.ReadFrom(buf)
|
||||
if err != nil {
|
||||
l.errRead.Store(err)
|
||||
l.logger.Tracef("error reading from connection err=%v", err)
|
||||
return
|
||||
}
|
||||
l.dispatchMsg(raddr, buf[:n])
|
||||
}
|
||||
}
|
||||
|
||||
func (l *listener) dispatchMsg(addr net.Addr, buf []byte) {
|
||||
conn, ok, err := l.getConn(addr, buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if ok {
|
||||
_, err := conn.buffer.Write(buf)
|
||||
if err != nil {
|
||||
l.logger.Tracef("error dispatching message addr=%v err=%v", addr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *listener) getConn(raddr net.Addr, buf []byte) (*Conn, bool, error) {
|
||||
l.connLock.Lock()
|
||||
defer l.connLock.Unlock()
|
||||
conn, ok := l.conns[raddr.String()]
|
||||
if !ok {
|
||||
if isAccepting, ok := l.accepting.Load().(bool); !isAccepting || !ok {
|
||||
return nil, false, ErrClosedListener
|
||||
}
|
||||
if l.acceptFilter != nil {
|
||||
if !l.acceptFilter(buf) {
|
||||
return nil, false, nil
|
||||
}
|
||||
}
|
||||
conn = l.newConn(raddr)
|
||||
select {
|
||||
case l.acceptCh <- conn:
|
||||
l.conns[raddr.String()] = conn
|
||||
default:
|
||||
return nil, false, ErrListenQueueExceeded
|
||||
}
|
||||
}
|
||||
return conn, true, nil
|
||||
}
|
||||
|
||||
// Conn augments a connection-oriented connection over a UDP PacketConn
|
||||
type Conn struct {
|
||||
listener *listener
|
||||
|
||||
rAddr net.Addr
|
||||
|
||||
buffer *packetio.Buffer
|
||||
|
||||
doneCh chan struct{}
|
||||
doneOnce sync.Once
|
||||
|
||||
writeDeadline *deadline.Deadline
|
||||
}
|
||||
|
||||
func (l *listener) newConn(rAddr net.Addr) *Conn {
|
||||
return &Conn{
|
||||
listener: l,
|
||||
rAddr: rAddr,
|
||||
buffer: packetio.NewBuffer(),
|
||||
doneCh: make(chan struct{}),
|
||||
writeDeadline: deadline.New(),
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads from c into p
|
||||
func (c *Conn) Read(p []byte) (int, error) {
|
||||
return c.buffer.Read(p)
|
||||
}
|
||||
|
||||
// Write writes len(p) bytes from p to the DTLS connection
|
||||
func (c *Conn) Write(p []byte) (n int, err error) {
|
||||
select {
|
||||
case <-c.writeDeadline.Done():
|
||||
return 0, context.DeadlineExceeded
|
||||
default:
|
||||
}
|
||||
return c.listener.pConn.WriteTo(p, c.rAddr)
|
||||
}
|
||||
|
||||
// Close closes the conn and releases any Read calls
|
||||
func (c *Conn) Close() error {
|
||||
var err error
|
||||
c.doneOnce.Do(func() {
|
||||
c.listener.connWG.Done()
|
||||
close(c.doneCh)
|
||||
c.listener.connLock.Lock()
|
||||
delete(c.listener.conns, c.rAddr.String())
|
||||
nConns := len(c.listener.conns)
|
||||
c.listener.connLock.Unlock()
|
||||
|
||||
if isAccepting, ok := c.listener.accepting.Load().(bool); nConns == 0 && !isAccepting && ok {
|
||||
// Wait if this is the final connection
|
||||
c.listener.readWG.Wait()
|
||||
if errClose, ok := c.listener.errClose.Load().(error); ok {
|
||||
err = errClose
|
||||
}
|
||||
} else {
|
||||
err = nil
|
||||
}
|
||||
|
||||
if errBuf := c.buffer.Close(); errBuf != nil && err == nil {
|
||||
err = errBuf
|
||||
}
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// LocalAddr implements net.Conn.LocalAddr
|
||||
func (c *Conn) LocalAddr() net.Addr {
|
||||
return c.listener.pConn.LocalAddr()
|
||||
}
|
||||
|
||||
// RemoteAddr implements net.Conn.RemoteAddr
|
||||
func (c *Conn) RemoteAddr() net.Addr {
|
||||
return c.rAddr
|
||||
}
|
||||
|
||||
// SetDeadline implements net.Conn.SetDeadline
|
||||
func (c *Conn) SetDeadline(t time.Time) error {
|
||||
c.writeDeadline.Set(t)
|
||||
return c.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
// SetReadDeadline implements net.Conn.SetDeadline
|
||||
func (c *Conn) SetReadDeadline(t time.Time) error {
|
||||
return c.buffer.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
// SetWriteDeadline implements net.Conn.SetDeadline
|
||||
func (c *Conn) SetWriteDeadline(t time.Time) error {
|
||||
c.writeDeadline.Set(t)
|
||||
// Write deadline of underlying connection should not be changed
|
||||
// since the connection can be shared.
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user