+# SPDX-License-Identifier: MIT
+
+run:
+ timeout: 5m
+
+linters-settings:
+ govet:
+ enable:
+ - shadow
+ misspell:
+ locale: US
+ exhaustive:
+ default-signifies-exhaustive: true
+ gomodguard:
+ blocked:
+ modules:
+ - github.com/pkg/errors:
+ recommendations:
+ - errors
+ forbidigo:
+ forbid:
+ - ^fmt.Print(f|ln)?$
+ - ^log.(Panic|Fatal|Print)(f|ln)?$
+ - ^os.Exit$
+ - ^panic$
+ - ^print(ln)?$
+ varnamelen:
+ max-distance: 12
+ min-name-length: 2
+ ignore-type-assert-ok: true
+ ignore-map-index-ok: true
+ ignore-chan-recv-ok: true
+ ignore-decls:
+ - i int
+ - n int
+ - w io.Writer
+ - r io.Reader
+ - b []byte
+
+linters:
+ enable:
+ - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
+ - bidichk # Checks for dangerous unicode character sequences
+ - bodyclose # checks whether HTTP response body is closed successfully
+ - containedctx # containedctx is a linter that detects struct contained context.Context field
+ - contextcheck # check the function whether use a non-inherited context
+ - cyclop # checks function and package cyclomatic complexity
+ - decorder # check declaration order and count of types, constants, variables and functions
+ - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
+ - dupl # Tool for code clone detection
+ - durationcheck # check for two durations multiplied together
+ - err113 # Golang linter to check the errors handling expressions
+ - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
+ - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted.
+ - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`.
+ - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13.
+ - exhaustive # check exhaustiveness of enum switch statements
+ - exportloopref # checks for pointers to enclosing loop variables
+ - forbidigo # Forbids identifiers
+ - forcetypeassert # finds forced type assertions
+ - gci # Gci control golang package import order and make it always deterministic.
+ - gochecknoglobals # Checks that no globals are present in Go code
+ - gocognit # Computes and checks the cognitive complexity of functions
+ - goconst # Finds repeated strings that could be replaced by a constant
+ - gocritic # The most opinionated Go source code linter
+ - gocyclo # Computes and checks the cyclomatic complexity of functions
+ - godot # Check if comments end in a period
+ - godox # Tool for detection of FIXME, TODO and other comment keywords
+ - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
+ - gofumpt # Gofumpt checks whether code was gofumpt-ed.
+ - goheader # Checks is file header matches to pattern
+ - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
+ - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod.
+ - goprintffuncname # Checks that printf-like functions are named with `f` at the end
+ - gosec # Inspects source code for security problems
+ - gosimple # Linter for Go source code that specializes in simplifying a code
+ - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
+ - grouper # An analyzer to analyze expression groups.
+ - importas # Enforces consistent import aliases
+ - ineffassign # Detects when assignments to existing variables are not used
+ - lll # Reports long lines
+ - maintidx # maintidx measures the maintainability index of each function.
+ - makezero # Finds slice declarations with non-zero initial length
+ - misspell # Finds commonly misspelled English words in comments
+ - nakedret # Finds naked returns in functions greater than a specified function length
+ - nestif # Reports deeply nested if statements
+ - nilerr # Finds the code that returns nil even if it checks that the error is not nil.
+ - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value.
+ - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
+ - noctx # noctx finds sending http request without context.Context
+ - predeclared # find code that shadows one of Go's predeclared identifiers
+ - revive # golint replacement, finds style mistakes
+ - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
+ - stylecheck # Stylecheck is a replacement for golint
+ - tagliatelle # Checks the struct tags.
+ - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17
+ - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers
+ - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
+ - unconvert # Remove unnecessary type conversions
+ - unparam # Reports unused function parameters
+ - unused # Checks Go code for unused constants, variables, functions and types
+ - varnamelen # checks that the length of a variable's name matches its scope
+ - wastedassign # wastedassign finds wasted assignment statements
+ - whitespace # Tool for detection of leading and trailing whitespace
+ disable:
+ - depguard # Go linter that checks if package imports are in a list of acceptable packages
+ - funlen # Tool for detection of long functions
+ - gochecknoinits # Checks that no init functions are present in Go code
+ - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
+ - interfacebloat # A linter that checks length of interface.
+ - ireturn # Accept Interfaces, Return Concrete Types
+ - mnd # An analyzer to detect magic numbers
+ - nolintlint # Reports ill-formed or insufficient nolint directives
+ - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test
+ - prealloc # Finds slice declarations that could potentially be preallocated
+ - promlinter # Check Prometheus metrics naming via promlint
+ - rowserrcheck # checks whether Err of rows is checked successfully
+ - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
+ - testpackage # linter that makes you use a separate _test package
+ - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes
+ - wrapcheck # Checks that errors returned from external packages are wrapped
+ - wsl # Whitespace Linter - Forces you to use empty lines!
+
+issues:
+ exclude-use-default: false
+ exclude-dirs-use-default: false
+ exclude-rules:
+ # Allow complex tests and examples, better to be self contained
+ - path: (examples|main\.go|_test\.go)
+ linters:
+ - forbidigo
+ - gocognit
+
+ # Allow forbidden identifiers in CLI commands
+ - path: cmd
+ linters:
+ - forbidigo
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v2/.goreleaser.yml b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/.goreleaser.yml
similarity index 100%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v2/.goreleaser.yml
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/.goreleaser.yml
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/LICENSE b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/LICENSE
similarity index 100%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/LICENSE
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/LICENSE
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/README.md b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/README.md
similarity index 65%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/README.md
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/README.md
index 111ca7057..5171a41ff 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/README.md
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/README.md
@@ -6,10 +6,10 @@
A Go implementation of ICE
-
+
-
+
@@ -20,15 +20,15 @@
The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
### Community
-Pion has an active community on the [Slack](https://pion.ly/slack).
+Pion has an active community on the [Discord](https://discord.gg/PngbdqpFbt).
-Follow the [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news.
+Follow the [Pion Bluesky](https://bsky.app/profile/pion.ly) or [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news.
We are always looking to support **your projects**. Please reach out if you have something to build!
If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
### Contributing
-Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible: [AUTHORS.txt](./AUTHORS.txt)
+Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible
### License
MIT License - see [LICENSE](LICENSE) for full text
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/active_tcp.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/active_tcp.go
new file mode 100644
index 000000000..b55e65041
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/active_tcp.go
@@ -0,0 +1,177 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ice
+
+import (
+ "context"
+ "io"
+ "net"
+ "net/netip"
+ "sync/atomic"
+ "time"
+
+ "github.com/pion/logging"
+ "github.com/pion/transport/v3/packetio"
+)
+
+type activeTCPConn struct {
+ readBuffer, writeBuffer *packetio.Buffer
+ localAddr, remoteAddr atomic.Value
+ closed int32
+}
+
+func newActiveTCPConn(
+ ctx context.Context,
+ localAddress string,
+ remoteAddress netip.AddrPort,
+ log logging.LeveledLogger,
+) (a *activeTCPConn) {
+ a = &activeTCPConn{
+ readBuffer: packetio.NewBuffer(),
+ writeBuffer: packetio.NewBuffer(),
+ }
+
+ laddr, err := getTCPAddrOnInterface(localAddress)
+ if err != nil {
+ atomic.StoreInt32(&a.closed, 1)
+ log.Infof("Failed to dial TCP address %s: %v", remoteAddress, err)
+
+ return a
+ }
+ a.localAddr.Store(laddr)
+
+ go func() {
+ defer func() {
+ atomic.StoreInt32(&a.closed, 1)
+ }()
+
+ dialer := &net.Dialer{
+ LocalAddr: laddr,
+ }
+ conn, err := dialer.DialContext(ctx, "tcp", remoteAddress.String())
+ if err != nil {
+ log.Infof("Failed to dial TCP address %s: %v", remoteAddress, err)
+
+ return
+ }
+ a.remoteAddr.Store(conn.RemoteAddr())
+
+ go func() {
+ buff := make([]byte, receiveMTU)
+
+ for atomic.LoadInt32(&a.closed) == 0 {
+ n, err := readStreamingPacket(conn, buff)
+ if err != nil {
+ log.Infof("Failed to read streaming packet: %s", err)
+
+ break
+ }
+
+ if _, err := a.readBuffer.Write(buff[:n]); err != nil {
+ log.Infof("Failed to write to buffer: %s", err)
+
+ break
+ }
+ }
+ }()
+
+ buff := make([]byte, receiveMTU)
+
+ for atomic.LoadInt32(&a.closed) == 0 {
+ n, err := a.writeBuffer.Read(buff)
+ if err != nil {
+ log.Infof("Failed to read from buffer: %s", err)
+
+ break
+ }
+
+ if _, err = writeStreamingPacket(conn, buff[:n]); err != nil {
+ log.Infof("Failed to write streaming packet: %s", err)
+
+ break
+ }
+ }
+
+ if err := conn.Close(); err != nil {
+ log.Infof("Failed to close connection: %s", err)
+ }
+ }()
+
+ return a
+}
+
+func (a *activeTCPConn) ReadFrom(buff []byte) (n int, srcAddr net.Addr, err error) {
+ if atomic.LoadInt32(&a.closed) == 1 {
+ return 0, nil, io.ErrClosedPipe
+ }
+
+ n, err = a.readBuffer.Read(buff)
+ // RemoteAddr is assuredly set *after* we can read from the buffer
+ srcAddr = a.RemoteAddr()
+
+ return
+}
+
+func (a *activeTCPConn) WriteTo(buff []byte, _ net.Addr) (n int, err error) {
+ if atomic.LoadInt32(&a.closed) == 1 {
+ return 0, io.ErrClosedPipe
+ }
+
+ return a.writeBuffer.Write(buff)
+}
+
+func (a *activeTCPConn) Close() error {
+ atomic.StoreInt32(&a.closed, 1)
+ _ = a.readBuffer.Close()
+ _ = a.writeBuffer.Close()
+
+ return nil
+}
+
+func (a *activeTCPConn) LocalAddr() net.Addr {
+ if v, ok := a.localAddr.Load().(*net.TCPAddr); ok {
+ return v
+ }
+
+ return &net.TCPAddr{}
+}
+
+// RemoteAddr returns the remote address of the connection which is only
+// set once a background goroutine has successfully dialed. That means
+// this may return ":0" for the address prior to that happening. If this
+// becomes an issue, we can introduce a synchronization point between Dial
+// and these methods.
+func (a *activeTCPConn) RemoteAddr() net.Addr {
+ if v, ok := a.remoteAddr.Load().(*net.TCPAddr); ok {
+ return v
+ }
+
+ return &net.TCPAddr{}
+}
+
+func (a *activeTCPConn) SetDeadline(time.Time) error { return io.EOF }
+func (a *activeTCPConn) SetReadDeadline(time.Time) error { return io.EOF }
+func (a *activeTCPConn) SetWriteDeadline(time.Time) error { return io.EOF }
+
+func getTCPAddrOnInterface(address string) (*net.TCPAddr, error) {
+ addr, err := net.ResolveTCPAddr("tcp", address)
+ if err != nil {
+ return nil, err
+ }
+
+ l, err := net.ListenTCP("tcp", addr)
+ if err != nil {
+ return nil, err
+ }
+ defer func() {
+ _ = l.Close()
+ }()
+
+ tcpAddr, ok := l.Addr().(*net.TCPAddr)
+ if !ok {
+ return nil, errInvalidAddress
+ }
+
+ return tcpAddr, nil
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/addr.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/addr.go
new file mode 100644
index 000000000..fad58b93a
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/addr.go
@@ -0,0 +1,153 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ice
+
+import (
+ "fmt"
+ "net"
+ "net/netip"
+)
+
+func addrWithOptionalZone(addr netip.Addr, zone string) netip.Addr {
+ if zone == "" {
+ return addr
+ }
+ if addr.Is6() && (addr.IsLinkLocalUnicast() || addr.IsLinkLocalMulticast()) {
+ return addr.WithZone(zone)
+ }
+
+ return addr
+}
+
+// parseAddrFromIface should only be used when it's known the address belongs to that interface.
+// e.g. it's LocalAddress on a listener.
+func parseAddrFromIface(in net.Addr, ifcName string) (netip.Addr, int, NetworkType, error) {
+ addr, port, nt, err := parseAddr(in)
+ if err != nil {
+ return netip.Addr{}, 0, 0, err
+ }
+ if _, ok := in.(*net.IPNet); ok {
+ // net.IPNet does not have a Zone but we provide it from the interface
+ addr = addrWithOptionalZone(addr, ifcName)
+ }
+
+ return addr, port, nt, nil
+}
+
+func parseAddr(in net.Addr) (netip.Addr, int, NetworkType, error) { //nolint:cyclop
+ switch addr := in.(type) {
+ case *net.IPNet:
+ ipAddr, err := ipAddrToNetIP(addr.IP, "")
+ if err != nil {
+ return netip.Addr{}, 0, 0, err
+ }
+
+ return ipAddr, 0, 0, nil
+ case *net.IPAddr:
+ ipAddr, err := ipAddrToNetIP(addr.IP, addr.Zone)
+ if err != nil {
+ return netip.Addr{}, 0, 0, err
+ }
+
+ return ipAddr, 0, 0, nil
+ case *net.UDPAddr:
+ ipAddr, err := ipAddrToNetIP(addr.IP, addr.Zone)
+ if err != nil {
+ return netip.Addr{}, 0, 0, err
+ }
+ var nt NetworkType
+ if ipAddr.Is4() {
+ nt = NetworkTypeUDP4
+ } else {
+ nt = NetworkTypeUDP6
+ }
+
+ return ipAddr, addr.Port, nt, nil
+ case *net.TCPAddr:
+ ipAddr, err := ipAddrToNetIP(addr.IP, addr.Zone)
+ if err != nil {
+ return netip.Addr{}, 0, 0, err
+ }
+ var nt NetworkType
+ if ipAddr.Is4() {
+ nt = NetworkTypeTCP4
+ } else {
+ nt = NetworkTypeTCP6
+ }
+
+ return ipAddr, addr.Port, nt, nil
+ default:
+ return netip.Addr{}, 0, 0, addrParseError{in}
+ }
+}
+
+type addrParseError struct {
+ addr net.Addr
+}
+
+func (e addrParseError) Error() string {
+ return fmt.Sprintf("do not know how to parse address type %T", e.addr)
+}
+
+type ipConvertError struct {
+ ip []byte
+}
+
+func (e ipConvertError) Error() string {
+ return fmt.Sprintf("failed to convert IP '%s' to netip.Addr", e.ip)
+}
+
+func ipAddrToNetIP(ip []byte, zone string) (netip.Addr, error) {
+ netIPAddr, ok := netip.AddrFromSlice(ip)
+ if !ok {
+ return netip.Addr{}, ipConvertError{ip}
+ }
+ // we'd rather have an IPv4-mapped IPv6 become IPv4 so that it is usable.
+ netIPAddr = netIPAddr.Unmap()
+ netIPAddr = addrWithOptionalZone(netIPAddr, zone)
+
+ return netIPAddr, nil
+}
+
+func createAddr(network NetworkType, ip netip.Addr, port int) net.Addr {
+ switch {
+ case network.IsTCP():
+ return &net.TCPAddr{IP: ip.AsSlice(), Port: port, Zone: ip.Zone()}
+ default:
+ return &net.UDPAddr{IP: ip.AsSlice(), Port: port, Zone: ip.Zone()}
+ }
+}
+
+func addrEqual(a, b net.Addr) bool {
+ aIP, aPort, aType, aErr := parseAddr(a)
+ if aErr != nil {
+ return false
+ }
+
+ bIP, bPort, bType, bErr := parseAddr(b)
+ if bErr != nil {
+ return false
+ }
+
+ return aType == bType && aIP.Compare(bIP) == 0 && aPort == bPort
+}
+
+// AddrPort is an IP and a port number.
+type AddrPort [18]byte
+
+func toAddrPort(addr net.Addr) AddrPort {
+ var ap AddrPort
+ switch addr := addr.(type) {
+ case *net.UDPAddr:
+ copy(ap[:16], addr.IP.To16())
+ ap[16] = uint8(addr.Port >> 8) //nolint:gosec // G115 false positive
+ ap[17] = uint8(addr.Port) //nolint:gosec // G115 false positive
+ case *net.TCPAddr:
+ copy(ap[:16], addr.IP.To16())
+ ap[16] = uint8(addr.Port >> 8) //nolint:gosec // G115 false positive
+ ap[17] = uint8(addr.Port) //nolint:gosec // G115 false positive
+ }
+
+ return ap
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/agent.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/agent.go
similarity index 58%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/agent.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/agent.go
index 5350330f6..80c89d05b 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/agent.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/agent.go
@@ -8,21 +8,23 @@ package ice
import (
"context"
"fmt"
+ "math"
"net"
+ "net/netip"
"strings"
"sync"
"sync/atomic"
"time"
- atomicx "github.com/pion/ice/v2/internal/atomic"
- stunx "github.com/pion/ice/v2/internal/stun"
+ stunx "github.com/pion/ice/v4/internal/stun"
+ "github.com/pion/ice/v4/internal/taskloop"
"github.com/pion/logging"
- "github.com/pion/mdns"
- "github.com/pion/stun"
- "github.com/pion/transport/v2"
- "github.com/pion/transport/v2/packetio"
- "github.com/pion/transport/v2/stdnet"
- "github.com/pion/transport/v2/vnet"
+ "github.com/pion/mdns/v2"
+ "github.com/pion/stun/v3"
+ "github.com/pion/transport/v3"
+ "github.com/pion/transport/v3/packetio"
+ "github.com/pion/transport/v3/stdnet"
+ "github.com/pion/transport/v3/vnet"
"golang.org/x/net/proxy"
)
@@ -33,17 +35,14 @@ type bindingRequest struct {
isUseCandidate bool
}
-// Agent represents the ICE agent
+// Agent represents the ICE agent.
type Agent struct {
- chanTask chan task
- afterRunFn []func(ctx context.Context)
- muAfterRun sync.Mutex
+ loop *taskloop.Loop
onConnectionStateChangeHdlr atomic.Value // func(ConnectionState)
onSelectedCandidatePairChangeHdlr atomic.Value // func(Candidate, Candidate)
onCandidateHdlr atomic.Value // func(Candidate)
- // State owned by the taskLoop
onConnected chan struct{}
onConnectedOnce sync.Once
@@ -71,6 +70,10 @@ type Agent struct {
srflxAcceptanceMinWait time.Duration
prflxAcceptanceMinWait time.Duration
relayAcceptanceMinWait time.Duration
+ stunGatherTimeout time.Duration
+
+ tcpPriorityOffset uint16
+ disableActiveTCP bool
portMin uint16
portMax uint16
@@ -116,17 +119,16 @@ type Agent struct {
// 1:1 D-NAT IP address mapping
extIPMapper *externalIPMapper
- // State for closing
- done chan struct{}
- taskLoopDone chan struct{}
- err atomicx.Error
+ // Callback that allows user to implement custom behavior
+ // for STUN Binding Requests
+ userBindingRequestHandler func(m *stun.Message, local, remote Candidate, pair *CandidatePair) bool
gatherCandidateCancel func()
gatherCandidateDone chan struct{}
- chanCandidate chan Candidate
- chanCandidatePair chan *CandidatePair
- chanState chan ConnectionState
+ connectionStateNotifier *handlerNotifier
+ candidateNotifier *handlerNotifier
+ selectedCandidatePairNotifier *handlerNotifier
loggerFactory logging.LoggerFactory
log logging.LeveledLogger
@@ -136,113 +138,19 @@ type Agent struct {
udpMux UDPMux
udpMuxSrflx UniversalUDPMux
- interfaceFilter func(string) bool
- ipFilter func(net.IP) bool
+ interfaceFilter func(string) (keep bool)
+ ipFilter func(net.IP) (keep bool)
includeLoopback bool
insecureSkipVerify bool
proxyDialer proxy.Dialer
+
+ enableUseCandidateCheckPriority bool
}
-type task struct {
- fn func(context.Context, *Agent)
- done chan struct{}
-}
-
-// afterRun registers function to be run after the task.
-func (a *Agent) afterRun(f func(context.Context)) {
- a.muAfterRun.Lock()
- a.afterRunFn = append(a.afterRunFn, f)
- a.muAfterRun.Unlock()
-}
-
-func (a *Agent) getAfterRunFn() []func(context.Context) {
- a.muAfterRun.Lock()
- defer a.muAfterRun.Unlock()
- fns := a.afterRunFn
- a.afterRunFn = nil
- return fns
-}
-
-func (a *Agent) ok() error {
- select {
- case <-a.done:
- return a.getErr()
- default:
- }
- return nil
-}
-
-func (a *Agent) getErr() error {
- if err := a.err.Load(); err != nil {
- return err
- }
- return ErrClosed
-}
-
-// Run task in serial. Blocking tasks must be cancelable by context.
-func (a *Agent) run(ctx context.Context, t func(context.Context, *Agent)) error {
- if err := a.ok(); err != nil {
- return err
- }
- done := make(chan struct{})
- select {
- case <-ctx.Done():
- return ctx.Err()
- case a.chanTask <- task{t, done}:
- <-done
- return nil
- }
-}
-
-// taskLoop handles registered tasks and agent close.
-func (a *Agent) taskLoop() {
- after := func() {
- for {
- // Get and run func registered by afterRun().
- fns := a.getAfterRunFn()
- if len(fns) == 0 {
- break
- }
- for _, fn := range fns {
- fn(a.context())
- }
- }
- }
- defer func() {
- a.deleteAllCandidates()
- a.startedFn()
-
- if err := a.buf.Close(); err != nil {
- a.log.Warnf("Failed to close buffer: %v", err)
- }
-
- a.closeMulticastConn()
- a.updateConnectionState(ConnectionStateClosed)
-
- after()
-
- close(a.chanState)
- close(a.chanCandidate)
- close(a.chanCandidatePair)
- close(a.taskLoopDone)
- }()
-
- for {
- select {
- case <-a.done:
- return
- case t := <-a.chanTask:
- t.fn(a.context(), a)
- close(t.done)
- after()
- }
- }
-}
-
-// NewAgent creates a new Agent
-func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit
+// NewAgent creates a new Agent.
+func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit,cyclop
var err error
if config.PortMax < config.PortMin {
return nil, ErrPort
@@ -272,34 +180,28 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit
startedCtx, startedFn := context.WithCancel(context.Background())
- a := &Agent{
- chanTask: make(chan task),
- chanState: make(chan ConnectionState),
- chanCandidate: make(chan Candidate),
- chanCandidatePair: make(chan *CandidatePair),
- tieBreaker: globalMathRandomGenerator.Uint64(),
- lite: config.Lite,
- gatheringState: GatheringStateNew,
- connectionState: ConnectionStateNew,
- localCandidates: make(map[NetworkType][]Candidate),
- remoteCandidates: make(map[NetworkType][]Candidate),
- urls: config.Urls,
- networkTypes: config.NetworkTypes,
- onConnected: make(chan struct{}),
- buf: packetio.NewBuffer(),
- done: make(chan struct{}),
- taskLoopDone: make(chan struct{}),
- startedCh: startedCtx.Done(),
- startedFn: startedFn,
- portMin: config.PortMin,
- portMax: config.PortMax,
- loggerFactory: loggerFactory,
- log: log,
- net: config.Net,
- proxyDialer: config.ProxyDialer,
- tcpMux: config.TCPMux,
- udpMux: config.UDPMux,
- udpMuxSrflx: config.UDPMuxSrflx,
+ agent := &Agent{
+ tieBreaker: globalMathRandomGenerator.Uint64(),
+ lite: config.Lite,
+ gatheringState: GatheringStateNew,
+ connectionState: ConnectionStateNew,
+ localCandidates: make(map[NetworkType][]Candidate),
+ remoteCandidates: make(map[NetworkType][]Candidate),
+ urls: config.Urls,
+ networkTypes: config.NetworkTypes,
+ onConnected: make(chan struct{}),
+ buf: packetio.NewBuffer(),
+ startedCh: startedCtx.Done(),
+ startedFn: startedFn,
+ portMin: config.PortMin,
+ portMax: config.PortMax,
+ loggerFactory: loggerFactory,
+ log: log,
+ net: config.Net,
+ proxyDialer: config.ProxyDialer,
+ tcpMux: config.TCPMux,
+ udpMux: config.UDPMux,
+ udpMuxSrflx: config.UDPMuxSrflx,
mDNSMode: mDNSMode,
mDNSName: mDNSName,
@@ -315,65 +217,115 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit
insecureSkipVerify: config.InsecureSkipVerify,
includeLoopback: config.IncludeLoopback,
+
+ disableActiveTCP: config.DisableActiveTCP,
+
+ userBindingRequestHandler: config.BindingRequestHandler,
+
+ enableUseCandidateCheckPriority: config.EnableUseCandidateCheckPriority,
+ }
+ agent.connectionStateNotifier = &handlerNotifier{
+ connectionStateFunc: agent.onConnectionStateChange,
+ done: make(chan struct{}),
+ }
+ agent.candidateNotifier = &handlerNotifier{candidateFunc: agent.onCandidate, done: make(chan struct{})}
+ agent.selectedCandidatePairNotifier = &handlerNotifier{
+ candidatePairFunc: agent.onSelectedCandidatePairChange,
+ done: make(chan struct{}),
}
- if a.net == nil {
- a.net, err = stdnet.NewNet()
+ if agent.net == nil {
+ agent.net, err = stdnet.NewNet()
if err != nil {
return nil, fmt.Errorf("failed to create network: %w", err)
}
- } else if _, isVirtual := a.net.(*vnet.Net); isVirtual {
- a.log.Warn("Virtual network is enabled")
- if a.mDNSMode != MulticastDNSModeDisabled {
- a.log.Warn("Virtual network does not support mDNS yet")
+ } else if _, isVirtual := agent.net.(*vnet.Net); isVirtual {
+ agent.log.Warn("Virtual network is enabled")
+ if agent.mDNSMode != MulticastDNSModeDisabled {
+ agent.log.Warn("Virtual network does not support mDNS yet")
}
}
+ localIfcs, _, err := localInterfaces(
+ agent.net,
+ agent.interfaceFilter,
+ agent.ipFilter,
+ agent.networkTypes,
+ agent.includeLoopback,
+ )
+ if err != nil {
+ return nil, fmt.Errorf("error getting local interfaces: %w", err)
+ }
+
// Opportunistic mDNS: If we can't open the connection, that's ok: we
// can continue without it.
- if a.mDNSConn, a.mDNSMode, err = createMulticastDNS(a.net, mDNSMode, mDNSName, log); err != nil {
+ if agent.mDNSConn, agent.mDNSMode, err = createMulticastDNS(
+ agent.net,
+ agent.networkTypes,
+ localIfcs,
+ agent.includeLoopback,
+ mDNSMode,
+ mDNSName,
+ log,
+ loggerFactory,
+ ); err != nil {
log.Warnf("Failed to initialize mDNS %s: %v", mDNSName, err)
}
- config.initWithDefaults(a)
+ config.initWithDefaults(agent)
// Make sure the buffer doesn't grow indefinitely.
// NOTE: We actually won't get anywhere close to this limit.
// SRTP will constantly read from the endpoint and drop packets if it's full.
- a.buf.SetLimitSize(maxBufferSize)
+ agent.buf.SetLimitSize(maxBufferSize)
+
+ if agent.lite && (len(agent.candidateTypes) != 1 || agent.candidateTypes[0] != CandidateTypeHost) {
+ agent.closeMulticastConn()
- if a.lite && (len(a.candidateTypes) != 1 || a.candidateTypes[0] != CandidateTypeHost) {
- a.closeMulticastConn()
return nil, ErrLiteUsingNonHostCandidates
}
- if config.Urls != nil && len(config.Urls) > 0 && !containsCandidateType(CandidateTypeServerReflexive, a.candidateTypes) && !containsCandidateType(CandidateTypeRelay, a.candidateTypes) {
- a.closeMulticastConn()
+ if len(config.Urls) > 0 &&
+ !containsCandidateType(CandidateTypeServerReflexive, agent.candidateTypes) &&
+ !containsCandidateType(CandidateTypeRelay, agent.candidateTypes) {
+ agent.closeMulticastConn()
+
return nil, ErrUselessUrlsProvided
}
- if err = config.initExtIPMapping(a); err != nil {
- a.closeMulticastConn()
+ if err = config.initExtIPMapping(agent); err != nil {
+ agent.closeMulticastConn()
+
return nil, err
}
- go a.taskLoop()
+ agent.loop = taskloop.New(func() {
+ agent.removeUfragFromMux()
+ agent.deleteAllCandidates()
+ agent.startedFn()
- // CandidatePair and ConnectionState are usually changed at once.
- // Blocking one by the other one causes deadlock.
- // Hence, we call handlers from independent Goroutines.
- go a.candidatePairRoutine()
- go a.connectionStateRoutine()
- go a.candidateRoutine()
+ if err := agent.buf.Close(); err != nil {
+ agent.log.Warnf("Failed to close buffer: %v", err)
+ }
+
+ agent.closeMulticastConn()
+ agent.updateConnectionState(ConnectionStateClosed)
+
+ agent.gatherCandidateCancel()
+ if agent.gatherCandidateDone != nil {
+ <-agent.gatherCandidateDone
+ }
+ })
// Restart is also used to initialize the agent for the first time
- if err := a.Restart(config.LocalUfrag, config.LocalPwd); err != nil {
- a.closeMulticastConn()
- _ = a.Close()
+ if err := agent.Restart(config.LocalUfrag, config.LocalPwd); err != nil {
+ agent.closeMulticastConn()
+ _ = agent.Close()
+
return nil, err
}
- return a, nil
+ return agent, nil
}
func (a *Agent) startConnectivityChecks(isControlling bool, remoteUfrag, remotePwd string) error {
@@ -390,10 +342,10 @@ func (a *Agent) startConnectivityChecks(isControlling bool, remoteUfrag, remoteP
a.log.Debugf("Started agent: isControlling? %t, remoteUfrag: %q, remotePwd: %q", isControlling, remoteUfrag, remotePwd)
- return a.run(a.context(), func(ctx context.Context, agent *Agent) {
- agent.isControlling = isControlling
- agent.remoteUfrag = remoteUfrag
- agent.remotePwd = remotePwd
+ return a.loop.Run(a.loop, func(_ context.Context) {
+ a.isControlling = isControlling
+ a.remoteUfrag = remoteUfrag
+ a.remotePwd = remotePwd
if isControlling {
a.selector = &controllingSelector{agent: a, log: a.log}
@@ -408,19 +360,19 @@ func (a *Agent) startConnectivityChecks(isControlling bool, remoteUfrag, remoteP
a.selector.Start()
a.startedFn()
- agent.updateConnectionState(ConnectionStateChecking)
+ a.updateConnectionState(ConnectionStateChecking)
a.requestConnectivityCheck()
go a.connectivityChecks() //nolint:contextcheck
})
}
-func (a *Agent) connectivityChecks() {
+func (a *Agent) connectivityChecks() { //nolint:cyclop
lastConnectionState := ConnectionState(0)
checkingDuration := time.Time{}
contact := func() {
- if err := a.run(a.context(), func(ctx context.Context, a *Agent) {
+ if err := a.loop.Run(a.loop, func(_ context.Context) {
defer func() {
lastConnectionState = a.connectionState
}()
@@ -439,6 +391,7 @@ func (a *Agent) connectivityChecks() {
// We have been in checking longer then Disconnect+Failed timeout, set the connection to Failed
if time.Since(checkingDuration) > a.disconnectedTimeout+a.failedTimeout {
a.updateConnectionState(ConnectionStateFailed)
+
return
}
default:
@@ -450,6 +403,9 @@ func (a *Agent) connectivityChecks() {
}
}
+ timer := time.NewTimer(math.MaxInt64)
+ timer.Stop()
+
for {
interval := defaultKeepaliveInterval
@@ -470,15 +426,19 @@ func (a *Agent) connectivityChecks() {
updateInterval(a.disconnectedTimeout)
updateInterval(a.failedTimeout)
- t := time.NewTimer(interval)
+ timer.Reset(interval)
+
select {
case <-a.forceCandidateContact:
- t.Stop()
+ if !timer.Stop() {
+ <-timer.C
+ }
contact()
- case <-t.C:
+ case <-timer.C:
contact()
- case <-a.done:
- t.Stop()
+ case <-a.loop.Done():
+ timer.Stop()
+
return
}
}
@@ -488,48 +448,43 @@ func (a *Agent) updateConnectionState(newState ConnectionState) {
if a.connectionState != newState {
// Connection has gone to failed, release all gathered candidates
if newState == ConnectionStateFailed {
+ a.removeUfragFromMux()
+ a.checklist = make([]*CandidatePair, 0)
+ a.pendingBindingRequests = make([]bindingRequest, 0)
+ a.setSelectedPair(nil)
a.deleteAllCandidates()
}
a.log.Infof("Setting new connection state: %s", newState)
a.connectionState = newState
-
- // Call handler after finishing current task since we may be holding the agent lock
- // and the handler may also require it
- a.afterRun(func(ctx context.Context) {
- a.chanState <- newState
- })
+ a.connectionStateNotifier.EnqueueConnectionState(newState)
}
}
-func (a *Agent) setSelectedPair(p *CandidatePair) {
- if p == nil {
+func (a *Agent) setSelectedPair(pair *CandidatePair) {
+ if pair == nil {
var nilPair *CandidatePair
a.selectedPair.Store(nilPair)
a.log.Tracef("Unset selected candidate pair")
+
return
}
- p.nominated = true
- a.selectedPair.Store(p)
- a.log.Tracef("Set selected candidate pair: %s", p)
+ pair.nominated = true
+ a.selectedPair.Store(pair)
+ a.log.Tracef("Set selected candidate pair: %s", pair)
a.updateConnectionState(ConnectionStateConnected)
// Notify when the selected pair changes
- a.afterRun(func(ctx context.Context) {
- select {
- case a.chanCandidatePair <- p:
- case <-ctx.Done():
- }
- })
+ a.selectedCandidatePairNotifier.EnqueueSelectedCandidatePair(pair)
// Signal connected
a.onConnectedOnce.Do(func() { close(a.onConnected) })
}
func (a *Agent) pingAllCandidates() {
- a.log.Trace("pinging all candidates")
+ a.log.Trace("Pinging all candidates")
if len(a.checklist) == 0 {
a.log.Warn("Failed to ping without candidate pairs. Connection is not possible yet.")
@@ -543,7 +498,7 @@ func (a *Agent) pingAllCandidates() {
}
if p.bindingRequestCount > a.maxBindingRequests {
- a.log.Tracef("max requests reached for pair %s, marking it as failed", p)
+ a.log.Tracef("Maximum requests reached for pair %s, marking it as failed", p)
p.state = CandidatePairStateFailed
} else {
a.selector.PingCandidate(p.Local, p.Remote)
@@ -565,6 +520,7 @@ func (a *Agent) getBestAvailableCandidatePair() *CandidatePair {
best = p
}
}
+
return best
}
@@ -581,12 +537,14 @@ func (a *Agent) getBestValidCandidatePair() *CandidatePair {
best = p
}
}
+
return best
}
func (a *Agent) addPair(local, remote Candidate) *CandidatePair {
p := newCandidatePair(local, remote, a.isControlling)
a.checklist = append(a.checklist, p)
+
return p
}
@@ -596,6 +554,7 @@ func (a *Agent) findPair(local, remote Candidate) *CandidatePair {
return p
}
}
+
return nil
}
@@ -636,82 +595,83 @@ func (a *Agent) checkKeepalive() {
return
}
- if (a.keepaliveInterval != 0) &&
- ((time.Since(selectedPair.Local.LastSent()) > a.keepaliveInterval) ||
- (time.Since(selectedPair.Remote.LastReceived()) > a.keepaliveInterval)) {
+ if a.keepaliveInterval != 0 {
// We use binding request instead of indication to support refresh consent schemas
// see https://tools.ietf.org/html/rfc7675
a.selector.PingCandidate(selectedPair.Local, selectedPair.Remote)
}
}
-// AddRemoteCandidate adds a new remote candidate
-func (a *Agent) AddRemoteCandidate(c Candidate) error {
- if c == nil {
+// AddRemoteCandidate adds a new remote candidate.
+func (a *Agent) AddRemoteCandidate(cand Candidate) error {
+ if cand == nil {
return nil
}
- // Cannot check for network yet because it might not be applied
- // when mDNS hostname is used.
- if c.TCPType() == TCPTypeActive {
- // TCP Candidates with TCP type active will probe server passive ones, so
- // no need to do anything with them.
- a.log.Infof("Ignoring remote candidate with tcpType active: %s", c)
+ // TCP Candidates with TCP type active will probe server passive ones, so
+ // no need to do anything with them.
+ if cand.TCPType() == TCPTypeActive {
+ a.log.Infof("Ignoring remote candidate with tcpType active: %s", cand)
+
return nil
}
// If we have a mDNS Candidate lets fully resolve it before adding it locally
- if c.Type() == CandidateTypeHost && strings.HasSuffix(c.Address(), ".local") {
+ if cand.Type() == CandidateTypeHost && strings.HasSuffix(cand.Address(), ".local") {
if a.mDNSMode == MulticastDNSModeDisabled {
- a.log.Warnf("Remote mDNS candidate added, but mDNS is disabled: (%s)", c.Address())
+ a.log.Warnf("Remote mDNS candidate added, but mDNS is disabled: (%s)", cand.Address())
+
return nil
}
- hostCandidate, ok := c.(*CandidateHost)
+ hostCandidate, ok := cand.(*CandidateHost)
if !ok {
return ErrAddressParseFailed
}
go a.resolveAndAddMulticastCandidate(hostCandidate)
+
return nil
}
go func() {
- if err := a.run(a.context(), func(ctx context.Context, agent *Agent) {
- agent.addRemoteCandidate(c)
+ if err := a.loop.Run(a.loop, func(_ context.Context) {
+ // nolint: contextcheck
+ a.addRemoteCandidate(cand)
}); err != nil {
- a.log.Warnf("Failed to add remote candidate %s: %v", c.Address(), err)
+ a.log.Warnf("Failed to add remote candidate %s: %v", cand.Address(), err)
+
return
}
}()
+
return nil
}
-func (a *Agent) resolveAndAddMulticastCandidate(c *CandidateHost) {
+func (a *Agent) resolveAndAddMulticastCandidate(cand *CandidateHost) {
if a.mDNSConn == nil {
return
}
- _, src, err := a.mDNSConn.Query(c.context(), c.Address())
+
+ _, src, err := a.mDNSConn.QueryAddr(cand.context(), cand.Address())
if err != nil {
- a.log.Warnf("Failed to discover mDNS candidate %s: %v", c.Address(), err)
+ a.log.Warnf("Failed to discover mDNS candidate %s: %v", cand.Address(), err)
+
return
}
- ip, ipOk := parseMulticastAnswerAddr(src)
- if !ipOk {
- a.log.Warnf("Failed to discover mDNS candidate %s: failed to parse IP", c.Address())
+ if err = cand.setIPAddr(src); err != nil {
+ a.log.Warnf("Failed to discover mDNS candidate %s: %v", cand.Address(), err)
+
return
}
- if err = c.setIP(ip); err != nil {
- a.log.Warnf("Failed to discover mDNS candidate %s: %v", c.Address(), err)
- return
- }
-
- if err = a.run(a.context(), func(ctx context.Context, agent *Agent) {
- agent.addRemoteCandidate(c)
+ if err = a.loop.Run(a.loop, func(_ context.Context) {
+ // nolint: contextcheck
+ a.addRemoteCandidate(cand)
}); err != nil {
- a.log.Warnf("Failed to add mDNS candidate %s: %v", c.Address(), err)
+ a.log.Warnf("Failed to add mDNS candidate %s: %v", cand.Address(), err)
+
return
}
}
@@ -723,68 +683,158 @@ func (a *Agent) requestConnectivityCheck() {
}
}
-// addRemoteCandidate assumes you are holding the lock (must be execute using a.run)
-func (a *Agent) addRemoteCandidate(c Candidate) {
- set := a.remoteCandidates[c.NetworkType()]
+func (a *Agent) addRemotePassiveTCPCandidate(remoteCandidate Candidate) {
+ _, localIPs, err := localInterfaces(
+ a.net,
+ a.interfaceFilter,
+ a.ipFilter,
+ []NetworkType{remoteCandidate.NetworkType()},
+ a.includeLoopback,
+ )
+ if err != nil {
+ a.log.Warnf("Failed to iterate local interfaces, host candidates will not be gathered %s", err)
+
+ return
+ }
+
+ for i := range localIPs {
+ ip, _, _, err := parseAddr(remoteCandidate.addr())
+ if err != nil {
+ a.log.Warnf("Failed to parse address: %s; error: %s", remoteCandidate.addr(), err)
+
+ continue
+ }
+
+ conn := newActiveTCPConn(
+ a.loop,
+ net.JoinHostPort(localIPs[i].String(), "0"),
+ netip.AddrPortFrom(ip, uint16(remoteCandidate.Port())), //nolint:gosec // G115, no overflow, a port
+ a.log,
+ )
+
+ tcpAddr, ok := conn.LocalAddr().(*net.TCPAddr)
+ if !ok {
+ closeConnAndLog(conn, a.log, "Failed to create Active ICE-TCP Candidate: %v", errInvalidAddress)
+
+ continue
+ }
+
+ localCandidate, err := NewCandidateHost(&CandidateHostConfig{
+ Network: remoteCandidate.NetworkType().String(),
+ Address: localIPs[i].String(),
+ Port: tcpAddr.Port,
+ Component: ComponentRTP,
+ TCPType: TCPTypeActive,
+ })
+ if err != nil {
+ closeConnAndLog(conn, a.log, "Failed to create Active ICE-TCP Candidate: %v", err)
+
+ continue
+ }
+
+ localCandidate.start(a, conn, a.startedCh)
+ a.localCandidates[localCandidate.NetworkType()] = append(
+ a.localCandidates[localCandidate.NetworkType()],
+ localCandidate,
+ )
+ a.candidateNotifier.EnqueueCandidate(localCandidate)
+
+ a.addPair(localCandidate, remoteCandidate)
+ }
+}
+
+// addRemoteCandidate assumes you are holding the lock (must be execute using a.run).
+func (a *Agent) addRemoteCandidate(cand Candidate) { //nolint:cyclop
+ set := a.remoteCandidates[cand.NetworkType()]
for _, candidate := range set {
- if candidate.Equal(c) {
+ if candidate.Equal(cand) {
return
}
}
- set = append(set, c)
- a.remoteCandidates[c.NetworkType()] = set
+ acceptRemotePassiveTCPCandidate := false
+ // Assert that TCP4 or TCP6 is a enabled NetworkType locally
+ if !a.disableActiveTCP && cand.TCPType() == TCPTypePassive {
+ for _, networkType := range a.networkTypes {
+ if cand.NetworkType() == networkType {
+ acceptRemotePassiveTCPCandidate = true
+ }
+ }
+ }
- if localCandidates, ok := a.localCandidates[c.NetworkType()]; ok {
- for _, localCandidate := range localCandidates {
- a.addPair(localCandidate, c)
+ if acceptRemotePassiveTCPCandidate {
+ a.addRemotePassiveTCPCandidate(cand)
+ }
+
+ set = append(set, cand)
+ a.remoteCandidates[cand.NetworkType()] = set
+
+ if cand.TCPType() != TCPTypePassive {
+ if localCandidates, ok := a.localCandidates[cand.NetworkType()]; ok {
+ for _, localCandidate := range localCandidates {
+ a.addPair(localCandidate, cand)
+ }
}
}
a.requestConnectivityCheck()
}
-func (a *Agent) addCandidate(ctx context.Context, c Candidate, candidateConn net.PacketConn) error {
- return a.run(ctx, func(ctx context.Context, agent *Agent) {
- set := a.localCandidates[c.NetworkType()]
+func (a *Agent) addCandidate(ctx context.Context, cand Candidate, candidateConn net.PacketConn) error {
+ return a.loop.Run(ctx, func(context.Context) {
+ set := a.localCandidates[cand.NetworkType()]
for _, candidate := range set {
- if candidate.Equal(c) {
- a.log.Debugf("Ignore duplicate candidate: %s", c.String())
- if err := c.close(); err != nil {
+ if candidate.Equal(cand) {
+ a.log.Debugf("Ignore duplicate candidate: %s", cand)
+ if err := cand.close(); err != nil {
a.log.Warnf("Failed to close duplicate candidate: %v", err)
}
if err := candidateConn.Close(); err != nil {
a.log.Warnf("Failed to close duplicate candidate connection: %v", err)
}
+
return
}
}
- c.start(a, candidateConn, a.startedCh)
+ a.setCandidateExtensions(cand)
+ cand.start(a, candidateConn, a.startedCh)
- set = append(set, c)
- a.localCandidates[c.NetworkType()] = set
+ set = append(set, cand)
+ a.localCandidates[cand.NetworkType()] = set
- if remoteCandidates, ok := a.remoteCandidates[c.NetworkType()]; ok {
+ if remoteCandidates, ok := a.remoteCandidates[cand.NetworkType()]; ok {
for _, remoteCandidate := range remoteCandidates {
- a.addPair(c, remoteCandidate)
+ a.addPair(cand, remoteCandidate)
}
}
a.requestConnectivityCheck()
- a.chanCandidate <- c
+ if !cand.filterForLocationTracking() {
+ a.candidateNotifier.EnqueueCandidate(cand)
+ }
})
}
-// GetLocalCandidates returns the local candidates
-func (a *Agent) GetLocalCandidates() ([]Candidate, error) {
+func (a *Agent) setCandidateExtensions(cand Candidate) {
+ err := cand.AddExtension(CandidateExtension{
+ Key: "ufrag",
+ Value: a.localUfrag,
+ })
+ if err != nil {
+ a.log.Errorf("Failed to add ufrag extension to candidate: %v", err)
+ }
+}
+
+// GetRemoteCandidates returns the remote candidates.
+func (a *Agent) GetRemoteCandidates() ([]Candidate, error) {
var res []Candidate
- err := a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ err := a.loop.Run(a.loop, func(_ context.Context) {
var candidates []Candidate
- for _, set := range agent.localCandidates {
+ for _, set := range a.remoteCandidates {
candidates = append(candidates, set...)
}
res = candidates
@@ -796,33 +846,58 @@ func (a *Agent) GetLocalCandidates() ([]Candidate, error) {
return res, nil
}
-// GetLocalUserCredentials returns the local user credentials
+// GetLocalCandidates returns the local candidates.
+func (a *Agent) GetLocalCandidates() ([]Candidate, error) {
+ var res []Candidate
+
+ err := a.loop.Run(a.loop, func(_ context.Context) {
+ var candidates []Candidate
+ for _, set := range a.localCandidates {
+ for _, c := range set {
+ if c.filterForLocationTracking() {
+ continue
+ }
+ candidates = append(candidates, c)
+ }
+ }
+ res = candidates
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return res, nil
+}
+
+// GetLocalUserCredentials returns the local user credentials.
func (a *Agent) GetLocalUserCredentials() (frag string, pwd string, err error) {
valSet := make(chan struct{})
- err = a.run(a.context(), func(ctx context.Context, agent *Agent) {
- frag = agent.localUfrag
- pwd = agent.localPwd
+ err = a.loop.Run(a.loop, func(_ context.Context) {
+ frag = a.localUfrag
+ pwd = a.localPwd
close(valSet)
})
if err == nil {
<-valSet
}
+
return
}
-// GetRemoteUserCredentials returns the remote user credentials
+// GetRemoteUserCredentials returns the remote user credentials.
func (a *Agent) GetRemoteUserCredentials() (frag string, pwd string, err error) {
valSet := make(chan struct{})
- err = a.run(a.context(), func(ctx context.Context, agent *Agent) {
- frag = agent.remoteUfrag
- pwd = agent.remotePwd
+ err = a.loop.Run(a.loop, func(_ context.Context) {
+ frag = a.remoteUfrag
+ pwd = a.remotePwd
close(valSet)
})
if err == nil {
<-valSet
}
+
return
}
@@ -838,31 +913,35 @@ func (a *Agent) removeUfragFromMux() {
}
}
-// Close cleans up the Agent
+// Close cleans up the Agent.
func (a *Agent) Close() error {
- if err := a.ok(); err != nil {
- return err
- }
+ return a.close(false)
+}
- a.afterRun(func(context.Context) {
- a.gatherCandidateCancel()
- if a.gatherCandidateDone != nil {
- <-a.gatherCandidateDone
- }
- })
- a.err.Store(ErrClosed)
+// GracefulClose cleans up the Agent and waits for any goroutines it started
+// to complete. This is only safe to call outside of Agent callbacks or if in a callback,
+// in its own goroutine.
+func (a *Agent) GracefulClose() error {
+ return a.close(true)
+}
- a.removeUfragFromMux()
+func (a *Agent) close(graceful bool) error {
+ // the loop is safe to wait on no matter what
+ a.loop.Close()
+
+ // but we are in less control of the notifiers, so we will
+ // pass through `graceful`.
+ a.connectionStateNotifier.Close(graceful)
+ a.candidateNotifier.Close(graceful)
+ a.selectedCandidatePairNotifier.Close(graceful)
- close(a.done)
- <-a.taskLoopDone
return nil
}
// Remove all candidates. This closes any listening sockets
// and removes both the local and remote candidate lists.
//
-// This is used for restarts, failures and on close
+// This is used for restarts, failures and on close.
func (a *Agent) deleteAllCandidates() {
for net, cs := range a.localCandidates {
for _, c := range cs {
@@ -883,9 +962,10 @@ func (a *Agent) deleteAllCandidates() {
}
func (a *Agent) findRemoteCandidate(networkType NetworkType, addr net.Addr) Candidate {
- ip, port, _, ok := parseAddr(addr)
- if !ok {
- a.log.Warnf("Failed to parse address: %s", addr)
+ ip, port, _, err := parseAddr(addr)
+ if err != nil {
+ a.log.Warnf("Failed to parse address: %s; error: %s", addr, err)
+
return nil
}
@@ -895,35 +975,42 @@ func (a *Agent) findRemoteCandidate(networkType NetworkType, addr net.Addr) Cand
return c
}
}
+
return nil
}
-func (a *Agent) sendBindingRequest(m *stun.Message, local, remote Candidate) {
- a.log.Tracef("ping STUN from %s to %s", local.String(), remote.String())
+func (a *Agent) sendBindingRequest(msg *stun.Message, local, remote Candidate) {
+ a.log.Tracef("Ping STUN from %s to %s", local, remote)
a.invalidatePendingBindingRequests(time.Now())
a.pendingBindingRequests = append(a.pendingBindingRequests, bindingRequest{
timestamp: time.Now(),
- transactionID: m.TransactionID,
+ transactionID: msg.TransactionID,
destination: remote.addr(),
- isUseCandidate: m.Contains(stun.AttrUseCandidate),
+ isUseCandidate: msg.Contains(stun.AttrUseCandidate),
})
- a.sendSTUN(m, local, remote)
+ if pair := a.findPair(local, remote); pair != nil {
+ pair.UpdateRequestSent()
+ } else {
+ a.log.Warnf("Failed to find pair for add binding request from %s to %s", local, remote)
+ }
+ a.sendSTUN(msg, local, remote)
}
func (a *Agent) sendBindingSuccess(m *stun.Message, local, remote Candidate) {
base := remote
- ip, port, _, ok := parseAddr(base.addr())
- if !ok {
- a.log.Warnf("Failed to parse address: %s", base.addr())
+ ip, port, _, err := parseAddr(base.addr())
+ if err != nil {
+ a.log.Warnf("Failed to parse address: %s; error: %s", base.addr(), err)
+
return
}
if out, err := stun.Build(m, stun.BindingSuccess,
&stun.XORMappedAddress{
- IP: ip,
+ IP: ip.AsSlice(),
Port: port,
},
stun.NewShortTermIntegrity(a.localPwd),
@@ -931,6 +1018,11 @@ func (a *Agent) sendBindingSuccess(m *stun.Message, local, remote Candidate) {
); err != nil {
a.log.Warnf("Failed to handle inbound ICE from: %s to: %s error: %s", local, remote, err)
} else {
+ if pair := a.findPair(local, remote); pair != nil {
+ pair.UpdateResponseSent()
+ } else {
+ a.log.Warnf("Failed to find pair for add binding response from %s to %s", local, remote)
+ }
a.sendSTUN(out, local, remote)
}
}
@@ -957,75 +1049,93 @@ func (a *Agent) invalidatePendingBindingRequests(filterTime time.Time) {
}
// Assert that the passed TransactionID is in our pendingBindingRequests and returns the destination
-// If the bindingRequest was valid remove it from our pending cache
-func (a *Agent) handleInboundBindingSuccess(id [stun.TransactionIDSize]byte) (bool, *bindingRequest) {
+// If the bindingRequest was valid remove it from our pending cache.
+func (a *Agent) handleInboundBindingSuccess(id [stun.TransactionIDSize]byte) (bool, *bindingRequest, time.Duration) {
a.invalidatePendingBindingRequests(time.Now())
for i := range a.pendingBindingRequests {
if a.pendingBindingRequests[i].transactionID == id {
validBindingRequest := a.pendingBindingRequests[i]
a.pendingBindingRequests = append(a.pendingBindingRequests[:i], a.pendingBindingRequests[i+1:]...)
- return true, &validBindingRequest
+
+ return true, &validBindingRequest, time.Since(validBindingRequest.timestamp)
}
}
- return false, nil
+
+ return false, nil, 0
}
-// handleInbound processes STUN traffic from a remote candidate
-func (a *Agent) handleInbound(m *stun.Message, local Candidate, remote net.Addr) { //nolint:gocognit
+// handleInbound processes STUN traffic from a remote candidate.
+func (a *Agent) handleInbound(msg *stun.Message, local Candidate, remote net.Addr) { //nolint:gocognit,cyclop
var err error
- if m == nil || local == nil {
+ if msg == nil || local == nil {
return
}
- if m.Type.Method != stun.MethodBinding ||
- !(m.Type.Class == stun.ClassSuccessResponse ||
- m.Type.Class == stun.ClassRequest ||
- m.Type.Class == stun.ClassIndication) {
- a.log.Tracef("unhandled STUN from %s to %s class(%s) method(%s)", remote, local, m.Type.Class, m.Type.Method)
+ if msg.Type.Method != stun.MethodBinding ||
+ !(msg.Type.Class == stun.ClassSuccessResponse ||
+ msg.Type.Class == stun.ClassRequest ||
+ msg.Type.Class == stun.ClassIndication) {
+ a.log.Tracef("Unhandled STUN from %s to %s class(%s) method(%s)", remote, local, msg.Type.Class, msg.Type.Method)
+
return
}
if a.isControlling {
- if m.Contains(stun.AttrICEControlling) {
+ if msg.Contains(stun.AttrICEControlling) {
a.log.Debug("Inbound STUN message: isControlling && a.isControlling == true")
+
return
- } else if m.Contains(stun.AttrUseCandidate) {
+ } else if msg.Contains(stun.AttrUseCandidate) {
a.log.Debug("Inbound STUN message: useCandidate && a.isControlling == true")
+
return
}
} else {
- if m.Contains(stun.AttrICEControlled) {
+ if msg.Contains(stun.AttrICEControlled) {
a.log.Debug("Inbound STUN message: isControlled && a.isControlling == false")
+
return
}
}
remoteCandidate := a.findRemoteCandidate(local.NetworkType(), remote)
- if m.Type.Class == stun.ClassSuccessResponse {
- if err = stun.MessageIntegrity([]byte(a.remotePwd)).Check(m); err != nil {
+ if msg.Type.Class == stun.ClassSuccessResponse { //nolint:nestif
+ if err = stun.MessageIntegrity([]byte(a.remotePwd)).Check(msg); err != nil {
a.log.Warnf("Discard message from (%s), %v", remote, err)
+
return
}
if remoteCandidate == nil {
a.log.Warnf("Discard success message from (%s), no such remote", remote)
+
return
}
- a.selector.HandleSuccessResponse(m, local, remoteCandidate, remote)
- } else if m.Type.Class == stun.ClassRequest {
- if err = stunx.AssertUsername(m, a.localUfrag+":"+a.remoteUfrag); err != nil {
+ a.selector.HandleSuccessResponse(msg, local, remoteCandidate, remote)
+ } else if msg.Type.Class == stun.ClassRequest {
+ a.log.Tracef(
+ "Inbound STUN (Request) from %s to %s, useCandidate: %v",
+ remote,
+ local,
+ msg.Contains(stun.AttrUseCandidate),
+ )
+
+ if err = stunx.AssertUsername(msg, a.localUfrag+":"+a.remoteUfrag); err != nil {
a.log.Warnf("Discard message from (%s), %v", remote, err)
+
return
- } else if err = stun.MessageIntegrity([]byte(a.localPwd)).Check(m); err != nil {
+ } else if err = stun.MessageIntegrity([]byte(a.localPwd)).Check(msg); err != nil {
a.log.Warnf("Discard message from (%s), %v", remote, err)
+
return
}
if remoteCandidate == nil {
- ip, port, networkType, ok := parseAddr(remote)
- if !ok {
- a.log.Errorf("Failed to create parse remote net.Addr when creating remote prflx candidate")
+ ip, port, networkType, err := parseAddr(remote)
+ if err != nil {
+ a.log.Errorf("Failed to create parse remote net.Addr when creating remote prflx candidate: %s", err)
+
return
}
@@ -1041,6 +1151,7 @@ func (a *Agent) handleInbound(m *stun.Message, local Candidate, remote net.Addr)
prflxCandidate, err := NewCandidatePeerReflexive(&prflxCandidateConfig)
if err != nil {
a.log.Errorf("Failed to create new remote prflx candidate (%s)", err)
+
return
}
remoteCandidate = prflxCandidate
@@ -1049,9 +1160,7 @@ func (a *Agent) handleInbound(m *stun.Message, local Candidate, remote net.Addr)
a.addRemoteCandidate(remoteCandidate)
}
- a.log.Tracef("inbound STUN (Request) from %s to %s", remote.String(), local.String())
-
- a.selector.HandleBindingRequest(m, local, remoteCandidate)
+ a.selector.HandleBindingRequest(msg, local, remoteCandidate)
}
if remoteCandidate != nil {
@@ -1060,10 +1169,10 @@ func (a *Agent) handleInbound(m *stun.Message, local Candidate, remote net.Addr)
}
// validateNonSTUNTraffic processes non STUN traffic from a remote candidate,
-// and returns true if it is an actual remote candidate
+// and returns true if it is an actual remote candidate.
func (a *Agent) validateNonSTUNTraffic(local Candidate, remote net.Addr) (Candidate, bool) {
var remoteCandidate Candidate
- if err := a.run(local.context(), func(ctx context.Context, agent *Agent) {
+ if err := a.loop.Run(local.context(), func(context.Context) {
remoteCandidate = a.findRemoteCandidate(local.NetworkType(), remote)
if remoteCandidate != nil {
remoteCandidate.seen(false)
@@ -1075,7 +1184,7 @@ func (a *Agent) validateNonSTUNTraffic(local Candidate, remote net.Addr) (Candid
return remoteCandidate, remoteCandidate != nil
}
-// GetSelectedCandidatePair returns the selected pair or nil if there is none
+// GetSelectedCandidatePair returns the selected pair or nil if there is none.
func (a *Agent) GetSelectedCandidatePair() (*CandidatePair, error) {
selectedPair := a.getSelectedPair()
if selectedPair == nil {
@@ -1111,7 +1220,7 @@ func (a *Agent) closeMulticastConn() {
}
}
-// SetRemoteCredentials sets the credentials of the remote agent
+// SetRemoteCredentials sets the credentials of the remote agent.
func (a *Agent) SetRemoteCredentials(remoteUfrag, remotePwd string) error {
switch {
case remoteUfrag == "":
@@ -1120,9 +1229,9 @@ func (a *Agent) SetRemoteCredentials(remoteUfrag, remotePwd string) error {
return ErrRemotePwdEmpty
}
- return a.run(a.context(), func(ctx context.Context, agent *Agent) {
- agent.remoteUfrag = remoteUfrag
- agent.remotePwd = remotePwd
+ return a.loop.Run(a.loop, func(_ context.Context) {
+ a.remoteUfrag = remoteUfrag
+ a.remotePwd = remotePwd
})
}
@@ -1133,7 +1242,7 @@ func (a *Agent) SetRemoteCredentials(remoteUfrag, remotePwd string) error {
// cancel it.
// After a Restart, the user must then call GatherCandidates explicitly
// to start generating new ones.
-func (a *Agent) Restart(ufrag, pwd string) error {
+func (a *Agent) Restart(ufrag, pwd string) error { //nolint:cyclop
if ufrag == "" {
var err error
ufrag, err = generateUFrag()
@@ -1157,17 +1266,17 @@ func (a *Agent) Restart(ufrag, pwd string) error {
}
var err error
- if runErr := a.run(a.context(), func(ctx context.Context, agent *Agent) {
- if agent.gatheringState == GatheringStateGathering {
- agent.gatherCandidateCancel()
+ if runErr := a.loop.Run(a.loop, func(_ context.Context) {
+ if a.gatheringState == GatheringStateGathering {
+ a.gatherCandidateCancel()
}
// Clear all agent needed to take back to fresh state
a.removeUfragFromMux()
- agent.localUfrag = ufrag
- agent.localPwd = pwd
- agent.remoteUfrag = ""
- agent.remotePwd = ""
+ a.localUfrag = ufrag
+ a.localPwd = pwd
+ a.remoteUfrag = ""
+ a.remotePwd = ""
a.gatheringState = GatheringStateNew
a.checklist = make([]*CandidatePair, 0)
a.pendingBindingRequests = make([]bindingRequest, 0)
@@ -1185,14 +1294,15 @@ func (a *Agent) Restart(ufrag, pwd string) error {
}); runErr != nil {
return runErr
}
+
return err
}
func (a *Agent) setGatheringState(newState GatheringState) error {
done := make(chan struct{})
- if err := a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ if err := a.loop.Run(a.loop, func(context.Context) {
if a.gatheringState != newState && newState == GatheringStateComplete {
- a.chanCandidate <- nil
+ a.candidateNotifier.EnqueueCandidate(nil)
}
a.gatheringState = newState
@@ -1202,5 +1312,10 @@ func (a *Agent) setGatheringState(newState GatheringState) error {
}
<-done
+
return nil
}
+
+func (a *Agent) needsToCheckPriorityOnNominated() bool {
+ return !a.lite || a.enableUseCandidateCheckPriority
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/agent_config.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/agent_config.go
similarity index 61%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/agent_config.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/agent_config.go
index f5897cd4a..708aab569 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/agent_config.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/agent_config.go
@@ -8,43 +8,50 @@ import (
"time"
"github.com/pion/logging"
- "github.com/pion/stun"
- "github.com/pion/transport/v2"
+ "github.com/pion/stun/v3"
+ "github.com/pion/transport/v3"
"golang.org/x/net/proxy"
)
const (
- // defaultCheckInterval is the interval at which the agent performs candidate checks in the connecting phase
+ // defaultCheckInterval is the interval at which the agent performs candidate checks in the connecting phase.
defaultCheckInterval = 200 * time.Millisecond
- // keepaliveInterval used to keep candidates alive
+ // keepaliveInterval used to keep candidates alive.
defaultKeepaliveInterval = 2 * time.Second
- // defaultDisconnectedTimeout is the default time till an Agent transitions disconnected
+ // defaultDisconnectedTimeout is the default time till an Agent transitions disconnected.
defaultDisconnectedTimeout = 5 * time.Second
- // defaultFailedTimeout is the default time till an Agent transitions to failed after disconnected
+ // defaultFailedTimeout is the default time till an Agent transitions to failed after disconnected.
defaultFailedTimeout = 25 * time.Second
- // defaultHostAcceptanceMinWait is the wait time before nominating a host candidate
+ // defaultHostAcceptanceMinWait is the wait time before nominating a host candidate.
defaultHostAcceptanceMinWait = 0
- // defaultSrflxAcceptanceMinWait is the wait time before nominating a srflx candidate
+ // defaultSrflxAcceptanceMinWait is the wait time before nominating a srflx candidate.
defaultSrflxAcceptanceMinWait = 500 * time.Millisecond
- // defaultPrflxAcceptanceMinWait is the wait time before nominating a prflx candidate
+ // defaultPrflxAcceptanceMinWait is the wait time before nominating a prflx candidate.
defaultPrflxAcceptanceMinWait = 1000 * time.Millisecond
- // defaultRelayAcceptanceMinWait is the wait time before nominating a relay candidate
+ // defaultRelayAcceptanceMinWait is the wait time before nominating a relay candidate.
defaultRelayAcceptanceMinWait = 2000 * time.Millisecond
- // defaultMaxBindingRequests is the maximum number of binding requests before considering a pair failed
+ // defaultSTUNGatherTimeout is the wait time for STUN responses.
+ defaultSTUNGatherTimeout = 5 * time.Second
+
+ // defaultMaxBindingRequests is the maximum number of binding requests before considering a pair failed.
defaultMaxBindingRequests = 7
- // maxBufferSize is the number of bytes that can be buffered before we start to error
+ // TCPPriorityOffset is a number which is subtracted from the default (UDP) candidate type preference
+ // for host, srflx and prfx candidate types.
+ defaultTCPPriorityOffset = 27
+
+ // maxBufferSize is the number of bytes that can be buffered before we start to error.
maxBufferSize = 1000 * 1000 // 1MB
- // maxBindingRequestTimeout is the wait time before binding requests can be deleted
+ // maxBindingRequestTimeout is the wait time before binding requests can be deleted.
maxBindingRequestTimeout = 4000 * time.Millisecond
)
@@ -53,7 +60,7 @@ func defaultCandidateTypes() []CandidateType {
}
// AgentConfig collects the arguments to ice.Agent construction into
-// a single structure, for future-proofness of the interface
+// a single structure, for future-proofness of the interface.
type AgentConfig struct {
Urls []*stun.URI
@@ -84,7 +91,7 @@ type AgentConfig struct {
// KeepaliveInterval determines how often should we send ICE
// keepalives (should be less then connectiontimeout above)
- // when this is nil, it defaults to 10 seconds.
+ // when this is nil, it defaults to 2 seconds.
// A keepalive interval of 0 means we never send keepalive packets
KeepaliveInterval *time.Duration
@@ -126,12 +133,14 @@ type AgentConfig struct {
// HostAcceptanceMinWait specify a minimum wait time before selecting host candidates
HostAcceptanceMinWait *time.Duration
- // HostAcceptanceMinWait specify a minimum wait time before selecting srflx candidates
+ // SrflxAcceptanceMinWait specify a minimum wait time before selecting srflx candidates
SrflxAcceptanceMinWait *time.Duration
- // HostAcceptanceMinWait specify a minimum wait time before selecting prflx candidates
+ // PrflxAcceptanceMinWait specify a minimum wait time before selecting prflx candidates
PrflxAcceptanceMinWait *time.Duration
- // HostAcceptanceMinWait specify a minimum wait time before selecting relay candidates
+ // RelayAcceptanceMinWait specify a minimum wait time before selecting relay candidates
RelayAcceptanceMinWait *time.Duration
+ // STUNGatherTimeout specify a minimum wait time for STUN responses
+ STUNGatherTimeout *time.Duration
// Net is the our abstracted network interface for internal development purpose only
// (see https://github.com/pion/transport)
@@ -139,11 +148,11 @@ type AgentConfig struct {
// InterfaceFilter is a function that you can use in order to whitelist or blacklist
// the interfaces which are used to gather ICE candidates.
- InterfaceFilter func(string) bool
+ InterfaceFilter func(string) (keep bool)
// IPFilter is a function that you can use in order to whitelist or blacklist
// the ips which are used to gather ICE candidates.
- IPFilter func(net.IP) bool
+ IPFilter func(net.IP) (keep bool)
// InsecureSkipVerify controls if self-signed certificates are accepted when connecting
// to TURN servers via TLS or DTLS
@@ -174,99 +183,137 @@ type AgentConfig struct {
// Include loopback addresses in the candidate list.
IncludeLoopback bool
+
+ // TCPPriorityOffset is a number which is subtracted from the default (UDP) candidate type preference
+ // for host, srflx and prfx candidate types. It helps to configure relative preference of UDP candidates
+ // against TCP ones. Relay candidates for TCP and UDP are always 0 and not affected by this setting.
+ // When this is nil, defaultTCPPriorityOffset is used.
+ TCPPriorityOffset *uint16
+
+ // DisableActiveTCP can be used to disable Active TCP candidates. Otherwise when TCP is enabled
+ // Active TCP candidates will be created when a new passive TCP remote candidate is added.
+ DisableActiveTCP bool
+
+ // BindingRequestHandler allows applications to perform logic on incoming STUN Binding Requests
+ // This was implemented to allow users to
+ // * Log incoming Binding Requests for debugging
+ // * Implement draft-thatcher-ice-renomination
+ // * Implement custom CandidatePair switching logic
+ BindingRequestHandler func(m *stun.Message, local, remote Candidate, pair *CandidatePair) bool
+
+ // EnableUseCandidateCheckPriority can be used to enable checking for equal or higher priority to
+ // switch selected candidate pair if the peer requests USE-CANDIDATE and agent is a lite agent.
+ // This is disabled by default, i. e. when peer requests USE-CANDIDATE, the selected pair will be
+ // switched to that irrespective of relative priority between current selected pair
+ // and priority of the pair being switched to.
+ EnableUseCandidateCheckPriority bool
}
-// initWithDefaults populates an agent and falls back to defaults if fields are unset
-func (config *AgentConfig) initWithDefaults(a *Agent) {
+// initWithDefaults populates an agent and falls back to defaults if fields are unset.
+func (config *AgentConfig) initWithDefaults(agent *Agent) { //nolint:cyclop
if config.MaxBindingRequests == nil {
- a.maxBindingRequests = defaultMaxBindingRequests
+ agent.maxBindingRequests = defaultMaxBindingRequests
} else {
- a.maxBindingRequests = *config.MaxBindingRequests
+ agent.maxBindingRequests = *config.MaxBindingRequests
}
if config.HostAcceptanceMinWait == nil {
- a.hostAcceptanceMinWait = defaultHostAcceptanceMinWait
+ agent.hostAcceptanceMinWait = defaultHostAcceptanceMinWait
} else {
- a.hostAcceptanceMinWait = *config.HostAcceptanceMinWait
+ agent.hostAcceptanceMinWait = *config.HostAcceptanceMinWait
}
if config.SrflxAcceptanceMinWait == nil {
- a.srflxAcceptanceMinWait = defaultSrflxAcceptanceMinWait
+ agent.srflxAcceptanceMinWait = defaultSrflxAcceptanceMinWait
} else {
- a.srflxAcceptanceMinWait = *config.SrflxAcceptanceMinWait
+ agent.srflxAcceptanceMinWait = *config.SrflxAcceptanceMinWait
}
if config.PrflxAcceptanceMinWait == nil {
- a.prflxAcceptanceMinWait = defaultPrflxAcceptanceMinWait
+ agent.prflxAcceptanceMinWait = defaultPrflxAcceptanceMinWait
} else {
- a.prflxAcceptanceMinWait = *config.PrflxAcceptanceMinWait
+ agent.prflxAcceptanceMinWait = *config.PrflxAcceptanceMinWait
}
if config.RelayAcceptanceMinWait == nil {
- a.relayAcceptanceMinWait = defaultRelayAcceptanceMinWait
+ agent.relayAcceptanceMinWait = defaultRelayAcceptanceMinWait
} else {
- a.relayAcceptanceMinWait = *config.RelayAcceptanceMinWait
+ agent.relayAcceptanceMinWait = *config.RelayAcceptanceMinWait
+ }
+
+ if config.STUNGatherTimeout == nil {
+ agent.stunGatherTimeout = defaultSTUNGatherTimeout
+ } else {
+ agent.stunGatherTimeout = *config.STUNGatherTimeout
+ }
+
+ if config.TCPPriorityOffset == nil {
+ agent.tcpPriorityOffset = defaultTCPPriorityOffset
+ } else {
+ agent.tcpPriorityOffset = *config.TCPPriorityOffset
}
if config.DisconnectedTimeout == nil {
- a.disconnectedTimeout = defaultDisconnectedTimeout
+ agent.disconnectedTimeout = defaultDisconnectedTimeout
} else {
- a.disconnectedTimeout = *config.DisconnectedTimeout
+ agent.disconnectedTimeout = *config.DisconnectedTimeout
}
if config.FailedTimeout == nil {
- a.failedTimeout = defaultFailedTimeout
+ agent.failedTimeout = defaultFailedTimeout
} else {
- a.failedTimeout = *config.FailedTimeout
+ agent.failedTimeout = *config.FailedTimeout
}
if config.KeepaliveInterval == nil {
- a.keepaliveInterval = defaultKeepaliveInterval
+ agent.keepaliveInterval = defaultKeepaliveInterval
} else {
- a.keepaliveInterval = *config.KeepaliveInterval
+ agent.keepaliveInterval = *config.KeepaliveInterval
}
if config.CheckInterval == nil {
- a.checkInterval = defaultCheckInterval
+ agent.checkInterval = defaultCheckInterval
} else {
- a.checkInterval = *config.CheckInterval
+ agent.checkInterval = *config.CheckInterval
}
- if config.CandidateTypes == nil || len(config.CandidateTypes) == 0 {
- a.candidateTypes = defaultCandidateTypes()
+ if len(config.CandidateTypes) == 0 {
+ agent.candidateTypes = defaultCandidateTypes()
} else {
- a.candidateTypes = config.CandidateTypes
+ agent.candidateTypes = config.CandidateTypes
}
}
-func (config *AgentConfig) initExtIPMapping(a *Agent) error {
+func (config *AgentConfig) initExtIPMapping(agent *Agent) error { //nolint:cyclop
var err error
- a.extIPMapper, err = newExternalIPMapper(config.NAT1To1IPCandidateType, config.NAT1To1IPs)
+ agent.extIPMapper, err = newExternalIPMapper(config.NAT1To1IPCandidateType, config.NAT1To1IPs)
if err != nil {
return err
}
- if a.extIPMapper == nil {
+ if agent.extIPMapper == nil {
return nil // This may happen when config.NAT1To1IPs is an empty array
}
- if a.extIPMapper.candidateType == CandidateTypeHost {
- if a.mDNSMode == MulticastDNSModeQueryAndGather {
+ if agent.extIPMapper.candidateType == CandidateTypeHost { //nolint:nestif
+ if agent.mDNSMode == MulticastDNSModeQueryAndGather {
return ErrMulticastDNSWithNAT1To1IPMapping
}
candiHostEnabled := false
- for _, candiType := range a.candidateTypes {
+ for _, candiType := range agent.candidateTypes {
if candiType == CandidateTypeHost {
candiHostEnabled = true
+
break
}
}
if !candiHostEnabled {
return ErrIneffectiveNAT1To1IPMappingHost
}
- } else if a.extIPMapper.candidateType == CandidateTypeServerReflexive {
+ } else if agent.extIPMapper.candidateType == CandidateTypeServerReflexive {
candiSrflxEnabled := false
- for _, candiType := range a.candidateTypes {
+ for _, candiType := range agent.candidateTypes {
if candiType == CandidateTypeServerReflexive {
candiSrflxEnabled = true
+
break
}
}
@@ -274,5 +321,6 @@ func (config *AgentConfig) initExtIPMapping(a *Agent) error {
return ErrIneffectiveNAT1To1IPMappingSrflx
}
}
+
return nil
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/agent_handlers.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/agent_handlers.go
new file mode 100644
index 000000000..823514e44
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/agent_handlers.go
@@ -0,0 +1,190 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ice
+
+import "sync"
+
+// OnConnectionStateChange sets a handler that is fired when the connection state changes.
+func (a *Agent) OnConnectionStateChange(f func(ConnectionState)) error {
+ a.onConnectionStateChangeHdlr.Store(f)
+
+ return nil
+}
+
+// OnSelectedCandidatePairChange sets a handler that is fired when the final candidate.
+// pair is selected.
+func (a *Agent) OnSelectedCandidatePairChange(f func(Candidate, Candidate)) error {
+ a.onSelectedCandidatePairChangeHdlr.Store(f)
+
+ return nil
+}
+
+// OnCandidate sets a handler that is fired when new candidates gathered. When
+// the gathering process complete the last candidate is nil.
+func (a *Agent) OnCandidate(f func(Candidate)) error {
+ a.onCandidateHdlr.Store(f)
+
+ return nil
+}
+
+func (a *Agent) onSelectedCandidatePairChange(p *CandidatePair) {
+ if h, ok := a.onSelectedCandidatePairChangeHdlr.Load().(func(Candidate, Candidate)); ok && h != nil {
+ h(p.Local, p.Remote)
+ }
+}
+
+func (a *Agent) onCandidate(c Candidate) {
+ if onCandidateHdlr, ok := a.onCandidateHdlr.Load().(func(Candidate)); ok && onCandidateHdlr != nil {
+ onCandidateHdlr(c)
+ }
+}
+
+func (a *Agent) onConnectionStateChange(s ConnectionState) {
+ if hdlr, ok := a.onConnectionStateChangeHdlr.Load().(func(ConnectionState)); ok && hdlr != nil {
+ hdlr(s)
+ }
+}
+
+type handlerNotifier struct {
+ sync.Mutex
+ running bool
+ notifiers sync.WaitGroup
+
+ connectionStates []ConnectionState
+ connectionStateFunc func(ConnectionState)
+
+ candidates []Candidate
+ candidateFunc func(Candidate)
+
+ selectedCandidatePairs []*CandidatePair
+ candidatePairFunc func(*CandidatePair)
+
+ // State for closing
+ done chan struct{}
+}
+
+func (h *handlerNotifier) Close(graceful bool) {
+ if graceful {
+ // if we were closed ungracefully before, we now
+ // want ot wait.
+ defer h.notifiers.Wait()
+ }
+
+ h.Lock()
+
+ select {
+ case <-h.done:
+ h.Unlock()
+
+ return
+ default:
+ }
+ close(h.done)
+ h.Unlock()
+}
+
+func (h *handlerNotifier) EnqueueConnectionState(state ConnectionState) {
+ h.Lock()
+ defer h.Unlock()
+
+ select {
+ case <-h.done:
+ return
+ default:
+ }
+
+ notify := func() {
+ defer h.notifiers.Done()
+ for {
+ h.Lock()
+ if len(h.connectionStates) == 0 {
+ h.running = false
+ h.Unlock()
+
+ return
+ }
+ notification := h.connectionStates[0]
+ h.connectionStates = h.connectionStates[1:]
+ h.Unlock()
+ h.connectionStateFunc(notification)
+ }
+ }
+
+ h.connectionStates = append(h.connectionStates, state)
+ if !h.running {
+ h.running = true
+ h.notifiers.Add(1)
+ go notify()
+ }
+}
+
+func (h *handlerNotifier) EnqueueCandidate(cand Candidate) {
+ h.Lock()
+ defer h.Unlock()
+
+ select {
+ case <-h.done:
+ return
+ default:
+ }
+
+ notify := func() {
+ defer h.notifiers.Done()
+ for {
+ h.Lock()
+ if len(h.candidates) == 0 {
+ h.running = false
+ h.Unlock()
+
+ return
+ }
+ notification := h.candidates[0]
+ h.candidates = h.candidates[1:]
+ h.Unlock()
+ h.candidateFunc(notification)
+ }
+ }
+
+ h.candidates = append(h.candidates, cand)
+ if !h.running {
+ h.running = true
+ h.notifiers.Add(1)
+ go notify()
+ }
+}
+
+func (h *handlerNotifier) EnqueueSelectedCandidatePair(pair *CandidatePair) {
+ h.Lock()
+ defer h.Unlock()
+
+ select {
+ case <-h.done:
+ return
+ default:
+ }
+
+ notify := func() {
+ defer h.notifiers.Done()
+ for {
+ h.Lock()
+ if len(h.selectedCandidatePairs) == 0 {
+ h.running = false
+ h.Unlock()
+
+ return
+ }
+ notification := h.selectedCandidatePairs[0]
+ h.selectedCandidatePairs = h.selectedCandidatePairs[1:]
+ h.Unlock()
+ h.candidatePairFunc(notification)
+ }
+ }
+
+ h.selectedCandidatePairs = append(h.selectedCandidatePairs, pair)
+ if !h.running {
+ h.running = true
+ h.notifiers.Add(1)
+ go notify()
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/agent_stats.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/agent_stats.go
new file mode 100644
index 000000000..c6b21f57e
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/agent_stats.go
@@ -0,0 +1,183 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ice
+
+import (
+ "context"
+ "time"
+)
+
+// GetCandidatePairsStats returns a list of candidate pair stats.
+func (a *Agent) GetCandidatePairsStats() []CandidatePairStats {
+ var res []CandidatePairStats
+ err := a.loop.Run(a.loop, func(_ context.Context) {
+ result := make([]CandidatePairStats, 0, len(a.checklist))
+ for _, cp := range a.checklist {
+ stat := CandidatePairStats{
+ Timestamp: time.Now(),
+ LocalCandidateID: cp.Local.ID(),
+ RemoteCandidateID: cp.Remote.ID(),
+ State: cp.state,
+ Nominated: cp.nominated,
+ // PacketsSent uint32
+ // PacketsReceived uint32
+ // BytesSent uint64
+ // BytesReceived uint64
+ // LastPacketSentTimestamp time.Time
+ // LastPacketReceivedTimestamp time.Time
+ FirstRequestTimestamp: cp.FirstRequestSentAt(),
+ LastRequestTimestamp: cp.LastRequestSentAt(),
+ FirstResponseTimestamp: cp.FirstReponseReceivedAt(),
+ LastResponseTimestamp: cp.LastResponseReceivedAt(),
+ FirstRequestReceivedTimestamp: cp.FirstRequestReceivedAt(),
+ LastRequestReceivedTimestamp: cp.LastRequestReceivedAt(),
+
+ TotalRoundTripTime: cp.TotalRoundTripTime(),
+ CurrentRoundTripTime: cp.CurrentRoundTripTime(),
+ // AvailableOutgoingBitrate float64
+ // AvailableIncomingBitrate float64
+ // CircuitBreakerTriggerCount uint32
+ RequestsReceived: cp.RequestsReceived(),
+ RequestsSent: cp.RequestsSent(),
+ ResponsesReceived: cp.ResponsesReceived(),
+ ResponsesSent: cp.ResponsesSent(),
+ // RetransmissionsReceived uint64
+ // RetransmissionsSent uint64
+ // ConsentRequestsSent uint64
+ // ConsentExpiredTimestamp time.Time
+ }
+ result = append(result, stat)
+ }
+ res = result
+ })
+ if err != nil {
+ a.log.Errorf("Failed to get candidate pairs stats: %v", err)
+
+ return []CandidatePairStats{}
+ }
+
+ return res
+}
+
+// GetSelectedCandidatePairStats returns a candidate pair stats for selected candidate pair.
+// Returns false if there is no selected pair.
+func (a *Agent) GetSelectedCandidatePairStats() (CandidatePairStats, bool) {
+ isAvailable := false
+ var res CandidatePairStats
+ err := a.loop.Run(a.loop, func(_ context.Context) {
+ sp := a.getSelectedPair()
+ if sp == nil {
+ return
+ }
+
+ isAvailable = true
+ res = CandidatePairStats{
+ Timestamp: time.Now(),
+ LocalCandidateID: sp.Local.ID(),
+ RemoteCandidateID: sp.Remote.ID(),
+ State: sp.state,
+ Nominated: sp.nominated,
+ // PacketsSent uint32
+ // PacketsReceived uint32
+ // BytesSent uint64
+ // BytesReceived uint64
+ // LastPacketSentTimestamp time.Time
+ // LastPacketReceivedTimestamp time.Time
+ // FirstRequestTimestamp time.Time
+ // LastRequestTimestamp time.Time
+ // LastResponseTimestamp time.Time
+ TotalRoundTripTime: sp.TotalRoundTripTime(),
+ CurrentRoundTripTime: sp.CurrentRoundTripTime(),
+ // AvailableOutgoingBitrate float64
+ // AvailableIncomingBitrate float64
+ // CircuitBreakerTriggerCount uint32
+ // RequestsReceived uint64
+ // RequestsSent uint64
+ ResponsesReceived: sp.ResponsesReceived(),
+ // ResponsesSent uint64
+ // RetransmissionsReceived uint64
+ // RetransmissionsSent uint64
+ // ConsentRequestsSent uint64
+ // ConsentExpiredTimestamp time.Time
+ }
+ })
+ if err != nil {
+ a.log.Errorf("Failed to get selected candidate pair stats: %v", err)
+
+ return CandidatePairStats{}, false
+ }
+
+ return res, isAvailable
+}
+
+// GetLocalCandidatesStats returns a list of local candidates stats.
+func (a *Agent) GetLocalCandidatesStats() []CandidateStats {
+ var res []CandidateStats
+ err := a.loop.Run(a.loop, func(_ context.Context) {
+ result := make([]CandidateStats, 0, len(a.localCandidates))
+ for networkType, localCandidates := range a.localCandidates {
+ for _, cand := range localCandidates {
+ relayProtocol := ""
+ if cand.Type() == CandidateTypeRelay {
+ if cRelay, ok := cand.(*CandidateRelay); ok {
+ relayProtocol = cRelay.RelayProtocol()
+ }
+ }
+ stat := CandidateStats{
+ Timestamp: time.Now(),
+ ID: cand.ID(),
+ NetworkType: networkType,
+ IP: cand.Address(),
+ Port: cand.Port(),
+ CandidateType: cand.Type(),
+ Priority: cand.Priority(),
+ // URL string
+ RelayProtocol: relayProtocol,
+ // Deleted bool
+ }
+ result = append(result, stat)
+ }
+ }
+ res = result
+ })
+ if err != nil {
+ a.log.Errorf("Failed to get candidate pair stats: %v", err)
+
+ return []CandidateStats{}
+ }
+
+ return res
+}
+
+// GetRemoteCandidatesStats returns a list of remote candidates stats.
+func (a *Agent) GetRemoteCandidatesStats() []CandidateStats {
+ var res []CandidateStats
+ err := a.loop.Run(a.loop, func(_ context.Context) {
+ result := make([]CandidateStats, 0, len(a.remoteCandidates))
+ for networkType, remoteCandidates := range a.remoteCandidates {
+ for _, c := range remoteCandidates {
+ stat := CandidateStats{
+ Timestamp: time.Now(),
+ ID: c.ID(),
+ NetworkType: networkType,
+ IP: c.Address(),
+ Port: c.Port(),
+ CandidateType: c.Type(),
+ Priority: c.Priority(),
+ // URL string
+ RelayProtocol: "",
+ }
+ result = append(result, stat)
+ }
+ }
+ res = result
+ })
+ if err != nil {
+ a.log.Errorf("Failed to get candidate pair stats: %v", err)
+
+ return []CandidateStats{}
+ }
+
+ return res
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidate.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidate.go
similarity index 53%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidate.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidate.go
index 92a007688..89082f986 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidate.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidate.go
@@ -13,13 +13,13 @@ const (
receiveMTU = 8192
defaultLocalPreference = 65535
- // ComponentRTP indicates that the candidate is used for RTP
+ // ComponentRTP indicates that the candidate is used for RTP.
ComponentRTP uint16 = 1
- // ComponentRTCP indicates that the candidate is used for RTCP
+ // ComponentRTCP indicates that the candidate is used for RTCP.
ComponentRTCP
)
-// Candidate represents an ICE candidate
+// Candidate represents an ICE candidate.
type Candidate interface {
// An arbitrary string used in the freezing algorithm to
// group similar candidates. It is the same for two candidates that
@@ -52,15 +52,38 @@ type Candidate interface {
// candidate, which is useful for diagnostics and other purposes
RelatedAddress() *CandidateRelatedAddress
+ // Extensions returns a copy of all extension attributes associated with the ICECandidate.
+ // In the order of insertion, *(key value).
+ // Extension attributes are defined in RFC 5245, Section 15.1:
+ // https://datatracker.ietf.org/doc/html/rfc5245#section-15.1
+ //.
+ Extensions() []CandidateExtension
+ // GetExtension returns the value of the extension attribute associated with the ICECandidate.
+ // Extension attributes are defined in RFC 5245, Section 15.1:
+ // https://datatracker.ietf.org/doc/html/rfc5245#section-15.1
+ //.
+ GetExtension(key string) (value CandidateExtension, ok bool)
+ // AddExtension adds an extension attribute to the ICECandidate.
+ // If an extension with the same key already exists, it will be overwritten.
+ // Extension attributes are defined in RFC 5245, Section 15.1:
+ AddExtension(extension CandidateExtension) error
+ // RemoveExtension removes an extension attribute from the ICECandidate.
+ // Extension attributes are defined in RFC 5245, Section 15.1:
+ RemoveExtension(key string) (ok bool)
+
String() string
Type() CandidateType
TCPType() TCPType
Equal(other Candidate) bool
+ // DeepEqual same as Equal, But it also compares the candidate extensions.
+ DeepEqual(other Candidate) bool
+
Marshal() string
addr() net.Addr
+ filterForLocationTracking() bool
agent() *Agent
context() context.Context
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidate_base.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidate_base.go
new file mode 100644
index 000000000..66fb776ee
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidate_base.go
@@ -0,0 +1,1059 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ice
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "hash/crc32"
+ "io"
+ "net"
+ "strconv"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/pion/stun/v3"
+)
+
+type candidateBase struct {
+ id string
+ networkType NetworkType
+ candidateType CandidateType
+
+ component uint16
+ address string
+ port int
+ relatedAddress *CandidateRelatedAddress
+ tcpType TCPType
+
+ resolvedAddr net.Addr
+
+ lastSent atomic.Value
+ lastReceived atomic.Value
+ conn net.PacketConn
+
+ currAgent *Agent
+ closeCh chan struct{}
+ closedCh chan struct{}
+
+ foundationOverride string
+ priorityOverride uint32
+
+ remoteCandidateCaches map[AddrPort]Candidate
+ isLocationTracked bool
+ extensions []CandidateExtension
+}
+
+// Done implements context.Context.
+func (c *candidateBase) Done() <-chan struct{} {
+ return c.closeCh
+}
+
+// Err implements context.Context.
+func (c *candidateBase) Err() error {
+ select {
+ case <-c.closedCh:
+ return ErrRunCanceled
+ default:
+ return nil
+ }
+}
+
+// Deadline implements context.Context.
+func (c *candidateBase) Deadline() (deadline time.Time, ok bool) {
+ return time.Time{}, false
+}
+
+// Value implements context.Context.
+func (c *candidateBase) Value(interface{}) interface{} {
+ return nil
+}
+
+// ID returns Candidate ID.
+func (c *candidateBase) ID() string {
+ return c.id
+}
+
+func (c *candidateBase) Foundation() string {
+ if c.foundationOverride != "" {
+ return c.foundationOverride
+ }
+
+ return fmt.Sprintf("%d", crc32.ChecksumIEEE([]byte(c.Type().String()+c.address+c.networkType.String())))
+}
+
+// Address returns Candidate Address.
+func (c *candidateBase) Address() string {
+ return c.address
+}
+
+// Port returns Candidate Port.
+func (c *candidateBase) Port() int {
+ return c.port
+}
+
+// Type returns candidate type.
+func (c *candidateBase) Type() CandidateType {
+ return c.candidateType
+}
+
+// NetworkType returns candidate NetworkType.
+func (c *candidateBase) NetworkType() NetworkType {
+ return c.networkType
+}
+
+// Component returns candidate component.
+func (c *candidateBase) Component() uint16 {
+ return c.component
+}
+
+func (c *candidateBase) SetComponent(component uint16) {
+ c.component = component
+}
+
+// LocalPreference returns the local preference for this candidate.
+func (c *candidateBase) LocalPreference() uint16 { //nolint:cyclop
+ if c.NetworkType().IsTCP() {
+ // RFC 6544, section 4.2
+ //
+ // In Section 4.1.2.1 of [RFC5245], a recommended formula for UDP ICE
+ // candidate prioritization is defined. For TCP candidates, the same
+ // formula and candidate type preferences SHOULD be used, and the
+ // RECOMMENDED type preferences for the new candidate types defined in
+ // this document (see Section 5) are 105 for NAT-assisted candidates and
+ // 75 for UDP-tunneled candidates.
+ //
+ // (...)
+ //
+ // With TCP candidates, the local preference part of the recommended
+ // priority formula is updated to also include the directionality
+ // (active, passive, or simultaneous-open) of the TCP connection. The
+ // RECOMMENDED local preference is then defined as:
+ //
+ // local preference = (2^13) * direction-pref + other-pref
+ //
+ // The direction-pref MUST be between 0 and 7 (both inclusive), with 7
+ // being the most preferred. The other-pref MUST be between 0 and 8191
+ // (both inclusive), with 8191 being the most preferred. It is
+ // RECOMMENDED that the host, UDP-tunneled, and relayed TCP candidates
+ // have the direction-pref assigned as follows: 6 for active, 4 for
+ // passive, and 2 for S-O. For the NAT-assisted and server reflexive
+ // candidates, the RECOMMENDED values are: 6 for S-O, 4 for active, and
+ // 2 for passive.
+ //
+ // (...)
+ //
+ // If any two candidates have the same type-preference and direction-
+ // pref, they MUST have a unique other-pref. With this specification,
+ // this usually only happens with multi-homed hosts, in which case
+ // other-pref is the preference for the particular IP address from which
+ // the candidate was obtained. When there is only a single IP address,
+ // this value SHOULD be set to the maximum allowed value (8191).
+ var otherPref uint16 = 8191
+
+ directionPref := func() uint16 {
+ switch c.Type() {
+ case CandidateTypeHost, CandidateTypeRelay:
+ switch c.tcpType {
+ case TCPTypeActive:
+ return 6
+ case TCPTypePassive:
+ return 4
+ case TCPTypeSimultaneousOpen:
+ return 2
+ case TCPTypeUnspecified:
+ return 0
+ }
+ case CandidateTypePeerReflexive, CandidateTypeServerReflexive:
+ switch c.tcpType {
+ case TCPTypeSimultaneousOpen:
+ return 6
+ case TCPTypeActive:
+ return 4
+ case TCPTypePassive:
+ return 2
+ case TCPTypeUnspecified:
+ return 0
+ }
+ case CandidateTypeUnspecified:
+ return 0
+ }
+
+ return 0
+ }()
+
+ return (1<<13)*directionPref + otherPref
+ }
+
+ return defaultLocalPreference
+}
+
+// RelatedAddress returns *CandidateRelatedAddress.
+func (c *candidateBase) RelatedAddress() *CandidateRelatedAddress {
+ return c.relatedAddress
+}
+
+func (c *candidateBase) TCPType() TCPType {
+ return c.tcpType
+}
+
+// start runs the candidate using the provided connection.
+func (c *candidateBase) start(a *Agent, conn net.PacketConn, initializedCh <-chan struct{}) {
+ if c.conn != nil {
+ c.agent().log.Warn("Can't start already started candidateBase")
+
+ return
+ }
+ c.currAgent = a
+ c.conn = conn
+ c.closeCh = make(chan struct{})
+ c.closedCh = make(chan struct{})
+
+ go c.recvLoop(initializedCh)
+}
+
+var bufferPool = sync.Pool{ // nolint:gochecknoglobals
+ New: func() interface{} {
+ return make([]byte, receiveMTU)
+ },
+}
+
+func (c *candidateBase) recvLoop(initializedCh <-chan struct{}) {
+ agent := c.agent()
+
+ defer close(c.closedCh)
+
+ select {
+ case <-initializedCh:
+ case <-c.closeCh:
+ return
+ }
+
+ bufferPoolBuffer := bufferPool.Get()
+ defer bufferPool.Put(bufferPoolBuffer)
+ buf, ok := bufferPoolBuffer.([]byte)
+ if !ok {
+ return
+ }
+
+ for {
+ n, srcAddr, err := c.conn.ReadFrom(buf)
+ if err != nil {
+ if !(errors.Is(err, io.EOF) || errors.Is(err, net.ErrClosed)) {
+ agent.log.Warnf("Failed to read from candidate %s: %v", c, err)
+ }
+
+ return
+ }
+
+ c.handleInboundPacket(buf[:n], srcAddr)
+ }
+}
+
+func (c *candidateBase) validateSTUNTrafficCache(addr net.Addr) bool {
+ if candidate, ok := c.remoteCandidateCaches[toAddrPort(addr)]; ok {
+ candidate.seen(false)
+
+ return true
+ }
+
+ return false
+}
+
+func (c *candidateBase) addRemoteCandidateCache(candidate Candidate, srcAddr net.Addr) {
+ if c.validateSTUNTrafficCache(srcAddr) {
+ return
+ }
+ c.remoteCandidateCaches[toAddrPort(srcAddr)] = candidate
+}
+
+func (c *candidateBase) handleInboundPacket(buf []byte, srcAddr net.Addr) {
+ agent := c.agent()
+
+ if stun.IsMessage(buf) {
+ msg := &stun.Message{
+ Raw: make([]byte, len(buf)),
+ }
+
+ // Explicitly copy raw buffer so Message can own the memory.
+ copy(msg.Raw, buf)
+
+ if err := msg.Decode(); err != nil {
+ agent.log.Warnf("Failed to handle decode ICE from %s to %s: %v", c.addr(), srcAddr, err)
+
+ return
+ }
+
+ if err := agent.loop.Run(c, func(_ context.Context) {
+ // nolint: contextcheck
+ agent.handleInbound(msg, c, srcAddr)
+ }); err != nil {
+ agent.log.Warnf("Failed to handle message: %v", err)
+ }
+
+ return
+ }
+
+ if !c.validateSTUNTrafficCache(srcAddr) {
+ remoteCandidate, valid := agent.validateNonSTUNTraffic(c, srcAddr) //nolint:contextcheck
+ if !valid {
+ agent.log.Warnf("Discarded message from %s, not a valid remote candidate", c.addr())
+
+ return
+ }
+ c.addRemoteCandidateCache(remoteCandidate, srcAddr)
+ }
+
+ // Note: This will return packetio.ErrFull if the buffer ever manages to fill up.
+ if _, err := agent.buf.Write(buf); err != nil {
+ agent.log.Warnf("Failed to write packet: %s", err)
+
+ return
+ }
+}
+
+// close stops the recvLoop.
+func (c *candidateBase) close() error {
+ // If conn has never been started will be nil
+ if c.Done() == nil {
+ return nil
+ }
+
+ // Assert that conn has not already been closed
+ select {
+ case <-c.Done():
+ return nil
+ default:
+ }
+
+ var firstErr error
+
+ // Unblock recvLoop
+ close(c.closeCh)
+ if err := c.conn.SetDeadline(time.Now()); err != nil {
+ firstErr = err
+ }
+
+ // Close the conn
+ if err := c.conn.Close(); err != nil && firstErr == nil {
+ firstErr = err
+ }
+
+ if firstErr != nil {
+ return firstErr
+ }
+
+ // Wait until the recvLoop is closed
+ <-c.closedCh
+
+ return nil
+}
+
+func (c *candidateBase) writeTo(raw []byte, dst Candidate) (int, error) {
+ n, err := c.conn.WriteTo(raw, dst.addr())
+ if err != nil {
+ // If the connection is closed, we should return the error
+ if errors.Is(err, io.ErrClosedPipe) {
+ return n, err
+ }
+ c.agent().log.Infof("Failed to send packet: %v", err)
+
+ return n, nil
+ }
+ c.seen(true)
+
+ return n, nil
+}
+
+// TypePreference returns the type preference for this candidate.
+func (c *candidateBase) TypePreference() uint16 {
+ pref := c.Type().Preference()
+ if pref == 0 {
+ return 0
+ }
+
+ if c.NetworkType().IsTCP() {
+ var tcpPriorityOffset uint16 = defaultTCPPriorityOffset
+ if c.agent() != nil {
+ tcpPriorityOffset = c.agent().tcpPriorityOffset
+ }
+
+ pref -= tcpPriorityOffset
+ }
+
+ return pref
+}
+
+// Priority computes the priority for this ICE Candidate
+// See: https://www.rfc-editor.org/rfc/rfc8445#section-5.1.2.1
+func (c *candidateBase) Priority() uint32 {
+ if c.priorityOverride != 0 {
+ return c.priorityOverride
+ }
+
+ // The local preference MUST be an integer from 0 (lowest preference) to
+ // 65535 (highest preference) inclusive. When there is only a single IP
+ // address, this value SHOULD be set to 65535. If there are multiple
+ // candidates for a particular component for a particular data stream
+ // that have the same type, the local preference MUST be unique for each
+ // one.
+
+ return (1<<24)*uint32(c.TypePreference()) +
+ (1<<8)*uint32(c.LocalPreference()) +
+ (1<<0)*uint32(256-c.Component())
+}
+
+// Equal is used to compare two candidateBases.
+func (c *candidateBase) Equal(other Candidate) bool {
+ if c.addr() != other.addr() {
+ if c.addr() == nil || other.addr() == nil {
+ return false
+ }
+ if !addrEqual(c.addr(), other.addr()) {
+ return false
+ }
+ }
+
+ return c.NetworkType() == other.NetworkType() &&
+ c.Type() == other.Type() &&
+ c.Address() == other.Address() &&
+ c.Port() == other.Port() &&
+ c.TCPType() == other.TCPType() &&
+ c.RelatedAddress().Equal(other.RelatedAddress())
+}
+
+// DeepEqual is same as Equal but also compares the extensions.
+func (c *candidateBase) DeepEqual(other Candidate) bool {
+ return c.Equal(other) && c.extensionsEqual(other.Extensions())
+}
+
+// String makes the candidateBase printable.
+func (c *candidateBase) String() string {
+ return fmt.Sprintf(
+ "%s %s %s%s (resolved: %v)",
+ c.NetworkType(),
+ c.Type(),
+ net.JoinHostPort(c.Address(), strconv.Itoa(c.Port())),
+ c.relatedAddress,
+ c.resolvedAddr,
+ )
+}
+
+// LastReceived returns a time.Time indicating the last time
+// this candidate was received.
+func (c *candidateBase) LastReceived() time.Time {
+ if lastReceived, ok := c.lastReceived.Load().(time.Time); ok {
+ return lastReceived
+ }
+
+ return time.Time{}
+}
+
+func (c *candidateBase) setLastReceived(t time.Time) {
+ c.lastReceived.Store(t)
+}
+
+// LastSent returns a time.Time indicating the last time
+// this candidate was sent.
+func (c *candidateBase) LastSent() time.Time {
+ if lastSent, ok := c.lastSent.Load().(time.Time); ok {
+ return lastSent
+ }
+
+ return time.Time{}
+}
+
+func (c *candidateBase) setLastSent(t time.Time) {
+ c.lastSent.Store(t)
+}
+
+func (c *candidateBase) seen(outbound bool) {
+ if outbound {
+ c.setLastSent(time.Now())
+ } else {
+ c.setLastReceived(time.Now())
+ }
+}
+
+func (c *candidateBase) addr() net.Addr {
+ return c.resolvedAddr
+}
+
+func (c *candidateBase) filterForLocationTracking() bool {
+ return c.isLocationTracked
+}
+
+func (c *candidateBase) agent() *Agent {
+ return c.currAgent
+}
+
+func (c *candidateBase) context() context.Context {
+ return c
+}
+
+func (c *candidateBase) copy() (Candidate, error) {
+ return UnmarshalCandidate(c.Marshal())
+}
+
+func removeZoneIDFromAddress(addr string) string {
+ if i := strings.Index(addr, "%"); i != -1 {
+ return addr[:i]
+ }
+
+ return addr
+}
+
+// Marshal returns the string representation of the ICECandidate.
+func (c *candidateBase) Marshal() string {
+ val := c.Foundation()
+ if val == " " {
+ val = ""
+ }
+
+ val = fmt.Sprintf("%s %d %s %d %s %d typ %s",
+ val,
+ c.Component(),
+ c.NetworkType().NetworkShort(),
+ c.Priority(),
+ removeZoneIDFromAddress(c.Address()),
+ c.Port(),
+ c.Type())
+
+ if r := c.RelatedAddress(); r != nil && r.Address != "" && r.Port != 0 {
+ val = fmt.Sprintf("%s raddr %s rport %d",
+ val,
+ r.Address,
+ r.Port)
+ }
+
+ extensions := c.marshalExtensions()
+
+ if extensions != "" {
+ val = fmt.Sprintf("%s %s", val, extensions)
+ }
+
+ return val
+}
+
+// CandidateExtension represents a single candidate extension
+// as defined in https://tools.ietf.org/html/rfc5245#section-15.1
+// .
+type CandidateExtension struct {
+ Key string
+ Value string
+}
+
+func (c *candidateBase) Extensions() []CandidateExtension {
+ tcpType := c.TCPType()
+ hasTCPType := 0
+ if tcpType != TCPTypeUnspecified {
+ hasTCPType = 1
+ }
+
+ extensions := make([]CandidateExtension, len(c.extensions)+hasTCPType)
+ // We store the TCPType in c.tcpType, but we need to return it as an extension.
+ if hasTCPType == 1 {
+ extensions[0] = CandidateExtension{
+ Key: "tcptype",
+ Value: tcpType.String(),
+ }
+ }
+
+ copy(extensions[hasTCPType:], c.extensions)
+
+ return extensions
+}
+
+// Get returns the value of the given key if it exists.
+func (c *candidateBase) GetExtension(key string) (CandidateExtension, bool) {
+ extension := CandidateExtension{Key: key}
+
+ for i := range c.extensions {
+ if c.extensions[i].Key == key {
+ extension.Value = c.extensions[i].Value
+
+ return extension, true
+ }
+ }
+
+ // TCPType was manually set.
+ if key == "tcptype" && c.TCPType() != TCPTypeUnspecified { //nolint:goconst
+ extension.Value = c.TCPType().String()
+
+ return extension, true
+ }
+
+ return extension, false
+}
+
+func (c *candidateBase) AddExtension(ext CandidateExtension) error {
+ if ext.Key == "tcptype" {
+ tcpType := NewTCPType(ext.Value)
+ if tcpType == TCPTypeUnspecified {
+ return fmt.Errorf("%w: invalid or unsupported TCPtype %s", errParseTCPType, ext.Value)
+ }
+
+ c.tcpType = tcpType
+
+ return nil
+ }
+
+ if ext.Key == "" {
+ return fmt.Errorf("%w: key is empty", errParseExtension)
+ }
+
+ // per spec, Extensions aren't explicitly unique, we only set the first one.
+ // If the exteion is set multiple times.
+ for i := range c.extensions {
+ if c.extensions[i].Key == ext.Key {
+ c.extensions[i] = ext
+
+ return nil
+ }
+ }
+
+ c.extensions = append(c.extensions, ext)
+
+ return nil
+}
+
+func (c *candidateBase) RemoveExtension(key string) (ok bool) {
+ if key == "tcptype" {
+ c.tcpType = TCPTypeUnspecified
+ ok = true
+ }
+
+ for i := range c.extensions {
+ if c.extensions[i].Key == key {
+ c.extensions = append(c.extensions[:i], c.extensions[i+1:]...)
+ ok = true
+
+ break
+ }
+ }
+
+ return ok
+}
+
+// marshalExtensions returns the string representation of the candidate extensions.
+func (c *candidateBase) marshalExtensions() string {
+ value := ""
+ exts := c.Extensions()
+
+ for i := range exts {
+ if value != "" {
+ value += " "
+ }
+
+ value += exts[i].Key + " " + exts[i].Value
+ }
+
+ return value
+}
+
+// Equal returns true if the candidate extensions are equal.
+func (c *candidateBase) extensionsEqual(other []CandidateExtension) bool {
+ freq1 := make(map[CandidateExtension]int)
+ freq2 := make(map[CandidateExtension]int)
+
+ if len(c.extensions) != len(other) {
+ return false
+ }
+
+ if len(c.extensions) == 0 {
+ return true
+ }
+
+ if len(c.extensions) == 1 {
+ return c.extensions[0] == other[0]
+ }
+
+ for i := range c.extensions {
+ freq1[c.extensions[i]]++
+ freq2[other[i]]++
+ }
+
+ for k, v := range freq1 {
+ if freq2[k] != v {
+ return false
+ }
+ }
+
+ return true
+}
+
+func (c *candidateBase) setExtensions(extensions []CandidateExtension) {
+ c.extensions = extensions
+}
+
+// UnmarshalCandidate Parses a candidate from a string
+// https://datatracker.ietf.org/doc/html/rfc5245#section-15.1
+func UnmarshalCandidate(raw string) (Candidate, error) { //nolint:cyclop
+ // Handle candidates with the "candidate:" prefix as defined in RFC 5245 section 15.1.
+ raw = strings.TrimPrefix(raw, "candidate:")
+
+ pos := 0
+ // foundation ( 1*32ice-char ) But we allow for empty foundation,
+ foundation, pos, err := readCandidateCharToken(raw, pos, 32)
+ if err != nil {
+ return nil, fmt.Errorf("%w: %v in %s", errParseFoundation, err, raw) //nolint:errorlint // we wrap the error
+ }
+
+ // Empty foundation, not RFC 8445 compliant but seen in the wild
+ if foundation == "" {
+ foundation = " "
+ }
+
+ if pos >= len(raw) {
+ return nil, fmt.Errorf("%w: expected component in %s", errAttributeTooShortICECandidate, raw)
+ }
+
+ // component-id ( 1*5DIGIT )
+ component, pos, err := readCandidateDigitToken(raw, pos, 5)
+ if err != nil {
+ return nil, fmt.Errorf("%w: %v in %s", errParseComponent, err, raw) //nolint:errorlint // we wrap the error
+ }
+
+ if pos >= len(raw) {
+ return nil, fmt.Errorf("%w: expected transport in %s", errAttributeTooShortICECandidate, raw)
+ }
+
+ // transport ( "UDP" / transport-extension ; from RFC 3261 ) SP
+ protocol, pos := readCandidateStringToken(raw, pos)
+
+ if pos >= len(raw) {
+ return nil, fmt.Errorf("%w: expected priority in %s", errAttributeTooShortICECandidate, raw)
+ }
+
+ // priority ( 1*10DIGIT ) SP
+ priority, pos, err := readCandidateDigitToken(raw, pos, 10)
+ if err != nil {
+ return nil, fmt.Errorf("%w: %v in %s", errParsePriority, err, raw) //nolint:errorlint // we wrap the error
+ }
+
+ if pos >= len(raw) {
+ return nil, fmt.Errorf("%w: expected address in %s", errAttributeTooShortICECandidate, raw)
+ }
+
+ // connection-address SP ;from RFC 4566
+ address, pos := readCandidateStringToken(raw, pos)
+
+ // Remove IPv6 ZoneID: https://github.com/pion/ice/pull/704
+ address = removeZoneIDFromAddress(address)
+
+ if pos >= len(raw) {
+ return nil, fmt.Errorf("%w: expected port in %s", errAttributeTooShortICECandidate, raw)
+ }
+
+ // port from RFC 4566
+ port, pos, err := readCandidatePort(raw, pos)
+ if err != nil {
+ return nil, fmt.Errorf("%w: %v in %s", errParsePort, err, raw) //nolint:errorlint // we wrap the error
+ }
+
+ // "typ" SP
+ typeKey, pos := readCandidateStringToken(raw, pos)
+ if typeKey != "typ" {
+ return nil, fmt.Errorf("%w (%s)", ErrUnknownCandidateTyp, typeKey)
+ }
+
+ if pos >= len(raw) {
+ return nil, fmt.Errorf("%w: expected candidate type in %s", errAttributeTooShortICECandidate, raw)
+ }
+
+ // SP cand-type ("host" / "srflx" / "prflx" / "relay")
+ typ, pos := readCandidateStringToken(raw, pos)
+
+ raddr, rport, pos, err := tryReadRelativeAddrs(raw, pos)
+ if err != nil {
+ return nil, err
+ }
+
+ tcpType := TCPTypeUnspecified
+ var extensions []CandidateExtension
+ var tcpTypeRaw string
+
+ if pos < len(raw) {
+ extensions, tcpTypeRaw, err = unmarshalCandidateExtensions(raw[pos:])
+ if err != nil {
+ return nil, fmt.Errorf("%w: %v", errParseExtension, err) //nolint:errorlint // we wrap the error
+ }
+
+ if tcpTypeRaw != "" {
+ tcpType = NewTCPType(tcpTypeRaw)
+ if tcpType == TCPTypeUnspecified {
+ return nil, fmt.Errorf("%w: invalid or unsupported TCPtype %s", errParseTCPType, tcpTypeRaw)
+ }
+ }
+ }
+
+ // this code is ugly because we can't break backwards compatibility
+ // with the old way of parsing candidates
+ switch typ {
+ case "host":
+ candidate, err := NewCandidateHost(&CandidateHostConfig{
+ "",
+ protocol,
+ address,
+ port,
+ uint16(component), //nolint:gosec // G115 no overflow we read 5 digits
+ uint32(priority), //nolint:gosec // G115 no overflow we read 5 digits
+ foundation,
+ tcpType,
+ false,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ candidate.setExtensions(extensions)
+
+ return candidate, nil
+ case "srflx":
+ candidate, err := NewCandidateServerReflexive(&CandidateServerReflexiveConfig{
+ "",
+ protocol,
+ address,
+ port,
+ uint16(component), //nolint:gosec // G115 no overflow we read 5 digits
+ uint32(priority), //nolint:gosec // G115 no overflow we read 5 digits
+ foundation,
+ raddr,
+ rport,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ candidate.setExtensions(extensions)
+
+ return candidate, nil
+ case "prflx":
+ candidate, err := NewCandidatePeerReflexive(&CandidatePeerReflexiveConfig{
+ "",
+ protocol,
+ address,
+ port,
+ uint16(component), //nolint:gosec // G115 no overflow we read 5 digits
+ uint32(priority), //nolint:gosec // G115 no overflow we read 5 digits
+ foundation,
+ raddr,
+ rport,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ candidate.setExtensions(extensions)
+
+ return candidate, nil
+ case "relay":
+ candidate, err := NewCandidateRelay(&CandidateRelayConfig{
+ "",
+ protocol,
+ address,
+ port,
+ uint16(component), //nolint:gosec // G115 no overflow we read 5 digits
+ uint32(priority), //nolint:gosec // G115 no overflow we read 5 digits
+ foundation,
+ raddr,
+ rport,
+ "",
+ nil,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ candidate.setExtensions(extensions)
+
+ return candidate, nil
+ default:
+ return nil, fmt.Errorf("%w (%s)", ErrUnknownCandidateTyp, typ)
+ }
+}
+
+// Read an ice-char token from the raw string
+// ice-char = ALPHA / DIGIT / "+" / "/"
+// stop reading when a space is encountered or the end of the string.
+func readCandidateCharToken(raw string, start int, limit int) (string, int, error) { //nolint:cyclop
+ for i, char := range raw[start:] {
+ if char == 0x20 { // SP
+ return raw[start : start+i], start + i + 1, nil
+ }
+
+ if i == limit {
+ //nolint: err113 // handled by caller
+ return "", 0, fmt.Errorf("token too long: %s expected 1x%d", raw[start:start+i], limit)
+ }
+
+ if !(char >= 'A' && char <= 'Z' ||
+ char >= 'a' && char <= 'z' ||
+ char >= '0' && char <= '9' ||
+ char == '+' || char == '/') {
+ return "", 0, fmt.Errorf("invalid ice-char token: %c", char) //nolint: err113 // handled by caller
+ }
+ }
+
+ return raw[start:], len(raw), nil
+}
+
+// Read an ice string token from the raw string until a space is encountered
+// Or the end of the string, we imply that ice string are UTF-8 encoded.
+func readCandidateStringToken(raw string, start int) (string, int) {
+ for i, char := range raw[start:] {
+ if char == 0x20 { // SP
+ return raw[start : start+i], start + i + 1
+ }
+ }
+
+ return raw[start:], len(raw)
+}
+
+// Read a digit token from the raw string
+// stop reading when a space is encountered or the end of the string.
+func readCandidateDigitToken(raw string, start, limit int) (int, int, error) {
+ var val int
+ for i, char := range raw[start:] {
+ if char == 0x20 { // SP
+ return val, start + i + 1, nil
+ }
+
+ if i == limit {
+ //nolint: err113 // handled by caller
+ return 0, 0, fmt.Errorf("token too long: %s expected 1x%d", raw[start:start+i], limit)
+ }
+
+ if !(char >= '0' && char <= '9') {
+ return 0, 0, fmt.Errorf("invalid digit token: %c", char) //nolint: err113 // handled by caller
+ }
+
+ val = val*10 + int(char-'0')
+ }
+
+ return val, len(raw), nil
+}
+
+// Read and validate RFC 4566 port from the raw string.
+func readCandidatePort(raw string, start int) (int, int, error) {
+ port, pos, err := readCandidateDigitToken(raw, start, 5)
+ if err != nil {
+ return 0, 0, err
+ }
+
+ if port > 65535 {
+ return 0, 0, fmt.Errorf("invalid RFC 4566 port %d", port) //nolint: err113 // handled by caller
+ }
+
+ return port, pos, nil
+}
+
+// Read a byte-string token from the raw string
+// As defined in RFC 4566 1*(%x01-09/%x0B-0C/%x0E-FF) ;any byte except NUL, CR, or LF
+// we imply that extensions byte-string are UTF-8 encoded.
+func readCandidateByteString(raw string, start int) (string, int, error) {
+ for i, char := range raw[start:] {
+ if char == 0x20 { // SP
+ return raw[start : start+i], start + i + 1, nil
+ }
+
+ // 1*(%x01-09/%x0B-0C/%x0E-FF)
+ if !(char >= 0x01 && char <= 0x09 ||
+ char >= 0x0B && char <= 0x0C ||
+ char >= 0x0E && char <= 0xFF) {
+ return "", 0, fmt.Errorf("invalid byte-string character: %c", char) //nolint: err113 // handled by caller
+ }
+ }
+
+ return raw[start:], len(raw), nil
+}
+
+// Read and validate raddr and rport from the raw string
+// [SP rel-addr] [SP rel-port]
+// defined in https://datatracker.ietf.org/doc/html/rfc5245#section-15.1
+// .
+func tryReadRelativeAddrs(raw string, start int) (raddr string, rport, pos int, err error) {
+ key, pos := readCandidateStringToken(raw, start)
+
+ if key != "raddr" {
+ return "", 0, start, nil
+ }
+
+ if pos >= len(raw) {
+ return "", 0, 0, fmt.Errorf("%w: expected raddr value in %s", errParseRelatedAddr, raw)
+ }
+
+ raddr, pos = readCandidateStringToken(raw, pos)
+
+ if pos >= len(raw) {
+ return "", 0, 0, fmt.Errorf("%w: expected rport in %s", errParseRelatedAddr, raw)
+ }
+
+ key, pos = readCandidateStringToken(raw, pos)
+ if key != "rport" {
+ return "", 0, 0, fmt.Errorf("%w: expected rport in %s", errParseRelatedAddr, raw)
+ }
+
+ if pos >= len(raw) {
+ return "", 0, 0, fmt.Errorf("%w: expected rport value in %s", errParseRelatedAddr, raw)
+ }
+
+ rport, pos, err = readCandidatePort(raw, pos)
+ if err != nil {
+ return "", 0, 0, fmt.Errorf("%w: %v", errParseRelatedAddr, err) //nolint:errorlint // we wrap the error
+ }
+
+ return raddr, rport, pos, nil
+}
+
+// UnmarshalCandidateExtensions parses the candidate extensions from the raw string.
+// *(SP extension-att-name SP extension-att-value)
+// Where extension-att-name, and extension-att-value are byte-strings
+// as defined in https://tools.ietf.org/html/rfc5245#section-15.1
+func unmarshalCandidateExtensions(raw string) (extensions []CandidateExtension, rawTCPTypeRaw string, err error) {
+ extensions = make([]CandidateExtension, 0)
+
+ if raw == "" {
+ return extensions, "", nil
+ }
+
+ if raw[0] == 0x20 { // SP
+ return extensions, "", fmt.Errorf("%w: unexpected space %s", errParseExtension, raw)
+ }
+
+ for i := 0; i < len(raw); {
+ key, next, err := readCandidateByteString(raw, i)
+ if err != nil {
+ return extensions, "", fmt.Errorf(
+ "%w: failed to read key %v", errParseExtension, err, //nolint: errorlint // we wrap the error
+ )
+ }
+ i = next
+
+ // while not spec-compliant, we allow for empty values, as seen in the wild
+ var value string
+ if i < len(raw) {
+ value, next, err = readCandidateByteString(raw, i)
+ if err != nil {
+ return extensions, "", fmt.Errorf(
+ "%w: failed to read value %v", errParseExtension, err, //nolint: errorlint // we are wrapping the error
+ )
+ }
+ i = next
+ }
+
+ if key == "tcptype" {
+ rawTCPTypeRaw = value
+
+ continue
+ }
+
+ extensions = append(extensions, CandidateExtension{key, value})
+ }
+
+ return extensions, rawTCPTypeRaw, nil
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidate_host.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidate_host.go
similarity index 58%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidate_host.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidate_host.go
index 5d207dd91..ac33ca3f3 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidate_host.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidate_host.go
@@ -4,30 +4,31 @@
package ice
import (
- "net"
+ "net/netip"
"strings"
)
-// CandidateHost is a candidate of type host
+// CandidateHost is a candidate of type host.
type CandidateHost struct {
candidateBase
network string
}
-// CandidateHostConfig is the config required to create a new CandidateHost
+// CandidateHostConfig is the config required to create a new CandidateHost.
type CandidateHostConfig struct {
- CandidateID string
- Network string
- Address string
- Port int
- Component uint16
- Priority uint32
- Foundation string
- TCPType TCPType
+ CandidateID string
+ Network string
+ Address string
+ Port int
+ Component uint16
+ Priority uint32
+ Foundation string
+ TCPType TCPType
+ IsLocationTracked bool
}
-// NewCandidateHost creates a new host candidate
+// NewCandidateHost creates a new host candidate.
func NewCandidateHost(config *CandidateHostConfig) (*CandidateHost, error) {
candidateID := config.CandidateID
@@ -35,7 +36,7 @@ func NewCandidateHost(config *CandidateHostConfig) (*CandidateHost, error) {
candidateID = globalCandidateIDGenerator.Generate()
}
- c := &CandidateHost{
+ candidateHost := &CandidateHost{
candidateBase: candidateBase{
id: candidateID,
address: config.Address,
@@ -46,35 +47,36 @@ func NewCandidateHost(config *CandidateHostConfig) (*CandidateHost, error) {
foundationOverride: config.Foundation,
priorityOverride: config.Priority,
remoteCandidateCaches: map[AddrPort]Candidate{},
+ isLocationTracked: config.IsLocationTracked,
},
network: config.Network,
}
if !strings.HasSuffix(config.Address, ".local") {
- ip := net.ParseIP(config.Address)
- if ip == nil {
- return nil, ErrAddressParseFailed
+ ipAddr, err := netip.ParseAddr(config.Address)
+ if err != nil {
+ return nil, err
}
- if err := c.setIP(ip); err != nil {
+ if err := candidateHost.setIPAddr(ipAddr); err != nil {
return nil, err
}
} else {
// Until mDNS candidate is resolved assume it is UDPv4
- c.candidateBase.networkType = NetworkTypeUDP4
+ candidateHost.candidateBase.networkType = NetworkTypeUDP4
}
- return c, nil
+ return candidateHost, nil
}
-func (c *CandidateHost) setIP(ip net.IP) error {
- networkType, err := determineNetworkType(c.network, ip)
+func (c *CandidateHost) setIPAddr(addr netip.Addr) error {
+ networkType, err := determineNetworkType(c.network, addr)
if err != nil {
return err
}
c.candidateBase.networkType = networkType
- c.candidateBase.resolvedAddr = createAddr(networkType, ip, c.port)
+ c.candidateBase.resolvedAddr = createAddr(networkType, addr, c.port)
return nil
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidate_peer_reflexive.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidate_peer_reflexive.go
similarity index 78%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidate_peer_reflexive.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidate_peer_reflexive.go
index f019ec698..9bf435cd7 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidate_peer_reflexive.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidate_peer_reflexive.go
@@ -6,14 +6,16 @@
//nolint:dupl
package ice
-import "net"
+import (
+ "net/netip"
+)
// CandidatePeerReflexive ...
type CandidatePeerReflexive struct {
candidateBase
}
-// CandidatePeerReflexiveConfig is the config required to create a new CandidatePeerReflexive
+// CandidatePeerReflexiveConfig is the config required to create a new CandidatePeerReflexive.
type CandidatePeerReflexiveConfig struct {
CandidateID string
Network string
@@ -26,22 +28,21 @@ type CandidatePeerReflexiveConfig struct {
RelPort int
}
-// NewCandidatePeerReflexive creates a new peer reflective candidate
+// NewCandidatePeerReflexive creates a new peer reflective candidate.
func NewCandidatePeerReflexive(config *CandidatePeerReflexiveConfig) (*CandidatePeerReflexive, error) {
- ip := net.ParseIP(config.Address)
- if ip == nil {
- return nil, ErrAddressParseFailed
+ ipAddr, err := netip.ParseAddr(config.Address)
+ if err != nil {
+ return nil, err
}
- networkType, err := determineNetworkType(config.Network, ip)
+ networkType, err := determineNetworkType(config.Network, ipAddr)
if err != nil {
return nil, err
}
candidateID := config.CandidateID
- candidateIDGenerator := newCandidateIDGenerator()
if candidateID == "" {
- candidateID = candidateIDGenerator.Generate()
+ candidateID = globalCandidateIDGenerator.Generate()
}
return &CandidatePeerReflexive{
@@ -51,7 +52,7 @@ func NewCandidatePeerReflexive(config *CandidatePeerReflexiveConfig) (*Candidate
candidateType: CandidateTypePeerReflexive,
address: config.Address,
port: config.Port,
- resolvedAddr: createAddr(networkType, ip, config.Port),
+ resolvedAddr: createAddr(networkType, ipAddr, config.Port),
component: config.Component,
foundationOverride: config.Foundation,
priorityOverride: config.Priority,
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidate_relay.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidate_relay.go
similarity index 63%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidate_relay.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidate_relay.go
index 449d077a6..9e88b1a63 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidate_relay.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidate_relay.go
@@ -5,6 +5,7 @@ package ice
import (
"net"
+ "net/netip"
)
// CandidateRelay ...
@@ -15,7 +16,7 @@ type CandidateRelay struct {
onClose func() error
}
-// CandidateRelayConfig is the config required to create a new CandidateRelay
+// CandidateRelayConfig is the config required to create a new CandidateRelay.
type CandidateRelayConfig struct {
CandidateID string
Network string
@@ -30,7 +31,7 @@ type CandidateRelayConfig struct {
OnClose func() error
}
-// NewCandidateRelay creates a new relay candidate
+// NewCandidateRelay creates a new relay candidate.
func NewCandidateRelay(config *CandidateRelayConfig) (*CandidateRelay, error) {
candidateID := config.CandidateID
@@ -38,24 +39,28 @@ func NewCandidateRelay(config *CandidateRelayConfig) (*CandidateRelay, error) {
candidateID = globalCandidateIDGenerator.Generate()
}
- ip := net.ParseIP(config.Address)
- if ip == nil {
- return nil, ErrAddressParseFailed
+ ipAddr, err := netip.ParseAddr(config.Address)
+ if err != nil {
+ return nil, err
}
- networkType, err := determineNetworkType(config.Network, ip)
+ networkType, err := determineNetworkType(config.Network, ipAddr)
if err != nil {
return nil, err
}
return &CandidateRelay{
candidateBase: candidateBase{
- id: candidateID,
- networkType: networkType,
- candidateType: CandidateTypeRelay,
- address: config.Address,
- port: config.Port,
- resolvedAddr: &net.UDPAddr{IP: ip, Port: config.Port},
+ id: candidateID,
+ networkType: networkType,
+ candidateType: CandidateTypeRelay,
+ address: config.Address,
+ port: config.Port,
+ resolvedAddr: &net.UDPAddr{
+ IP: ipAddr.AsSlice(),
+ Port: config.Port,
+ Zone: ipAddr.Zone(),
+ },
component: config.Component,
foundationOverride: config.Foundation,
priorityOverride: config.Priority,
@@ -70,6 +75,23 @@ func NewCandidateRelay(config *CandidateRelayConfig) (*CandidateRelay, error) {
}, nil
}
+// LocalPreference returns the local preference for this candidate.
+func (c *CandidateRelay) LocalPreference() uint16 {
+ // These preference values come from libwebrtc
+ // https://github.com/mozilla/libwebrtc/blob/1389c76d9c79839a2ca069df1db48aa3f2e6a1ac/p2p/base/turn_port.cc#L61
+ var relayPreference uint16
+ switch c.relayProtocol {
+ case relayProtocolTLS, relayProtocolDTLS:
+ relayPreference = 2
+ case tcp:
+ relayPreference = 1
+ default:
+ relayPreference = 0
+ }
+
+ return c.candidateBase.LocalPreference() + relayPreference
+}
+
// RelayProtocol returns the protocol used between the endpoint and the relay server.
func (c *CandidateRelay) RelayProtocol() string {
return c.relayProtocol
@@ -81,6 +103,7 @@ func (c *CandidateRelay) close() error {
err = c.onClose()
c.onClose = nil
}
+
return err
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidate_server_reflexive.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidate_server_reflexive.go
similarity index 69%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidate_server_reflexive.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidate_server_reflexive.go
index 3a8ac0ff7..3612edbce 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidate_server_reflexive.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidate_server_reflexive.go
@@ -3,14 +3,17 @@
package ice
-import "net"
+import (
+ "net"
+ "net/netip"
+)
// CandidateServerReflexive ...
type CandidateServerReflexive struct {
candidateBase
}
-// CandidateServerReflexiveConfig is the config required to create a new CandidateServerReflexive
+// CandidateServerReflexiveConfig is the config required to create a new CandidateServerReflexive.
type CandidateServerReflexiveConfig struct {
CandidateID string
Network string
@@ -23,14 +26,14 @@ type CandidateServerReflexiveConfig struct {
RelPort int
}
-// NewCandidateServerReflexive creates a new server reflective candidate
+// NewCandidateServerReflexive creates a new server reflective candidate.
func NewCandidateServerReflexive(config *CandidateServerReflexiveConfig) (*CandidateServerReflexive, error) {
- ip := net.ParseIP(config.Address)
- if ip == nil {
- return nil, ErrAddressParseFailed
+ ipAddr, err := netip.ParseAddr(config.Address)
+ if err != nil {
+ return nil, err
}
- networkType, err := determineNetworkType(config.Network, ip)
+ networkType, err := determineNetworkType(config.Network, ipAddr)
if err != nil {
return nil, err
}
@@ -42,12 +45,16 @@ func NewCandidateServerReflexive(config *CandidateServerReflexiveConfig) (*Candi
return &CandidateServerReflexive{
candidateBase: candidateBase{
- id: candidateID,
- networkType: networkType,
- candidateType: CandidateTypeServerReflexive,
- address: config.Address,
- port: config.Port,
- resolvedAddr: &net.UDPAddr{IP: ip, Port: config.Port},
+ id: candidateID,
+ networkType: networkType,
+ candidateType: CandidateTypeServerReflexive,
+ address: config.Address,
+ port: config.Port,
+ resolvedAddr: &net.UDPAddr{
+ IP: ipAddr.AsSlice(),
+ Port: config.Port,
+ Zone: ipAddr.Zone(),
+ },
component: config.Component,
foundationOverride: config.Foundation,
priorityOverride: config.Priority,
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidatepair.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidatepair.go
new file mode 100644
index 000000000..82655aa5f
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidatepair.go
@@ -0,0 +1,256 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ice
+
+import (
+ "fmt"
+ "sync/atomic"
+ "time"
+
+ "github.com/pion/stun/v3"
+)
+
+func newCandidatePair(local, remote Candidate, controlling bool) *CandidatePair {
+ return &CandidatePair{
+ iceRoleControlling: controlling,
+ Remote: remote,
+ Local: local,
+ state: CandidatePairStateWaiting,
+ }
+}
+
+// CandidatePair is a combination of a local and remote candidate.
+type CandidatePair struct {
+ iceRoleControlling bool
+ Remote Candidate
+ Local Candidate
+ bindingRequestCount uint16
+ state CandidatePairState
+ nominated bool
+ nominateOnBindingSuccess bool
+
+ // stats
+ currentRoundTripTime int64 // in ns
+ totalRoundTripTime int64 // in ns
+
+ requestsReceived uint64
+ requestsSent uint64
+ responsesReceived uint64
+ responsesSent uint64
+
+ firstRequestSentAt atomic.Value // time.Time
+ lastRequestSentAt atomic.Value // time.Time
+ firstReponseReceivedAt atomic.Value // time.Time
+ lastResponseReceivedAt atomic.Value // time.Time
+ firstRequestReceivedAt atomic.Value // time.Time
+ lastRequestReceivedAt atomic.Value // time.Time
+}
+
+func (p *CandidatePair) String() string {
+ if p == nil {
+ return ""
+ }
+
+ return fmt.Sprintf(
+ "prio %d (local, prio %d) %s <-> %s (remote, prio %d), state: %s, nominated: %v, nominateOnBindingSuccess: %v",
+ p.priority(),
+ p.Local.Priority(),
+ p.Local,
+ p.Remote,
+ p.Remote.Priority(),
+ p.state,
+ p.nominated,
+ p.nominateOnBindingSuccess,
+ )
+}
+
+func (p *CandidatePair) equal(other *CandidatePair) bool {
+ if p == nil && other == nil {
+ return true
+ }
+ if p == nil || other == nil {
+ return false
+ }
+
+ return p.Local.Equal(other.Local) && p.Remote.Equal(other.Remote)
+}
+
+// RFC 5245 - 5.7.2. Computing Pair Priority and Ordering Pairs
+// Let G be the priority for the candidate provided by the controlling
+// agent. Let D be the priority for the candidate provided by the
+// controlled agent.
+// pair priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0).
+func (p *CandidatePair) priority() uint64 {
+ var g, d uint32 //nolint:varnamelen // clearer to use g and d here
+ if p.iceRoleControlling {
+ g = p.Local.Priority()
+ d = p.Remote.Priority()
+ } else {
+ g = p.Remote.Priority()
+ d = p.Local.Priority()
+ }
+
+ // Just implement these here rather
+ // than fooling around with the math package
+ localMin := func(x, y uint32) uint64 {
+ if x < y {
+ return uint64(x)
+ }
+
+ return uint64(y)
+ }
+ localMax := func(x, y uint32) uint64 {
+ if x > y {
+ return uint64(x)
+ }
+
+ return uint64(y)
+ }
+ cmp := func(x, y uint32) uint64 {
+ if x > y {
+ return uint64(1)
+ }
+
+ return uint64(0)
+ }
+
+ // 1<<32 overflows uint32; and if both g && d are
+ // maxUint32, this result would overflow uint64
+ return (1<<32-1)*localMin(g, d) + 2*localMax(g, d) + cmp(g, d)
+}
+
+func (p *CandidatePair) Write(b []byte) (int, error) {
+ return p.Local.writeTo(b, p.Remote)
+}
+
+func (a *Agent) sendSTUN(msg *stun.Message, local, remote Candidate) {
+ _, err := local.writeTo(msg.Raw, remote)
+ if err != nil {
+ a.log.Tracef("Failed to send STUN message: %s", err)
+ }
+}
+
+// UpdateRoundTripTime sets the current round time of this pair and
+// accumulates total round trip time and responses received.
+func (p *CandidatePair) UpdateRoundTripTime(rtt time.Duration) {
+ rttNs := rtt.Nanoseconds()
+ atomic.StoreInt64(&p.currentRoundTripTime, rttNs)
+ atomic.AddInt64(&p.totalRoundTripTime, rttNs)
+ atomic.AddUint64(&p.responsesReceived, 1)
+
+ now := time.Now()
+ p.firstReponseReceivedAt.CompareAndSwap(nil, now)
+ p.lastResponseReceivedAt.Store(now)
+}
+
+// CurrentRoundTripTime returns the current round trip time in seconds
+// https://www.w3.org/TR/webrtc-stats/#dom-rtcicecandidatepairstats-currentroundtriptime
+func (p *CandidatePair) CurrentRoundTripTime() float64 {
+ return time.Duration(atomic.LoadInt64(&p.currentRoundTripTime)).Seconds()
+}
+
+// TotalRoundTripTime returns the current round trip time in seconds
+// https://www.w3.org/TR/webrtc-stats/#dom-rtcicecandidatepairstats-totalroundtriptime
+func (p *CandidatePair) TotalRoundTripTime() float64 {
+ return time.Duration(atomic.LoadInt64(&p.totalRoundTripTime)).Seconds()
+}
+
+// RequestsReceived returns the total number of connectivity checks received
+// https://www.w3.org/TR/webrtc-stats/#dom-rtcicecandidatepairstats-requestsreceived
+func (p *CandidatePair) RequestsReceived() uint64 {
+ return atomic.LoadUint64(&p.requestsReceived)
+}
+
+// RequestsSent returns the total number of connectivity checks sent
+// https://www.w3.org/TR/webrtc-stats/#dom-rtcicecandidatepairstats-requestssent
+func (p *CandidatePair) RequestsSent() uint64 {
+ return atomic.LoadUint64(&p.requestsSent)
+}
+
+// ResponsesReceived returns the total number of connectivity responses received
+// https://www.w3.org/TR/webrtc-stats/#dom-rtcicecandidatepairstats-responsesreceived
+func (p *CandidatePair) ResponsesReceived() uint64 {
+ return atomic.LoadUint64(&p.responsesReceived)
+}
+
+// ResponsesSent returns the total number of connectivity responses sent
+// https://www.w3.org/TR/webrtc-stats/#dom-rtcicecandidatepairstats-responsessent
+func (p *CandidatePair) ResponsesSent() uint64 {
+ return atomic.LoadUint64(&p.responsesSent)
+}
+
+// FirstRequestSentAt returns the timestamp of the first connectivity check sent.
+func (p *CandidatePair) FirstRequestSentAt() time.Time {
+ if v, ok := p.firstRequestSentAt.Load().(time.Time); ok {
+ return v
+ }
+
+ return time.Time{}
+}
+
+// LastRequestSentAt returns the timestamp of the last connectivity check sent.
+func (p *CandidatePair) LastRequestSentAt() time.Time {
+ if v, ok := p.lastRequestSentAt.Load().(time.Time); ok {
+ return v
+ }
+
+ return time.Time{}
+}
+
+// FirstReponseReceivedAt returns the timestamp of the first connectivity response received.
+func (p *CandidatePair) FirstReponseReceivedAt() time.Time {
+ if v, ok := p.firstReponseReceivedAt.Load().(time.Time); ok {
+ return v
+ }
+
+ return time.Time{}
+}
+
+// LastResponseReceivedAt returns the timestamp of the last connectivity response received.
+func (p *CandidatePair) LastResponseReceivedAt() time.Time {
+ if v, ok := p.lastResponseReceivedAt.Load().(time.Time); ok {
+ return v
+ }
+
+ return time.Time{}
+}
+
+// FirstRequestReceivedAt returns the timestamp of the first connectivity check received.
+func (p *CandidatePair) FirstRequestReceivedAt() time.Time {
+ if v, ok := p.firstRequestReceivedAt.Load().(time.Time); ok {
+ return v
+ }
+
+ return time.Time{}
+}
+
+// LastRequestReceivedAt returns the timestamp of the last connectivity check received.
+func (p *CandidatePair) LastRequestReceivedAt() time.Time {
+ if v, ok := p.lastRequestReceivedAt.Load().(time.Time); ok {
+ return v
+ }
+
+ return time.Time{}
+}
+
+// UpdateRequestSent increments the number of requests sent and updates the timestamp.
+func (p *CandidatePair) UpdateRequestSent() {
+ atomic.AddUint64(&p.requestsSent, 1)
+ now := time.Now()
+ p.firstRequestSentAt.CompareAndSwap(nil, now)
+ p.lastRequestSentAt.Store(now)
+}
+
+// UpdateResponseSent increments the number of responses sent.
+func (p *CandidatePair) UpdateResponseSent() {
+ atomic.AddUint64(&p.responsesSent, 1)
+}
+
+// UpdateRequestReceived increments the number of requests received and updates the timestamp.
+func (p *CandidatePair) UpdateRequestReceived() {
+ atomic.AddUint64(&p.requestsReceived, 1)
+ now := time.Now()
+ p.firstRequestReceivedAt.CompareAndSwap(nil, now)
+ p.lastRequestReceivedAt.Store(now)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidatepair_state.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidatepair_state.go
similarity index 88%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidatepair_state.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidatepair_state.go
index 1a1e827b0..e1efd39a1 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidatepair_state.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidatepair_state.go
@@ -3,13 +3,13 @@
package ice
-// CandidatePairState represent the ICE candidate pair state
+// CandidatePairState represent the ICE candidate pair state.
type CandidatePairState int
const (
// CandidatePairStateWaiting means a check has not been performed for
- // this pair
- CandidatePairStateWaiting = iota + 1
+ // this pair.
+ CandidatePairStateWaiting CandidatePairState = iota + 1
// CandidatePairStateInProgress means a check has been sent for this pair,
// but the transaction is in progress.
@@ -36,5 +36,6 @@ func (c CandidatePairState) String() string {
case CandidatePairStateSucceeded:
return "succeeded"
}
+
return "Unknown candidate pair state"
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidaterelatedaddress.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidaterelatedaddress.go
similarity index 93%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidaterelatedaddress.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidaterelatedaddress.go
index e87c70514..161adf83e 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidaterelatedaddress.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidaterelatedaddress.go
@@ -12,7 +12,7 @@ type CandidateRelatedAddress struct {
Port int
}
-// String makes CandidateRelatedAddress printable
+// String makes CandidateRelatedAddress printable.
func (c *CandidateRelatedAddress) String() string {
if c == nil {
return ""
@@ -27,6 +27,7 @@ func (c *CandidateRelatedAddress) Equal(other *CandidateRelatedAddress) bool {
if c == nil && other == nil {
return true
}
+
return c != nil && other != nil &&
c.Address == other.Address &&
c.Port == other.Port
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidatetype.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidatetype.go
similarity index 92%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidatetype.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidatetype.go
index 3972934cb..fef798b6b 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/candidatetype.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/candidatetype.go
@@ -3,10 +3,10 @@
package ice
-// CandidateType represents the type of candidate
+// CandidateType represents the type of candidate.
type CandidateType byte
-// CandidateType enum
+// CandidateType enum.
const (
CandidateTypeUnspecified CandidateType = iota
CandidateTypeHost
@@ -15,7 +15,7 @@ const (
CandidateTypeRelay
)
-// String makes CandidateType printable
+// String makes CandidateType printable.
func (c CandidateType) String() string {
switch c {
case CandidateTypeHost:
@@ -29,6 +29,7 @@ func (c CandidateType) String() string {
case CandidateTypeUnspecified:
return "Unknown candidate type"
}
+
return "Unknown candidate type"
}
@@ -49,6 +50,7 @@ func (c CandidateType) Preference() uint16 {
case CandidateTypeRelay, CandidateTypeUnspecified:
return 0
}
+
return 0
}
@@ -61,5 +63,6 @@ func containsCandidateType(candidateType CandidateType, candidateTypeList []Cand
return true
}
}
+
return false
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/codecov.yml b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/codecov.yml
similarity index 100%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/codecov.yml
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/codecov.yml
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/errors.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/errors.go
similarity index 74%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/errors.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/errors.go
index e05cce898..39b22e5bd 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/errors.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/errors.go
@@ -3,7 +3,11 @@
package ice
-import "errors"
+import (
+ "errors"
+
+ "github.com/pion/ice/v4/internal/taskloop"
+)
var (
// ErrUnknownType indicates an error with Unknown info.
@@ -25,69 +29,71 @@ var (
ErrPort = errors.New("invalid port")
// ErrLocalUfragInsufficientBits indicates local username fragment insufficient bits are provided.
- // Have to be at least 24 bits long
+ // Have to be at least 24 bits long.
ErrLocalUfragInsufficientBits = errors.New("local username fragment is less than 24 bits long")
// ErrLocalPwdInsufficientBits indicates local password insufficient bits are provided.
- // Have to be at least 128 bits long
+ // Have to be at least 128 bits long.
ErrLocalPwdInsufficientBits = errors.New("local password is less than 128 bits long")
// ErrProtoType indicates an unsupported transport type was provided.
ErrProtoType = errors.New("invalid transport protocol type")
- // ErrClosed indicates the agent is closed
- ErrClosed = errors.New("the agent is closed")
+ // ErrClosed indicates the agent is closed.
+ ErrClosed = taskloop.ErrClosed
- // ErrNoCandidatePairs indicates agent does not have a valid candidate pair
+ // ErrNoCandidatePairs indicates agent does not have a valid candidate pair.
ErrNoCandidatePairs = errors.New("no candidate pairs available")
- // ErrCanceledByCaller indicates agent connection was canceled by the caller
+ // ErrCanceledByCaller indicates agent connection was canceled by the caller.
ErrCanceledByCaller = errors.New("connecting canceled by caller")
- // ErrMultipleStart indicates agent was started twice
+ // ErrMultipleStart indicates agent was started twice.
ErrMultipleStart = errors.New("attempted to start agent twice")
- // ErrRemoteUfragEmpty indicates agent was started with an empty remote ufrag
+ // ErrRemoteUfragEmpty indicates agent was started with an empty remote ufrag.
ErrRemoteUfragEmpty = errors.New("remote ufrag is empty")
- // ErrRemotePwdEmpty indicates agent was started with an empty remote pwd
+ // ErrRemotePwdEmpty indicates agent was started with an empty remote pwd.
ErrRemotePwdEmpty = errors.New("remote pwd is empty")
- // ErrNoOnCandidateHandler indicates agent was started without OnCandidate
+ // ErrNoOnCandidateHandler indicates agent was started without OnCandidate.
ErrNoOnCandidateHandler = errors.New("no OnCandidate provided")
- // ErrMultipleGatherAttempted indicates GatherCandidates has been called multiple times
+ // ErrMultipleGatherAttempted indicates GatherCandidates has been called multiple times.
ErrMultipleGatherAttempted = errors.New("attempting to gather candidates during gathering state")
- // ErrUsernameEmpty indicates agent was give TURN URL with an empty Username
+ // ErrUsernameEmpty indicates agent was give TURN URL with an empty Username.
ErrUsernameEmpty = errors.New("username is empty")
- // ErrPasswordEmpty indicates agent was give TURN URL with an empty Password
+ // ErrPasswordEmpty indicates agent was give TURN URL with an empty Password.
ErrPasswordEmpty = errors.New("password is empty")
- // ErrAddressParseFailed indicates we were unable to parse a candidate address
+ // ErrAddressParseFailed indicates we were unable to parse a candidate address.
ErrAddressParseFailed = errors.New("failed to parse address")
- // ErrLiteUsingNonHostCandidates indicates non host candidates were selected for a lite agent
+ // ErrLiteUsingNonHostCandidates indicates non host candidates were selected for a lite agent.
ErrLiteUsingNonHostCandidates = errors.New("lite agents must only use host candidates")
// ErrUselessUrlsProvided indicates that one or more URL was provided to the agent but no host
- // candidate required them
+ // candidate required them.
ErrUselessUrlsProvided = errors.New("agent does not need URL with selected candidate types")
// ErrUnsupportedNAT1To1IPCandidateType indicates that the specified NAT1To1IPCandidateType is
- // unsupported
+ // unsupported.
ErrUnsupportedNAT1To1IPCandidateType = errors.New("unsupported 1:1 NAT IP candidate type")
- // ErrInvalidNAT1To1IPMapping indicates that the given 1:1 NAT IP mapping is invalid
+ // ErrInvalidNAT1To1IPMapping indicates that the given 1:1 NAT IP mapping is invalid.
ErrInvalidNAT1To1IPMapping = errors.New("invalid 1:1 NAT IP mapping")
- // ErrExternalMappedIPNotFound in NAT1To1IPMapping
+ // ErrExternalMappedIPNotFound in NAT1To1IPMapping.
ErrExternalMappedIPNotFound = errors.New("external mapped IP not found")
// ErrMulticastDNSWithNAT1To1IPMapping indicates that the mDNS gathering cannot be used along
// with 1:1 NAT IP mapping for host candidate.
- ErrMulticastDNSWithNAT1To1IPMapping = errors.New("mDNS gathering cannot be used with 1:1 NAT IP mapping for host candidate")
+ ErrMulticastDNSWithNAT1To1IPMapping = errors.New(
+ "mDNS gathering cannot be used with 1:1 NAT IP mapping for host candidate",
+ )
// ErrIneffectiveNAT1To1IPMappingHost indicates that 1:1 NAT IP mapping for host candidate is
// requested, but the host candidate type is disabled.
@@ -97,10 +103,12 @@ var (
// requested, but the srflx candidate type is disabled.
ErrIneffectiveNAT1To1IPMappingSrflx = errors.New("1:1 NAT IP mapping for srflx candidate ineffective")
- // ErrInvalidMulticastDNSHostName indicates an invalid MulticastDNSHostName
- ErrInvalidMulticastDNSHostName = errors.New("invalid mDNS HostName, must end with .local and can only contain a single '.'")
+ // ErrInvalidMulticastDNSHostName indicates an invalid MulticastDNSHostName.
+ ErrInvalidMulticastDNSHostName = errors.New(
+ "invalid mDNS HostName, must end with .local and can only contain a single '.'",
+ )
- // ErrRunCanceled indicates a run operation was canceled by its individual done
+ // ErrRunCanceled indicates a run operation was canceled by its individual done.
ErrRunCanceled = errors.New("run was canceled by done")
// ErrTCPRemoteAddrAlreadyExists indicates we already have the connection with same remote addr.
@@ -109,34 +117,36 @@ var (
// ErrUnknownCandidateTyp indicates that a candidate had a unknown type value.
ErrUnknownCandidateTyp = errors.New("unknown candidate typ")
- // ErrDetermineNetworkType indicates that the NetworkType was not able to be parsed
+ // ErrDetermineNetworkType indicates that the NetworkType was not able to be parsed.
ErrDetermineNetworkType = errors.New("unable to determine networkType")
- errSendPacket = errors.New("failed to send packet")
errAttributeTooShortICECandidate = errors.New("attribute not long enough to be ICE candidate")
- errParseComponent = errors.New("could not parse component")
- errParsePriority = errors.New("could not parse priority")
- errParsePort = errors.New("could not parse port")
- errParseRelatedAddr = errors.New("could not parse related addresses")
- errParseTCPType = errors.New("could not parse TCP type")
- errGetXorMappedAddrResponse = errors.New("failed to get XOR-MAPPED-ADDRESS response")
+ errClosingConnection = errors.New("failed to close connection")
errConnectionAddrAlreadyExist = errors.New("connection with same remote address already exists")
- errReadingStreamingPacket = errors.New("error reading streaming packet")
- errWriting = errors.New("error writing to")
- errClosingConnection = errors.New("error closing connection")
- errRead = errors.New("unexpected error trying to read")
- errUnknownRole = errors.New("unknown role")
- errICEWriteSTUNMessage = errors.New("the ICE conn can't write STUN messages")
- errUDPMuxDisabled = errors.New("UDPMux is not enabled")
- errNoXorAddrMapping = errors.New("no address mapping")
- errSendSTUNPacket = errors.New("failed to send STUN packet")
- errXORMappedAddrTimeout = errors.New("timeout while waiting for XORMappedAddr")
+ errGetXorMappedAddrResponse = errors.New("failed to get XOR-MAPPED-ADDRESS response")
+ errInvalidAddress = errors.New("invalid address")
+ errNoTCPMuxAvailable = errors.New("no TCP mux is available")
errNotImplemented = errors.New("not implemented yet")
errNoUDPMuxAvailable = errors.New("no UDP mux is available")
- errNoTCPMuxAvailable = errors.New("no TCP mux is available")
- errInvalidAddress = errors.New("invalid address")
+ errNoXorAddrMapping = errors.New("no address mapping")
+ errParseFoundation = errors.New("failed to parse foundation")
+ errParseComponent = errors.New("failed to parse component")
+ errParsePort = errors.New("failed to parse port")
+ errParsePriority = errors.New("failed to parse priority")
+ errParseRelatedAddr = errors.New("failed to parse related addresses")
+ errParseExtension = errors.New("failed to parse extension")
+ errParseTCPType = errors.New("failed to parse TCP type")
+ errRead = errors.New("failed to read")
+ errUDPMuxDisabled = errors.New("UDPMux is not enabled")
+ errUnknownRole = errors.New("unknown role")
+ errWrite = errors.New("failed to write")
+ errWriteSTUNMessage = errors.New("failed to send STUN message")
+ errWriteSTUNMessageToIceConn = errors.New("failed to write STUN message to ICE connection")
+ errXORMappedAddrTimeout = errors.New("timeout while waiting for XORMappedAddr")
+ errFailedToCastUDPAddr = errors.New("failed to cast net.Addr to net.UDPAddr")
+ errInvalidIPAddress = errors.New("invalid ip address")
// UDPMuxDefault should not listen on unspecified address, but to keep backward compatibility, don't return error now.
// will be used in the future.
- // errListenUnspecified = errors.New("can't listen on unspecified address")
+ // errListenUnspecified = errors.New("can't listen on unspecified address").
)
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/external_ip_mapper.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/external_ip_mapper.go
similarity index 83%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/external_ip_mapper.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/external_ip_mapper.go
index 3d542fb1a..2d483ff96 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/external_ip_mapper.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/external_ip_mapper.go
@@ -13,10 +13,13 @@ func validateIPString(ipStr string) (net.IP, bool, error) {
if ip == nil {
return nil, false, ErrInvalidNAT1To1IPMapping
}
+
return ip, (ip.To4() != nil), nil
}
-// ipMapping holds the mapping of local and external IP address for a particular IP family
+// ipMapping holds the mapping of local and external IP address
+//
+// for a particular IP family.
type ipMapping struct {
ipSole net.IP // When non-nil, this is the sole external IP for one local IP assumed
ipMap map[string]net.IP // Local-to-external IP mapping (k: local, v: external)
@@ -75,7 +78,11 @@ type externalIPMapper struct {
candidateType CandidateType
}
-func newExternalIPMapper(candidateType CandidateType, ips []string) (*externalIPMapper, error) { //nolint:gocognit
+//nolint:gocognit,cyclop
+func newExternalIPMapper(
+ candidateType CandidateType,
+ ips []string,
+) (*externalIPMapper, error) {
if len(ips) == 0 {
return nil, nil //nolint:nilnil
}
@@ -85,7 +92,7 @@ func newExternalIPMapper(candidateType CandidateType, ips []string) (*externalIP
return nil, ErrUnsupportedNAT1To1IPCandidateType
}
- m := &externalIPMapper{
+ mapper := &externalIPMapper{
ipv4Mapping: ipMapping{ipMap: map[string]net.IP{}},
ipv6Mapping: ipMapping{ipMap: map[string]net.IP{}},
candidateType: candidateType,
@@ -101,13 +108,13 @@ func newExternalIPMapper(candidateType CandidateType, ips []string) (*externalIP
if err != nil {
return nil, err
}
- if len(ipPair) == 1 {
+ if len(ipPair) == 1 { //nolint:nestif
if isExtIPv4 {
- if err := m.ipv4Mapping.setSoleIP(extIP); err != nil {
+ if err := mapper.ipv4Mapping.setSoleIP(extIP); err != nil {
return nil, err
}
} else {
- if err := m.ipv6Mapping.setSoleIP(extIP); err != nil {
+ if err := mapper.ipv6Mapping.setSoleIP(extIP); err != nil {
return nil, err
}
}
@@ -121,7 +128,7 @@ func newExternalIPMapper(candidateType CandidateType, ips []string) (*externalIP
return nil, ErrInvalidNAT1To1IPMapping
}
- if err := m.ipv4Mapping.addIPMapping(locIP, extIP); err != nil {
+ if err := mapper.ipv4Mapping.addIPMapping(locIP, extIP); err != nil {
return nil, err
}
} else {
@@ -129,14 +136,14 @@ func newExternalIPMapper(candidateType CandidateType, ips []string) (*externalIP
return nil, ErrInvalidNAT1To1IPMapping
}
- if err := m.ipv6Mapping.addIPMapping(locIP, extIP); err != nil {
+ if err := mapper.ipv6Mapping.addIPMapping(locIP, extIP); err != nil {
return nil, err
}
}
}
}
- return m, nil
+ return mapper, nil
}
func (m *externalIPMapper) findExternalIP(localIPStr string) (net.IP, error) {
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/gather.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/gather.go
similarity index 74%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/gather.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/gather.go
index 15e2da146..a1e024716 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/gather.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/gather.go
@@ -9,30 +9,27 @@ import (
"fmt"
"io"
"net"
+ "net/netip"
"reflect"
"sync"
- "time"
- "github.com/pion/dtls/v2"
- "github.com/pion/ice/v2/internal/fakenet"
- stunx "github.com/pion/ice/v2/internal/stun"
+ "github.com/pion/dtls/v3"
+ "github.com/pion/ice/v4/internal/fakenet"
+ stunx "github.com/pion/ice/v4/internal/stun"
"github.com/pion/logging"
- "github.com/pion/stun"
- "github.com/pion/turn/v2"
+ "github.com/pion/stun/v3"
+ "github.com/pion/turn/v4"
)
-const (
- stunGatherTimeout = time.Second * 5
-)
-
-// Close a net.Conn and log if we have a failure
+// Close a net.Conn and log if we have a failure.
func closeConnAndLog(c io.Closer, log logging.LeveledLogger, msg string, args ...interface{}) {
if c == nil || (reflect.ValueOf(c).Kind() == reflect.Ptr && reflect.ValueOf(c).IsNil()) {
log.Warnf("Connection is not allocated: "+msg, args...)
+
return
}
- log.Warnf(msg)
+ log.Warnf(msg, args...)
if err := c.Close(); err != nil {
log.Warnf("Failed to close connection: %v", err)
}
@@ -42,12 +39,14 @@ func closeConnAndLog(c io.Closer, log logging.LeveledLogger, msg string, args ..
func (a *Agent) GatherCandidates() error {
var gatherErr error
- if runErr := a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ if runErr := a.loop.Run(a.loop, func(ctx context.Context) {
if a.gatheringState != GatheringStateNew {
gatherErr = ErrMultipleGatherAttempted
+
return
} else if a.onCandidateHdlr.Load() == nil {
gatherErr = ErrNoOnCandidateHandler
+
return
}
@@ -61,13 +60,15 @@ func (a *Agent) GatherCandidates() error {
}); runErr != nil {
return runErr
}
+
return gatherErr
}
-func (a *Agent) gatherCandidates(ctx context.Context, done chan struct{}) {
+func (a *Agent) gatherCandidates(ctx context.Context, done chan struct{}) { //nolint:cyclop
defer close(done)
if err := a.setGatheringState(GatheringStateGathering); err != nil { //nolint:contextcheck
a.log.Warnf("Failed to set gatheringState to GatheringStateGathering: %v", err)
+
return
}
@@ -115,7 +116,8 @@ func (a *Agent) gatherCandidates(ctx context.Context, done chan struct{}) {
}
}
-func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []NetworkType) { //nolint:gocognit
+//nolint:gocognit,gocyclo,cyclop
+func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []NetworkType) {
networks := map[string]struct{}{}
for _, networkType := range networkTypes {
if networkType.IsTCP() {
@@ -133,25 +135,40 @@ func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []Networ
delete(networks, udp)
}
- localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, networkTypes, a.includeLoopback)
+ _, localAddrs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, networkTypes, a.includeLoopback)
if err != nil {
a.log.Warnf("Failed to iterate local interfaces, host candidates will not be gathered %s", err)
+
return
}
- for _, ip := range localIPs {
- mappedIP := ip
- if a.mDNSMode != MulticastDNSModeQueryAndGather && a.extIPMapper != nil && a.extIPMapper.candidateType == CandidateTypeHost {
- if _mappedIP, innerErr := a.extIPMapper.findExternalIP(ip.String()); innerErr == nil {
- mappedIP = _mappedIP
+ for _, addr := range localAddrs {
+ mappedIP := addr
+ if a.mDNSMode != MulticastDNSModeQueryAndGather &&
+ a.extIPMapper != nil && a.extIPMapper.candidateType == CandidateTypeHost {
+ if _mappedIP, innerErr := a.extIPMapper.findExternalIP(addr.String()); innerErr == nil {
+ conv, ok := netip.AddrFromSlice(_mappedIP)
+ if !ok {
+ a.log.Warnf("failed to convert mapped external IP to netip.Addr'%s'", addr.String())
+
+ continue
+ }
+ // we'd rather have an IPv4-mapped IPv6 become IPv4 so that it is usable
+ mappedIP = conv.Unmap()
} else {
- a.log.Warnf("1:1 NAT mapping is enabled but no external IP is found for %s", ip.String())
+ a.log.Warnf("1:1 NAT mapping is enabled but no external IP is found for %s", addr.String())
}
}
address := mappedIP.String()
+ var isLocationTracked bool
if a.mDNSMode == MulticastDNSModeQueryAndGather {
address = a.mDNSName
+ } else {
+ // Here, we are not doing multicast gathering, so we will need to skip this address so
+ // that we don't accidentally reveal location tracking information. Otherwise, the
+ // case above hides the IP behind an mDNS address.
+ isLocationTracked = shouldFilterLocationTrackedIP(mappedIP)
}
for network := range networks {
@@ -174,16 +191,20 @@ func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []Networ
var muxConns []net.PacketConn
if multi, ok := a.tcpMux.(AllConnsGetter); ok {
a.log.Debugf("GetAllConns by ufrag: %s", a.localUfrag)
- muxConns, err = multi.GetAllConns(a.localUfrag, mappedIP.To4() == nil, ip)
+ // Note: this is missing zone for IPv6 by just grabbing the IP slice
+ muxConns, err = multi.GetAllConns(a.localUfrag, mappedIP.Is6(), addr.AsSlice())
if err != nil {
- a.log.Warnf("Failed to get all TCP connections by ufrag: %s %s %s", network, ip, a.localUfrag)
+ a.log.Warnf("Failed to get all TCP connections by ufrag: %s %s %s", network, addr, a.localUfrag)
+
continue
}
} else {
a.log.Debugf("GetConn by ufrag: %s", a.localUfrag)
- conn, err := a.tcpMux.GetConnByUfrag(a.localUfrag, mappedIP.To4() == nil, ip)
+ // Note: this is missing zone for IPv6 by just grabbing the IP slice
+ conn, err := a.tcpMux.GetConnByUfrag(a.localUfrag, mappedIP.Is6(), addr.AsSlice())
if err != nil {
- a.log.Warnf("Failed to get TCP connections by ufrag: %s %s %s", network, ip, a.localUfrag)
+ a.log.Warnf("Failed to get TCP connections by ufrag: %s %s %s", network, addr, a.localUfrag)
+
continue
}
muxConns = []net.PacketConn{conn}
@@ -194,7 +215,7 @@ func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []Networ
if tcpConn, ok := conn.LocalAddr().(*net.TCPAddr); ok {
conns = append(conns, connAndPort{conn, tcpConn.Port})
} else {
- a.log.Warnf("Failed to get port of connection from TCPMux: %s %s %s", network, ip, a.localUfrag)
+ a.log.Warnf("Failed to get port of connection from TCPMux: %s %s %s", network, addr, a.localUfrag)
}
}
if len(conns) == 0 {
@@ -205,16 +226,22 @@ func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []Networ
// Is there a way to verify that the listen address is even
// accessible from the current interface.
case udp:
- conn, err := listenUDPInPortRange(a.net, a.log, int(a.portMax), int(a.portMin), network, &net.UDPAddr{IP: ip, Port: 0})
+ conn, err := listenUDPInPortRange(a.net, a.log, int(a.portMax), int(a.portMin), network, &net.UDPAddr{
+ IP: addr.AsSlice(),
+ Port: 0,
+ Zone: addr.Zone(),
+ })
if err != nil {
- a.log.Warnf("Failed to listen %s %s", network, ip)
+ a.log.Warnf("Failed to listen %s %s", network, addr)
+
continue
}
if udpConn, ok := conn.LocalAddr().(*net.UDPAddr); ok {
conns = append(conns, connAndPort{conn, udpConn.Port})
} else {
- a.log.Warnf("Failed to get port of UDPAddr from ListenUDPInPortRange: %s %s %s", network, ip, a.localUfrag)
+ a.log.Warnf("Failed to get port of UDPAddr from ListenUDPInPortRange: %s %s %s", network, addr, a.localUfrag)
+
continue
}
}
@@ -226,23 +253,43 @@ func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []Networ
Port: connAndPort.port,
Component: ComponentRTP,
TCPType: tcpType,
+ // we will still process this candidate so that we start up the right
+ // listeners.
+ IsLocationTracked: isLocationTracked,
}
- c, err := NewCandidateHost(&hostConfig)
+ candidateHost, err := NewCandidateHost(&hostConfig)
if err != nil {
- closeConnAndLog(connAndPort.conn, a.log, "failed to create host candidate: %s %s %d: %v", network, mappedIP, connAndPort.port, err)
+ closeConnAndLog(
+ connAndPort.conn,
+ a.log,
+ "failed to create host candidate: %s %s %d: %v",
+ network, mappedIP,
+ connAndPort.port,
+ err,
+ )
+
continue
}
if a.mDNSMode == MulticastDNSModeQueryAndGather {
- if err = c.setIP(ip); err != nil {
- closeConnAndLog(connAndPort.conn, a.log, "failed to create host candidate: %s %s %d: %v", network, mappedIP, connAndPort.port, err)
+ if err = candidateHost.setIPAddr(addr); err != nil {
+ closeConnAndLog(
+ connAndPort.conn,
+ a.log,
+ "failed to create host candidate: %s %s %d: %v",
+ network,
+ mappedIP,
+ connAndPort.port,
+ err,
+ )
+
continue
}
}
- if err := a.addCandidate(ctx, c, connAndPort.conn); err != nil {
- if closeErr := c.close(); closeErr != nil {
+ if err := a.addCandidate(ctx, candidateHost, connAndPort.conn); err != nil {
+ if closeErr := candidateHost.close(); closeErr != nil {
a.log.Warnf("Failed to close candidate: %v", closeErr)
}
a.log.Warnf("Failed to append to localCandidates and run onCandidateHdlr: %v", err)
@@ -252,7 +299,29 @@ func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []Networ
}
}
-func (a *Agent) gatherCandidatesLocalUDPMux(ctx context.Context) error { //nolint:gocognit
+// shouldFilterLocationTrackedIP returns if this candidate IP should be filtered out from
+// any candidate publishing/notification for location tracking reasons.
+func shouldFilterLocationTrackedIP(candidateIP netip.Addr) bool {
+ // https://tools.ietf.org/html/rfc8445#section-5.1.1.1
+ // Similarly, when host candidates corresponding to
+ // an IPv6 address generated using a mechanism that prevents location
+ // tracking are gathered, then host candidates corresponding to IPv6
+ // link-local addresses [RFC4291] MUST NOT be gathered.
+ return candidateIP.Is6() && (candidateIP.IsLinkLocalUnicast() || candidateIP.IsLinkLocalMulticast())
+}
+
+// shouldFilterLocationTracked returns if this candidate IP should be filtered out from
+// any candidate publishing/notification for location tracking reasons.
+func shouldFilterLocationTracked(candidateIP net.IP) bool {
+ addr, ok := netip.AddrFromSlice(candidateIP)
+ if !ok {
+ return false
+ }
+
+ return shouldFilterLocationTrackedIP(addr)
+}
+
+func (a *Agent) gatherCandidatesLocalUDPMux(ctx context.Context) error { //nolint:gocognit,cyclop
if a.udpMux == nil {
return errUDPMuxDisabled
}
@@ -266,21 +335,44 @@ func (a *Agent) gatherCandidatesLocalUDPMux(ctx context.Context) error { //nolin
return errInvalidAddress
}
candidateIP := udpAddr.IP
- if a.extIPMapper != nil && a.extIPMapper.candidateType == CandidateTypeHost {
+
+ if _, ok := a.udpMux.(*UDPMuxDefault); ok && !a.includeLoopback && candidateIP.IsLoopback() {
+ // Unlike MultiUDPMux Default, UDPMuxDefault doesn't have
+ // a separate param to include loopback, so we respect agent config
+ continue
+ }
+
+ if a.mDNSMode != MulticastDNSModeQueryAndGather &&
+ a.extIPMapper != nil &&
+ a.extIPMapper.candidateType == CandidateTypeHost {
mappedIP, err := a.extIPMapper.findExternalIP(candidateIP.String())
if err != nil {
a.log.Warnf("1:1 NAT mapping is enabled but no external IP is found for %s", candidateIP.String())
+
continue
}
candidateIP = mappedIP
}
+ var address string
+ var isLocationTracked bool
+ if a.mDNSMode == MulticastDNSModeQueryAndGather {
+ address = a.mDNSName
+ } else {
+ address = candidateIP.String()
+ // Here, we are not doing multicast gathering, so we will need to skip this address so
+ // that we don't accidentally reveal location tracking information. Otherwise, the
+ // case above hides the IP behind an mDNS address.
+ isLocationTracked = shouldFilterLocationTracked(candidateIP)
+ }
+
hostConfig := CandidateHostConfig{
- Network: udp,
- Address: candidateIP.String(),
- Port: udpAddr.Port,
- Component: ComponentRTP,
+ Network: udp,
+ Address: address,
+ Port: udpAddr.Port,
+ Component: ComponentRTP,
+ IsLocationTracked: isLocationTracked,
}
// Detect a duplicate candidate before calling addCandidate().
@@ -299,6 +391,7 @@ func (a *Agent) gatherCandidatesLocalUDPMux(ctx context.Context) error { //nolin
c, err := NewCandidateHost(&hostConfig)
if err != nil {
closeConnAndLog(conn, a.log, "failed to create host mux candidate: %s %d: %v", candidateIP, udpAddr.Port, err)
+
continue
}
@@ -308,6 +401,7 @@ func (a *Agent) gatherCandidatesLocalUDPMux(ctx context.Context) error { //nolin
}
closeConnAndLog(conn, a.log, "failed to add candidate: %s %d: %v", candidateIP, udpAddr.Port, err)
+
continue
}
@@ -331,21 +425,37 @@ func (a *Agent) gatherCandidatesSrflxMapped(ctx context.Context, networkTypes []
go func() {
defer wg.Done()
- conn, err := listenUDPInPortRange(a.net, a.log, int(a.portMax), int(a.portMin), network, &net.UDPAddr{IP: nil, Port: 0})
+ conn, err := listenUDPInPortRange(
+ a.net,
+ a.log,
+ int(a.portMax),
+ int(a.portMin),
+ network,
+ &net.UDPAddr{IP: nil, Port: 0},
+ )
if err != nil {
a.log.Warnf("Failed to listen %s: %v", network, err)
+
return
}
lAddr, ok := conn.LocalAddr().(*net.UDPAddr)
if !ok {
closeConnAndLog(conn, a.log, "1:1 NAT mapping is enabled but LocalAddr is not a UDPAddr")
+
return
}
mappedIP, err := a.extIPMapper.findExternalIP(lAddr.IP.String())
if err != nil {
closeConnAndLog(conn, a.log, "1:1 NAT mapping is enabled but no external IP is found for %s", lAddr.IP.String())
+
+ return
+ }
+
+ if shouldFilterLocationTracked(mappedIP) {
+ closeConnAndLog(conn, a.log, "external IP is somehow filtered for location tracking reasons %s", mappedIP)
+
return
}
@@ -364,6 +474,7 @@ func (a *Agent) gatherCandidatesSrflxMapped(ctx context.Context, networkTypes []
mappedIP.String(),
lAddr.Port,
err)
+
return
}
@@ -377,7 +488,8 @@ func (a *Agent) gatherCandidatesSrflxMapped(ctx context.Context, networkTypes []
}
}
-func (a *Agent) gatherCandidatesSrflxUDPMux(ctx context.Context, urls []*stun.URI, networkTypes []NetworkType) { //nolint:gocognit
+//nolint:gocognit,cyclop
+func (a *Agent) gatherCandidatesSrflxUDPMux(ctx context.Context, urls []*stun.URI, networkTypes []NetworkType) {
var wg sync.WaitGroup
defer wg.Wait()
@@ -391,6 +503,7 @@ func (a *Agent) gatherCandidatesSrflxUDPMux(ctx context.Context, urls []*stun.UR
udpAddr, ok := listenAddr.(*net.UDPAddr)
if !ok {
a.log.Warn("Failed to cast udpMuxSrflx listen address to UDPAddr")
+
continue
}
wg.Add(1)
@@ -400,19 +513,28 @@ func (a *Agent) gatherCandidatesSrflxUDPMux(ctx context.Context, urls []*stun.UR
hostPort := fmt.Sprintf("%s:%d", url.Host, url.Port)
serverAddr, err := a.net.ResolveUDPAddr(network, hostPort)
if err != nil {
- a.log.Warnf("Failed to resolve STUN host: %s: %v", hostPort, err)
+ a.log.Debugf("Failed to resolve STUN host: %s %s: %v", network, hostPort, err)
+
return
}
- xorAddr, err := a.udpMuxSrflx.GetXORMappedAddr(serverAddr, stunGatherTimeout)
+ if shouldFilterLocationTracked(serverAddr.IP) {
+ a.log.Warnf("STUN host %s is somehow filtered for location tracking reasons", hostPort)
+
+ return
+ }
+
+ xorAddr, err := a.udpMuxSrflx.GetXORMappedAddr(serverAddr, a.stunGatherTimeout)
if err != nil {
a.log.Warnf("Failed get server reflexive address %s %s: %v", network, url, err)
+
return
}
conn, err := a.udpMuxSrflx.GetConnForURL(a.localUfrag, url.String(), localAddr)
if err != nil {
a.log.Warnf("Failed to find connection in UDPMuxSrflx %s %s: %v", network, url, err)
+
return
}
@@ -430,6 +552,7 @@ func (a *Agent) gatherCandidatesSrflxUDPMux(ctx context.Context, urls []*stun.UR
c, err := NewCandidateServerReflexive(&srflxConfig)
if err != nil {
closeConnAndLog(conn, a.log, "failed to create server reflexive candidate: %s %s %d: %v", network, ip, port, err)
+
return
}
@@ -445,7 +568,8 @@ func (a *Agent) gatherCandidatesSrflxUDPMux(ctx context.Context, urls []*stun.UR
}
}
-func (a *Agent) gatherCandidatesSrflx(ctx context.Context, urls []*stun.URI, networkTypes []NetworkType) { //nolint:gocognit
+//nolint:cyclop,gocognit
+func (a *Agent) gatherCandidatesSrflx(ctx context.Context, urls []*stun.URI, networkTypes []NetworkType) {
var wg sync.WaitGroup
defer wg.Wait()
@@ -462,13 +586,28 @@ func (a *Agent) gatherCandidatesSrflx(ctx context.Context, urls []*stun.URI, net
hostPort := fmt.Sprintf("%s:%d", url.Host, url.Port)
serverAddr, err := a.net.ResolveUDPAddr(network, hostPort)
if err != nil {
- a.log.Warnf("Failed to resolve STUN host: %s: %v", hostPort, err)
+ a.log.Debugf("Failed to resolve STUN host: %s %s: %v", network, hostPort, err)
+
return
}
- conn, err := listenUDPInPortRange(a.net, a.log, int(a.portMax), int(a.portMin), network, &net.UDPAddr{IP: nil, Port: 0})
+ if shouldFilterLocationTracked(serverAddr.IP) {
+ a.log.Warnf("STUN host %s is somehow filtered for location tracking reasons", hostPort)
+
+ return
+ }
+
+ conn, err := listenUDPInPortRange(
+ a.net,
+ a.log,
+ int(a.portMax),
+ int(a.portMin),
+ network,
+ &net.UDPAddr{IP: nil, Port: 0},
+ )
if err != nil {
closeConnAndLog(conn, a.log, "failed to listen for %s: %v", serverAddr.String(), err)
+
return
}
// If the agent closes midway through the connection
@@ -479,14 +618,15 @@ func (a *Agent) gatherCandidatesSrflx(ctx context.Context, urls []*stun.URI, net
select {
case <-cancelCtx.Done():
return
- case <-a.done:
+ case <-a.loop.Done():
_ = conn.Close()
}
}()
- xorAddr, err := stunx.GetXORMappedAddr(conn, serverAddr, stunGatherTimeout)
+ xorAddr, err := stunx.GetXORMappedAddr(conn, serverAddr, a.stunGatherTimeout)
if err != nil {
closeConnAndLog(conn, a.log, "failed to get server reflexive address %s %s: %v", network, url, err)
+
return
}
@@ -505,6 +645,7 @@ func (a *Agent) gatherCandidatesSrflx(ctx context.Context, urls []*stun.URI, net
c, err := NewCandidateServerReflexive(&srflxConfig)
if err != nil {
closeConnAndLog(conn, a.log, "failed to create server reflexive candidate: %s %s %d: %v", network, ip, port, err)
+
return
}
@@ -519,7 +660,8 @@ func (a *Agent) gatherCandidatesSrflx(ctx context.Context, urls []*stun.URI, net
}
}
-func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { //nolint:gocognit
+//nolint:maintidx,gocognit,gocyclo,cyclop
+func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) {
var wg sync.WaitGroup
defer wg.Wait()
@@ -530,9 +672,11 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { /
continue
case urls[i].Username == "":
a.log.Errorf("Failed to gather relay candidates: %v", ErrUsernameEmpty)
+
return
case urls[i].Password == "":
a.log.Errorf("Failed to gather relay candidates: %v", ErrPasswordEmpty)
+
return
}
@@ -552,6 +696,7 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { /
case url.Proto == stun.ProtoTypeUDP && url.Scheme == stun.SchemeTypeTURN:
if locConn, err = a.net.ListenPacket(network, "0.0.0.0:0"); err != nil {
a.log.Warnf("Failed to listen %s: %v", network, err)
+
return
}
@@ -563,6 +708,7 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { /
conn, connectErr := a.proxyDialer.Dial(NetworkTypeTCP4.String(), turnServerAddr)
if connectErr != nil {
a.log.Warnf("Failed to dial TCP address %s via proxy dialer: %v", turnServerAddr, connectErr)
+
return
}
@@ -579,12 +725,14 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { /
tcpAddr, connectErr := a.net.ResolveTCPAddr(NetworkTypeTCP4.String(), turnServerAddr)
if connectErr != nil {
a.log.Warnf("Failed to resolve TCP address %s: %v", turnServerAddr, connectErr)
+
return
}
conn, connectErr := a.net.DialTCP(NetworkTypeTCP4.String(), nil, tcpAddr)
if connectErr != nil {
a.log.Warnf("Failed to dial TCP address %s: %v", turnServerAddr, connectErr)
+
return
}
@@ -596,38 +744,50 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { /
udpAddr, connectErr := a.net.ResolveUDPAddr(network, turnServerAddr)
if connectErr != nil {
a.log.Warnf("Failed to resolve UDP address %s: %v", turnServerAddr, connectErr)
+
return
}
udpConn, dialErr := a.net.DialUDP("udp", nil, udpAddr)
if dialErr != nil {
a.log.Warnf("Failed to dial DTLS address %s: %v", turnServerAddr, dialErr)
+
return
}
- conn, connectErr := dtls.ClientWithContext(ctx, udpConn, &dtls.Config{
+ conn, connectErr := dtls.Client(&fakenet.PacketConn{Conn: udpConn}, udpConn.RemoteAddr(), &dtls.Config{
ServerName: url.Host,
InsecureSkipVerify: a.insecureSkipVerify, //nolint:gosec
+ LoggerFactory: a.loggerFactory,
})
if connectErr != nil {
a.log.Warnf("Failed to create DTLS client: %v", turnServerAddr, connectErr)
+
+ return
+ }
+
+ if connectErr = conn.HandshakeContext(ctx); connectErr != nil {
+ a.log.Warnf("Failed to create DTLS client: %v", turnServerAddr, connectErr)
+
return
}
relAddr = conn.LocalAddr().(*net.UDPAddr).IP.String() //nolint:forcetypeassert
relPort = conn.LocalAddr().(*net.UDPAddr).Port //nolint:forcetypeassert
- relayProtocol = "dtls"
+ relayProtocol = relayProtocolDTLS
locConn = &fakenet.PacketConn{Conn: conn}
case url.Proto == stun.ProtoTypeTCP && url.Scheme == stun.SchemeTypeTURNS:
tcpAddr, resolvErr := a.net.ResolveTCPAddr(NetworkTypeTCP4.String(), turnServerAddr)
if resolvErr != nil {
a.log.Warnf("Failed to resolve relay address %s: %v", turnServerAddr, resolvErr)
+
return
}
tcpConn, dialErr := a.net.DialTCP(NetworkTypeTCP4.String(), nil, tcpAddr)
if dialErr != nil {
a.log.Warnf("Failed to connect to relay: %v", dialErr)
+
return
}
@@ -641,15 +801,17 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { /
a.log.Errorf("Failed to close relay connection: %v", closeErr)
}
a.log.Warnf("Failed to connect to relay: %v", hsErr)
+
return
}
relAddr = conn.LocalAddr().(*net.TCPAddr).IP.String() //nolint:forcetypeassert
relPort = conn.LocalAddr().(*net.TCPAddr).Port //nolint:forcetypeassert
- relayProtocol = "tls"
+ relayProtocol = relayProtocolTLS
locConn = turn.NewSTUNConn(conn)
default:
a.log.Warnf("Unable to handle URL in gatherCandidatesRelay %v", url)
+
return
}
@@ -663,12 +825,14 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { /
})
if err != nil {
closeConnAndLog(locConn, a.log, "failed to create new TURN client %s %s", turnServerAddr, err)
+
return
}
if err = client.Listen(); err != nil {
client.Close()
closeConnAndLog(locConn, a.log, "failed to listen on TURN client %s %s", turnServerAddr, err)
+
return
}
@@ -676,10 +840,18 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { /
if err != nil {
client.Close()
closeConnAndLog(locConn, a.log, "failed to allocate on TURN client %s %s", turnServerAddr, err)
+
return
}
rAddr := relayConn.LocalAddr().(*net.UDPAddr) //nolint:forcetypeassert
+
+ if shouldFilterLocationTracked(rAddr.IP) {
+ a.log.Warnf("TURN address %s is somehow filtered for location tracking reasons", rAddr.IP)
+
+ return
+ }
+
relayConfig := CandidateRelayConfig{
Network: network,
Component: ComponentRTP,
@@ -690,6 +862,7 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { /
RelayProtocol: relayProtocol,
OnClose: func() error {
client.Close()
+
return locConn.Close()
},
}
@@ -704,6 +877,7 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { /
client.Close()
closeConnAndLog(locConn, a.log, "failed to create relay candidate: %s %s: %v", network, rAddr.String(), err)
+
return
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/ice.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/ice.go
similarity index 78%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/ice.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/ice.go
index bd551206e..bd53702f5 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/ice.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/ice.go
@@ -3,33 +3,33 @@
package ice
-// ConnectionState is an enum showing the state of a ICE Connection
+// ConnectionState is an enum showing the state of a ICE Connection.
type ConnectionState int
-// List of supported States
+// List of supported States.
const (
- // ConnectionStateUnknown represents an unknown state
+ // ConnectionStateUnknown represents an unknown state.
ConnectionStateUnknown ConnectionState = iota
- // ConnectionStateNew ICE agent is gathering addresses
+ // ConnectionStateNew ICE agent is gathering addresses.
ConnectionStateNew
- // ConnectionStateChecking ICE agent has been given local and remote candidates, and is attempting to find a match
+ // ConnectionStateChecking ICE agent has been given local and remote candidates, and is attempting to find a match.
ConnectionStateChecking
- // ConnectionStateConnected ICE agent has a pairing, but is still checking other pairs
+ // ConnectionStateConnected ICE agent has a pairing, but is still checking other pairs.
ConnectionStateConnected
- // ConnectionStateCompleted ICE agent has finished
+ // ConnectionStateCompleted ICE agent has finished.
ConnectionStateCompleted
- // ConnectionStateFailed ICE agent never could successfully connect
+ // ConnectionStateFailed ICE agent never could successfully connect.
ConnectionStateFailed
- // ConnectionStateDisconnected ICE agent connected successfully, but has entered a failed state
+ // ConnectionStateDisconnected ICE agent connected successfully, but has entered a failed state.
ConnectionStateDisconnected
- // ConnectionStateClosed ICE agent has finished and is no longer handling requests
+ // ConnectionStateClosed ICE agent has finished and is no longer handling requests.
ConnectionStateClosed
)
@@ -54,20 +54,20 @@ func (c ConnectionState) String() string {
}
}
-// GatheringState describes the state of the candidate gathering process
+// GatheringState describes the state of the candidate gathering process.
type GatheringState int
const (
- // GatheringStateUnknown represents an unknown state
+ // GatheringStateUnknown represents an unknown state.
GatheringStateUnknown GatheringState = iota
- // GatheringStateNew indicates candidate gathering is not yet started
+ // GatheringStateNew indicates candidate gathering is not yet started.
GatheringStateNew
- // GatheringStateGathering indicates candidate gathering is ongoing
+ // GatheringStateGathering indicates candidate gathering is ongoing.
GatheringStateGathering
- // GatheringStateComplete indicates candidate gathering has been completed
+ // GatheringStateComplete indicates candidate gathering has been completed.
GatheringStateComplete
)
@@ -83,3 +83,8 @@ func (t GatheringState) String() string {
return ErrUnknownType.Error()
}
}
+
+const (
+ relayProtocolDTLS = "dtls"
+ relayProtocolTLS = "tls"
+)
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/icecontrol.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/icecontrol.go
similarity index 98%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/icecontrol.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/icecontrol.go
index b086bd892..fcf08a0ed 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/icecontrol.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/icecontrol.go
@@ -6,7 +6,7 @@ package ice
import (
"encoding/binary"
- "github.com/pion/stun"
+ "github.com/pion/stun/v3"
)
// tiebreaker is common helper for ICE-{CONTROLLED,CONTROLLING}
@@ -20,6 +20,7 @@ func (a tiebreaker) AddToAs(m *stun.Message, t stun.AttrType) error {
v := make([]byte, tiebreakerSize)
binary.BigEndian.PutUint64(v, uint64(a))
m.Add(t, v)
+
return nil
}
@@ -33,6 +34,7 @@ func (a *tiebreaker) GetFromAs(m *stun.Message, t stun.AttrType) error {
return err
}
*a = tiebreaker(binary.BigEndian.Uint64(v))
+
return nil
}
@@ -73,6 +75,7 @@ func (c AttrControl) AddTo(m *stun.Message) error {
if c.Role == Controlling {
return tiebreaker(c.Tiebreaker).AddToAs(m, stun.AttrICEControlling)
}
+
return tiebreaker(c.Tiebreaker).AddToAs(m, stun.AttrICEControlled)
}
@@ -80,11 +83,14 @@ func (c AttrControl) AddTo(m *stun.Message) error {
func (c *AttrControl) GetFrom(m *stun.Message) error {
if m.Contains(stun.AttrICEControlling) {
c.Role = Controlling
+
return (*tiebreaker)(&c.Tiebreaker).GetFromAs(m, stun.AttrICEControlling)
}
if m.Contains(stun.AttrICEControlled) {
c.Role = Controlled
+
return (*tiebreaker)(&c.Tiebreaker).GetFromAs(m, stun.AttrICEControlled)
}
+
return stun.ErrAttributeNotFound
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/internal/atomic/atomic.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/internal/atomic/atomic.go
similarity index 73%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/internal/atomic/atomic.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/internal/atomic/atomic.go
index f8caf5a2e..f170133b0 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/internal/atomic/atomic.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/internal/atomic/atomic.go
@@ -6,18 +6,19 @@ package atomic
import "sync/atomic"
-// Error is an atomic error
+// Error is an atomic error.
type Error struct {
v atomic.Value
}
-// Store updates the value of the atomic variable
+// Store updates the value of the atomic variable.
func (a *Error) Store(err error) {
a.v.Store(struct{ error }{err})
}
-// Load retrieves the current value of the atomic variable
+// Load retrieves the current value of the atomic variable.
func (a *Error) Load() error {
err, _ := a.v.Load().(struct{ error })
+
return err.error
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/internal/fakenet/mock_conn.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/internal/fakenet/mock_conn.go
similarity index 97%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/internal/fakenet/mock_conn.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/internal/fakenet/mock_conn.go
index cc98849d5..baf012ab5 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/internal/fakenet/mock_conn.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/internal/fakenet/mock_conn.go
@@ -11,7 +11,7 @@ import (
"time"
)
-// MockPacketConn for tests
+// MockPacketConn for tests.
type MockPacketConn struct{}
func (m *MockPacketConn) ReadFrom([]byte) (n int, addr net.Addr, err error) { return 0, nil, nil } //nolint:revive
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/internal/fakenet/packet_conn.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/internal/fakenet/packet_conn.go
similarity index 80%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/internal/fakenet/packet_conn.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/internal/fakenet/packet_conn.go
index 0b9faaa84..f9cb66fdd 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/internal/fakenet/packet_conn.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/internal/fakenet/packet_conn.go
@@ -8,18 +8,19 @@ import (
"net"
)
-// Compile-time assertion
+// Compile-time assertion.
var _ net.PacketConn = (*PacketConn)(nil)
-// PacketConn wraps a net.Conn and emulates net.PacketConn
+// PacketConn wraps a net.Conn and emulates net.PacketConn.
type PacketConn struct {
net.Conn
}
-// ReadFrom reads a packet from the connection,
+// ReadFrom reads a packet from the connection.
func (f *PacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
n, err = f.Conn.Read(p)
addr = f.Conn.RemoteAddr()
+
return
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/internal/stun/stun.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/internal/stun/stun.go
similarity index 96%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/internal/stun/stun.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/internal/stun/stun.go
index 230cf850a..55ccb9b4a 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/internal/stun/stun.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/internal/stun/stun.go
@@ -10,7 +10,7 @@ import (
"net"
"time"
- "github.com/pion/stun"
+ "github.com/pion/stun/v3"
)
var (
@@ -59,7 +59,7 @@ func GetXORMappedAddr(conn net.PacketConn, serverAddr net.Addr, timeout time.Dur
return &addr, nil
}
-// AssertUsername checks that the given STUN message m has a USERNAME attribute with a given value
+// AssertUsername checks that the given STUN message m has a USERNAME attribute with a given value.
func AssertUsername(m *stun.Message, expectedUsername string) error {
var username stun.Username
if err := username.GetFrom(m); err != nil {
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/internal/taskloop/taskloop.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/internal/taskloop/taskloop.go
new file mode 100644
index 000000000..15a266654
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/internal/taskloop/taskloop.go
@@ -0,0 +1,121 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package taskloop implements a task loop to run
+// tasks sequentially in a separate Goroutine.
+package taskloop
+
+import (
+ "context"
+ "errors"
+ "time"
+
+ atomicx "github.com/pion/ice/v4/internal/atomic"
+)
+
+// ErrClosed indicates that the loop has been stopped.
+var ErrClosed = errors.New("the agent is closed")
+
+type task struct {
+ fn func(context.Context)
+ done chan struct{}
+}
+
+// Loop runs submitted task serially in a dedicated Goroutine.
+type Loop struct {
+ tasks chan task
+
+ // State for closing
+ done chan struct{}
+ taskLoopDone chan struct{}
+ err atomicx.Error
+}
+
+// New creates and starts a new task loop.
+func New(onClose func()) *Loop {
+ l := &Loop{
+ tasks: make(chan task),
+ done: make(chan struct{}),
+ taskLoopDone: make(chan struct{}),
+ }
+
+ go l.runLoop(onClose)
+
+ return l
+}
+
+// runLoop handles registered tasks and agent close.
+func (l *Loop) runLoop(onClose func()) {
+ defer func() {
+ onClose()
+ close(l.taskLoopDone)
+ }()
+
+ for {
+ select {
+ case <-l.done:
+ return
+ case t := <-l.tasks:
+ t.fn(l)
+ close(t.done)
+ }
+ }
+}
+
+// Close stops the loop after finishing the execution of the current task.
+// Other pending tasks will not be executed.
+func (l *Loop) Close() {
+ if err := l.Err(); err != nil {
+ return
+ }
+
+ l.err.Store(ErrClosed)
+
+ close(l.done)
+ <-l.taskLoopDone
+}
+
+// Run serially executes the submitted callback.
+// Blocking tasks must be cancelable by context.
+func (l *Loop) Run(ctx context.Context, t func(context.Context)) error {
+ if err := l.Err(); err != nil {
+ return err
+ }
+ done := make(chan struct{})
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ case l.tasks <- task{t, done}:
+ <-done
+
+ return nil
+ }
+}
+
+// The following methods implement context.Context for TaskLoop
+
+// Done returns a channel that's closed when the task loop has been stopped.
+func (l *Loop) Done() <-chan struct{} {
+ return l.done
+}
+
+// Err returns nil if the task loop is still running.
+// Otherwise it return errClosed if the loop has been closed/stopped.
+func (l *Loop) Err() error {
+ select {
+ case <-l.done:
+ return ErrClosed
+ default:
+ return nil
+ }
+}
+
+// Deadline returns the no valid time as task loops have no deadline.
+func (l *Loop) Deadline() (deadline time.Time, ok bool) {
+ return time.Time{}, false
+}
+
+// Value is not supported for task loops.
+func (l *Loop) Value(interface{}) interface{} {
+ return nil
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/mdns.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/mdns.go
new file mode 100644
index 000000000..b88909d29
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/mdns.go
@@ -0,0 +1,144 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ice
+
+import (
+ "net"
+
+ "github.com/google/uuid"
+ "github.com/pion/logging"
+ "github.com/pion/mdns/v2"
+ "github.com/pion/transport/v3"
+ "golang.org/x/net/ipv4"
+ "golang.org/x/net/ipv6"
+)
+
+// MulticastDNSMode represents the different Multicast modes ICE can run in.
+type MulticastDNSMode byte
+
+// MulticastDNSMode enum.
+const (
+ // MulticastDNSModeDisabled means remote mDNS candidates will be discarded, and local host candidates will use IPs.
+ MulticastDNSModeDisabled MulticastDNSMode = iota + 1
+
+ // MulticastDNSModeQueryOnly means remote mDNS candidates will be accepted, and local host candidates will use IPs.
+ MulticastDNSModeQueryOnly
+
+ // MulticastDNSModeQueryAndGather means remote mDNS candidates will be accepted,
+ // and local host candidates will use mDNS.
+ MulticastDNSModeQueryAndGather
+)
+
+func generateMulticastDNSName() (string, error) {
+ // https://tools.ietf.org/id/draft-ietf-rtcweb-mdns-ice-candidates-02.html#gathering
+ // The unique name MUST consist of a version 4 UUID as defined in [RFC4122], followed by “.local”.
+ u, err := uuid.NewRandom()
+
+ return u.String() + ".local", err
+}
+
+//nolint:cyclop
+func createMulticastDNS(
+ netTransport transport.Net,
+ networkTypes []NetworkType,
+ interfaces []*transport.Interface,
+ includeLoopback bool,
+ mDNSMode MulticastDNSMode,
+ mDNSName string,
+ log logging.LeveledLogger,
+ loggerFactory logging.LoggerFactory,
+) (*mdns.Conn, MulticastDNSMode, error) {
+ if mDNSMode == MulticastDNSModeDisabled {
+ return nil, mDNSMode, nil
+ }
+
+ var useV4, useV6 bool
+ if len(networkTypes) == 0 {
+ useV4 = true
+ useV6 = true
+ } else {
+ for _, nt := range networkTypes {
+ if nt.IsIPv4() {
+ useV4 = true
+
+ continue
+ }
+ if nt.IsIPv6() {
+ useV6 = true
+ }
+ }
+ }
+
+ addr4, mdnsErr := netTransport.ResolveUDPAddr("udp4", mdns.DefaultAddressIPv4)
+ if mdnsErr != nil {
+ return nil, mDNSMode, mdnsErr
+ }
+ addr6, mdnsErr := netTransport.ResolveUDPAddr("udp6", mdns.DefaultAddressIPv6)
+ if mdnsErr != nil {
+ return nil, mDNSMode, mdnsErr
+ }
+
+ var pktConnV4 *ipv4.PacketConn
+ var mdns4Err error
+ if useV4 {
+ var l transport.UDPConn
+ l, mdns4Err = netTransport.ListenUDP("udp4", addr4)
+ if mdns4Err != nil {
+ // If ICE fails to start MulticastDNS server just warn the user and continue
+ log.Errorf("Failed to enable mDNS over IPv4: (%s)", mdns4Err)
+
+ return nil, MulticastDNSModeDisabled, nil
+ }
+ pktConnV4 = ipv4.NewPacketConn(l)
+ }
+
+ var pktConnV6 *ipv6.PacketConn
+ var mdns6Err error
+ if useV6 {
+ var l transport.UDPConn
+ l, mdns6Err = netTransport.ListenUDP("udp6", addr6)
+ if mdns6Err != nil {
+ log.Errorf("Failed to enable mDNS over IPv6: (%s)", mdns6Err)
+
+ return nil, MulticastDNSModeDisabled, nil
+ }
+ pktConnV6 = ipv6.NewPacketConn(l)
+ }
+
+ if mdns4Err != nil && mdns6Err != nil {
+ // If ICE fails to start MulticastDNS server just warn the user and continue
+ log.Errorf("Failed to enable mDNS, continuing in mDNS disabled mode")
+ //nolint:nilerr
+ return nil, MulticastDNSModeDisabled, nil
+ }
+ var ifcs []net.Interface
+ if interfaces != nil {
+ ifcs = make([]net.Interface, 0, len(ifcs))
+ for _, ifc := range interfaces {
+ ifcs = append(ifcs, ifc.Interface)
+ }
+ }
+
+ switch mDNSMode {
+ case MulticastDNSModeQueryOnly:
+ conn, err := mdns.Server(pktConnV4, pktConnV6, &mdns.Config{
+ Interfaces: ifcs,
+ IncludeLoopback: includeLoopback,
+ LoggerFactory: loggerFactory,
+ })
+
+ return conn, mDNSMode, err
+ case MulticastDNSModeQueryAndGather:
+ conn, err := mdns.Server(pktConnV4, pktConnV6, &mdns.Config{
+ Interfaces: ifcs,
+ IncludeLoopback: includeLoopback,
+ LocalNames: []string{mDNSName},
+ LoggerFactory: loggerFactory,
+ })
+
+ return conn, mDNSMode, err
+ default:
+ return nil, mDNSMode, nil
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/net.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/net.go
new file mode 100644
index 000000000..54ec4ca26
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/net.go
@@ -0,0 +1,171 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ice
+
+import (
+ "net"
+ "net/netip"
+
+ "github.com/pion/logging"
+ "github.com/pion/transport/v3"
+)
+
+// The conditions of invalidation written below are defined in
+// https://tools.ietf.org/html/rfc8445#section-5.1.1.1
+// It is partial because the link-local check is done later in various gather local
+// candidate methods which conditionally accept IPv6 based on usage of mDNS or not.
+func isSupportedIPv6Partial(ip net.IP) bool {
+ if len(ip) != net.IPv6len ||
+ // Deprecated IPv4-compatible IPv6 addresses [RFC4291] and IPv6 site-
+ // local unicast addresses [RFC3879] MUST NOT be included in the
+ // address candidates.
+ isZeros(ip[0:12]) || // !(IPv4-compatible IPv6)
+ ip[0] == 0xfe && ip[1]&0xc0 == 0xc0 { // !(IPv6 site-local unicast)
+ return false
+ }
+
+ return true
+}
+
+func isZeros(ip net.IP) bool {
+ for i := 0; i < len(ip); i++ {
+ if ip[i] != 0 {
+ return false
+ }
+ }
+
+ return true
+}
+
+//nolint:gocognit,cyclop
+func localInterfaces(
+ n transport.Net,
+ interfaceFilter func(string) (keep bool),
+ ipFilter func(net.IP) (keep bool),
+ networkTypes []NetworkType,
+ includeLoopback bool,
+) ([]*transport.Interface, []netip.Addr, error) {
+ ipAddrs := []netip.Addr{}
+ ifaces, err := n.Interfaces()
+ if err != nil {
+ return nil, ipAddrs, err
+ }
+
+ filteredIfaces := make([]*transport.Interface, 0, len(ifaces))
+
+ var ipV4Requested, ipv6Requested bool
+ if len(networkTypes) == 0 {
+ ipV4Requested = true
+ ipv6Requested = true
+ } else {
+ for _, typ := range networkTypes {
+ if typ.IsIPv4() {
+ ipV4Requested = true
+ }
+
+ if typ.IsIPv6() {
+ ipv6Requested = true
+ }
+ }
+ }
+
+ for _, iface := range ifaces {
+ if iface.Flags&net.FlagUp == 0 {
+ continue // Interface down
+ }
+ if (iface.Flags&net.FlagLoopback != 0) && !includeLoopback {
+ continue // Loopback interface
+ }
+
+ if interfaceFilter != nil && !interfaceFilter(iface.Name) {
+ continue
+ }
+
+ ifaceAddrs, err := iface.Addrs()
+ if err != nil {
+ continue
+ }
+
+ atLeastOneAddr := false
+ for _, addr := range ifaceAddrs {
+ ipAddr, _, _, err := parseAddrFromIface(addr, iface.Name)
+ if err != nil || (ipAddr.IsLoopback() && !includeLoopback) {
+ continue
+ }
+ if ipAddr.Is6() {
+ if !ipv6Requested {
+ continue
+ } else if !isSupportedIPv6Partial(ipAddr.AsSlice()) {
+ continue
+ }
+ } else if !ipV4Requested {
+ continue
+ }
+
+ if ipFilter != nil && !ipFilter(ipAddr.AsSlice()) {
+ continue
+ }
+
+ atLeastOneAddr = true
+ ipAddrs = append(ipAddrs, ipAddr)
+ }
+
+ if atLeastOneAddr {
+ ifaceCopy := iface
+ filteredIfaces = append(filteredIfaces, ifaceCopy)
+ }
+ }
+
+ return filteredIfaces, ipAddrs, nil
+}
+
+//nolint:cyclop
+func listenUDPInPortRange(
+ netTransport transport.Net,
+ log logging.LeveledLogger,
+ portMax, portMin int,
+ network string,
+ lAddr *net.UDPAddr,
+) (transport.UDPConn, error) {
+ if (lAddr.Port != 0) || ((portMin == 0) && (portMax == 0)) {
+ return netTransport.ListenUDP(network, lAddr)
+ }
+
+ if portMin == 0 {
+ portMin = 1024 // Start at 1024 which is non-privileged
+ }
+
+ if portMax == 0 {
+ portMax = 0xFFFF
+ }
+
+ if portMin > portMax {
+ return nil, ErrPort
+ }
+
+ portStart := globalMathRandomGenerator.Intn(portMax-portMin+1) + portMin
+ portCurrent := portStart
+ for {
+ addr := &net.UDPAddr{
+ IP: lAddr.IP,
+ Zone: lAddr.Zone,
+ Port: portCurrent,
+ }
+
+ c, e := netTransport.ListenUDP(network, addr)
+ if e == nil {
+ return c, e //nolint:nilerr
+ }
+ log.Debugf("Failed to listen %s: %v", lAddr.String(), e)
+ portCurrent++
+ if portCurrent > portMax {
+ portCurrent = portMin
+ }
+ if portCurrent == portStart {
+ break
+ }
+ }
+
+ return nil, ErrPort
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/networktype.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/networktype.go
similarity index 87%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/networktype.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/networktype.go
index 57df18631..af055c593 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/networktype.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/networktype.go
@@ -5,7 +5,7 @@ package ice
import (
"fmt"
- "net"
+ "net/netip"
"strings"
)
@@ -27,7 +27,7 @@ func supportedNetworkTypes() []NetworkType {
}
}
-// NetworkType represents the type of network
+// NetworkType represents the type of network.
type NetworkType int
const (
@@ -69,7 +69,7 @@ func (t NetworkType) IsTCP() bool {
return t == NetworkTypeTCP4 || t == NetworkTypeTCP6
}
-// NetworkShort returns the short network description
+// NetworkShort returns the short network description.
func (t NetworkType) NetworkShort() string {
switch t {
case NetworkTypeUDP4, NetworkTypeUDP6:
@@ -81,7 +81,7 @@ func (t NetworkType) NetworkShort() string {
}
}
-// IsReliable returns true if the network is reliable
+// IsReliable returns true if the network is reliable.
func (t NetworkType) IsReliable() bool {
switch t {
case NetworkTypeUDP4, NetworkTypeUDP6:
@@ -89,6 +89,7 @@ func (t NetworkType) IsReliable() bool {
case NetworkTypeTCP4, NetworkTypeTCP6:
return true
}
+
return false
}
@@ -100,6 +101,7 @@ func (t NetworkType) IsIPv4() bool {
case NetworkTypeUDP6, NetworkTypeTCP6:
return false
}
+
return false
}
@@ -111,25 +113,28 @@ func (t NetworkType) IsIPv6() bool {
case NetworkTypeUDP6, NetworkTypeTCP6:
return true
}
+
return false
}
// determineNetworkType determines the type of network based on
// the short network string and an IP address.
-func determineNetworkType(network string, ip net.IP) (NetworkType, error) {
- ipv4 := ip.To4() != nil
-
+func determineNetworkType(network string, ip netip.Addr) (NetworkType, error) {
+ // we'd rather have an IPv4-mapped IPv6 become IPv4 so that it is usable.
+ ip = ip.Unmap()
switch {
case strings.HasPrefix(strings.ToLower(network), udp):
- if ipv4 {
+ if ip.Is4() {
return NetworkTypeUDP4, nil
}
+
return NetworkTypeUDP6, nil
case strings.HasPrefix(strings.ToLower(network), tcp):
- if ipv4 {
+ if ip.Is4() {
return NetworkTypeTCP4, nil
}
+
return NetworkTypeTCP6, nil
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/priority.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/priority.go
similarity index 96%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/priority.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/priority.go
index 16ac5cc73..f8c8740fa 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/priority.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/priority.go
@@ -6,7 +6,7 @@ package ice
import (
"encoding/binary"
- "github.com/pion/stun"
+ "github.com/pion/stun/v3"
)
// PriorityAttr represents PRIORITY attribute.
@@ -19,6 +19,7 @@ func (p PriorityAttr) AddTo(m *stun.Message) error {
v := make([]byte, prioritySize)
binary.BigEndian.PutUint32(v, uint32(p))
m.Add(stun.AttrPriority, v)
+
return nil
}
@@ -32,5 +33,6 @@ func (p *PriorityAttr) GetFrom(m *stun.Message) error {
return err
}
*p = PriorityAttr(binary.BigEndian.Uint32(v))
+
return nil
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/rand.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/rand.go
similarity index 100%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/rand.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/rand.go
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/renovate.json b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/renovate.json
similarity index 100%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/renovate.json
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/renovate.json
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/role.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/role.go
similarity index 99%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/role.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/role.go
index e9a7bda94..49f68963b 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/role.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/role.go
@@ -26,6 +26,7 @@ func (r *Role) UnmarshalText(text []byte) error {
default:
return fmt.Errorf("%w %q", errUnknownRole, text)
}
+
return nil
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/selection.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/selection.go
similarity index 66%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/selection.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/selection.go
index 9f312637f..b67898dd7 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/selection.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/selection.go
@@ -8,7 +8,7 @@ import (
"time"
"github.com/pion/logging"
- "github.com/pion/stun"
+ "github.com/pion/stun/v3"
)
type pairCandidateSelector interface {
@@ -44,6 +44,7 @@ func (s *controllingSelector) isNominatable(c Candidate) bool {
}
s.log.Errorf("Invalid candidate type: %s", c.Type())
+
return false
}
@@ -51,7 +52,7 @@ func (s *controllingSelector) ContactCandidates() {
switch {
case s.agent.getSelectedPair() != nil:
if s.agent.validateSelectedPair() {
- s.log.Trace("checking keepalive")
+ s.log.Trace("Checking keepalive")
s.agent.checkKeepalive()
}
case s.nominatedPair != nil:
@@ -59,10 +60,11 @@ func (s *controllingSelector) ContactCandidates() {
default:
p := s.agent.getBestValidCandidatePair()
if p != nil && s.isNominatable(p.Local) && s.isNominatable(p.Remote) {
- s.log.Tracef("Nominatable pair found, nominating (%s, %s)", p.Local.String(), p.Remote.String())
+ s.log.Tracef("Nominatable pair found, nominating (%s, %s)", p.Local, p.Remote)
p.nominated = true
s.nominatedPair = p
s.nominatePair(p)
+
return
}
s.agent.pingAllCandidates()
@@ -84,40 +86,54 @@ func (s *controllingSelector) nominatePair(pair *CandidatePair) {
)
if err != nil {
s.log.Error(err.Error())
+
return
}
- s.log.Tracef("ping STUN (nominate candidate pair) from %s to %s", pair.Local.String(), pair.Remote.String())
+ s.log.Tracef("Ping STUN (nominate candidate pair) from %s to %s", pair.Local, pair.Remote)
s.agent.sendBindingRequest(msg, pair.Local, pair.Remote)
}
-func (s *controllingSelector) HandleBindingRequest(m *stun.Message, local, remote Candidate) {
- s.agent.sendBindingSuccess(m, local, remote)
+func (s *controllingSelector) HandleBindingRequest(message *stun.Message, local, remote Candidate) { //nolint:cyclop
+ s.agent.sendBindingSuccess(message, local, remote)
- p := s.agent.findPair(local, remote)
+ pair := s.agent.findPair(local, remote)
+
+ if pair == nil {
+ pair = s.agent.addPair(local, remote)
+ pair.UpdateRequestReceived()
- if p == nil {
- s.agent.addPair(local, remote)
return
}
+ pair.UpdateRequestReceived()
- if p.state == CandidatePairStateSucceeded && s.nominatedPair == nil && s.agent.getSelectedPair() == nil {
+ if pair.state == CandidatePairStateSucceeded && s.nominatedPair == nil && s.agent.getSelectedPair() == nil {
bestPair := s.agent.getBestAvailableCandidatePair()
if bestPair == nil {
s.log.Tracef("No best pair available")
- } else if bestPair.equal(p) && s.isNominatable(p.Local) && s.isNominatable(p.Remote) {
- s.log.Tracef("The candidate (%s, %s) is the best candidate available, marking it as nominated",
- p.Local.String(), p.Remote.String())
- s.nominatedPair = p
- s.nominatePair(p)
+ } else if bestPair.equal(pair) && s.isNominatable(pair.Local) && s.isNominatable(pair.Remote) {
+ s.log.Tracef(
+ "The candidate (%s, %s) is the best candidate available, marking it as nominated",
+ pair.Local,
+ pair.Remote,
+ )
+ s.nominatedPair = pair
+ s.nominatePair(pair)
+ }
+ }
+
+ if s.agent.userBindingRequestHandler != nil {
+ if shouldSwitch := s.agent.userBindingRequestHandler(message, local, remote, pair); shouldSwitch {
+ s.agent.setSelectedPair(pair)
}
}
}
func (s *controllingSelector) HandleSuccessResponse(m *stun.Message, local, remote Candidate, remoteAddr net.Addr) {
- ok, pendingRequest := s.agent.handleInboundBindingSuccess(m.TransactionID)
+ ok, pendingRequest, rtt := s.agent.handleInboundBindingSuccess(m.TransactionID)
if !ok {
s.log.Warnf("Discard message from (%s), unknown TransactionID 0x%x", remote, m.TransactionID)
+
return
}
@@ -126,24 +142,32 @@ func (s *controllingSelector) HandleSuccessResponse(m *stun.Message, local, remo
// Assert that NAT is not symmetric
// https://tools.ietf.org/html/rfc8445#section-7.2.5.2.1
if !addrEqual(transactionAddr, remoteAddr) {
- s.log.Debugf("Discard message: transaction source and destination does not match expected(%s), actual(%s)", transactionAddr, remote)
+ s.log.Debugf(
+ "Discard message: transaction source and destination does not match expected(%s), actual(%s)",
+ transactionAddr,
+ remote,
+ )
+
return
}
- s.log.Tracef("inbound STUN (SuccessResponse) from %s to %s", remote.String(), local.String())
- p := s.agent.findPair(local, remote)
+ s.log.Tracef("Inbound STUN (SuccessResponse) from %s to %s", remote, local)
+ pair := s.agent.findPair(local, remote)
- if p == nil {
+ if pair == nil {
// This shouldn't happen
s.log.Error("Success response from invalid candidate pair")
+
return
}
- p.state = CandidatePairStateSucceeded
- s.log.Tracef("Found valid candidate pair: %s", p)
+ pair.state = CandidatePairStateSucceeded
+ s.log.Tracef("Found valid candidate pair: %s", pair)
if pendingRequest.isUseCandidate && s.agent.getSelectedPair() == nil {
- s.agent.setSelectedPair(p)
+ s.agent.setSelectedPair(pair)
}
+
+ pair.UpdateRoundTripTime(rtt)
}
func (s *controllingSelector) PingCandidate(local, remote Candidate) {
@@ -156,6 +180,7 @@ func (s *controllingSelector) PingCandidate(local, remote Candidate) {
)
if err != nil {
s.log.Error(err.Error())
+
return
}
@@ -173,7 +198,7 @@ func (s *controlledSelector) Start() {
func (s *controlledSelector) ContactCandidates() {
if s.agent.getSelectedPair() != nil {
if s.agent.validateSelectedPair() {
- s.log.Trace("checking keepalive")
+ s.log.Trace("Checking keepalive")
s.agent.checkKeepalive()
}
} else {
@@ -191,6 +216,7 @@ func (s *controlledSelector) PingCandidate(local, remote Candidate) {
)
if err != nil {
s.log.Error(err.Error())
+
return
}
@@ -206,9 +232,10 @@ func (s *controlledSelector) HandleSuccessResponse(m *stun.Message, local, remot
// request with an appropriate error code response (e.g., 400)
// [RFC5389].
- ok, pendingRequest := s.agent.handleInboundBindingSuccess(m.TransactionID)
+ ok, pendingRequest, rtt := s.agent.handleInboundBindingSuccess(m.TransactionID)
if !ok {
s.log.Warnf("Discard message from (%s), unknown TransactionID 0x%x", remote, m.TransactionID)
+
return
}
@@ -217,52 +244,63 @@ func (s *controlledSelector) HandleSuccessResponse(m *stun.Message, local, remot
// Assert that NAT is not symmetric
// https://tools.ietf.org/html/rfc8445#section-7.2.5.2.1
if !addrEqual(transactionAddr, remoteAddr) {
- s.log.Debugf("Discard message: transaction source and destination does not match expected(%s), actual(%s)", transactionAddr, remote)
+ s.log.Debugf(
+ "Discard message: transaction source and destination does not match expected(%s), actual(%s)",
+ transactionAddr,
+ remote,
+ )
+
return
}
- s.log.Tracef("inbound STUN (SuccessResponse) from %s to %s", remote.String(), local.String())
+ s.log.Tracef("Inbound STUN (SuccessResponse) from %s to %s", remote, local)
- p := s.agent.findPair(local, remote)
- if p == nil {
+ pair := s.agent.findPair(local, remote)
+ if pair == nil {
// This shouldn't happen
s.log.Error("Success response from invalid candidate pair")
+
return
}
- p.state = CandidatePairStateSucceeded
- s.log.Tracef("Found valid candidate pair: %s", p)
- if p.nominateOnBindingSuccess {
+ pair.state = CandidatePairStateSucceeded
+ s.log.Tracef("Found valid candidate pair: %s", pair)
+ if pair.nominateOnBindingSuccess {
if selectedPair := s.agent.getSelectedPair(); selectedPair == nil ||
- (selectedPair != p && selectedPair.priority() <= p.priority()) {
- s.agent.setSelectedPair(p)
- } else if selectedPair != p {
- s.log.Tracef("ignore nominate new pair %s, already nominated pair %s", p, selectedPair)
+ (selectedPair != pair &&
+ (!s.agent.needsToCheckPriorityOnNominated() || selectedPair.priority() <= pair.priority())) {
+ s.agent.setSelectedPair(pair)
+ } else if selectedPair != pair {
+ s.log.Tracef("Ignore nominate new pair %s, already nominated pair %s", pair, selectedPair)
}
}
+
+ pair.UpdateRoundTripTime(rtt)
}
-func (s *controlledSelector) HandleBindingRequest(m *stun.Message, local, remote Candidate) {
- useCandidate := m.Contains(stun.AttrUseCandidate)
-
- p := s.agent.findPair(local, remote)
- if p == nil {
- p = s.agent.addPair(local, remote)
+func (s *controlledSelector) HandleBindingRequest(message *stun.Message, local, remote Candidate) { //nolint:cyclop
+ pair := s.agent.findPair(local, remote)
+ if pair == nil {
+ pair = s.agent.addPair(local, remote)
}
+ pair.UpdateRequestReceived()
- if useCandidate {
+ if message.Contains(stun.AttrUseCandidate) { //nolint:nestif
// https://tools.ietf.org/html/rfc8445#section-7.3.1.5
- if p.state == CandidatePairStateSucceeded {
+ if pair.state == CandidatePairStateSucceeded {
// If the state of this pair is Succeeded, it means that the check
// previously sent by this pair produced a successful response and
// generated a valid pair (Section 7.2.5.3.2). The agent sets the
// nominated flag value of the valid pair to true.
- if selectedPair := s.agent.getSelectedPair(); selectedPair == nil ||
- (selectedPair != p && selectedPair.priority() <= p.priority()) {
- s.agent.setSelectedPair(p)
- } else if selectedPair != p {
- s.log.Tracef("ignore nominate new pair %s, already nominated pair %s", p, selectedPair)
+ selectedPair := s.agent.getSelectedPair()
+ if selectedPair == nil ||
+ (selectedPair != pair &&
+ (!s.agent.needsToCheckPriorityOnNominated() ||
+ selectedPair.priority() <= pair.priority())) {
+ s.agent.setSelectedPair(pair)
+ } else if selectedPair != pair {
+ s.log.Tracef("Ignore nominate new pair %s, already nominated pair %s", pair, selectedPair)
}
} else {
// If the received Binding request triggered a new check to be
@@ -273,19 +311,25 @@ func (s *controlledSelector) HandleBindingRequest(m *stun.Message, local, remote
// MUST remove the candidate pair from the valid list, set the
// candidate pair state to Failed, and set the checklist state to
// Failed.
- p.nominateOnBindingSuccess = true
+ pair.nominateOnBindingSuccess = true
}
}
- s.agent.sendBindingSuccess(m, local, remote)
+ s.agent.sendBindingSuccess(message, local, remote)
s.PingCandidate(local, remote)
+
+ if s.agent.userBindingRequestHandler != nil {
+ if shouldSwitch := s.agent.userBindingRequestHandler(message, local, remote, pair); shouldSwitch {
+ s.agent.setSelectedPair(pair)
+ }
+ }
}
type liteSelector struct {
pairCandidateSelector
}
-// A lite selector should not contact candidates
+// A lite selector should not contact candidates.
func (s *liteSelector) ContactCandidates() {
if _, ok := s.pairCandidateSelector.(*controllingSelector); ok {
//nolint:godox
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/stats.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/stats.go
similarity index 93%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/stats.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/stats.go
index 9b83bea85..30a0c02b8 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/stats.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/stats.go
@@ -7,7 +7,7 @@ import (
"time"
)
-// CandidatePairStats contains ICE candidate pair statistics
+// CandidatePairStats contains ICE candidate pair statistics.
type CandidatePairStats struct {
// Timestamp is the timestamp associated with this object.
Timestamp time.Time
@@ -58,10 +58,22 @@ type CandidatePairStats struct {
// (LastRequestTimestamp - FirstRequestTimestamp) / RequestsSent.
LastRequestTimestamp time.Time
+ // FirstResponseTimestamp represents the timestamp at which the first STUN response
+ // was received on this particular candidate pair.
+ FirstResponseTimestamp time.Time
+
// LastResponseTimestamp represents the timestamp at which the last STUN response
// was received on this particular candidate pair.
LastResponseTimestamp time.Time
+ // FirstRequestReceivedTimestamp represents the timestamp at which the first
+ // connectivity check request was received.
+ FirstRequestReceivedTimestamp time.Time
+
+ // LastRequestReceivedTimestamp represents the timestamp at which the last
+ // connectivity check request was received.
+ LastRequestReceivedTimestamp time.Time
+
// TotalRoundTripTime represents the sum of all round trip time measurements
// in seconds since the beginning of the session, based on STUN connectivity
// check responses (ResponsesReceived), including those that reply to requests
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/tcp_mux.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/tcp_mux.go
similarity index 71%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/tcp_mux.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/tcp_mux.go
index fb4e5243e..829eac195 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/tcp_mux.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/tcp_mux.go
@@ -10,9 +10,10 @@ import (
"net"
"strings"
"sync"
+ "time"
"github.com/pion/logging"
- "github.com/pion/stun"
+ "github.com/pion/stun/v3"
)
// ErrGetTransportAddress can't convert net.Addr to underlying type (UDPAddr or TCPAddr).
@@ -52,6 +53,16 @@ type TCPMuxParams struct {
// if the write buffer is full, the subsequent write packet will be dropped until it has enough space.
// a default 4MB is recommended.
WriteBufferSize int
+
+ // A new established connection will be removed if the first STUN binding request is not received within this timeout,
+ // avoiding the client with bad network or attacker to create a lot of empty connections.
+ // Default 30s timeout will be used if not set.
+ FirstStunBindTimeout time.Duration
+
+ // TCPMux will create connection from STUN binding request with an unknown username, if
+ // the connection is not used in the timeout, it will be removed to avoid resource leak / attack.
+ // Default 30s timeout will be used if not set.
+ AliveDurationForConnFromStun time.Duration
}
// NewTCPMuxDefault creates a new instance of TCPMuxDefault.
@@ -60,20 +71,28 @@ func NewTCPMuxDefault(params TCPMuxParams) *TCPMuxDefault {
params.Logger = logging.NewDefaultLoggerFactory().NewLogger("ice")
}
- m := &TCPMuxDefault{
+ if params.FirstStunBindTimeout == 0 {
+ params.FirstStunBindTimeout = 30 * time.Second
+ }
+
+ if params.AliveDurationForConnFromStun == 0 {
+ params.AliveDurationForConnFromStun = 30 * time.Second
+ }
+
+ mux := &TCPMuxDefault{
params: ¶ms,
connsIPv4: map[string]map[ipAddr]*tcpPacketConn{},
connsIPv6: map[string]map[ipAddr]*tcpPacketConn{},
}
- m.wg.Add(1)
+ mux.wg.Add(1)
go func() {
- defer m.wg.Done()
- m.start()
+ defer mux.wg.Done()
+ mux.start()
}()
- return m
+ return mux
}
func (m *TCPMuxDefault) start() {
@@ -82,6 +101,7 @@ func (m *TCPMuxDefault) start() {
conn, err := m.params.Listener.Accept()
if err != nil {
m.params.Logger.Infof("Error accepting connection: %s", err)
+
return
}
@@ -110,25 +130,34 @@ func (m *TCPMuxDefault) GetConnByUfrag(ufrag string, isIPv6 bool, local net.IP)
}
if conn, ok := m.getConn(ufrag, isIPv6, local); ok {
+ conn.ClearAliveTimer()
+
return conn, nil
}
- return m.createConn(ufrag, isIPv6, local)
+ return m.createConn(ufrag, isIPv6, local, false)
}
-func (m *TCPMuxDefault) createConn(ufrag string, isIPv6 bool, local net.IP) (*tcpPacketConn, error) {
+func (m *TCPMuxDefault) createConn(ufrag string, isIPv6 bool, local net.IP, fromStun bool) (*tcpPacketConn, error) {
addr, ok := m.LocalAddr().(*net.TCPAddr)
if !ok {
return nil, ErrGetTransportAddress
}
localAddr := *addr
+ // Note: this is missing zone for IPv6
localAddr.IP = local
+ var alive time.Duration
+ if fromStun {
+ alive = m.params.AliveDurationForConnFromStun
+ }
+
conn := newTCPPacketConn(tcpPacketParams{
- ReadBuffer: m.params.ReadBufferSize,
- WriteBuffer: m.params.WriteBufferSize,
- LocalAddr: &localAddr,
- Logger: m.params.Logger,
+ ReadBuffer: m.params.ReadBufferSize,
+ WriteBuffer: m.params.WriteBufferSize,
+ LocalAddr: &localAddr,
+ Logger: m.params.Logger,
+ AliveDuration: alive,
})
var conns map[ipAddr]*tcpPacketConn
@@ -143,13 +172,15 @@ func (m *TCPMuxDefault) createConn(ufrag string, isIPv6 bool, local net.IP) (*tc
m.connsIPv4[ufrag] = conns
}
}
- conns[ipAddr(local.String())] = conn
+ // Note: this is missing zone for IPv6
+ connKey := ipAddr(local.String())
+ conns[connKey] = conn
m.wg.Add(1)
go func() {
defer m.wg.Done()
<-conn.CloseChannel()
- m.removeConnByUfragAndLocalHost(ufrag, local)
+ m.removeConnByUfragAndLocalHost(ufrag, connKey)
}()
return conn, nil
@@ -162,14 +193,33 @@ func (m *TCPMuxDefault) closeAndLogError(closer io.Closer) {
}
}
-func (m *TCPMuxDefault) handleConn(conn net.Conn) {
- buf := make([]byte, receiveMTU)
+func (m *TCPMuxDefault) handleConn(conn net.Conn) { //nolint:cyclop
+ buf := make([]byte, 512)
+ if m.params.FirstStunBindTimeout > 0 {
+ if err := conn.SetReadDeadline(time.Now().Add(m.params.FirstStunBindTimeout)); err != nil {
+ m.params.Logger.Warnf(
+ "Failed to set read deadline for first STUN message: %s to %s , err: %s",
+ conn.RemoteAddr(),
+ conn.LocalAddr(),
+ err,
+ )
+ }
+ }
n, err := readStreamingPacket(conn, buf)
if err != nil {
- m.params.Logger.Warnf("Error reading first packet from %s: %s", conn.RemoteAddr().String(), err)
+ if errors.Is(err, io.ErrShortBuffer) {
+ m.params.Logger.Warnf("Buffer too small for first packet from %s: %s", conn.RemoteAddr(), err)
+ } else {
+ m.params.Logger.Warnf("Error reading first packet from %s: %s", conn.RemoteAddr(), err)
+ }
+ m.closeAndLogError(conn)
+
return
}
+ if err = conn.SetReadDeadline(time.Time{}); err != nil {
+ m.params.Logger.Warnf("Failed to reset read deadline from %s: %s", conn.RemoteAddr(), err)
+ }
buf = buf[:n]
@@ -181,36 +231,45 @@ func (m *TCPMuxDefault) handleConn(conn net.Conn) {
if err = msg.Decode(); err != nil {
m.closeAndLogError(conn)
m.params.Logger.Warnf("Failed to handle decode ICE from %s to %s: %v", conn.RemoteAddr(), conn.LocalAddr(), err)
+
return
}
if m == nil || msg.Type.Method != stun.MethodBinding { // Not a STUN
m.closeAndLogError(conn)
m.params.Logger.Warnf("Not a STUN message from %s to %s", conn.RemoteAddr(), conn.LocalAddr())
+
return
}
for _, attr := range msg.Attributes {
- m.params.Logger.Debugf("msg attr: %s", attr.String())
+ m.params.Logger.Debugf("Message attribute: %s", attr.String())
}
attr, err := msg.Get(stun.AttrUsername)
if err != nil {
m.closeAndLogError(conn)
- m.params.Logger.Warnf("No Username attribute in STUN message from %s to %s", conn.RemoteAddr(), conn.LocalAddr())
+ m.params.Logger.Warnf(
+ "No Username attribute in STUN message from %s to %s",
+ conn.RemoteAddr(),
+ conn.LocalAddr(),
+ )
+
return
}
ufrag := strings.Split(string(attr), ":")[0]
m.params.Logger.Debugf("Ufrag: %s", ufrag)
- m.mu.Lock()
- defer m.mu.Unlock()
-
host, _, err := net.SplitHostPort(conn.RemoteAddr().String())
if err != nil {
m.closeAndLogError(conn)
- m.params.Logger.Warnf("Failed to get host in STUN message from %s to %s", conn.RemoteAddr(), conn.LocalAddr())
+ m.params.Logger.Warnf(
+ "Failed to get host in STUN message from %s to %s",
+ conn.RemoteAddr(),
+ conn.LocalAddr(),
+ )
+
return
}
@@ -219,22 +278,42 @@ func (m *TCPMuxDefault) handleConn(conn net.Conn) {
localAddr, ok := conn.LocalAddr().(*net.TCPAddr)
if !ok {
m.closeAndLogError(conn)
- m.params.Logger.Warnf("Failed to get local tcp address in STUN message from %s to %s", conn.RemoteAddr(), conn.LocalAddr())
+ m.params.Logger.Warnf(
+ "Failed to get local tcp address in STUN message from %s to %s",
+ conn.RemoteAddr(),
+ conn.LocalAddr(),
+ )
+
return
}
+ m.mu.Lock()
+
packetConn, ok := m.getConn(ufrag, isIPv6, localAddr.IP)
if !ok {
- packetConn, err = m.createConn(ufrag, isIPv6, localAddr.IP)
+ packetConn, err = m.createConn(ufrag, isIPv6, localAddr.IP, true)
if err != nil {
+ m.mu.Unlock()
m.closeAndLogError(conn)
- m.params.Logger.Warnf("Failed to create packetConn for STUN message from %s to %s", conn.RemoteAddr(), conn.LocalAddr())
+ m.params.Logger.Warnf(
+ "Failed to create packetConn for STUN message from %s to %s",
+ conn.RemoteAddr(),
+ conn.LocalAddr(),
+ )
+
return
}
}
+ m.mu.Unlock()
if err := packetConn.AddConn(conn, buf); err != nil {
m.closeAndLogError(conn)
- m.params.Logger.Warnf("Error adding conn to tcpPacketConn from %s to %s: %s", conn.RemoteAddr(), conn.LocalAddr(), err)
+ m.params.Logger.Warnf(
+ "Error adding conn to tcpPacketConn from %s to %s: %s",
+ conn.RemoteAddr(),
+ conn.LocalAddr(),
+ err,
+ )
+
return
}
}
@@ -295,15 +374,14 @@ func (m *TCPMuxDefault) RemoveConnByUfrag(ufrag string) {
}
}
-func (m *TCPMuxDefault) removeConnByUfragAndLocalHost(ufrag string, local net.IP) {
+func (m *TCPMuxDefault) removeConnByUfragAndLocalHost(ufrag string, localIPAddr ipAddr) {
removedConns := make([]*tcpPacketConn, 0, 4)
- localIP := ipAddr(local.String())
// Keep lock section small to avoid deadlock with conn lock
m.mu.Lock()
if conns, ok := m.connsIPv4[ufrag]; ok {
- if conn, ok := conns[localIP]; ok {
- delete(conns, localIP)
+ if conn, ok := conns[localIPAddr]; ok {
+ delete(conns, localIPAddr)
if len(conns) == 0 {
delete(m.connsIPv4, ufrag)
}
@@ -311,8 +389,8 @@ func (m *TCPMuxDefault) removeConnByUfragAndLocalHost(ufrag string, local net.IP
}
}
if conns, ok := m.connsIPv6[ufrag]; ok {
- if conn, ok := conns[localIP]; ok {
- delete(conns, localIP)
+ if conn, ok := conns[localIPAddr]; ok {
+ delete(conns, localIPAddr)
if len(conns) == 0 {
delete(m.connsIPv6, ufrag)
}
@@ -336,7 +414,9 @@ func (m *TCPMuxDefault) getConn(ufrag string, isIPv6 bool, local net.IP) (val *t
conns, ok = m.connsIPv4[ufrag]
}
if conns != nil {
- val, ok = conns[ipAddr(local.String())]
+ // Note: this is missing zone for IPv6
+ connKey := ipAddr(local.String())
+ val, ok = conns[connKey]
}
return
@@ -384,7 +464,7 @@ func readStreamingPacket(conn net.Conn, buf []byte) (int, error) {
func writeStreamingPacket(conn net.Conn, buf []byte) (int, error) {
bufCopy := make([]byte, streamingPacketHeaderLen+len(buf))
- binary.BigEndian.PutUint16(bufCopy, uint16(len(buf)))
+ binary.BigEndian.PutUint16(bufCopy, uint16(len(buf))) //nolint:gosec // G115
copy(bufCopy[2:], buf)
n, err := conn.Write(bufCopy)
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/tcp_mux_multi.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/tcp_mux_multi.go
similarity index 94%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/tcp_mux_multi.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/tcp_mux_multi.go
index e32acbf3e..225cefe2b 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/tcp_mux_multi.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/tcp_mux_multi.go
@@ -3,7 +3,9 @@
package ice
-import "net"
+import (
+ "net"
+)
// AllConnsGetter allows multiple fixed TCP ports to be used,
// each of which is multiplexed like TCPMux. AllConnsGetter also acts as
@@ -38,6 +40,7 @@ func (m *MultiTCPMuxDefault) GetConnByUfrag(ufrag string, isIPv6 bool, local net
if len(m.muxes) == 0 {
return nil, errNoTCPMuxAvailable
}
+
return m.muxes[0].GetConnByUfrag(ufrag, isIPv6, local)
}
@@ -49,7 +52,7 @@ func (m *MultiTCPMuxDefault) RemoveConnByUfrag(ufrag string) {
}
}
-// GetAllConns returns a PacketConn for each underlying TCPMux
+// GetAllConns returns a PacketConn for each underlying TCPMux.
func (m *MultiTCPMuxDefault) GetAllConns(ufrag string, isIPv6 bool, local net.IP) ([]net.PacketConn, error) {
if len(m.muxes) == 0 {
// Make sure that we either return at least one connection or an error.
@@ -66,10 +69,11 @@ func (m *MultiTCPMuxDefault) GetAllConns(ufrag string, isIPv6 bool, local net.IP
conns = append(conns, conn)
}
}
+
return conns, nil
}
-// Close the multi mux, no further connections could be created
+// Close the multi mux, no further connections could be created.
func (m *MultiTCPMuxDefault) Close() error {
var err error
for _, mux := range m.muxes {
@@ -77,5 +81,6 @@ func (m *MultiTCPMuxDefault) Close() error {
err = e
}
}
+
return err
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/tcp_packet_conn.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/tcp_packet_conn.go
similarity index 79%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/tcp_packet_conn.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/tcp_packet_conn.go
index 8c51fd1cc..1d1dffcb8 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/tcp_packet_conn.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/tcp_packet_conn.go
@@ -13,7 +13,7 @@ import (
"time"
"github.com/pion/logging"
- "github.com/pion/transport/v2/packetio"
+ "github.com/pion/transport/v3/packetio"
)
type bufferedConn struct {
@@ -36,6 +36,7 @@ func newBufferedConn(conn net.Conn, bufSize int, logger logging.LeveledLogger) n
}
go bc.writeProcess()
+
return bc
}
@@ -44,6 +45,7 @@ func (bc *bufferedConn) Write(b []byte) (int, error) {
if err != nil {
return n, err
}
+
return n, nil
}
@@ -56,12 +58,14 @@ func (bc *bufferedConn) writeProcess() {
}
if err != nil {
- bc.logger.Warnf("read buffer error: %s", err)
+ bc.logger.Warnf("Failed to read from buffer: %s", err)
+
continue
}
if _, err := bc.Conn.Write(pktBuf[:n]); err != nil {
- bc.logger.Warnf("write error: %s", err)
+ bc.logger.Warnf("Failed to write: %s", err)
+
continue
}
}
@@ -70,6 +74,7 @@ func (bc *bufferedConn) writeProcess() {
func (bc *bufferedConn) Close() error {
atomic.StoreInt32(&bc.closed, 1)
_ = bc.buf.Close()
+
return bc.Conn.Close()
}
@@ -85,6 +90,7 @@ type tcpPacketConn struct {
wg sync.WaitGroup
closedChan chan struct{}
closeOnce sync.Once
+ aliveTimer *time.Timer
}
type streamingPacket struct {
@@ -94,14 +100,15 @@ type streamingPacket struct {
}
type tcpPacketParams struct {
- ReadBuffer int
- LocalAddr net.Addr
- Logger logging.LeveledLogger
- WriteBuffer int
+ ReadBuffer int
+ LocalAddr net.Addr
+ Logger logging.LeveledLogger
+ WriteBuffer int
+ AliveDuration time.Duration
}
func newTCPPacketConn(params tcpPacketParams) *tcpPacketConn {
- p := &tcpPacketConn{
+ packet := &tcpPacketConn{
params: ¶ms,
conns: map[string]net.Conn{},
@@ -110,11 +117,31 @@ func newTCPPacketConn(params tcpPacketParams) *tcpPacketConn {
closedChan: make(chan struct{}),
}
- return p
+ if params.AliveDuration > 0 {
+ packet.aliveTimer = time.AfterFunc(params.AliveDuration, func() {
+ packet.params.Logger.Warn("close tcp packet conn by alive timeout")
+ _ = packet.Close()
+ })
+ }
+
+ return packet
+}
+
+func (t *tcpPacketConn) ClearAliveTimer() {
+ t.mu.Lock()
+ if t.aliveTimer != nil {
+ t.aliveTimer.Stop()
+ }
+ t.mu.Unlock()
}
func (t *tcpPacketConn) AddConn(conn net.Conn, firstPacketData []byte) error {
- t.params.Logger.Infof("AddConn: %s remote %s to local %s", conn.RemoteAddr().Network(), conn.RemoteAddr(), conn.LocalAddr())
+ t.params.Logger.Infof(
+ "Added connection: %s remote %s to local %s",
+ conn.RemoteAddr().Network(),
+ conn.RemoteAddr(),
+ conn.LocalAddr(),
+ )
t.mu.Lock()
defer t.mu.Unlock()
@@ -160,9 +187,13 @@ func (t *tcpPacketConn) startReading(conn net.Conn) {
for {
n, err := readStreamingPacket(conn, buf)
if err != nil {
- t.params.Logger.Infof("%v: %s", errReadingStreamingPacket, err)
- t.handleRecv(streamingPacket{nil, conn.RemoteAddr(), err})
- t.removeConn(conn)
+ t.params.Logger.Warnf("Failed to read streaming packet: %s", err)
+ last := t.removeConn(conn)
+ // Only propagate connection closure errors if no other open connection exists.
+ if last || !(errors.Is(err, io.EOF) || errors.Is(err, net.ErrClosed)) {
+ t.handleRecv(streamingPacket{nil, conn.RemoteAddr(), err})
+ }
+
return
}
@@ -216,6 +247,7 @@ func (t *tcpPacketConn) ReadFrom(b []byte) (n int, rAddr net.Addr, err error) {
n = len(pkt.Data)
copy(b, pkt.Data[:n])
+
return n, pkt.RAddr, err
}
@@ -231,7 +263,8 @@ func (t *tcpPacketConn) WriteTo(buf []byte, rAddr net.Addr) (n int, err error) {
n, err = writeStreamingPacket(conn, buf)
if err != nil {
- t.params.Logger.Tracef("%w %s", errWriting, rAddr)
+ t.params.Logger.Tracef("%w %s", errWrite, rAddr)
+
return n, err
}
@@ -245,13 +278,15 @@ func (t *tcpPacketConn) closeAndLogError(closer io.Closer) {
}
}
-func (t *tcpPacketConn) removeConn(conn net.Conn) {
+func (t *tcpPacketConn) removeConn(conn net.Conn) bool {
t.mu.Lock()
defer t.mu.Unlock()
t.closeAndLogError(conn)
delete(t.conns, conn.RemoteAddr().String())
+
+ return len(t.conns) == 0
}
func (t *tcpPacketConn) Close() error {
@@ -261,6 +296,9 @@ func (t *tcpPacketConn) Close() error {
t.closeOnce.Do(func() {
close(t.closedChan)
shouldCloseRecvChan = true
+ if t.aliveTimer != nil {
+ t.aliveTimer.Stop()
+ }
})
for _, conn := range t.conns {
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/tcptype.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/tcptype.go
similarity index 100%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/tcptype.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/tcptype.go
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/transport.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/transport.go
similarity index 79%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/transport.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/transport.go
index cd37a9b26..a28605dfe 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/transport.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/transport.go
@@ -9,7 +9,7 @@ import (
"sync/atomic"
"time"
- "github.com/pion/stun"
+ "github.com/pion/stun/v3"
)
// Dial connects to the remote agent, acting as the controlling ice agent.
@@ -32,18 +32,18 @@ type Conn struct {
agent *Agent
}
-// BytesSent returns the number of bytes sent
+// BytesSent returns the number of bytes sent.
func (c *Conn) BytesSent() uint64 {
return atomic.LoadUint64(&c.bytesSent)
}
-// BytesReceived returns the number of bytes received
+// BytesReceived returns the number of bytes received.
func (c *Conn) BytesReceived() uint64 {
return atomic.LoadUint64(&c.bytesReceived)
}
func (a *Agent) connect(ctx context.Context, isControlling bool, remoteUfrag, remotePwd string) (*Conn, error) {
- err := a.ok()
+ err := a.loop.Err()
if err != nil {
return nil, err
}
@@ -54,8 +54,8 @@ func (a *Agent) connect(ctx context.Context, isControlling bool, remoteUfrag, re
// Block until pair selected
select {
- case <-a.done:
- return nil, a.getErr()
+ case <-a.loop.Done():
+ return nil, a.loop.Err()
case <-ctx.Done():
return nil, ErrCanceledByCaller
case <-a.onConnected:
@@ -68,31 +68,32 @@ func (a *Agent) connect(ctx context.Context, isControlling bool, remoteUfrag, re
// Read implements the Conn Read method.
func (c *Conn) Read(p []byte) (int, error) {
- err := c.agent.ok()
+ err := c.agent.loop.Err()
if err != nil {
return 0, err
}
n, err := c.agent.buf.Read(p)
- atomic.AddUint64(&c.bytesReceived, uint64(n))
+ atomic.AddUint64(&c.bytesReceived, uint64(n)) //nolint:gosec // G115
+
return n, err
}
// Write implements the Conn Write method.
-func (c *Conn) Write(p []byte) (int, error) {
- err := c.agent.ok()
+func (c *Conn) Write(packet []byte) (int, error) {
+ err := c.agent.loop.Err()
if err != nil {
return 0, err
}
- if stun.IsMessage(p) {
- return 0, errICEWriteSTUNMessage
+ if stun.IsMessage(packet) {
+ return 0, errWriteSTUNMessageToIceConn
}
pair := c.agent.getSelectedPair()
if pair == nil {
- if err = c.agent.run(c.agent.context(), func(ctx context.Context, a *Agent) {
- pair = a.getBestValidCandidatePair()
+ if err = c.agent.loop.Run(c.agent.loop, func(_ context.Context) {
+ pair = c.agent.getBestValidCandidatePair()
}); err != nil {
return 0, err
}
@@ -102,8 +103,9 @@ func (c *Conn) Write(p []byte) (int, error) {
}
}
- atomic.AddUint64(&c.bytesSent, uint64(len(p)))
- return pair.Write(p)
+ atomic.AddUint64(&c.bytesSent, uint64(len(packet)))
+
+ return pair.Write(packet)
}
// Close implements the Conn Close method. It is used to close
@@ -132,17 +134,17 @@ func (c *Conn) RemoteAddr() net.Addr {
return pair.Remote.addr()
}
-// SetDeadline is a stub
+// SetDeadline is a stub.
func (c *Conn) SetDeadline(time.Time) error {
return nil
}
-// SetReadDeadline is a stub
+// SetReadDeadline is a stub.
func (c *Conn) SetReadDeadline(time.Time) error {
return nil
}
-// SetWriteDeadline is a stub
+// SetWriteDeadline is a stub.
func (c *Conn) SetWriteDeadline(time.Time) error {
return nil
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/udp_mux.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/udp_mux.go
similarity index 70%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/udp_mux.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/udp_mux.go
index 405bb7b1a..3732b2685 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/udp_mux.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/udp_mux.go
@@ -7,17 +7,18 @@ import (
"errors"
"io"
"net"
+ "net/netip"
"os"
"strings"
"sync"
"github.com/pion/logging"
- "github.com/pion/stun"
- "github.com/pion/transport/v2"
- "github.com/pion/transport/v2/stdnet"
+ "github.com/pion/stun/v3"
+ "github.com/pion/transport/v3"
+ "github.com/pion/transport/v3/stdnet"
)
-// UDPMux allows multiple connections to go over a single UDP port
+// UDPMux allows multiple connections to go over a single UDP port.
type UDPMux interface {
io.Closer
GetConn(ufrag string, addr net.Addr) (net.PacketConn, error)
@@ -25,7 +26,7 @@ type UDPMux interface {
GetListenAddresses() []net.Addr
}
-// UDPMuxDefault is an implementation of the interface
+// UDPMuxDefault is an implementation of the interface.
type UDPMuxDefault struct {
params UDPMuxParams
@@ -36,7 +37,7 @@ type UDPMuxDefault struct {
connsIPv4, connsIPv6 map[string]*udpMuxedConn
addressMapMu sync.RWMutex
- addressMap map[string]*udpMuxedConn
+ addressMap map[ipPort]*udpMuxedConn
// Buffer pool to recycle buffers for net.UDPAddr encodes/decodes
pool *sync.Pool
@@ -47,12 +48,11 @@ type UDPMuxDefault struct {
localAddrsForUnspecified []net.Addr
}
-const maxAddrSize = 512
-
// UDPMuxParams are parameters for UDPMux.
type UDPMuxParams struct {
- Logger logging.LeveledLogger
- UDPConn net.PacketConn
+ Logger logging.LeveledLogger
+ UDPConn net.PacketConn
+ UDPConnString string
// Required for gathering local addresses
// in case a un UDPConn is passed which does not
@@ -60,26 +60,26 @@ type UDPMuxParams struct {
Net transport.Net
}
-// NewUDPMuxDefault creates an implementation of UDPMux
-func NewUDPMuxDefault(params UDPMuxParams) *UDPMuxDefault {
+// NewUDPMuxDefault creates an implementation of UDPMux.
+func NewUDPMuxDefault(params UDPMuxParams) *UDPMuxDefault { //nolint:cyclop
if params.Logger == nil {
params.Logger = logging.NewDefaultLoggerFactory().NewLogger("ice")
}
var localAddrsForUnspecified []net.Addr
- if addr, ok := params.UDPConn.LocalAddr().(*net.UDPAddr); !ok {
+ if udpAddr, ok := params.UDPConn.LocalAddr().(*net.UDPAddr); !ok { //nolint:nestif
params.Logger.Errorf("LocalAddr is not a net.UDPAddr, got %T", params.UDPConn.LocalAddr())
- } else if ok && addr.IP.IsUnspecified() {
+ } else if ok && udpAddr.IP.IsUnspecified() {
// For unspecified addresses, the correct behavior is to return errListenUnspecified, but
// it will break the applications that are already using unspecified UDP connection
// with UDPMuxDefault, so print a warn log and create a local address list for mux.
params.Logger.Warn("UDPMuxDefault should not listening on unspecified address, use NewMultiUDPMuxFromPort instead")
var networks []NetworkType
switch {
- case addr.IP.To4() != nil:
+ case udpAddr.IP.To4() != nil:
networks = []NetworkType{NetworkTypeUDP4}
- case addr.IP.To16() != nil:
+ case udpAddr.IP.To16() != nil:
networks = []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6}
default:
@@ -89,23 +89,28 @@ func NewUDPMuxDefault(params UDPMuxParams) *UDPMuxDefault {
if params.Net == nil {
var err error
if params.Net, err = stdnet.NewNet(); err != nil {
- params.Logger.Errorf("failed to get create network: %v", err)
+ params.Logger.Errorf("Failed to get create network: %v", err)
}
}
- ips, err := localInterfaces(params.Net, nil, nil, networks, true)
+ _, addrs, err := localInterfaces(params.Net, nil, nil, networks, true)
if err == nil {
- for _, ip := range ips {
- localAddrsForUnspecified = append(localAddrsForUnspecified, &net.UDPAddr{IP: ip, Port: addr.Port})
+ for _, addr := range addrs {
+ localAddrsForUnspecified = append(localAddrsForUnspecified, &net.UDPAddr{
+ IP: addr.AsSlice(),
+ Port: udpAddr.Port,
+ Zone: addr.Zone(),
+ })
}
} else {
- params.Logger.Errorf("failed to get local interfaces for unspecified addr: %v", err)
+ params.Logger.Errorf("Failed to get local interfaces for unspecified addr: %v", err)
}
}
}
+ params.UDPConnString = params.UDPConn.LocalAddr().String()
- m := &UDPMuxDefault{
- addressMap: map[string]*udpMuxedConn{},
+ mux := &UDPMuxDefault{
+ addressMap: map[ipPort]*udpMuxedConn{},
params: params,
connsIPv4: make(map[string]*udpMuxedConn),
connsIPv6: make(map[string]*udpMuxedConn),
@@ -113,23 +118,23 @@ func NewUDPMuxDefault(params UDPMuxParams) *UDPMuxDefault {
pool: &sync.Pool{
New: func() interface{} {
// Big enough buffer to fit both packet and address
- return newBufferHolder(receiveMTU + maxAddrSize)
+ return newBufferHolder(receiveMTU)
},
},
localAddrsForUnspecified: localAddrsForUnspecified,
}
- go m.connWorker()
+ go mux.connWorker()
- return m
+ return mux
}
-// LocalAddr returns the listening address of this UDPMuxDefault
+// LocalAddr returns the listening address of this UDPMuxDefault.
func (m *UDPMuxDefault) LocalAddr() net.Addr {
return m.params.UDPConn.LocalAddr()
}
-// GetListenAddresses returns the list of addresses that this mux is listening on
+// GetListenAddresses returns the list of addresses that this mux is listening on.
func (m *UDPMuxDefault) GetListenAddresses() []net.Addr {
if len(m.localAddrsForUnspecified) > 0 {
return m.localAddrsForUnspecified
@@ -138,11 +143,11 @@ func (m *UDPMuxDefault) GetListenAddresses() []net.Addr {
return []net.Addr{m.LocalAddr()}
}
-// GetConn returns a PacketConn given the connection's ufrag and network address
-// creates the connection if an existing one can't be found
+// GetConn returns a PacketConn given the connection's ufrag and network address.
+// creates the connection if an existing one can't be found.
func (m *UDPMuxDefault) GetConn(ufrag string, addr net.Addr) (net.PacketConn, error) {
// don't check addr for mux using unspecified address
- if len(m.localAddrsForUnspecified) == 0 && m.params.UDPConn.LocalAddr().String() != addr.String() {
+ if len(m.localAddrsForUnspecified) == 0 && m.params.UDPConnString != addr.String() {
return nil, errInvalidAddress
}
@@ -176,11 +181,11 @@ func (m *UDPMuxDefault) GetConn(ufrag string, addr net.Addr) (net.PacketConn, er
return c, nil
}
-// RemoveConnByUfrag stops and removes the muxed packet connection
+// RemoveConnByUfrag stops and removes the muxed packet connection.
func (m *UDPMuxDefault) RemoveConnByUfrag(ufrag string) {
removedConns := make([]*udpMuxedConn, 0, 2)
- // Keep lock section small to avoid deadlock with conn lock
+ // Keep lock section small to avoid deadlock with conn lock.
m.mu.Lock()
if c, ok := m.connsIPv4[ufrag]; ok {
delete(m.connsIPv4, ufrag)
@@ -193,7 +198,7 @@ func (m *UDPMuxDefault) RemoveConnByUfrag(ufrag string) {
m.mu.Unlock()
if len(removedConns) == 0 {
- // No need to lock if no connection was found
+ // No need to lock if no connection was found.
return
}
@@ -208,7 +213,7 @@ func (m *UDPMuxDefault) RemoveConnByUfrag(ufrag string) {
}
}
-// IsClosed returns true if the mux had been closed
+// IsClosed returns true if the mux had been closed.
func (m *UDPMuxDefault) IsClosed() bool {
select {
case <-m.closedChan:
@@ -218,7 +223,7 @@ func (m *UDPMuxDefault) IsClosed() bool {
}
}
-// Close the mux, no further connections could be created
+// Close the mux, no further connections could be created.
func (m *UDPMuxDefault) Close() error {
var err error
m.closeOnce.Do(func() {
@@ -239,6 +244,7 @@ func (m *UDPMuxDefault) Close() error {
_ = m.params.UDPConn.Close()
})
+
return err
}
@@ -246,7 +252,7 @@ func (m *UDPMuxDefault) writeTo(buf []byte, rAddr net.Addr) (n int, err error) {
return m.params.UDPConn.WriteTo(buf, rAddr)
}
-func (m *UDPMuxDefault) registerConnForAddress(conn *udpMuxedConn, addr string) {
+func (m *UDPMuxDefault) registerConnForAddress(conn *udpMuxedConn, addr ipPort) {
if m.IsClosed() {
return
}
@@ -260,7 +266,7 @@ func (m *UDPMuxDefault) registerConnForAddress(conn *udpMuxedConn, addr string)
}
m.addressMap[addr] = conn
- m.params.Logger.Debugf("Registered %s for %s", addr, conn.params.Key)
+ m.params.Logger.Debugf("Registered %s for %s", addr.addr.String(), conn.params.Key)
}
func (m *UDPMuxDefault) createMuxedConn(key string) *udpMuxedConn {
@@ -271,10 +277,11 @@ func (m *UDPMuxDefault) createMuxedConn(key string) *udpMuxedConn {
LocalAddr: m.LocalAddr(),
Logger: m.params.Logger,
})
+
return c
}
-func (m *UDPMuxDefault) connWorker() {
+func (m *UDPMuxDefault) connWorker() { //nolint:cyclop
logger := m.params.Logger
defer func() {
@@ -290,21 +297,28 @@ func (m *UDPMuxDefault) connWorker() {
if os.IsTimeout(err) {
continue
} else if !errors.Is(err, io.EOF) {
- logger.Errorf("could not read udp packet: %v", err)
+ logger.Errorf("Failed to read UDP packet: %v", err)
}
return
}
- udpAddr, ok := addr.(*net.UDPAddr)
+ netUDPAddr, ok := addr.(*net.UDPAddr)
if !ok {
- logger.Errorf("underlying PacketConn did not return a UDPAddr")
+ logger.Errorf("Underlying PacketConn did not return a UDPAddr")
+
+ return
+ }
+ udpAddr, err := newIPPort(netUDPAddr.IP, netUDPAddr.Zone, uint16(netUDPAddr.Port)) //nolint:gosec
+ if err != nil {
+ logger.Errorf("Failed to create a new IP/Port host pair")
+
return
}
// If we have already seen this address dispatch to the appropriate destination
m.addressMapMu.Lock()
- destinationConn := m.addressMap[addr.String()]
+ destinationConn := m.addressMap[udpAddr]
m.addressMapMu.Unlock()
// If we haven't seen this address before but is a STUN packet lookup by ufrag
@@ -315,17 +329,19 @@ func (m *UDPMuxDefault) connWorker() {
if err = msg.Decode(); err != nil {
m.params.Logger.Warnf("Failed to handle decode ICE from %s: %v", addr.String(), err)
+
continue
}
attr, stunAttrErr := msg.Get(stun.AttrUsername)
if stunAttrErr != nil {
m.params.Logger.Warnf("No Username attribute in STUN message from %s", addr.String())
+
continue
}
ufrag := strings.Split(string(attr), ":")[0]
- isIPv6 := udpAddr.IP.To4() == nil
+ isIPv6 := netUDPAddr.IP.To4() == nil
m.mu.Lock()
destinationConn, _ = m.getConn(ufrag, isIPv6)
@@ -333,12 +349,13 @@ func (m *UDPMuxDefault) connWorker() {
}
if destinationConn == nil {
- m.params.Logger.Tracef("dropping packet from %s, addr: %s", udpAddr.String(), addr.String())
+ m.params.Logger.Tracef("Dropping packet from %s, addr: %s", udpAddr.addr, addr)
+
continue
}
- if err = destinationConn.writePacket(buf[:n], udpAddr); err != nil {
- m.params.Logger.Errorf("could not write packet: %v", err)
+ if err = destinationConn.writePacket(buf[:n], netUDPAddr); err != nil {
+ m.params.Logger.Errorf("Failed to write packet: %v", err)
}
}
}
@@ -349,11 +366,14 @@ func (m *UDPMuxDefault) getConn(ufrag string, isIPv6 bool) (val *udpMuxedConn, o
} else {
val, ok = m.connsIPv4[ufrag]
}
+
return
}
type bufferHolder struct {
- buf []byte
+ next *bufferHolder
+ buf []byte
+ addr *net.UDPAddr
}
func newBufferHolder(size int) *bufferHolder {
@@ -361,3 +381,28 @@ func newBufferHolder(size int) *bufferHolder {
buf: make([]byte, size),
}
}
+
+func (b *bufferHolder) reset() {
+ b.next = nil
+ b.addr = nil
+}
+
+type ipPort struct {
+ addr netip.Addr
+ port uint16
+}
+
+// newIPPort create a custom type of address based on netip.Addr and
+// port. The underlying ip address passed is converted to IPv6 format
+// to simplify ip address handling.
+func newIPPort(ip net.IP, zone string, port uint16) (ipPort, error) {
+ n, ok := netip.AddrFromSlice(ip.To16())
+ if !ok {
+ return ipPort{}, errInvalidIPAddress
+ }
+
+ return ipPort{
+ addr: n.WithZone(zone),
+ port: port,
+ }, nil
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/udp_mux_multi.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/udp_mux_multi.go
similarity index 84%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/udp_mux_multi.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/udp_mux_multi.go
index 158cbc37f..46c88bb93 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/udp_mux_multi.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/udp_mux_multi.go
@@ -8,8 +8,8 @@ import (
"net"
"github.com/pion/logging"
- "github.com/pion/transport/v2"
- "github.com/pion/transport/v2/stdnet"
+ "github.com/pion/transport/v3"
+ "github.com/pion/transport/v3/stdnet"
)
// MultiUDPMuxDefault implements both UDPMux and AllConnsGetter,
@@ -29,6 +29,7 @@ func NewMultiUDPMuxDefault(muxes ...UDPMux) *MultiUDPMuxDefault {
addrToMux[addr.String()] = mux
}
}
+
return &MultiUDPMuxDefault{
muxes: muxes,
localAddrToMux: addrToMux,
@@ -42,6 +43,7 @@ func (m *MultiUDPMuxDefault) GetConn(ufrag string, addr net.Addr) (net.PacketCon
if !ok {
return nil, errNoUDPMuxAvailable
}
+
return mux.GetConn(ufrag, addr)
}
@@ -53,7 +55,7 @@ func (m *MultiUDPMuxDefault) RemoveConnByUfrag(ufrag string) {
}
}
-// Close the multi mux, no further connections could be created
+// Close the multi mux, no further connections could be created.
func (m *MultiUDPMuxDefault) Close() error {
var err error
for _, mux := range m.muxes {
@@ -61,21 +63,23 @@ func (m *MultiUDPMuxDefault) Close() error {
err = e
}
}
+
return err
}
-// GetListenAddresses returns the list of addresses that this mux is listening on
+// GetListenAddresses returns the list of addresses that this mux is listening on.
func (m *MultiUDPMuxDefault) GetListenAddresses() []net.Addr {
addrs := make([]net.Addr, 0, len(m.localAddrToMux))
for _, mux := range m.muxes {
addrs = append(addrs, mux.GetListenAddresses()...)
}
+
return addrs
}
// NewMultiUDPMuxFromPort creates an instance of MultiUDPMuxDefault that
// listen all interfaces on the provided port.
-func NewMultiUDPMuxFromPort(port int, opts ...UDPMuxFromPortOption) (*MultiUDPMuxDefault, error) {
+func NewMultiUDPMuxFromPort(port int, opts ...UDPMuxFromPortOption) (*MultiUDPMuxDefault, error) { //nolint:cyclop
params := multiUDPMuxFromPortParam{
networks: []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6},
}
@@ -90,16 +94,21 @@ func NewMultiUDPMuxFromPort(port int, opts ...UDPMuxFromPortOption) (*MultiUDPMu
}
}
- ips, err := localInterfaces(params.net, params.ifFilter, params.ipFilter, params.networks, params.includeLoopback)
+ _, addrs, err := localInterfaces(params.net, params.ifFilter, params.ipFilter, params.networks, params.includeLoopback)
if err != nil {
return nil, err
}
- conns := make([]net.PacketConn, 0, len(ips))
- for _, ip := range ips {
- conn, listenErr := params.net.ListenUDP("udp", &net.UDPAddr{IP: ip, Port: port})
+ conns := make([]net.PacketConn, 0, len(addrs))
+ for _, addr := range addrs {
+ conn, listenErr := params.net.ListenUDP("udp", &net.UDPAddr{
+ IP: addr.AsSlice(),
+ Port: port,
+ Zone: addr.Zone(),
+ })
if listenErr != nil {
err = listenErr
+
break
}
if params.readBufferSize > 0 {
@@ -115,6 +124,7 @@ func NewMultiUDPMuxFromPort(port int, opts ...UDPMuxFromPortOption) (*MultiUDPMu
for _, conn := range conns {
_ = conn.Close()
}
+
return nil, err
}
@@ -131,14 +141,14 @@ func NewMultiUDPMuxFromPort(port int, opts ...UDPMuxFromPortOption) (*MultiUDPMu
return NewMultiUDPMuxDefault(muxes...), nil
}
-// UDPMuxFromPortOption provide options for NewMultiUDPMuxFromPort
+// UDPMuxFromPortOption provide options for NewMultiUDPMuxFromPort.
type UDPMuxFromPortOption interface {
apply(*multiUDPMuxFromPortParam)
}
type multiUDPMuxFromPortParam struct {
- ifFilter func(string) bool
- ipFilter func(ip net.IP) bool
+ ifFilter func(string) (keep bool)
+ ipFilter func(ip net.IP) (keep bool)
networks []NetworkType
readBufferSize int
writeBufferSize int
@@ -155,8 +165,8 @@ func (o *udpMuxFromPortOption) apply(p *multiUDPMuxFromPortParam) {
o.f(p)
}
-// UDPMuxFromPortWithInterfaceFilter set the filter to filter out interfaces that should not be used
-func UDPMuxFromPortWithInterfaceFilter(f func(string) bool) UDPMuxFromPortOption {
+// UDPMuxFromPortWithInterfaceFilter set the filter to filter out interfaces that should not be used.
+func UDPMuxFromPortWithInterfaceFilter(f func(string) (keep bool)) UDPMuxFromPortOption {
return &udpMuxFromPortOption{
f: func(p *multiUDPMuxFromPortParam) {
p.ifFilter = f
@@ -164,8 +174,8 @@ func UDPMuxFromPortWithInterfaceFilter(f func(string) bool) UDPMuxFromPortOption
}
}
-// UDPMuxFromPortWithIPFilter set the filter to filter out IP addresses that should not be used
-func UDPMuxFromPortWithIPFilter(f func(ip net.IP) bool) UDPMuxFromPortOption {
+// UDPMuxFromPortWithIPFilter set the filter to filter out IP addresses that should not be used.
+func UDPMuxFromPortWithIPFilter(f func(ip net.IP) (keep bool)) UDPMuxFromPortOption {
return &udpMuxFromPortOption{
f: func(p *multiUDPMuxFromPortParam) {
p.ipFilter = f
@@ -173,7 +183,7 @@ func UDPMuxFromPortWithIPFilter(f func(ip net.IP) bool) UDPMuxFromPortOption {
}
}
-// UDPMuxFromPortWithNetworks set the networks that should be used. default is both IPv4 and IPv6
+// UDPMuxFromPortWithNetworks set the networks that should be used. default is both IPv4 and IPv6.
func UDPMuxFromPortWithNetworks(networks ...NetworkType) UDPMuxFromPortOption {
return &udpMuxFromPortOption{
f: func(p *multiUDPMuxFromPortParam) {
@@ -182,7 +192,7 @@ func UDPMuxFromPortWithNetworks(networks ...NetworkType) UDPMuxFromPortOption {
}
}
-// UDPMuxFromPortWithReadBufferSize set the UDP connection read buffer size
+// UDPMuxFromPortWithReadBufferSize set the UDP connection read buffer size.
func UDPMuxFromPortWithReadBufferSize(size int) UDPMuxFromPortOption {
return &udpMuxFromPortOption{
f: func(p *multiUDPMuxFromPortParam) {
@@ -191,7 +201,7 @@ func UDPMuxFromPortWithReadBufferSize(size int) UDPMuxFromPortOption {
}
}
-// UDPMuxFromPortWithWriteBufferSize set the UDP connection write buffer size
+// UDPMuxFromPortWithWriteBufferSize set the UDP connection write buffer size.
func UDPMuxFromPortWithWriteBufferSize(size int) UDPMuxFromPortOption {
return &udpMuxFromPortOption{
f: func(p *multiUDPMuxFromPortParam) {
@@ -200,7 +210,7 @@ func UDPMuxFromPortWithWriteBufferSize(size int) UDPMuxFromPortOption {
}
}
-// UDPMuxFromPortWithLogger set the logger for the created UDPMux
+// UDPMuxFromPortWithLogger set the logger for the created UDPMux.
func UDPMuxFromPortWithLogger(logger logging.LeveledLogger) UDPMuxFromPortOption {
return &udpMuxFromPortOption{
f: func(p *multiUDPMuxFromPortParam) {
@@ -209,7 +219,7 @@ func UDPMuxFromPortWithLogger(logger logging.LeveledLogger) UDPMuxFromPortOption
}
}
-// UDPMuxFromPortWithLoopback set loopback interface should be included
+// UDPMuxFromPortWithLoopback set loopback interface should be included.
func UDPMuxFromPortWithLoopback() UDPMuxFromPortOption {
return &udpMuxFromPortOption{
f: func(p *multiUDPMuxFromPortParam) {
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/udp_mux_universal.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/udp_mux_universal.go
similarity index 85%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/udp_mux_universal.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/udp_mux_universal.go
index 07b6a70ea..50c80af5d 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/udp_mux_universal.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/udp_mux_universal.go
@@ -9,8 +9,8 @@ import (
"time"
"github.com/pion/logging"
- "github.com/pion/stun"
- "github.com/pion/transport/v2"
+ "github.com/pion/stun/v3"
+ "github.com/pion/transport/v3"
)
// UniversalUDPMux allows multiple connections to go over a single UDP port for
@@ -29,7 +29,8 @@ type UniversalUDPMuxDefault struct {
*UDPMuxDefault
params UniversalUDPMuxParams
- // Since we have a shared socket, for srflx candidates it makes sense to have a shared mapped address across all the agents
+ // Since we have a shared socket, for srflx candidates it makes sense
+ // to have a shared mapped address across all the agents
// stun.XORMappedAddress indexed by the STUN server addr
xorMappedMap map[string]*xorMapped
}
@@ -42,7 +43,7 @@ type UniversalUDPMuxParams struct {
Net transport.Net
}
-// NewUniversalUDPMuxDefault creates an implementation of UniversalUDPMux embedding UDPMux
+// NewUniversalUDPMuxDefault creates an implementation of UniversalUDPMux embedding UDPMux.
func NewUniversalUDPMuxDefault(params UniversalUDPMuxParams) *UniversalUDPMuxDefault {
if params.Logger == nil {
params.Logger = logging.NewDefaultLoggerFactory().NewLogger("ice")
@@ -51,31 +52,31 @@ func NewUniversalUDPMuxDefault(params UniversalUDPMuxParams) *UniversalUDPMuxDef
params.XORMappedAddrCacheTTL = time.Second * 25
}
- m := &UniversalUDPMuxDefault{
+ mux := &UniversalUDPMuxDefault{
params: params,
xorMappedMap: make(map[string]*xorMapped),
}
// Wrap UDP connection, process server reflexive messages
// before they are passed to the UDPMux connection handler (connWorker)
- m.params.UDPConn = &udpConn{
+ mux.params.UDPConn = &udpConn{
PacketConn: params.UDPConn,
- mux: m,
+ mux: mux,
logger: params.Logger,
}
// Embed UDPMux
udpMuxParams := UDPMuxParams{
Logger: params.Logger,
- UDPConn: m.params.UDPConn,
- Net: m.params.Net,
+ UDPConn: mux.params.UDPConn,
+ Net: mux.params.Net,
}
- m.UDPMuxDefault = NewUDPMuxDefault(udpMuxParams)
+ mux.UDPMuxDefault = NewUDPMuxDefault(udpMuxParams)
- return m
+ return mux
}
-// udpConn is a wrapper around UDPMux conn that overrides ReadFrom and handles STUN/TURN packets
+// udpConn is a wrapper around UDPMux conn that overrides ReadFrom and handles STUN/TURN packets.
type udpConn struct {
net.PacketConn
mux *UniversalUDPMuxDefault
@@ -88,7 +89,8 @@ func (m *UniversalUDPMuxDefault) GetRelayedAddr(net.Addr, time.Duration) (*net.A
return nil, errNotImplemented
}
-// GetConnForURL add uniques to the muxed connection by concatenating ufrag and URL (e.g. STUN URL) to be able to support multiple STUN/TURN servers
+// GetConnForURL add uniques to the muxed connection by concatenating ufrag and URL
+// (e.g. STUN URL) to be able to support multiple STUN/TURN servers
// and return a unique connection per server.
func (m *UniversalUDPMuxDefault) GetConnForURL(ufrag string, url string, addr net.Addr) (net.PacketConn, error) {
return m.UDPMuxDefault.GetConn(fmt.Sprintf("%s%s", ufrag, url), addr)
@@ -99,24 +101,24 @@ func (m *UniversalUDPMuxDefault) GetConnForURL(ufrag string, url string, addr ne
func (c *udpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
n, addr, err = c.PacketConn.ReadFrom(p)
if err != nil {
- return
+ return n, addr, err
}
- if stun.IsMessage(p[:n]) {
+ if stun.IsMessage(p[:n]) { //nolint:nestif
msg := &stun.Message{
Raw: append([]byte{}, p[:n]...),
}
if err = msg.Decode(); err != nil {
c.logger.Warnf("Failed to handle decode ICE from %s: %v", addr.String(), err)
- err = nil
- return
+
+ return n, addr, nil
}
udpAddr, ok := addr.(*net.UDPAddr)
if !ok {
// Message about this err will be logged in the UDPMux
- return
+ return n, addr, err
}
if c.mux.isXORMappedResponse(msg, udpAddr.String()) {
@@ -125,9 +127,11 @@ func (c *udpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
c.logger.Debugf("%w: %v", errGetXorMappedAddrResponse, err)
err = nil
}
- return
+
+ return n, addr, err
}
}
+
return n, addr, err
}
@@ -135,14 +139,16 @@ func (c *udpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
func (m *UniversalUDPMuxDefault) isXORMappedResponse(msg *stun.Message, stunAddr string) bool {
m.mu.Lock()
defer m.mu.Unlock()
- // Check first if it is a STUN server address because remote peer can also send similar messages but as a BindingSuccess
+ // Check first if it is a STUN server address,
+ // because remote peer can also send similar messages but as a BindingSuccess.
_, ok := m.xorMappedMap[stunAddr]
_, err := msg.Get(stun.AttrXORMappedAddress)
+
return err == nil && ok
}
-// handleXORMappedResponse parses response from the STUN server, extracts XORMappedAddress attribute
-// and set the mapped address for the server
+// handleXORMappedResponse parses response from the STUN server, extracts XORMappedAddress attribute.
+// and set the mapped address for the server.
func (m *UniversalUDPMuxDefault) handleXORMappedResponse(stunAddr *net.UDPAddr, msg *stun.Message) error {
m.mu.Lock()
defer m.mu.Unlock()
@@ -167,7 +173,10 @@ func (m *UniversalUDPMuxDefault) handleXORMappedResponse(stunAddr *net.UDPAddr,
// Makes a STUN binding request to discover mapped address otherwise.
// Blocks until the stun.XORMappedAddress has been discovered or deadline.
// Method is safe for concurrent use.
-func (m *UniversalUDPMuxDefault) GetXORMappedAddr(serverAddr net.Addr, deadline time.Duration) (*stun.XORMappedAddress, error) {
+func (m *UniversalUDPMuxDefault) GetXORMappedAddr(
+ serverAddr net.Addr,
+ deadline time.Duration,
+) (*stun.XORMappedAddress, error) {
m.mu.Lock()
mappedAddr, ok := m.xorMappedMap[serverAddr.String()]
// If we already have a mapping for this STUN server (address already received)
@@ -188,9 +197,9 @@ func (m *UniversalUDPMuxDefault) GetXORMappedAddr(serverAddr net.Addr, deadline
// Otherwise, make a STUN request to discover the address
// or wait for already sent request to complete
- waitAddrReceived, err := m.sendSTUN(serverAddr)
+ waitAddrReceived, err := m.writeSTUN(serverAddr)
if err != nil {
- return nil, fmt.Errorf("%w: %s", errSendSTUNPacket, err) //nolint:errorlint
+ return nil, fmt.Errorf("%w: %s", errWriteSTUNMessage, err) //nolint:errorlint
}
// Block until response was handled by the connWorker routine and XORMappedAddress was updated
@@ -203,17 +212,18 @@ func (m *UniversalUDPMuxDefault) GetXORMappedAddr(serverAddr net.Addr, deadline
if mappedAddr.addr == nil {
return nil, errNoXorAddrMapping
}
+
return mappedAddr.addr, nil
case <-time.After(deadline):
return nil, errXORMappedAddrTimeout
}
}
-// sendSTUN sends a STUN request via UDP conn.
+// writeSTUN sends a STUN request via UDP conn.
//
// The returned channel is closed when the STUN response has been received.
// Method is safe for concurrent use.
-func (m *UniversalUDPMuxDefault) sendSTUN(serverAddr net.Addr) (chan struct{}, error) {
+func (m *UniversalUDPMuxDefault) writeSTUN(serverAddr net.Addr) (chan struct{}, error) {
m.mu.Lock()
defer m.mu.Unlock()
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/udp_muxed_conn.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/udp_muxed_conn.go
new file mode 100644
index 000000000..e32cb3059
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/udp_muxed_conn.go
@@ -0,0 +1,252 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ice
+
+import (
+ "io"
+ "net"
+ "sync"
+ "time"
+
+ "github.com/pion/logging"
+)
+
+type udpMuxedConnState int
+
+const (
+ udpMuxedConnOpen udpMuxedConnState = iota
+ udpMuxedConnWaiting
+ udpMuxedConnClosed
+)
+
+type udpMuxedConnParams struct {
+ Mux *UDPMuxDefault
+ AddrPool *sync.Pool
+ Key string
+ LocalAddr net.Addr
+ Logger logging.LeveledLogger
+}
+
+// udpMuxedConn represents a logical packet conn for a single remote as identified by ufrag.
+type udpMuxedConn struct {
+ params *udpMuxedConnParams
+ // Remote addresses that we have sent to on this conn
+ addresses []ipPort
+
+ // FIFO queue holding incoming packets
+ bufHead, bufTail *bufferHolder
+ notify chan struct{}
+ closedChan chan struct{}
+ state udpMuxedConnState
+ mu sync.Mutex
+}
+
+func newUDPMuxedConn(params *udpMuxedConnParams) *udpMuxedConn {
+ return &udpMuxedConn{
+ params: params,
+ notify: make(chan struct{}, 1),
+ closedChan: make(chan struct{}),
+ }
+}
+
+func (c *udpMuxedConn) ReadFrom(b []byte) (n int, rAddr net.Addr, err error) {
+ for {
+ c.mu.Lock()
+ if c.bufTail != nil {
+ pkt := c.bufTail
+ c.bufTail = pkt.next
+
+ if pkt == c.bufHead {
+ c.bufHead = nil
+ }
+ c.mu.Unlock()
+
+ if len(b) < len(pkt.buf) {
+ err = io.ErrShortBuffer
+ } else {
+ n = copy(b, pkt.buf)
+ rAddr = pkt.addr
+ }
+
+ pkt.reset()
+ c.params.AddrPool.Put(pkt)
+
+ return n, rAddr, err
+ }
+
+ if c.state == udpMuxedConnClosed {
+ c.mu.Unlock()
+
+ return 0, nil, io.EOF
+ }
+
+ c.state = udpMuxedConnWaiting
+ c.mu.Unlock()
+
+ select {
+ case <-c.notify:
+ case <-c.closedChan:
+ return 0, nil, io.EOF
+ }
+ }
+}
+
+func (c *udpMuxedConn) WriteTo(buf []byte, rAddr net.Addr) (n int, err error) {
+ if c.isClosed() {
+ return 0, io.ErrClosedPipe
+ }
+ // Each time we write to a new address, we'll register it with the mux
+ netUDPAddr, ok := rAddr.(*net.UDPAddr)
+ if !ok {
+ return 0, errFailedToCastUDPAddr
+ }
+
+ //nolint:gosec // TODO add port validation G115
+ ipAndPort, err := newIPPort(netUDPAddr.IP, netUDPAddr.Zone, uint16(netUDPAddr.Port))
+ if err != nil {
+ return 0, err
+ }
+ if !c.containsAddress(ipAndPort) {
+ c.addAddress(ipAndPort)
+ }
+
+ return c.params.Mux.writeTo(buf, rAddr)
+}
+
+func (c *udpMuxedConn) LocalAddr() net.Addr {
+ return c.params.LocalAddr
+}
+
+func (c *udpMuxedConn) SetDeadline(time.Time) error {
+ return nil
+}
+
+func (c *udpMuxedConn) SetReadDeadline(time.Time) error {
+ return nil
+}
+
+func (c *udpMuxedConn) SetWriteDeadline(time.Time) error {
+ return nil
+}
+
+func (c *udpMuxedConn) CloseChannel() <-chan struct{} {
+ return c.closedChan
+}
+
+func (c *udpMuxedConn) Close() error {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ if c.state != udpMuxedConnClosed {
+ for pkt := c.bufTail; pkt != nil; {
+ next := pkt.next
+
+ pkt.reset()
+ c.params.AddrPool.Put(pkt)
+
+ pkt = next
+ }
+ c.bufHead = nil
+ c.bufTail = nil
+
+ c.state = udpMuxedConnClosed
+ close(c.closedChan)
+ }
+
+ return nil
+}
+
+func (c *udpMuxedConn) isClosed() bool {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ return c.state == udpMuxedConnClosed
+}
+
+func (c *udpMuxedConn) getAddresses() []ipPort {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ addresses := make([]ipPort, len(c.addresses))
+ copy(addresses, c.addresses)
+
+ return addresses
+}
+
+func (c *udpMuxedConn) addAddress(addr ipPort) {
+ c.mu.Lock()
+ c.addresses = append(c.addresses, addr)
+ c.mu.Unlock()
+
+ // Map it on mux
+ c.params.Mux.registerConnForAddress(c, addr)
+}
+
+func (c *udpMuxedConn) removeAddress(addr ipPort) {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ newAddresses := make([]ipPort, 0, len(c.addresses))
+ for _, a := range c.addresses {
+ if a != addr {
+ newAddresses = append(newAddresses, a)
+ }
+ }
+
+ c.addresses = newAddresses
+}
+
+func (c *udpMuxedConn) containsAddress(addr ipPort) bool {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ for _, a := range c.addresses {
+ if addr == a {
+ return true
+ }
+ }
+
+ return false
+}
+
+func (c *udpMuxedConn) writePacket(data []byte, addr *net.UDPAddr) error {
+ pkt := c.params.AddrPool.Get().(*bufferHolder) //nolint:forcetypeassert
+ if cap(pkt.buf) < len(data) {
+ c.params.AddrPool.Put(pkt)
+
+ return io.ErrShortBuffer
+ }
+
+ pkt.buf = append(pkt.buf[:0], data...)
+ pkt.addr = addr
+
+ c.mu.Lock()
+ if c.state == udpMuxedConnClosed {
+ c.mu.Unlock()
+
+ pkt.reset()
+ c.params.AddrPool.Put(pkt)
+
+ return io.ErrClosedPipe
+ }
+
+ if c.bufHead != nil {
+ c.bufHead.next = pkt
+ }
+ c.bufHead = pkt
+
+ if c.bufTail == nil {
+ c.bufTail = pkt
+ }
+
+ state := c.state
+ c.state = udpMuxedConnOpen
+ c.mu.Unlock()
+
+ if state == udpMuxedConnWaiting {
+ select {
+ case c.notify <- struct{}{}:
+ default:
+ }
+ }
+
+ return nil
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/url.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/url.go
similarity index 67%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/url.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/url.go
index b2b9f8bd3..f18d78772 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/url.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/url.go
@@ -3,80 +3,80 @@
package ice
-import "github.com/pion/stun"
+import "github.com/pion/stun/v3"
type (
- // URL represents a STUN (rfc7064) or TURN (rfc7065) URI
+ // URL represents a STUN (rfc7064) or TURN (rfc7065) URI.
//
- // Deprecated: Please use pion/stun.URI
+ // Deprecated: Please use pion/stun.URI.
URL = stun.URI
// ProtoType indicates the transport protocol type that is used in the ice.URL
// structure.
//
- // Deprecated: TPlease use pion/stun.ProtoType
+ // Deprecated: TPlease use pion/stun.ProtoType.
ProtoType = stun.ProtoType
// SchemeType indicates the type of server used in the ice.URL structure.
//
- // Deprecated: Please use pion/stun.SchemeType
+ // Deprecated: Please use pion/stun.SchemeType.
SchemeType = stun.SchemeType
)
const (
// SchemeTypeSTUN indicates the URL represents a STUN server.
//
- // Deprecated: Please use pion/stun.SchemeTypeSTUN
+ // Deprecated: Please use pion/stun.SchemeTypeSTUN.
SchemeTypeSTUN = stun.SchemeTypeSTUN
// SchemeTypeSTUNS indicates the URL represents a STUNS (secure) server.
//
- // Deprecated: Please use pion/stun.SchemeTypeSTUNS
+ // Deprecated: Please use pion/stun.SchemeTypeSTUNS.
SchemeTypeSTUNS = stun.SchemeTypeSTUNS
// SchemeTypeTURN indicates the URL represents a TURN server.
//
- // Deprecated: Please use pion/stun.SchemeTypeTURN
+ // Deprecated: Please use pion/stun.SchemeTypeTURN.
SchemeTypeTURN = stun.SchemeTypeTURN
// SchemeTypeTURNS indicates the URL represents a TURNS (secure) server.
//
- // Deprecated: Please use pion/stun.SchemeTypeTURNS
+ // Deprecated: Please use pion/stun.SchemeTypeTURNS.
SchemeTypeTURNS = stun.SchemeTypeTURNS
)
const (
// ProtoTypeUDP indicates the URL uses a UDP transport.
//
- // Deprecated: Please use pion/stun.ProtoTypeUDP
+ // Deprecated: Please use pion/stun.ProtoTypeUDP.
ProtoTypeUDP = stun.ProtoTypeUDP
// ProtoTypeTCP indicates the URL uses a TCP transport.
//
- // Deprecated: Please use pion/stun.ProtoTypeTCP
+ // Deprecated: Please use pion/stun.ProtoTypeTCP.
ProtoTypeTCP = stun.ProtoTypeTCP
)
-// Unknown represents and unknown ProtoType or SchemeType
+// Unknown represents and unknown ProtoType or SchemeType.
//
-// Deprecated: Please use pion/stun.SchemeTypeUnknown or pion/stun.ProtoTypeUnknown
+// Deprecated: Please use pion/stun.SchemeTypeUnknown or pion/stun.ProtoTypeUnknown.
const Unknown = 0
-// ParseURL parses a STUN or TURN urls following the ABNF syntax described in
+// ParseURL parses a STUN or TURN urls following the ABNF syntax described in.
// https://tools.ietf.org/html/rfc7064 and https://tools.ietf.org/html/rfc7065
// respectively.
//
-// Deprecated: Please use pion/stun.ParseURI
+// Deprecated: Please use pion/stun.ParseURI.
var ParseURL = stun.ParseURI //nolint:gochecknoglobals
-// NewSchemeType defines a procedure for creating a new SchemeType from a raw
+// NewSchemeType defines a procedure for creating a new SchemeType from a raw.
// string naming the scheme type.
//
-// Deprecated: Please use pion/stun.NewSchemeType
+// Deprecated: Please use pion/stun.NewSchemeType.
var NewSchemeType = stun.NewSchemeType //nolint:gochecknoglobals
-// NewProtoType defines a procedure for creating a new ProtoType from a raw
+// NewProtoType defines a procedure for creating a new ProtoType from a raw.
// string naming the transport protocol type.
//
-// Deprecated: Please use pion/stun.NewProtoType
+// Deprecated: Please use pion/stun.NewProtoType.
var NewProtoType = stun.NewProtoType //nolint:gochecknoglobals
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/usecandidate.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/usecandidate.go
similarity index 94%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/usecandidate.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/usecandidate.go
index 6fc7ed50c..512f50438 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/usecandidate.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v4/usecandidate.go
@@ -3,7 +3,7 @@
package ice
-import "github.com/pion/stun"
+import "github.com/pion/stun/v3"
// UseCandidateAttr represents USE-CANDIDATE attribute.
type UseCandidateAttr struct{}
@@ -11,12 +11,14 @@ type UseCandidateAttr struct{}
// AddTo adds USE-CANDIDATE attribute to message.
func (UseCandidateAttr) AddTo(m *stun.Message) error {
m.Add(stun.AttrUseCandidate, nil)
+
return nil
}
// IsSet returns true if USE-CANDIDATE attribute is set.
func (UseCandidateAttr) IsSet(m *stun.Message) bool {
_, err := m.Get(stun.AttrUseCandidate)
+
return err == nil
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/.golangci.yml b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/.golangci.yml
index 4e3eddf42..a3235bec2 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/.golangci.yml
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/.golangci.yml
@@ -1,9 +1,13 @@
# SPDX-FileCopyrightText: 2023 The Pion community
# SPDX-License-Identifier: MIT
+run:
+ timeout: 5m
+
linters-settings:
govet:
- check-shadowing: true
+ enable:
+ - shadow
misspell:
locale: US
exhaustive:
@@ -29,7 +33,6 @@ linters:
- bodyclose # checks whether HTTP response body is closed successfully
- contextcheck # check the function whether use a non-inherited context
- decorder # check declaration order and count of types, constants, variables and functions
- - depguard # Go linter that checks if package imports are in a list of acceptable packages
- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
- dupl # Tool for code clone detection
- durationcheck # check for two durations multiplied together
@@ -48,7 +51,7 @@ linters:
- goconst # Finds repeated strings that could be replaced by a constant
- gocritic # The most opinionated Go source code linter
- godox # Tool for detection of FIXME, TODO and other comment keywords
- - goerr113 # Golang linter to check the errors handling expressions
+ - err113 # Golang linter to check the errors handling expressions
- gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
- gofumpt # Gofumpt checks whether code was gofumpt-ed.
- goheader # Checks is file header matches to pattern
@@ -63,7 +66,6 @@ linters:
- importas # Enforces consistent import aliases
- ineffassign # Detects when assignments to existing variables are not used
- misspell # Finds commonly misspelled English words in comments
- - nakedret # Finds naked returns in functions greater than a specified function length
- nilerr # Finds the code that returns nil even if it checks that the error is not nil.
- nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value.
- noctx # noctx finds sending http request without context.Context
@@ -81,19 +83,18 @@ linters:
- wastedassign # wastedassign finds wasted assignment statements
- whitespace # Tool for detection of leading and trailing whitespace
disable:
+ - depguard # Go linter that checks if package imports are in a list of acceptable packages
- containedctx # containedctx is a linter that detects struct contained context.Context field
- cyclop # checks function and package cyclomatic complexity
- - exhaustivestruct # Checks if all struct's fields are initialized
- funlen # Tool for detection of long functions
- gocyclo # Computes and checks the cyclomatic complexity of functions
- godot # Check if comments end in a period
- gomnd # An analyzer to detect magic numbers.
- - ifshort # Checks that your code uses short syntax for if-statements whenever possible
- ireturn # Accept Interfaces, Return Concrete Types
- lll # Reports long lines
- maintidx # maintidx measures the maintainability index of each function.
- makezero # Finds slice declarations with non-zero initial length
- - maligned # Tool to detect Go structs that would take less memory if their fields were sorted
+ - nakedret # Finds naked returns in functions greater than a specified function length
- nestif # Reports deeply nested if statements
- nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
- nolintlint # Reports ill-formed or insufficient nolint directives
@@ -110,28 +111,15 @@ linters:
issues:
exclude-use-default: false
+ exclude-dirs-use-default: false
exclude-rules:
- # Allow complex tests, better to be self contained
- - path: _test\.go
- linters:
- - gocognit
- - forbidigo
-
- # Allow complex main function in examples
- - path: examples
- text: "of func `main` is high"
- linters:
- - gocognit
-
- # Allow forbidden identifiers in examples
- - path: examples
+ # Allow complex tests and examples, better to be self contained
+ - path: (examples|main\.go|_test\.go)
linters:
- forbidigo
+ - gocognit
# Allow forbidden identifiers in CLI commands
- path: cmd
linters:
- forbidigo
-
-run:
- skip-dirs-use-default: false
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/AUTHORS.txt b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/AUTHORS.txt
deleted file mode 100644
index 44a421ef8..000000000
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/AUTHORS.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-# Thank you to everyone that made Pion possible. If you are interested in contributing
-# we would love to have you https://github.com/pion/webrtc/wiki/Contributing
-#
-# This file is auto generated, using git to list all individuals contributors.
-# see https://github.com/pion/.goassets/blob/master/scripts/generate-authors.sh for the scripting
-Aaron Boushley
-Adam Kiss
-adamroach
-Aditya Kumar
-aler9 <46489434+aler9@users.noreply.github.com>
-Antoine Baché
-Atsushi Watanabe
-Bobby Peck
-boks1971
-David Zhao
-Jonathan Müller
-Kevin Caffrey
-Maksim Nesterov
-Mathis Engelbart
-Miroslav
-Miroslav Šedivý
-Quentin Renard
-Rayleigh Li
-Sean DuBois
-Steffen Vogel
-XLPolar
-ziminghua <565209960@qq.com>
-
-# List of contributors not appearing in Git history
-
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/README.md b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/README.md
index dcc856f1e..5db5d4414 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/README.md
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/README.md
@@ -78,7 +78,7 @@ We are always looking to support **your projects**. Please reach out if you have
If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
### Contributing
-Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible: [AUTHORS.txt](./AUTHORS.txt)
+Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible
### License
MIT License - see [LICENSE](LICENSE) for full text
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/attributes.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/attributes.go
index d7936d526..8b6d0f5cf 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/attributes.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/attributes.go
@@ -33,7 +33,7 @@ func (a Attributes) Set(key interface{}, val interface{}) {
}
// GetRTPHeader gets the RTP header if present. If it is not present, it will be
-// unmarshalled from the raw byte slice and stored in the attribtues.
+// unmarshalled from the raw byte slice and stored in the attributes.
func (a Attributes) GetRTPHeader(raw []byte) (*rtp.Header, error) {
if val, ok := a[rtpHeaderKey]; ok {
if header, ok := val.(*rtp.Header); ok {
@@ -50,7 +50,7 @@ func (a Attributes) GetRTPHeader(raw []byte) (*rtp.Header, error) {
}
// GetRTCPPackets gets the RTCP packets if present. If the packet slice is not
-// present, it will be unmarshaled from the raw byte slice and stored in the
+// present, it will be unmarshalled from the raw byte slice and stored in the
// attributes.
func (a Attributes) GetRTCPPackets(raw []byte) ([]rtcp.Packet, error) {
if val, ok := a[rtcpPacketsKey]; ok {
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/internal/sequencenumber/unwrapper.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/internal/sequencenumber/unwrapper.go
new file mode 100644
index 000000000..311ff09df
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/internal/sequencenumber/unwrapper.go
@@ -0,0 +1,45 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package sequencenumber provides a sequence number unwrapper
+package sequencenumber
+
+const (
+ maxSequenceNumberPlusOne = int64(65536)
+ breakpoint = 32768 // half of max uint16
+)
+
+// Unwrapper stores an unwrapped sequence number
+type Unwrapper struct {
+ init bool
+ lastUnwrapped int64
+}
+
+func isNewer(value, previous uint16) bool {
+ if value-previous == breakpoint {
+ return value > previous
+ }
+ return value != previous && (value-previous) < breakpoint
+}
+
+// Unwrap unwraps the next sequencenumber
+func (u *Unwrapper) Unwrap(i uint16) int64 {
+ if !u.init {
+ u.init = true
+ u.lastUnwrapped = int64(i)
+ return u.lastUnwrapped
+ }
+
+ lastWrapped := uint16(u.lastUnwrapped)
+ delta := int64(i - lastWrapped)
+ if isNewer(i, lastWrapped) {
+ if delta < 0 {
+ delta += maxSequenceNumberPlusOne
+ }
+ } else if delta > 0 && u.lastUnwrapped+delta-maxSequenceNumberPlusOne >= 0 {
+ delta -= maxSequenceNumberPlusOne
+ }
+
+ u.lastUnwrapped += delta
+ return u.lastUnwrapped
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/generator_interceptor.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/generator_interceptor.go
index faf533ba4..ab2bb2c52 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/generator_interceptor.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/generator_interceptor.go
@@ -21,12 +21,15 @@ type GeneratorInterceptorFactory struct {
// NewInterceptor constructs a new ReceiverInterceptor
func (g *GeneratorInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) {
i := &GeneratorInterceptor{
- size: 512,
- skipLastN: 0,
- interval: time.Millisecond * 100,
- receiveLogs: map[uint32]*receiveLog{},
- close: make(chan struct{}),
- log: logging.NewDefaultLoggerFactory().NewLogger("nack_generator"),
+ streamsFilter: streamSupportNack,
+ size: 512,
+ skipLastN: 0,
+ maxNacksPerPacket: 0,
+ interval: time.Millisecond * 100,
+ receiveLogs: map[uint32]*receiveLog{},
+ nackCountLogs: map[uint32]map[uint16]uint16{},
+ close: make(chan struct{}),
+ log: logging.NewDefaultLoggerFactory().NewLogger("nack_generator"),
}
for _, opt := range g.opts {
@@ -45,13 +48,16 @@ func (g *GeneratorInterceptorFactory) NewInterceptor(_ string) (interceptor.Inte
// GeneratorInterceptor interceptor generates nack feedback messages.
type GeneratorInterceptor struct {
interceptor.NoOp
- size uint16
- skipLastN uint16
- interval time.Duration
- m sync.Mutex
- wg sync.WaitGroup
- close chan struct{}
- log logging.LeveledLogger
+ streamsFilter func(info *interceptor.StreamInfo) bool
+ size uint16
+ skipLastN uint16
+ maxNacksPerPacket uint16
+ interval time.Duration
+ m sync.Mutex
+ wg sync.WaitGroup
+ close chan struct{}
+ log logging.LeveledLogger
+ nackCountLogs map[uint32]map[uint16]uint16
receiveLogs map[uint32]*receiveLog
receiveLogsMu sync.Mutex
@@ -82,7 +88,7 @@ func (n *GeneratorInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) int
// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method
// will be called once per rtp packet.
func (n *GeneratorInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
- if !streamSupportNack(info) {
+ if !n.streamsFilter(info) {
return reader
}
@@ -131,6 +137,7 @@ func (n *GeneratorInterceptor) Close() error {
return nil
}
+// nolint:gocognit
func (n *GeneratorInterceptor) loop(rtcpWriter interceptor.RTCPWriter) {
defer n.wg.Done()
@@ -147,14 +154,47 @@ func (n *GeneratorInterceptor) loop(rtcpWriter interceptor.RTCPWriter) {
for ssrc, receiveLog := range n.receiveLogs {
missing := receiveLog.missingSeqNumbers(n.skipLastN)
+
+ if len(missing) == 0 || n.nackCountLogs[ssrc] == nil {
+ n.nackCountLogs[ssrc] = map[uint16]uint16{}
+ }
if len(missing) == 0 {
continue
}
+ filteredMissing := []uint16{}
+ if n.maxNacksPerPacket > 0 {
+ for _, missingSeq := range missing {
+ if n.nackCountLogs[ssrc][missingSeq] < n.maxNacksPerPacket {
+ filteredMissing = append(filteredMissing, missingSeq)
+ }
+ n.nackCountLogs[ssrc][missingSeq]++
+ }
+ } else {
+ filteredMissing = missing
+ }
+
nack := &rtcp.TransportLayerNack{
SenderSSRC: senderSSRC,
MediaSSRC: ssrc,
- Nacks: rtcp.NackPairsFromSequenceNumbers(missing),
+ Nacks: rtcp.NackPairsFromSequenceNumbers(filteredMissing),
+ }
+
+ for nackSeq := range n.nackCountLogs[ssrc] {
+ isMissing := false
+ for _, missingSeq := range missing {
+ if missingSeq == nackSeq {
+ isMissing = true
+ break
+ }
+ }
+ if !isMissing {
+ delete(n.nackCountLogs[ssrc], nackSeq)
+ }
+ }
+
+ if len(filteredMissing) == 0 {
+ continue
}
if _, err := rtcpWriter.Write([]rtcp.Packet{nack}, interceptor.Attributes{}); err != nil {
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/generator_option.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/generator_option.go
index e4f46f7c9..5403e3eee 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/generator_option.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/generator_option.go
@@ -6,6 +6,7 @@ package nack
import (
"time"
+ "github.com/pion/interceptor"
"github.com/pion/logging"
)
@@ -30,6 +31,15 @@ func GeneratorSkipLastN(skipLastN uint16) GeneratorOption {
}
}
+// GeneratorMaxNacksPerPacket sets the maximum number of NACKs sent per missing packet, e.g. if set to 2, a missing
+// packet will only be NACKed at most twice. If set to 0 (default), max number of NACKs is unlimited
+func GeneratorMaxNacksPerPacket(maxNacks uint16) GeneratorOption {
+ return func(r *GeneratorInterceptor) error {
+ r.maxNacksPerPacket = maxNacks
+ return nil
+ }
+}
+
// GeneratorLog sets a logger for the interceptor
func GeneratorLog(log logging.LeveledLogger) GeneratorOption {
return func(r *GeneratorInterceptor) error {
@@ -45,3 +55,11 @@ func GeneratorInterval(interval time.Duration) GeneratorOption {
return nil
}
}
+
+// GeneratorStreamsFilter sets filter for generator streams
+func GeneratorStreamsFilter(filter func(info *interceptor.StreamInfo) bool) GeneratorOption {
+ return func(r *GeneratorInterceptor) error {
+ r.streamsFilter = filter
+ return nil
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/responder_interceptor.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/responder_interceptor.go
index 8f74952dd..22d038ba4 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/responder_interceptor.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/responder_interceptor.go
@@ -18,15 +18,16 @@ type ResponderInterceptorFactory struct {
}
type packetFactory interface {
- NewPacket(header *rtp.Header, payload []byte) (*retainablePacket, error)
+ NewPacket(header *rtp.Header, payload []byte, rtxSsrc uint32, rtxPayloadType uint8) (*retainablePacket, error)
}
// NewInterceptor constructs a new ResponderInterceptor
func (r *ResponderInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) {
i := &ResponderInterceptor{
- size: 1024,
- log: logging.NewDefaultLoggerFactory().NewLogger("nack_responder"),
- streams: map[uint32]*localStream{},
+ streamsFilter: streamSupportNack,
+ size: 1024,
+ log: logging.NewDefaultLoggerFactory().NewLogger("nack_responder"),
+ streams: map[uint32]*localStream{},
}
for _, opt := range r.opts {
@@ -49,6 +50,7 @@ func (r *ResponderInterceptorFactory) NewInterceptor(_ string) (interceptor.Inte
// ResponderInterceptor responds to nack feedback messages
type ResponderInterceptor struct {
interceptor.NoOp
+ streamsFilter func(info *interceptor.StreamInfo) bool
size uint16
log logging.LeveledLogger
packetFactory packetFactory
@@ -99,18 +101,21 @@ func (n *ResponderInterceptor) BindRTCPReader(reader interceptor.RTCPReader) int
// BindLocalStream lets you modify any outgoing RTP packets. It is called once for per LocalStream. The returned method
// will be called once per rtp packet.
func (n *ResponderInterceptor) BindLocalStream(info *interceptor.StreamInfo, writer interceptor.RTPWriter) interceptor.RTPWriter {
- if !streamSupportNack(info) {
+ if !n.streamsFilter(info) {
return writer
}
// error is already checked in NewGeneratorInterceptor
sendBuffer, _ := newSendBuffer(n.size)
n.streamsMu.Lock()
- n.streams[info.SSRC] = &localStream{sendBuffer: sendBuffer, rtpWriter: writer}
+ n.streams[info.SSRC] = &localStream{
+ sendBuffer: sendBuffer,
+ rtpWriter: writer,
+ }
n.streamsMu.Unlock()
return interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
- pkt, err := n.packetFactory.NewPacket(header, payload)
+ pkt, err := n.packetFactory.NewPacket(header, payload, info.SSRCRetransmission, info.PayloadTypeRetransmission)
if err != nil {
return 0, err
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/responder_option.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/responder_option.go
index 0aaa7577f..24c7c4693 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/responder_option.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/responder_option.go
@@ -3,7 +3,10 @@
package nack
-import "github.com/pion/logging"
+import (
+ "github.com/pion/interceptor"
+ "github.com/pion/logging"
+)
// ResponderOption can be used to configure ResponderInterceptor
type ResponderOption func(s *ResponderInterceptor) error
@@ -33,3 +36,11 @@ func DisableCopy() ResponderOption {
return nil
}
}
+
+// ResponderStreamsFilter sets filter for local streams
+func ResponderStreamsFilter(filter func(info *interceptor.StreamInfo) bool) ResponderOption {
+ return func(r *ResponderInterceptor) error {
+ r.streamsFilter = filter
+ return nil
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/retainable_packet.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/retainable_packet.go
index 31e9d832f..18c533a8a 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/retainable_packet.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/retainable_packet.go
@@ -4,6 +4,7 @@
package nack
import (
+ "encoding/binary"
"io"
"sync"
@@ -13,8 +14,9 @@ import (
const maxPayloadLen = 1460
type packetManager struct {
- headerPool *sync.Pool
- payloadPool *sync.Pool
+ headerPool *sync.Pool
+ payloadPool *sync.Pool
+ rtxSequencer rtp.Sequencer
}
func newPacketManager() *packetManager {
@@ -30,16 +32,18 @@ func newPacketManager() *packetManager {
return &buf
},
},
+ rtxSequencer: rtp.NewRandomSequencer(),
}
}
-func (m *packetManager) NewPacket(header *rtp.Header, payload []byte) (*retainablePacket, error) {
+func (m *packetManager) NewPacket(header *rtp.Header, payload []byte, rtxSsrc uint32, rtxPayloadType uint8) (*retainablePacket, error) {
if len(payload) > maxPayloadLen {
return nil, io.ErrShortBuffer
}
p := &retainablePacket{
- onRelease: m.releasePacket,
+ onRelease: m.releasePacket,
+ sequenceNumber: header.SequenceNumber,
// new packets have retain count of 1
count: 1,
}
@@ -62,6 +66,29 @@ func (m *packetManager) NewPacket(header *rtp.Header, payload []byte) (*retainab
p.payload = (*p.buffer)[:size]
}
+ if rtxSsrc != 0 && rtxPayloadType != 0 {
+ // Store the original sequence number and rewrite the sequence number.
+ originalSequenceNumber := p.header.SequenceNumber
+ p.header.SequenceNumber = m.rtxSequencer.NextSequenceNumber()
+
+ // Rewrite the SSRC.
+ p.header.SSRC = rtxSsrc
+ // Rewrite the payload type.
+ p.header.PayloadType = rtxPayloadType
+
+ // Remove padding if present.
+ paddingLength := 0
+ if p.header.Padding && p.payload != nil && len(p.payload) > 0 {
+ paddingLength = int(p.payload[len(p.payload)-1])
+ p.header.Padding = false
+ }
+
+ // Write the original sequence number at the beginning of the payload.
+ payload := make([]byte, 2)
+ binary.BigEndian.PutUint16(payload, originalSequenceNumber)
+ p.payload = append(payload, p.payload[:len(p.payload)-paddingLength]...)
+ }
+
return p, nil
}
@@ -74,12 +101,13 @@ func (m *packetManager) releasePacket(header *rtp.Header, payload *[]byte) {
type noOpPacketFactory struct{}
-func (f *noOpPacketFactory) NewPacket(header *rtp.Header, payload []byte) (*retainablePacket, error) {
+func (f *noOpPacketFactory) NewPacket(header *rtp.Header, payload []byte, _ uint32, _ uint8) (*retainablePacket, error) {
return &retainablePacket{
- onRelease: f.releasePacket,
- count: 1,
- header: header,
- payload: payload,
+ onRelease: f.releasePacket,
+ count: 1,
+ header: header,
+ payload: payload,
+ sequenceNumber: header.SequenceNumber,
}, nil
}
@@ -96,6 +124,8 @@ type retainablePacket struct {
header *rtp.Header
buffer *[]byte
payload []byte
+
+ sequenceNumber uint16
}
func (p *retainablePacket) Header() *rtp.Header {
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/send_buffer.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/send_buffer.go
index e8e816f61..2b3b076f5 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/send_buffer.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/send_buffer.go
@@ -46,7 +46,7 @@ func (s *sendBuffer) add(packet *retainablePacket) {
s.m.Lock()
defer s.m.Unlock()
- seq := packet.Header().SequenceNumber
+ seq := packet.sequenceNumber
if !s.started {
s.packets[seq%s.size] = packet
s.lastAdded = seq
@@ -92,7 +92,7 @@ func (s *sendBuffer) get(seq uint16) *retainablePacket {
pkt := s.packets[seq%s.size]
if pkt != nil {
- if pkt.Header().SequenceNumber != seq {
+ if pkt.sequenceNumber != seq {
return nil
}
// already released
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/receiver_interceptor.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/receiver_interceptor.go
index c126f9abc..0afbd08f5 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/receiver_interceptor.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/receiver_interceptor.go
@@ -100,7 +100,7 @@ func (r *ReceiverInterceptor) loop(rtcpWriter interceptor.RTCPWriter) {
select {
case <-ticker.C:
now := r.now()
- r.streams.Range(func(key, value interface{}) bool {
+ r.streams.Range(func(_, value interface{}) bool {
if stream, ok := value.(*receiverStream); !ok {
r.log.Warnf("failed to cast ReceiverInterceptor stream")
} else if _, err := rtcpWriter.Write([]rtcp.Packet{stream.generateReport(now)}, interceptor.Attributes{}); err != nil {
@@ -142,8 +142,8 @@ func (r *ReceiverInterceptor) BindRemoteStream(info *interceptor.StreamInfo, rea
})
}
-// UnbindLocalStream is called when the Stream is removed. It can be used to clean up any data related to that track.
-func (r *ReceiverInterceptor) UnbindLocalStream(info *interceptor.StreamInfo) {
+// UnbindRemoteStream is called when the Stream is removed. It can be used to clean up any data related to that track.
+func (r *ReceiverInterceptor) UnbindRemoteStream(info *interceptor.StreamInfo) {
r.streams.Delete(info.SSRC)
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/receiver_stream.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/receiver_stream.go
index b899bb1b2..ebe08473d 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/receiver_stream.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/receiver_stream.go
@@ -12,6 +12,13 @@ import (
"github.com/pion/rtp"
)
+const (
+ // packetsPerHistoryEntry represents how many packets are in the bitmask for
+ // each entry in the `packets` slice in the receiver stream. Because we use
+ // a uint64, we can keep track of 64 packets per entry.
+ packetsPerHistoryEntry = 64
+)
+
type receiverStream struct {
ssrc uint32
receiverSSRC uint32
@@ -57,10 +64,10 @@ func (stream *receiverStream) processRTP(now time.Time, pktHeader *rtp.Header) {
} else { // following frames
stream.setReceived(pktHeader.SequenceNumber)
- diff := int32(pktHeader.SequenceNumber) - int32(stream.lastSeqnum)
- if diff > 0 || diff < -0x0FFF {
- // overflow
- if diff < -0x0FFF {
+ diff := pktHeader.SequenceNumber - stream.lastSeqnum
+ if diff > 0 && diff < (1<<15) {
+ // wrap around
+ if pktHeader.SequenceNumber < stream.lastSeqnum {
stream.seqnumCycles++
}
@@ -86,18 +93,18 @@ func (stream *receiverStream) processRTP(now time.Time, pktHeader *rtp.Header) {
}
func (stream *receiverStream) setReceived(seq uint16) {
- pos := seq % stream.size
- stream.packets[pos/64] |= 1 << (pos % 64)
+ pos := seq % (stream.size * packetsPerHistoryEntry)
+ stream.packets[pos/packetsPerHistoryEntry] |= 1 << (pos % packetsPerHistoryEntry)
}
func (stream *receiverStream) delReceived(seq uint16) {
- pos := seq % stream.size
- stream.packets[pos/64] &^= 1 << (pos % 64)
+ pos := seq % (stream.size * packetsPerHistoryEntry)
+ stream.packets[pos/packetsPerHistoryEntry] &^= 1 << (pos % packetsPerHistoryEntry)
}
func (stream *receiverStream) getReceived(seq uint16) bool {
- pos := seq % stream.size
- return (stream.packets[pos/64] & (1 << (pos % 64))) != 0
+ pos := seq % (stream.size * packetsPerHistoryEntry)
+ return (stream.packets[pos/packetsPerHistoryEntry] & (1 << (pos % packetsPerHistoryEntry))) != 0
}
func (stream *receiverStream) processSenderReport(now time.Time, sr *rtcp.SenderReport) {
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/sender_interceptor.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/sender_interceptor.go
index 02d1c0001..1b6f4b252 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/sender_interceptor.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/sender_interceptor.go
@@ -13,6 +13,9 @@ import (
"github.com/pion/rtp"
)
+// TickerFactory is a factory to create new tickers
+type TickerFactory func(d time.Duration) Ticker
+
// SenderInterceptorFactory is a interceptor.Factory for a SenderInterceptor
type SenderInterceptorFactory struct {
opts []SenderOption
@@ -23,8 +26,11 @@ func (s *SenderInterceptorFactory) NewInterceptor(_ string) (interceptor.Interce
i := &SenderInterceptor{
interval: 1 * time.Second,
now: time.Now,
- log: logging.NewDefaultLoggerFactory().NewLogger("sender_interceptor"),
- close: make(chan struct{}),
+ newTicker: func(d time.Duration) Ticker {
+ return &timeTicker{time.NewTicker(d)}
+ },
+ log: logging.NewDefaultLoggerFactory().NewLogger("sender_interceptor"),
+ close: make(chan struct{}),
}
for _, opt := range s.opts {
@@ -44,13 +50,17 @@ func NewSenderInterceptor(opts ...SenderOption) (*SenderInterceptorFactory, erro
// SenderInterceptor interceptor generates sender reports.
type SenderInterceptor struct {
interceptor.NoOp
- interval time.Duration
- now func() time.Time
- streams sync.Map
- log logging.LeveledLogger
- m sync.Mutex
- wg sync.WaitGroup
- close chan struct{}
+ interval time.Duration
+ now func() time.Time
+ newTicker TickerFactory
+ streams sync.Map
+ log logging.LeveledLogger
+ m sync.Mutex
+ wg sync.WaitGroup
+ close chan struct{}
+ started chan struct{}
+
+ useLatestPacket bool
}
func (s *SenderInterceptor) isClosed() bool {
@@ -95,13 +105,18 @@ func (s *SenderInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) interc
func (s *SenderInterceptor) loop(rtcpWriter interceptor.RTCPWriter) {
defer s.wg.Done()
- ticker := time.NewTicker(s.interval)
+ ticker := s.newTicker(s.interval)
defer ticker.Stop()
+ if s.started != nil {
+ // This lets us synchronize in tests to know whether the loop has begun or not.
+ // It only happens if started was initialized, which should not occur in non-tests.
+ close(s.started)
+ }
for {
select {
- case <-ticker.C:
+ case <-ticker.Ch():
now := s.now()
- s.streams.Range(func(key, value interface{}) bool {
+ s.streams.Range(func(_, value interface{}) bool {
if stream, ok := value.(*senderStream); !ok {
s.log.Warnf("failed to cast SenderInterceptor stream")
} else if _, err := rtcpWriter.Write([]rtcp.Packet{stream.generateReport(now)}, interceptor.Attributes{}); err != nil {
@@ -120,7 +135,7 @@ func (s *SenderInterceptor) loop(rtcpWriter interceptor.RTCPWriter) {
// BindLocalStream lets you modify any outgoing RTP packets. It is called once for per LocalStream. The returned method
// will be called once per rtp packet.
func (s *SenderInterceptor) BindLocalStream(info *interceptor.StreamInfo, writer interceptor.RTPWriter) interceptor.RTPWriter {
- stream := newSenderStream(info.SSRC, info.ClockRate)
+ stream := newSenderStream(info.SSRC, info.ClockRate, s.useLatestPacket)
s.streams.Store(info.SSRC, stream)
return interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, a interceptor.Attributes) (int, error) {
@@ -129,3 +144,8 @@ func (s *SenderInterceptor) BindLocalStream(info *interceptor.StreamInfo, writer
return writer.Write(header, payload, a)
})
}
+
+// UnbindLocalStream is called when the Stream is removed. It can be used to clean up any data related to that track.
+func (s *SenderInterceptor) UnbindLocalStream(info *interceptor.StreamInfo) {
+ s.streams.Delete(info.SSRC)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/sender_option.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/sender_option.go
index 6932e1c0a..1e489b0b3 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/sender_option.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/sender_option.go
@@ -35,3 +35,29 @@ func SenderNow(f func() time.Time) SenderOption {
return nil
}
}
+
+// SenderTicker sets an alternative for the time.NewTicker function.
+func SenderTicker(f TickerFactory) SenderOption {
+ return func(r *SenderInterceptor) error {
+ r.newTicker = f
+ return nil
+ }
+}
+
+// SenderUseLatestPacket sets the interceptor to always use the latest packet, even
+// if it appears to be out-of-order.
+func SenderUseLatestPacket() SenderOption {
+ return func(r *SenderInterceptor) error {
+ r.useLatestPacket = true
+ return nil
+ }
+}
+
+// enableStartTracking is used by tests to synchronize whether the loop() has begun
+// and it's safe to start sending ticks to the ticker.
+func enableStartTracking(startedCh chan struct{}) SenderOption {
+ return func(r *SenderInterceptor) error {
+ r.started = startedCh
+ return nil
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/sender_stream.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/sender_stream.go
index 29d14da41..44658e246 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/sender_stream.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/sender_stream.go
@@ -17,17 +17,21 @@ type senderStream struct {
clockRate float64
m sync.Mutex
+ useLatestPacket bool
+
// data from rtp packets
lastRTPTimeRTP uint32
lastRTPTimeTime time.Time
+ lastRTPSN uint16
packetCount uint32
octetCount uint32
}
-func newSenderStream(ssrc uint32, clockRate uint32) *senderStream {
+func newSenderStream(ssrc uint32, clockRate uint32, useLatestPacket bool) *senderStream {
return &senderStream{
- ssrc: ssrc,
- clockRate: float64(clockRate),
+ ssrc: ssrc,
+ clockRate: float64(clockRate),
+ useLatestPacket: useLatestPacket,
}
}
@@ -35,9 +39,13 @@ func (stream *senderStream) processRTP(now time.Time, header *rtp.Header, payloa
stream.m.Lock()
defer stream.m.Unlock()
- // always update time to minimize errors
- stream.lastRTPTimeRTP = header.Timestamp
- stream.lastRTPTimeTime = now
+ diff := header.SequenceNumber - stream.lastRTPSN
+ if stream.useLatestPacket || stream.packetCount == 0 || (diff > 0 && diff < (1<<15)) {
+ // Told to consider every packet, or this was the first packet, or it's in-order
+ stream.lastRTPSN = header.SequenceNumber
+ stream.lastRTPTimeRTP = header.Timestamp
+ stream.lastRTPTimeTime = now
+ }
stream.packetCount++
stream.octetCount += uint32(len(payload))
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/ticker.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/ticker.go
new file mode 100644
index 000000000..59a8c6c50
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/ticker.go
@@ -0,0 +1,20 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package report
+
+import "time"
+
+// Ticker is an interface for *time.Ticker for use with the SenderTicker option.
+type Ticker interface {
+ Ch() <-chan time.Time
+ Stop()
+}
+
+type timeTicker struct {
+ *time.Ticker
+}
+
+func (t *timeTicker) Ch() <-chan time.Time {
+ return t.C
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/rfc8888/interceptor.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/rfc8888/interceptor.go
new file mode 100644
index 000000000..efe2df29c
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/rfc8888/interceptor.go
@@ -0,0 +1,182 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package rfc8888 provides an interceptor that generates congestion control
+// feedback reports as defined by RFC 8888.
+package rfc8888
+
+import (
+ "sync"
+ "time"
+
+ "github.com/pion/interceptor"
+ "github.com/pion/logging"
+ "github.com/pion/rtcp"
+)
+
+// TickerFactory is a factory to create new tickers
+type TickerFactory func(d time.Duration) ticker
+
+// SenderInterceptorFactory is a interceptor.Factory for a SenderInterceptor
+type SenderInterceptorFactory struct {
+ opts []Option
+}
+
+// NewInterceptor constructs a new SenderInterceptor
+func (s *SenderInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) {
+ i := &SenderInterceptor{
+ NoOp: interceptor.NoOp{},
+ log: logging.NewDefaultLoggerFactory().NewLogger("rfc8888_interceptor"),
+ lock: sync.Mutex{},
+ wg: sync.WaitGroup{},
+ recorder: NewRecorder(),
+ interval: 100 * time.Millisecond,
+ maxReportSize: 1200,
+ packetChan: make(chan packet),
+ newTicker: func(d time.Duration) ticker {
+ return &timeTicker{time.NewTicker(d)}
+ },
+ now: time.Now,
+ close: make(chan struct{}),
+ }
+ for _, opt := range s.opts {
+ err := opt(i)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return i, nil
+}
+
+// NewSenderInterceptor returns a new SenderInterceptorFactory configured with the given options.
+func NewSenderInterceptor(opts ...Option) (*SenderInterceptorFactory, error) {
+ return &SenderInterceptorFactory{opts: opts}, nil
+}
+
+// SenderInterceptor sends congestion control feedback as specified in RFC 8888.
+type SenderInterceptor struct {
+ interceptor.NoOp
+ log logging.LeveledLogger
+ lock sync.Mutex
+ wg sync.WaitGroup
+ recorder *Recorder
+ interval time.Duration
+ maxReportSize int64
+ packetChan chan packet
+ newTicker TickerFactory
+ now func() time.Time
+ close chan struct{}
+}
+
+type packet struct {
+ arrival time.Time
+ ssrc uint32
+ sequenceNumber uint16
+ ecn uint8
+}
+
+// BindRTCPWriter lets you modify any outgoing RTCP packets. It is called once per PeerConnection. The returned method
+// will be called once per packet batch.
+func (s *SenderInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) interceptor.RTCPWriter {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ if s.isClosed() {
+ return writer
+ }
+
+ s.wg.Add(1)
+ go s.loop(writer)
+
+ return writer
+}
+
+// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method
+// will be called once per rtp packet.
+func (s *SenderInterceptor) BindRemoteStream(_ *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
+ return interceptor.RTPReaderFunc(func(b []byte, a interceptor.Attributes) (int, interceptor.Attributes, error) {
+ i, attr, err := reader.Read(b, a)
+ if err != nil {
+ return 0, nil, err
+ }
+
+ if attr == nil {
+ attr = make(interceptor.Attributes)
+ }
+ header, err := attr.GetRTPHeader(b[:i])
+ if err != nil {
+ return 0, nil, err
+ }
+
+ p := packet{
+ arrival: s.now(),
+ ssrc: header.SSRC,
+ sequenceNumber: header.SequenceNumber,
+ ecn: 0, // ECN is not supported (yet).
+ }
+ s.packetChan <- p
+ return i, attr, nil
+ })
+}
+
+// Close closes the interceptor.
+func (s *SenderInterceptor) Close() error {
+ s.log.Trace("close")
+ defer s.wg.Wait()
+
+ if !s.isClosed() {
+ close(s.close)
+ }
+
+ return nil
+}
+
+func (s *SenderInterceptor) isClosed() bool {
+ select {
+ case <-s.close:
+ return true
+ default:
+ return false
+ }
+}
+
+func (s *SenderInterceptor) loop(writer interceptor.RTCPWriter) {
+ defer s.wg.Done()
+
+ select {
+ case <-s.close:
+ return
+ case pkt := <-s.packetChan:
+ s.log.Tracef("got first packet: %v", pkt)
+ s.recorder.AddPacket(pkt.arrival, pkt.ssrc, pkt.sequenceNumber, pkt.ecn)
+ }
+
+ s.log.Trace("start loop")
+ t := s.newTicker(s.interval)
+ for {
+ select {
+ case <-s.close:
+ t.Stop()
+ return
+
+ case pkt := <-s.packetChan:
+ s.log.Tracef("got packet: %v", pkt)
+ s.recorder.AddPacket(pkt.arrival, pkt.ssrc, pkt.sequenceNumber, pkt.ecn)
+
+ case now := <-t.Ch():
+ s.log.Tracef("report triggered at %v", now)
+ if writer == nil {
+ s.log.Trace("no writer added, continue")
+ continue
+ }
+ pkts := s.recorder.BuildReport(now, int(s.maxReportSize))
+ if pkts == nil {
+ continue
+ }
+ s.log.Tracef("got report: %v", pkts)
+ if _, err := writer.Write([]rtcp.Packet{pkts}, nil); err != nil {
+ s.log.Error(err.Error())
+ }
+ }
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/rfc8888/option.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/rfc8888/option.go
new file mode 100644
index 000000000..1214868bf
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/rfc8888/option.go
@@ -0,0 +1,33 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package rfc8888
+
+import "time"
+
+// An Option is a function that can be used to configure a SenderInterceptor
+type Option func(*SenderInterceptor) error
+
+// SenderTicker sets an alternative for time.Ticker.
+func SenderTicker(f TickerFactory) Option {
+ return func(i *SenderInterceptor) error {
+ i.newTicker = f
+ return nil
+ }
+}
+
+// SenderNow sets an alternative for the time.Now function.
+func SenderNow(f func() time.Time) Option {
+ return func(i *SenderInterceptor) error {
+ i.now = f
+ return nil
+ }
+}
+
+// SendInterval sets the feedback send interval for the interceptor
+func SendInterval(interval time.Duration) Option {
+ return func(s *SenderInterceptor) error {
+ s.interval = interval
+ return nil
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/rfc8888/recorder.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/rfc8888/recorder.go
new file mode 100644
index 000000000..4423df1e3
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/rfc8888/recorder.go
@@ -0,0 +1,78 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package rfc8888
+
+import (
+ "time"
+
+ "github.com/pion/rtcp"
+)
+
+type packetReport struct {
+ arrivalTime time.Time
+ ecn uint8
+}
+
+// Recorder records incoming RTP packets and their arrival times. Recorder can
+// be used to create feedback reports as defined by RFC 8888.
+type Recorder struct {
+ ssrc uint32
+ streams map[uint32]*streamLog
+}
+
+// NewRecorder creates a new Recorder
+func NewRecorder() *Recorder {
+ return &Recorder{
+ streams: map[uint32]*streamLog{},
+ }
+}
+
+// AddPacket writes a packet to the underlying stream.
+func (r *Recorder) AddPacket(ts time.Time, ssrc uint32, seq uint16, ecn uint8) {
+ stream, ok := r.streams[ssrc]
+ if !ok {
+ stream = newStreamLog(ssrc)
+ r.streams[ssrc] = stream
+ }
+ stream.add(ts, seq, ecn)
+}
+
+// BuildReport creates a new rtcp.CCFeedbackReport containing all packets that
+// were added by AddPacket and missing packets.
+func (r *Recorder) BuildReport(now time.Time, maxSize int) *rtcp.CCFeedbackReport {
+ report := &rtcp.CCFeedbackReport{
+ SenderSSRC: r.ssrc,
+ ReportBlocks: []rtcp.CCFeedbackReportBlock{},
+ ReportTimestamp: ntpTime32(now),
+ }
+
+ maxReportBlocks := (maxSize - 12 - (8 * len(r.streams))) / 2
+ var maxReportBlocksPerStream int
+ if len(r.streams) > 1 {
+ maxReportBlocksPerStream = maxReportBlocks / (len(r.streams) - 1)
+ } else {
+ maxReportBlocksPerStream = maxReportBlocks
+ }
+
+ for i, log := range r.streams {
+ if len(r.streams) > 1 && int(i) == len(r.streams)-1 {
+ maxReportBlocksPerStream = maxReportBlocks % len(r.streams)
+ }
+ block := log.metricsAfter(now, int64(maxReportBlocksPerStream))
+ report.ReportBlocks = append(report.ReportBlocks, block)
+ }
+
+ return report
+}
+
+func ntpTime32(t time.Time) uint32 {
+ // seconds since 1st January 1900
+ s := (float64(t.UnixNano()) / 1000000000.0) + 2208988800
+
+ integerPart := uint32(s)
+ fractionalPart := uint32((s - float64(integerPart)) * 0xFFFFFFFF)
+
+ // higher 32 bits are the integer part, lower 32 bits are the fractional part
+ return uint32(((uint64(integerPart)<<32 | uint64(fractionalPart)) >> 16) & 0xFFFFFFFF)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/rfc8888/stream_log.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/rfc8888/stream_log.go
new file mode 100644
index 000000000..8dfb14fc9
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/rfc8888/stream_log.go
@@ -0,0 +1,110 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package rfc8888
+
+import (
+ "time"
+
+ "github.com/pion/rtcp"
+)
+
+const maxReportsPerReportBlock = 16384
+
+type streamLog struct {
+ ssrc uint32
+ sequence unwrapper
+ init bool
+ nextSequenceNumberToReport int64 // next to report
+ lastSequenceNumberReceived int64 // highest received
+ log map[int64]*packetReport
+}
+
+func newStreamLog(ssrc uint32) *streamLog {
+ return &streamLog{
+ ssrc: ssrc,
+ sequence: unwrapper{},
+ init: false,
+ nextSequenceNumberToReport: 0,
+ lastSequenceNumberReceived: 0,
+ log: map[int64]*packetReport{},
+ }
+}
+
+func (l *streamLog) add(ts time.Time, sequenceNumber uint16, ecn uint8) {
+ unwrappedSequenceNumber := l.sequence.unwrap(sequenceNumber)
+ if !l.init {
+ l.init = true
+ l.nextSequenceNumberToReport = unwrappedSequenceNumber
+ }
+ l.log[unwrappedSequenceNumber] = &packetReport{
+ arrivalTime: ts,
+ ecn: ecn,
+ }
+ if l.lastSequenceNumberReceived < unwrappedSequenceNumber {
+ l.lastSequenceNumberReceived = unwrappedSequenceNumber
+ }
+}
+
+// metricsAfter iterates over all packets order of their sequence number.
+// Packets are removed until the first loss is detected.
+func (l *streamLog) metricsAfter(reference time.Time, maxReportBlocks int64) rtcp.CCFeedbackReportBlock {
+ if len(l.log) == 0 {
+ return rtcp.CCFeedbackReportBlock{
+ MediaSSRC: l.ssrc,
+ BeginSequence: uint16(l.nextSequenceNumberToReport),
+ MetricBlocks: []rtcp.CCFeedbackMetricBlock{},
+ }
+ }
+ numReports := l.lastSequenceNumberReceived - l.nextSequenceNumberToReport + 1
+ if numReports > maxReportBlocks {
+ numReports = maxReportBlocks
+ l.nextSequenceNumberToReport = l.lastSequenceNumberReceived - maxReportBlocks + 1
+ }
+ metricBlocks := make([]rtcp.CCFeedbackMetricBlock, numReports)
+ offset := l.nextSequenceNumberToReport
+ lastReceived := l.nextSequenceNumberToReport
+ gapDetected := false
+ for i := offset; i <= l.lastSequenceNumberReceived; i++ {
+ received := false
+ ecn := uint8(0)
+ ato := uint16(0)
+ if report, ok := l.log[i]; ok {
+ received = true
+ ecn = report.ecn
+ ato = getArrivalTimeOffset(reference, report.arrivalTime)
+ }
+ metricBlocks[i-offset] = rtcp.CCFeedbackMetricBlock{
+ Received: received,
+ ECN: rtcp.ECN(ecn),
+ ArrivalTimeOffset: ato,
+ }
+
+ if !gapDetected {
+ if received && i == l.nextSequenceNumberToReport {
+ delete(l.log, i)
+ l.nextSequenceNumberToReport++
+ lastReceived = i
+ }
+ if i > lastReceived+1 {
+ gapDetected = true
+ }
+ }
+ }
+ return rtcp.CCFeedbackReportBlock{
+ MediaSSRC: l.ssrc,
+ BeginSequence: uint16(offset),
+ MetricBlocks: metricBlocks,
+ }
+}
+
+func getArrivalTimeOffset(base time.Time, arrival time.Time) uint16 {
+ if base.Before(arrival) {
+ return 0x1FFF
+ }
+ ato := uint16(base.Sub(arrival).Seconds() * 1024.0)
+ if ato > 0x1FFD {
+ return 0x1FFE
+ }
+ return ato
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/rfc8888/ticker.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/rfc8888/ticker.go
new file mode 100644
index 000000000..571b28e0b
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/rfc8888/ticker.go
@@ -0,0 +1,19 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package rfc8888
+
+import "time"
+
+type ticker interface {
+ Ch() <-chan time.Time
+ Stop()
+}
+
+type timeTicker struct {
+ *time.Ticker
+}
+
+func (t *timeTicker) Ch() <-chan time.Time {
+ return t.C
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/rfc8888/unwrapper.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/rfc8888/unwrapper.go
new file mode 100644
index 000000000..f15f33e6e
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/rfc8888/unwrapper.go
@@ -0,0 +1,42 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package rfc8888
+
+const (
+ maxSequenceNumberPlusOne = int64(65536)
+ breakpoint = 32768 // half of max uint16
+)
+
+type unwrapper struct {
+ init bool
+ lastUnwrapped int64
+}
+
+func isNewer(value, previous uint16) bool {
+ if value-previous == breakpoint {
+ return value > previous
+ }
+ return value != previous && (value-previous) < breakpoint
+}
+
+func (u *unwrapper) unwrap(i uint16) int64 {
+ if !u.init {
+ u.init = true
+ u.lastUnwrapped = int64(i)
+ return u.lastUnwrapped
+ }
+
+ lastWrapped := uint16(u.lastUnwrapped)
+ delta := int64(i - lastWrapped)
+ if isNewer(i, lastWrapped) {
+ if delta < 0 {
+ delta += maxSequenceNumberPlusOne
+ }
+ } else if delta > 0 && u.lastUnwrapped+delta-maxSequenceNumberPlusOne >= 0 {
+ delta -= maxSequenceNumberPlusOne
+ }
+
+ u.lastUnwrapped += delta
+ return u.lastUnwrapped
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/twcc/arrival_time_map.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/twcc/arrival_time_map.go
new file mode 100644
index 000000000..d3e2a8af3
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/twcc/arrival_time_map.go
@@ -0,0 +1,192 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package twcc
+
+const (
+ minCapacity = 128
+ maxNumberOfPackets = 1 << 15
+)
+
+// packetArrivalTimeMap is adapted from Chrome's implementation of TWCC, and keeps track
+// of the arrival times of packets. It is used by the TWCC interceptor to build feedback
+// packets.
+// See https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:third_party/webrtc/modules/remote_bitrate_estimator/packet_arrival_map.h;drc=b5cd13bb6d5d157a5fbe3628b2dd1c1e106203c6
+type packetArrivalTimeMap struct {
+ // arrivalTimes is a circular buffer, where the packet with sequence number sn is stored
+ // in slot sn % len(arrivalTimes)
+ arrivalTimes []int64
+
+ // The unwrapped sequence numbers for the range of valid sequence numbers in arrivalTimes.
+ // beginSequenceNumber is inclusive, and endSequenceNumber is exclusive.
+ beginSequenceNumber, endSequenceNumber int64
+}
+
+// AddPacket records the fact that the packet with sequence number sequenceNumber arrived
+// at arrivalTime.
+func (m *packetArrivalTimeMap) AddPacket(sequenceNumber int64, arrivalTime int64) {
+ if m.arrivalTimes == nil {
+ // First packet
+ m.reallocate(minCapacity)
+ m.beginSequenceNumber = sequenceNumber
+ m.endSequenceNumber = sequenceNumber + 1
+ m.arrivalTimes[m.index(sequenceNumber)] = arrivalTime
+ return
+ }
+
+ if sequenceNumber >= m.beginSequenceNumber && sequenceNumber < m.endSequenceNumber {
+ // The packet is within the buffer, no need to resize.
+ m.arrivalTimes[m.index(sequenceNumber)] = arrivalTime
+ return
+ }
+
+ if sequenceNumber < m.beginSequenceNumber {
+ // The packet goes before the current buffer. Expand to add packet,
+ // but only if it fits within the maximum number of packets.
+ newSize := int(m.endSequenceNumber - sequenceNumber)
+ if newSize > maxNumberOfPackets {
+ // Don't expand the buffer back for this packet, as it would remove newer received
+ // packets.
+ return
+ }
+ m.adjustToSize(newSize)
+ m.arrivalTimes[m.index(sequenceNumber)] = arrivalTime
+ m.setNotReceived(sequenceNumber+1, m.beginSequenceNumber)
+ m.beginSequenceNumber = sequenceNumber
+ return
+ }
+
+ // The packet goes after the buffer.
+ newEndSequenceNumber := sequenceNumber + 1
+
+ if newEndSequenceNumber >= m.endSequenceNumber+maxNumberOfPackets {
+ // All old packets have to be removed.
+ m.beginSequenceNumber = sequenceNumber
+ m.endSequenceNumber = newEndSequenceNumber
+ m.arrivalTimes[m.index(sequenceNumber)] = arrivalTime
+ return
+ }
+
+ if m.beginSequenceNumber < newEndSequenceNumber-maxNumberOfPackets {
+ // Remove oldest entries.
+ m.beginSequenceNumber = newEndSequenceNumber - maxNumberOfPackets
+ }
+
+ m.adjustToSize(int(newEndSequenceNumber - m.beginSequenceNumber))
+
+ // Packets can be received out of order. If this isn't the next expected packet,
+ // add enough placeholders to fill the gap.
+ m.setNotReceived(m.endSequenceNumber, sequenceNumber)
+ m.endSequenceNumber = newEndSequenceNumber
+ m.arrivalTimes[m.index(sequenceNumber)] = arrivalTime
+}
+
+func (m *packetArrivalTimeMap) setNotReceived(startInclusive, endExclusive int64) {
+ for sn := startInclusive; sn < endExclusive; sn++ {
+ m.arrivalTimes[m.index(sn)] = -1
+ }
+}
+
+// BeginSequenceNumber returns the first valid sequence number in the map.
+func (m *packetArrivalTimeMap) BeginSequenceNumber() int64 {
+ return m.beginSequenceNumber
+}
+
+// EndSequenceNumber returns the first sequence number after the last valid sequence number in the map.
+func (m *packetArrivalTimeMap) EndSequenceNumber() int64 {
+ return m.endSequenceNumber
+}
+
+// FindNextAtOrAfter returns the sequence number and timestamp of the first received packet that has a sequence number
+// greator or equal to sequenceNumber.
+func (m *packetArrivalTimeMap) FindNextAtOrAfter(sequenceNumber int64) (foundSequenceNumber int64, arrivalTime int64, ok bool) {
+ for sequenceNumber = m.Clamp(sequenceNumber); sequenceNumber < m.endSequenceNumber; sequenceNumber++ {
+ if t := m.get(sequenceNumber); t >= 0 {
+ return sequenceNumber, t, true
+ }
+ }
+ return -1, -1, false
+}
+
+// EraseTo erases all elements from the beginning of the map until sequenceNumber.
+func (m *packetArrivalTimeMap) EraseTo(sequenceNumber int64) {
+ if sequenceNumber < m.beginSequenceNumber {
+ return
+ }
+ if sequenceNumber >= m.endSequenceNumber {
+ // Erase all.
+ m.beginSequenceNumber = m.endSequenceNumber
+ return
+ }
+ // Remove some
+ m.beginSequenceNumber = sequenceNumber
+ m.adjustToSize(int(m.endSequenceNumber - m.beginSequenceNumber))
+}
+
+// RemoveOldPackets removes packets from the beginning of the map as long as they are before
+// sequenceNumber and with an age older than arrivalTimeLimit.
+func (m *packetArrivalTimeMap) RemoveOldPackets(sequenceNumber int64, arrivalTimeLimit int64) {
+ checkTo := min64(sequenceNumber, m.endSequenceNumber)
+ for m.beginSequenceNumber < checkTo && m.get(m.beginSequenceNumber) <= arrivalTimeLimit {
+ m.beginSequenceNumber++
+ }
+ m.adjustToSize(int(m.endSequenceNumber - m.beginSequenceNumber))
+}
+
+// HasReceived returns whether a packet with the sequence number has been received.
+func (m *packetArrivalTimeMap) HasReceived(sequenceNumber int64) bool {
+ return m.get(sequenceNumber) >= 0
+}
+
+// Clamp returns sequenceNumber clamped to [beginSequenceNumber, endSequenceNumber]
+func (m *packetArrivalTimeMap) Clamp(sequenceNumber int64) int64 {
+ if sequenceNumber < m.beginSequenceNumber {
+ return m.beginSequenceNumber
+ }
+ if m.endSequenceNumber < sequenceNumber {
+ return m.endSequenceNumber
+ }
+ return sequenceNumber
+}
+
+func (m *packetArrivalTimeMap) get(sequenceNumber int64) int64 {
+ if sequenceNumber < m.beginSequenceNumber || sequenceNumber >= m.endSequenceNumber {
+ return -1
+ }
+ return m.arrivalTimes[m.index(sequenceNumber)]
+}
+
+func (m *packetArrivalTimeMap) index(sequenceNumber int64) int {
+ // Sequence number might be negative, and we always guarantee that arrivalTimes
+ // length is a power of 2, so it's easier to use "&" instead of "%"
+ return int(sequenceNumber & int64(m.capacity()-1))
+}
+
+func (m *packetArrivalTimeMap) adjustToSize(newSize int) {
+ if newSize > m.capacity() {
+ newCapacity := m.capacity()
+ for newCapacity < newSize {
+ newCapacity *= 2
+ }
+ m.reallocate(newCapacity)
+ }
+ if m.capacity() > maxInt(minCapacity, newSize*4) {
+ newCapacity := m.capacity()
+ for newCapacity >= 2*maxInt(newSize, minCapacity) {
+ newCapacity /= 2
+ }
+ m.reallocate(newCapacity)
+ }
+}
+
+func (m *packetArrivalTimeMap) capacity() int {
+ return len(m.arrivalTimes)
+}
+
+func (m *packetArrivalTimeMap) reallocate(newCapacity int) {
+ newBuffer := make([]int64, newCapacity)
+ for sn := m.beginSequenceNumber; sn < m.endSequenceNumber; sn++ {
+ newBuffer[int(sn&(int64(newCapacity-1)))] = m.get(sn)
+ }
+ m.arrivalTimes = newBuffer
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/twcc/sender_interceptor.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/twcc/sender_interceptor.go
index 8706e4518..d7906fc68 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/twcc/sender_interceptor.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/twcc/sender_interceptor.go
@@ -196,7 +196,7 @@ func (s *SenderInterceptor) loop(w interceptor.RTCPWriter) {
case <-ticker.C:
// build and send twcc
pkts := s.recorder.BuildFeedbackPacket()
- if pkts == nil {
+ if len(pkts) == 0 {
continue
}
if _, err := w.Write(pkts, nil); err != nil {
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/twcc/twcc.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/twcc/twcc.go
index 235f1f11c..6464c77bc 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/twcc/twcc.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/twcc/twcc.go
@@ -7,94 +7,157 @@ package twcc
import (
"math"
+ "github.com/pion/interceptor/internal/sequencenumber"
"github.com/pion/rtcp"
)
-type pktInfo struct {
- sequenceNumber uint32
- arrivalTime int64
-}
+const (
+ packetWindowMicroseconds = 500_000
+ maxMissingSequenceNumbers = 0x7FFE
+)
// Recorder records incoming RTP packets and their delays and creates
// transport wide congestion control feedback reports as specified in
// https://datatracker.ietf.org/doc/html/draft-holmer-rmcat-transport-wide-cc-extensions-01
type Recorder struct {
- receivedPackets []pktInfo
+ arrivalTimeMap packetArrivalTimeMap
- cycles uint32
- lastSequenceNumber uint16
+ sequenceUnwrapper sequencenumber.Unwrapper
+
+ // startSequenceNumber is the first sequence number that will be included in the the
+ // next feedback packet.
+ startSequenceNumber *int64
senderSSRC uint32
mediaSSRC uint32
fbPktCnt uint8
+
+ packetsHeld int
}
// NewRecorder creates a new Recorder which uses the given senderSSRC in the created
// feedback packets.
func NewRecorder(senderSSRC uint32) *Recorder {
return &Recorder{
- receivedPackets: []pktInfo{},
- senderSSRC: senderSSRC,
+ senderSSRC: senderSSRC,
}
}
// Record marks a packet with mediaSSRC and a transport wide sequence number sequenceNumber as received at arrivalTime.
func (r *Recorder) Record(mediaSSRC uint32, sequenceNumber uint16, arrivalTime int64) {
r.mediaSSRC = mediaSSRC
- if sequenceNumber < 0x0fff && (r.lastSequenceNumber&0xffff) > 0xf000 {
- r.cycles += 1 << 16
+
+ // "Unwrap" the sequence number to get a monotonically increasing sequence number that
+ // won't wrap around after math.MaxUint16.
+ unwrappedSN := r.sequenceUnwrapper.Unwrap(sequenceNumber)
+ r.maybeCullOldPackets(unwrappedSN, arrivalTime)
+ if r.startSequenceNumber == nil || unwrappedSN < *r.startSequenceNumber {
+ r.startSequenceNumber = &unwrappedSN
+ }
+
+ // We are only interested in the first time a packet is received.
+ if r.arrivalTimeMap.HasReceived(unwrappedSN) {
+ return
+ }
+
+ r.arrivalTimeMap.AddPacket(unwrappedSN, arrivalTime)
+ r.packetsHeld++
+
+ // Limit the range of sequence numbers to send feedback for.
+ if *r.startSequenceNumber < r.arrivalTimeMap.BeginSequenceNumber() {
+ sn := r.arrivalTimeMap.BeginSequenceNumber()
+ r.startSequenceNumber = &sn
}
- r.receivedPackets = insertSorted(r.receivedPackets, pktInfo{
- sequenceNumber: r.cycles | uint32(sequenceNumber),
- arrivalTime: arrivalTime,
- })
- r.lastSequenceNumber = sequenceNumber
}
-func insertSorted(list []pktInfo, element pktInfo) []pktInfo {
- if len(list) == 0 {
- return append(list, element)
+func (r *Recorder) maybeCullOldPackets(sequenceNumber int64, arrivalTime int64) {
+ if r.startSequenceNumber != nil && *r.startSequenceNumber >= r.arrivalTimeMap.EndSequenceNumber() && arrivalTime >= packetWindowMicroseconds {
+ r.arrivalTimeMap.RemoveOldPackets(sequenceNumber, arrivalTime-packetWindowMicroseconds)
}
- for i := len(list) - 1; i >= 0; i-- {
- if list[i].sequenceNumber < element.sequenceNumber {
- list = append(list, pktInfo{})
- copy(list[i+2:], list[i+1:])
- list[i+1] = element
- return list
- }
- if list[i].sequenceNumber == element.sequenceNumber {
- list[i] = element
- return list
- }
- }
- // element.sequenceNumber is between 0 and first ever received sequenceNumber
- return append([]pktInfo{element}, list...)
+}
+
+// PacketsHeld returns the number of received packets currently held by the recorder
+func (r *Recorder) PacketsHeld() int {
+ return r.packetsHeld
}
// BuildFeedbackPacket creates a new RTCP packet containing a TWCC feedback report.
func (r *Recorder) BuildFeedbackPacket() []rtcp.Packet {
- if len(r.receivedPackets) < 2 {
+ if r.startSequenceNumber == nil {
return nil
}
- feedback := newFeedback(r.senderSSRC, r.mediaSSRC, r.fbPktCnt)
- r.fbPktCnt++
- feedback.setBase(uint16(r.receivedPackets[0].sequenceNumber&0xffff), r.receivedPackets[0].arrivalTime)
-
- var pkts []rtcp.Packet
- for _, pkt := range r.receivedPackets {
- ok := feedback.addReceived(uint16(pkt.sequenceNumber&0xffff), pkt.arrivalTime)
- if !ok {
- pkts = append(pkts, feedback.getRTCP())
- feedback = newFeedback(r.senderSSRC, r.mediaSSRC, r.fbPktCnt)
- r.fbPktCnt++
- feedback.addReceived(uint16(pkt.sequenceNumber&0xffff), pkt.arrivalTime)
+ endSN := r.arrivalTimeMap.EndSequenceNumber()
+ var feedbacks []rtcp.Packet
+ for *r.startSequenceNumber < endSN {
+ feedback := r.maybeBuildFeedbackPacket(*r.startSequenceNumber, endSN)
+ if feedback == nil {
+ break
}
- }
- r.receivedPackets = []pktInfo{}
- pkts = append(pkts, feedback.getRTCP())
+ feedbacks = append(feedbacks, feedback.getRTCP())
- return pkts
+ // NOTE: we don't erase packets from the history in case they need to be resent
+ // after a reordering. They will be removed instead in Record when they get too
+ // old.
+ }
+ r.packetsHeld = 0
+ return feedbacks
+}
+
+// maybeBuildFeedbackPacket builds a feedback packet starting from startSN (inclusive) until
+// endSN (exclusive).
+func (r *Recorder) maybeBuildFeedbackPacket(beginSeqNumInclusive, endSeqNumExclusive int64) *feedback {
+ // NOTE: The logic of this method is inspired by the implementation in Chrome.
+ // See https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:third_party/webrtc/modules/remote_bitrate_estimator/remote_estimator_proxy.cc;l=276;drc=b5cd13bb6d5d157a5fbe3628b2dd1c1e106203c6
+ startSNInclusive, endSNExclusive := r.arrivalTimeMap.Clamp(beginSeqNumInclusive), r.arrivalTimeMap.Clamp(endSeqNumExclusive)
+
+ // Create feedback on demand, as we don't yet know if there are packets in the range that have been
+ // received.
+ var fb *feedback
+
+ nextSequenceNumber := beginSeqNumInclusive
+
+ for seq := startSNInclusive; seq < endSNExclusive; seq++ {
+ foundSeq, arrivalTime, ok := r.arrivalTimeMap.FindNextAtOrAfter(seq)
+ seq = foundSeq
+ if !ok || seq >= endSNExclusive {
+ break
+ }
+
+ if fb == nil {
+ fb = newFeedback(r.senderSSRC, r.mediaSSRC, r.fbPktCnt)
+ r.fbPktCnt++
+
+ // It should be possible to add seq to this new packet.
+ // If the difference between seq and beginSeqNumInclusive is too large, discard
+ // reporting too old missing packets.
+ baseSequenceNumber := max64(beginSeqNumInclusive, seq-maxMissingSequenceNumbers)
+
+ // baseSequenceNumber is the expected first sequence number. This is known,
+ // but we may not have actually received it, so the base time should be the time
+ // of the first received packet in the feedback.
+ fb.setBase(uint16(baseSequenceNumber), arrivalTime)
+
+ if !fb.addReceived(uint16(seq), arrivalTime) {
+ // Could not add a single received packet to the feedback.
+ // This is unexpected to actually occur, but if it does, we'll
+ // try again after skipping any missing packets.
+ // NOTE: It's fine that we already incremented fbPktCnt, as in essence
+ // we did actually "skip" a feedback (and this matches Chrome's behavior).
+ r.startSequenceNumber = &seq
+ return nil
+ }
+ } else if !fb.addReceived(uint16(seq), arrivalTime) {
+ // Could not add timestamp. Packet may be full. Return
+ // and try again with a fresh packet.
+ break
+ }
+
+ nextSequenceNumber = seq + 1
+ }
+
+ r.startSequenceNumber = &nextSequenceNumber
+ return fb
}
type feedback struct {
@@ -154,10 +217,16 @@ func (f *feedback) getRTCP() *rtcp.TransportLayerCC {
func (f *feedback) addReceived(sequenceNumber uint16, timestampUS int64) bool {
deltaUS := timestampUS - f.lastTimestampUS
- delta250US := deltaUS / 250
+ var delta250US int64
+ if deltaUS >= 0 {
+ delta250US = (deltaUS + rtcp.TypeTCCDeltaScaleFactor/2) / rtcp.TypeTCCDeltaScaleFactor
+ } else {
+ delta250US = (deltaUS - rtcp.TypeTCCDeltaScaleFactor/2) / rtcp.TypeTCCDeltaScaleFactor
+ }
if delta250US < math.MinInt16 || delta250US > math.MaxInt16 { // delta doesn't fit into 16 bit, need to create new packet
return false
}
+ deltaUSRounded := delta250US * rtcp.TypeTCCDeltaScaleFactor
for ; f.nextSequenceNumber != sequenceNumber; f.nextSequenceNumber++ {
if !f.lastChunk.canAdd(rtcp.TypeTCCPacketNotReceived) {
@@ -183,9 +252,9 @@ func (f *feedback) addReceived(sequenceNumber uint16, timestampUS int64) bool {
f.lastChunk.add(recvDelta)
f.deltas = append(f.deltas, &rtcp.RecvDelta{
Type: recvDelta,
- Delta: deltaUS,
+ Delta: deltaUSRounded,
})
- f.lastTimestampUS = timestampUS
+ f.lastTimestampUS += deltaUSRounded
f.sequenceNumberCount++
f.nextSequenceNumber++
return true
@@ -238,7 +307,7 @@ func (c *chunk) encode() rtcp.PacketStatusChunk {
}
}
- minCap := min(maxTwoBitCap, len(c.deltas))
+ minCap := minInt(maxTwoBitCap, len(c.deltas))
svc := &rtcp.StatusVectorChunk{
SymbolSize: rtcp.TypeTCCSymbolSizeTwoBit,
SymbolList: c.deltas[:minCap],
@@ -268,7 +337,28 @@ func (c *chunk) reset() {
c.hasDifferentTypes = false
}
-func min(a, b int) int {
+func maxInt(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+}
+
+func minInt(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+func max64(a, b int64) int64 {
+ if a > b {
+ return a
+ }
+ return b
+}
+
+func min64(a, b int64) int64 {
if a < b {
return a
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/streaminfo.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/streaminfo.go
index 4108159c5..cb261304f 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/streaminfo.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/streaminfo.go
@@ -11,16 +11,20 @@ type RTPHeaderExtension struct {
// StreamInfo is the Context passed when a StreamLocal or StreamRemote has been Binded or Unbinded
type StreamInfo struct {
- ID string
- Attributes Attributes
- SSRC uint32
- PayloadType uint8
- RTPHeaderExtensions []RTPHeaderExtension
- MimeType string
- ClockRate uint32
- Channels uint16
- SDPFmtpLine string
- RTCPFeedback []RTCPFeedback
+ ID string
+ Attributes Attributes
+ SSRC uint32
+ SSRCRetransmission uint32
+ SSRCForwardErrorCorrection uint32
+ PayloadType uint8
+ PayloadTypeRetransmission uint8
+ PayloadTypeForwardErrorCorrection uint8
+ RTPHeaderExtensions []RTPHeaderExtension
+ MimeType string
+ ClockRate uint32
+ Channels uint16
+ SDPFmtpLine string
+ RTCPFeedback []RTCPFeedback
}
// RTCPFeedback signals the connection to use additional RTCP packet types.
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v2/.gitignore b/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/.gitignore
similarity index 100%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v2/.gitignore
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/.gitignore
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/.golangci.yml b/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/.golangci.yml
index ffb0058e6..50211be0c 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/.golangci.yml
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/.golangci.yml
@@ -1,13 +1,138 @@
+# SPDX-FileCopyrightText: 2023 The Pion community
+# SPDX-License-Identifier: MIT
+
+run:
+ timeout: 5m
+
linters-settings:
govet:
- check-shadowing: true
+ enable:
+ - shadow
misspell:
locale: US
+ exhaustive:
+ default-signifies-exhaustive: true
+ gomodguard:
+ blocked:
+ modules:
+ - github.com/pkg/errors:
+ recommendations:
+ - errors
+ forbidigo:
+ forbid:
+ - ^fmt.Print(f|ln)?$
+ - ^log.(Panic|Fatal|Print)(f|ln)?$
+ - ^os.Exit$
+ - ^panic$
+ - ^print(ln)?$
+ varnamelen:
+ max-distance: 12
+ min-name-length: 2
+ ignore-type-assert-ok: true
+ ignore-map-index-ok: true
+ ignore-chan-recv-ok: true
+ ignore-decls:
+ - i int
+ - n int
+ - w io.Writer
+ - r io.Reader
+ - b []byte
linters:
- enable-all: true
+ enable:
+ - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
+ - bidichk # Checks for dangerous unicode character sequences
+ - bodyclose # checks whether HTTP response body is closed successfully
+ - containedctx # containedctx is a linter that detects struct contained context.Context field
+ - contextcheck # check the function whether use a non-inherited context
+ - cyclop # checks function and package cyclomatic complexity
+ - decorder # check declaration order and count of types, constants, variables and functions
+ - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
+ - dupl # Tool for code clone detection
+ - durationcheck # check for two durations multiplied together
+ - err113 # Golang linter to check the errors handling expressions
+ - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
+ - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted.
+ - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`.
+ - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13.
+ - exhaustive # check exhaustiveness of enum switch statements
+ - exportloopref # checks for pointers to enclosing loop variables
+ - forbidigo # Forbids identifiers
+ - forcetypeassert # finds forced type assertions
+ - funlen # Tool for detection of long functions
+ - gci # Gci control golang package import order and make it always deterministic.
+ - gochecknoglobals # Checks that no globals are present in Go code
+ - gocognit # Computes and checks the cognitive complexity of functions
+ - goconst # Finds repeated strings that could be replaced by a constant
+ - gocritic # The most opinionated Go source code linter
+ - gocyclo # Computes and checks the cyclomatic complexity of functions
+ - godot # Check if comments end in a period
+ - godox # Tool for detection of FIXME, TODO and other comment keywords
+ - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
+ - gofumpt # Gofumpt checks whether code was gofumpt-ed.
+ - goheader # Checks is file header matches to pattern
+ - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
+ - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod.
+ - goprintffuncname # Checks that printf-like functions are named with `f` at the end
+ - gosec # Inspects source code for security problems
+ - gosimple # Linter for Go source code that specializes in simplifying a code
+ - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
+ - grouper # An analyzer to analyze expression groups.
+ - importas # Enforces consistent import aliases
+ - ineffassign # Detects when assignments to existing variables are not used
+ - lll # Reports long lines
+ - maintidx # maintidx measures the maintainability index of each function.
+ - makezero # Finds slice declarations with non-zero initial length
+ - misspell # Finds commonly misspelled English words in comments
+ - nakedret # Finds naked returns in functions greater than a specified function length
+ - nestif # Reports deeply nested if statements
+ - nilerr # Finds the code that returns nil even if it checks that the error is not nil.
+ - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value.
+ - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
+ - noctx # noctx finds sending http request without context.Context
+ - predeclared # find code that shadows one of Go's predeclared identifiers
+ - revive # golint replacement, finds style mistakes
+ - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
+ - stylecheck # Stylecheck is a replacement for golint
+ - tagliatelle # Checks the struct tags.
+ - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17
+ - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers
+ - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
+ - unconvert # Remove unnecessary type conversions
+ - unparam # Reports unused function parameters
+ - unused # Checks Go code for unused constants, variables, functions and types
+ - varnamelen # checks that the length of a variable's name matches its scope
+ - wastedassign # wastedassign finds wasted assignment statements
+ - whitespace # Tool for detection of leading and trailing whitespace
+ disable:
+ - depguard # Go linter that checks if package imports are in a list of acceptable packages
+ - gochecknoinits # Checks that no init functions are present in Go code
+ - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
+ - interfacebloat # A linter that checks length of interface.
+ - ireturn # Accept Interfaces, Return Concrete Types
+ - mnd # An analyzer to detect magic numbers
+ - nolintlint # Reports ill-formed or insufficient nolint directives
+ - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test
+ - prealloc # Finds slice declarations that could potentially be preallocated
+ - promlinter # Check Prometheus metrics naming via promlint
+ - rowserrcheck # checks whether Err of rows is checked successfully
+ - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
+ - testpackage # linter that makes you use a separate _test package
+ - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes
+ - wrapcheck # Checks that errors returned from external packages are wrapped
+ - wsl # Whitespace Linter - Forces you to use empty lines!
issues:
exclude-use-default: false
- max-per-linter: 0
- max-same-issues: 50
+ exclude-dirs-use-default: false
+ exclude-rules:
+ # Allow complex tests and examples, better to be self contained
+ - path: (examples|main\.go|_test\.go)
+ linters:
+ - forbidigo
+ - gocognit
+
+ # Allow forbidden identifiers in CLI commands
+ - path: cmd
+ linters:
+ - forbidigo
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/stun/.goreleaser.yml b/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/.goreleaser.yml
similarity index 100%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/stun/.goreleaser.yml
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/.goreleaser.yml
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/.travis.yml b/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/.travis.yml
deleted file mode 100644
index b96a1edb9..000000000
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/.travis.yml
+++ /dev/null
@@ -1,19 +0,0 @@
-language: go
-
-go:
- - "1.x" # use the latest Go release
-
-env:
- - GO111MODULE=on
-
-before_script:
- - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.15.0
-
-script:
- - golangci-lint run ./...
-# - rm -rf examples # Remove examples, no test coverage for them
- - go test -coverpkg=$(go list ./... | tr '\n' ',') -coverprofile=cover.out -v -race -covermode=atomic ./...
- - bash <(curl -s https://codecov.io/bash)
- - bash .github/assert-contributors.sh
- - bash .github/lint-disallowed-functions-in-library.sh
- - bash .github/lint-commit-message.sh
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/LICENSE b/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/LICENSE
index ab602974d..491caf6b0 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/LICENSE
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/LICENSE
@@ -1,21 +1,9 @@
MIT License
-Copyright (c) 2018
+Copyright (c) 2023 The Pion community
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/README.md b/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/README.md
index c15471d61..b9824abb6 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/README.md
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/README.md
@@ -8,8 +8,8 @@
-
-
+
+
@@ -20,22 +20,15 @@
The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
### Community
-Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion).
+Pion has an active community on the [Slack](https://pion.ly/slack).
+
+Follow the [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news.
We are always looking to support **your projects**. Please reach out if you have something to build!
-
If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
### Contributing
-Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
-
-* [John Bradley](https://github.com/kc5nra) - *Original Author*
-* [Sean DuBois](https://github.com/Sean-Der) - *Original Author*
-* [Michael MacDonald](https://github.com/mjmac) - *Original Author*
-* [Woodrow Douglass](https://github.com/wdouglass) - *Test coverage*
-* [Michiel De Backker](https://github.com/backkem) - *Docs*
-* [Hugo Arregui](https://github.com/hugoArregui) - *Custom Logs*
-* [Justin Okamoto](https://github.com/justinokamoto) - *Disabled Logs Update*
+Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible
### License
MIT License - see [LICENSE](LICENSE) for full text
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v2/codecov.yml b/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/codecov.yml
similarity index 100%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v2/codecov.yml
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/codecov.yml
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/logger.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/logger.go
index 35f650581..eb1e56af6 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/logger.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/logger.go
@@ -1,3 +1,7 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package logging provides the logging library used by Pion
package logging
import (
@@ -9,8 +13,8 @@ import (
"sync"
)
-// Use this abstraction to ensure thread-safe access to the logger's io.Writer
-// (which could change at runtime)
+// Use this abstraction to ensure thread-safe access to the logger's io.Writer.
+// (which could change at runtime).
type loggerWriter struct {
sync.RWMutex
output io.Writer
@@ -25,11 +29,12 @@ func (lw *loggerWriter) SetOutput(output io.Writer) {
func (lw *loggerWriter) Write(data []byte) (int, error) {
lw.RLock()
defer lw.RUnlock()
+
return lw.output.Write(data)
}
-// DefaultLeveledLogger encapsulates functionality for providing logging at
-// user-defined levels
+// DefaultLeveledLogger encapsulates functionality for providing logging at.
+// user-defined levels.
type DefaultLeveledLogger struct {
level LogLevel
writer *loggerWriter
@@ -41,44 +46,50 @@ type DefaultLeveledLogger struct {
}
// WithTraceLogger is a chainable configuration function which sets the
-// Trace-level logger
+// Trace-level logger.
func (ll *DefaultLeveledLogger) WithTraceLogger(log *log.Logger) *DefaultLeveledLogger {
ll.trace = log
+
return ll
}
// WithDebugLogger is a chainable configuration function which sets the
-// Debug-level logger
+// Debug-level logger.
func (ll *DefaultLeveledLogger) WithDebugLogger(log *log.Logger) *DefaultLeveledLogger {
ll.debug = log
+
return ll
}
// WithInfoLogger is a chainable configuration function which sets the
-// Info-level logger
+// Info-level logger.
func (ll *DefaultLeveledLogger) WithInfoLogger(log *log.Logger) *DefaultLeveledLogger {
ll.info = log
+
return ll
}
// WithWarnLogger is a chainable configuration function which sets the
-// Warn-level logger
+// Warn-level logger.
func (ll *DefaultLeveledLogger) WithWarnLogger(log *log.Logger) *DefaultLeveledLogger {
ll.warn = log
+
return ll
}
// WithErrorLogger is a chainable configuration function which sets the
-// Error-level logger
+// Error-level logger.
func (ll *DefaultLeveledLogger) WithErrorLogger(log *log.Logger) *DefaultLeveledLogger {
ll.err = log
+
return ll
}
// WithOutput is a chainable configuration function which sets the logger's
-// logging output to the supplied io.Writer
+// logging output to the supplied io.Writer.
func (ll *DefaultLeveledLogger) WithOutput(output io.Writer) *DefaultLeveledLogger {
ll.writer.SetOutput(output)
+
return ll
}
@@ -94,70 +105,71 @@ func (ll *DefaultLeveledLogger) logf(logger *log.Logger, level LogLevel, format
}
}
-// SetLevel sets the logger's logging level
+// SetLevel sets the logger's logging level.
func (ll *DefaultLeveledLogger) SetLevel(newLevel LogLevel) {
ll.level.Set(newLevel)
}
-// Trace emits the preformatted message if the logger is at or below LogLevelTrace
+// Trace emits the preformatted message if the logger is at or below LogLevelTrace.
func (ll *DefaultLeveledLogger) Trace(msg string) {
- ll.logf(ll.trace, LogLevelTrace, msg)
+ ll.logf(ll.trace, LogLevelTrace, msg) // nolint: govet
}
-// Tracef formats and emits a message if the logger is at or below LogLevelTrace
+// Tracef formats and emits a message if the logger is at or below LogLevelTrace.
func (ll *DefaultLeveledLogger) Tracef(format string, args ...interface{}) {
ll.logf(ll.trace, LogLevelTrace, format, args...)
}
-// Debug emits the preformatted message if the logger is at or below LogLevelDebug
+// Debug emits the preformatted message if the logger is at or below LogLevelDebug.
func (ll *DefaultLeveledLogger) Debug(msg string) {
- ll.logf(ll.debug, LogLevelDebug, msg)
+ ll.logf(ll.debug, LogLevelDebug, msg) // nolint: govet
}
-// Debugf formats and emits a message if the logger is at or below LogLevelDebug
+// Debugf formats and emits a message if the logger is at or below LogLevelDebug.
func (ll *DefaultLeveledLogger) Debugf(format string, args ...interface{}) {
ll.logf(ll.debug, LogLevelDebug, format, args...)
}
-// Info emits the preformatted message if the logger is at or below LogLevelInfo
+// Info emits the preformatted message if the logger is at or below LogLevelInfo.
func (ll *DefaultLeveledLogger) Info(msg string) {
- ll.logf(ll.info, LogLevelInfo, msg)
+ ll.logf(ll.info, LogLevelInfo, msg) // nolint: govet
}
-// Infof formats and emits a message if the logger is at or below LogLevelInfo
+// Infof formats and emits a message if the logger is at or below LogLevelInfo.
func (ll *DefaultLeveledLogger) Infof(format string, args ...interface{}) {
ll.logf(ll.info, LogLevelInfo, format, args...)
}
-// Warn emits the preformatted message if the logger is at or below LogLevelWarn
+// Warn emits the preformatted message if the logger is at or below LogLevelWarn.
func (ll *DefaultLeveledLogger) Warn(msg string) {
- ll.logf(ll.warn, LogLevelWarn, msg)
+ ll.logf(ll.warn, LogLevelWarn, msg) // nolint: govet
}
-// Warnf formats and emits a message if the logger is at or below LogLevelWarn
+// Warnf formats and emits a message if the logger is at or below LogLevelWarn.
func (ll *DefaultLeveledLogger) Warnf(format string, args ...interface{}) {
ll.logf(ll.warn, LogLevelWarn, format, args...)
}
-// Error emits the preformatted message if the logger is at or below LogLevelError
+// Error emits the preformatted message if the logger is at or below LogLevelError.
func (ll *DefaultLeveledLogger) Error(msg string) {
- ll.logf(ll.err, LogLevelError, msg)
+ ll.logf(ll.err, LogLevelError, msg) // nolint: govet
}
-// Errorf formats and emits a message if the logger is at or below LogLevelError
+// Errorf formats and emits a message if the logger is at or below LogLevelError.
func (ll *DefaultLeveledLogger) Errorf(format string, args ...interface{}) {
ll.logf(ll.err, LogLevelError, format, args...)
}
-// NewDefaultLeveledLoggerForScope returns a configured LeveledLogger
+// NewDefaultLeveledLoggerForScope returns a configured LeveledLogger.
func NewDefaultLeveledLoggerForScope(scope string, level LogLevel, writer io.Writer) *DefaultLeveledLogger {
if writer == nil {
- writer = os.Stdout
+ writer = os.Stderr
}
logger := &DefaultLeveledLogger{
writer: &loggerWriter{output: writer},
level: level,
}
+
return logger.
WithTraceLogger(log.New(logger.writer, fmt.Sprintf("%s TRACE: ", scope), log.Lmicroseconds|log.Lshortfile)).
WithDebugLogger(log.New(logger.writer, fmt.Sprintf("%s DEBUG: ", scope), log.Lmicroseconds|log.Lshortfile)).
@@ -166,19 +178,19 @@ func NewDefaultLeveledLoggerForScope(scope string, level LogLevel, writer io.Wri
WithErrorLogger(log.New(logger.writer, fmt.Sprintf("%s ERROR: ", scope), log.LstdFlags))
}
-// DefaultLoggerFactory define levels by scopes and creates new DefaultLeveledLogger
+// DefaultLoggerFactory define levels by scopes and creates new DefaultLeveledLogger.
type DefaultLoggerFactory struct {
Writer io.Writer
DefaultLogLevel LogLevel
ScopeLevels map[string]LogLevel
}
-// NewDefaultLoggerFactory creates a new DefaultLoggerFactory
+// NewDefaultLoggerFactory creates a new DefaultLoggerFactory.
func NewDefaultLoggerFactory() *DefaultLoggerFactory {
factory := DefaultLoggerFactory{}
factory.DefaultLogLevel = LogLevelError
factory.ScopeLevels = make(map[string]LogLevel)
- factory.Writer = os.Stdout
+ factory.Writer = os.Stderr
logLevels := map[string]LogLevel{
"DISABLE": LogLevelDisabled,
@@ -201,7 +213,10 @@ func NewDefaultLoggerFactory() *DefaultLoggerFactory {
}
if strings.ToLower(env) == "all" {
- factory.DefaultLogLevel = level
+ if factory.DefaultLogLevel < level {
+ factory.DefaultLogLevel = level
+ }
+
continue
}
@@ -214,7 +229,7 @@ func NewDefaultLoggerFactory() *DefaultLoggerFactory {
return &factory
}
-// NewLogger returns a configured LeveledLogger for the given , argsscope
+// NewLogger returns a configured LeveledLogger for the given, argsscope.
func (f *DefaultLoggerFactory) NewLogger(scope string) LeveledLogger {
logLevel := f.DefaultLogLevel
if f.ScopeLevels != nil {
@@ -224,5 +239,6 @@ func (f *DefaultLoggerFactory) NewLogger(scope string) LeveledLogger {
logLevel = scopeLevel
}
}
+
return NewDefaultLeveledLoggerForScope(scope, logLevel, f.Writer)
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/renovate.json b/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/renovate.json
similarity index 100%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/renovate.json
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/renovate.json
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/scoped.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/scoped.go
index 678bab426..7b3a550ee 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/scoped.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/logging/scoped.go
@@ -1,18 +1,21 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package logging
import (
"sync/atomic"
)
-// LogLevel represents the level at which the logger will emit log messages
+// LogLevel represents the level at which the logger will emit log messages.
type LogLevel int32
-// Set updates the LogLevel to the supplied value
+// Set updates the LogLevel to the supplied value.
func (ll *LogLevel) Set(newLevel LogLevel) {
atomic.StoreInt32((*int32)(ll), int32(newLevel))
}
-// Get retrieves the current LogLevel value
+// Get retrieves the current LogLevel value.
func (ll *LogLevel) Get() LogLevel {
return LogLevel(atomic.LoadInt32((*int32)(ll)))
}
@@ -37,22 +40,22 @@ func (ll LogLevel) String() string {
}
const (
- // LogLevelDisabled completely disables logging of any events
+ // LogLevelDisabled completely disables logging of any events.
LogLevelDisabled LogLevel = iota
// LogLevelError is for fatal errors which should be handled by user code,
- // but are logged to ensure that they are seen
+ // but are logged to ensure that they are seen.
LogLevelError
- // LogLevelWarn is for logging abnormal, but non-fatal library operation
+ // LogLevelWarn is for logging abnormal, but non-fatal library operation.
LogLevelWarn
- // LogLevelInfo is for logging normal library operation (e.g. state transitions, etc.)
+ // LogLevelInfo is for logging normal library operation (e.g. state transitions, etc.).
LogLevelInfo
- // LogLevelDebug is for logging low-level library information (e.g. internal operations)
+ // LogLevelDebug is for logging low-level library information (e.g. internal operations).
LogLevelDebug
- // LogLevelTrace is for logging very low-level library information (e.g. network traces)
+ // LogLevelTrace is for logging very low-level library information (e.g. network traces).
LogLevelTrace
)
-// LeveledLogger is the basic pion Logger interface
+// LeveledLogger is the basic pion Logger interface.
type LeveledLogger interface {
Trace(msg string)
Tracef(format string, args ...interface{})
@@ -66,7 +69,7 @@ type LeveledLogger interface {
Errorf(format string, args ...interface{})
}
-// LoggerFactory is the basic pion LoggerFactory interface
+// LoggerFactory is the basic pion LoggerFactory interface.
type LoggerFactory interface {
NewLogger(scope string) LeveledLogger
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/.golangci.yml b/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/.golangci.yml
deleted file mode 100644
index 48696f16b..000000000
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/.golangci.yml
+++ /dev/null
@@ -1,116 +0,0 @@
-linters-settings:
- govet:
- check-shadowing: true
- misspell:
- locale: US
- exhaustive:
- default-signifies-exhaustive: true
- gomodguard:
- blocked:
- modules:
- - github.com/pkg/errors:
- recommendations:
- - errors
-
-linters:
- enable:
- - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
- - bidichk # Checks for dangerous unicode character sequences
- - bodyclose # checks whether HTTP response body is closed successfully
- - contextcheck # check the function whether use a non-inherited context
- - decorder # check declaration order and count of types, constants, variables and functions
- - depguard # Go linter that checks if package imports are in a list of acceptable packages
- - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
- - dupl # Tool for code clone detection
- - durationcheck # check for two durations multiplied together
- - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
- - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted.
- - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`.
- - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13.
- - exhaustive # check exhaustiveness of enum switch statements
- - exportloopref # checks for pointers to enclosing loop variables
- - forcetypeassert # finds forced type assertions
- - gci # Gci control golang package import order and make it always deterministic.
- - gochecknoglobals # Checks that no globals are present in Go code
- - gochecknoinits # Checks that no init functions are present in Go code
- - gocognit # Computes and checks the cognitive complexity of functions
- - goconst # Finds repeated strings that could be replaced by a constant
- - gocritic # The most opinionated Go source code linter
- - godox # Tool for detection of FIXME, TODO and other comment keywords
- - goerr113 # Golang linter to check the errors handling expressions
- - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
- - gofumpt # Gofumpt checks whether code was gofumpt-ed.
- - goheader # Checks is file header matches to pattern
- - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
- - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod.
- - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
- - goprintffuncname # Checks that printf-like functions are named with `f` at the end
- - gosec # Inspects source code for security problems
- - gosimple # Linter for Go source code that specializes in simplifying a code
- - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
- - grouper # An analyzer to analyze expression groups.
- - importas # Enforces consistent import aliases
- - ineffassign # Detects when assignments to existing variables are not used
- - misspell # Finds commonly misspelled English words in comments
- - nakedret # Finds naked returns in functions greater than a specified function length
- - nilerr # Finds the code that returns nil even if it checks that the error is not nil.
- - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value.
- - noctx # noctx finds sending http request without context.Context
- - predeclared # find code that shadows one of Go's predeclared identifiers
- - revive # golint replacement, finds style mistakes
- - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
- - stylecheck # Stylecheck is a replacement for golint
- - tagliatelle # Checks the struct tags.
- - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17
- - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes
- - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
- - unconvert # Remove unnecessary type conversions
- - unparam # Reports unused function parameters
- - unused # Checks Go code for unused constants, variables, functions and types
- - wastedassign # wastedassign finds wasted assignment statements
- - whitespace # Tool for detection of leading and trailing whitespace
- disable:
- - containedctx # containedctx is a linter that detects struct contained context.Context field
- - cyclop # checks function and package cyclomatic complexity
- - exhaustivestruct # Checks if all struct's fields are initialized
- - forbidigo # Forbids identifiers
- - funlen # Tool for detection of long functions
- - gocyclo # Computes and checks the cyclomatic complexity of functions
- - godot # Check if comments end in a period
- - gomnd # An analyzer to detect magic numbers.
- - ifshort # Checks that your code uses short syntax for if-statements whenever possible
- - ireturn # Accept Interfaces, Return Concrete Types
- - lll # Reports long lines
- - maintidx # maintidx measures the maintainability index of each function.
- - makezero # Finds slice declarations with non-zero initial length
- - maligned # Tool to detect Go structs that would take less memory if their fields were sorted
- - nestif # Reports deeply nested if statements
- - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
- - nolintlint # Reports ill-formed or insufficient nolint directives
- - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test
- - prealloc # Finds slice declarations that could potentially be preallocated
- - promlinter # Check Prometheus metrics naming via promlint
- - rowserrcheck # checks whether Err of rows is checked successfully
- - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
- - testpackage # linter that makes you use a separate _test package
- - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers
- - varnamelen # checks that the length of a variable's name matches its scope
- - wrapcheck # Checks that errors returned from external packages are wrapped
- - wsl # Whitespace Linter - Forces you to use empty lines!
-
-issues:
- exclude-use-default: false
- exclude-rules:
- # Allow complex tests, better to be self contained
- - path: _test\.go
- linters:
- - gocognit
-
- # Allow complex main function in examples
- - path: examples
- text: "of func `main` is high"
- linters:
- - gocognit
-
-run:
- skip-dirs-use-default: false
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/.goreleaser.yml b/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/.goreleaser.yml
deleted file mode 100644
index 2caa5fbd3..000000000
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/.goreleaser.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-builds:
-- skip: true
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/AUTHORS.txt b/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/AUTHORS.txt
deleted file mode 100644
index 48ceb8211..000000000
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/AUTHORS.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-# Thank you to everyone that made Pion possible. If you are interested in contributing
-# we would love to have you https://github.com/pion/webrtc/wiki/Contributing
-#
-# This file is auto generated, using git to list all individuals contributors.
-# see https://github.com/pion/.goassets/blob/master/scripts/generate-authors.sh for the scripting
-Atsushi Watanabe
-Bjørn Remseth
-Doug Cone
-Hugo Arregui
-Javier Peletier
-Jonas van den Berg <24623262+vonas@users.noreply.github.com>
-Konstantin Itskov
-Sean DuBois
-Sean DuBois
-
-# List of contributors not appearing in Git history
-
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/config.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/config.go
deleted file mode 100644
index e028046e7..000000000
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/config.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package mdns
-
-import (
- "time"
-
- "github.com/pion/logging"
-)
-
-const (
- // DefaultAddress is the default used by mDNS
- // and in most cases should be the address that the
- // net.Conn passed to Server is bound to
- DefaultAddress = "224.0.0.0:5353"
-)
-
-// Config is used to configure a mDNS client or server.
-type Config struct {
- // QueryInterval controls how often we sends Queries until we
- // get a response for the requested name
- QueryInterval time.Duration
-
- // LocalNames are the names that we will generate answers for
- // when we get questions
- LocalNames []string
-
- LoggerFactory logging.LoggerFactory
-}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/conn.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/conn.go
deleted file mode 100644
index 31037e252..000000000
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/conn.go
+++ /dev/null
@@ -1,384 +0,0 @@
-package mdns
-
-import (
- "context"
- "errors"
- "math/big"
- "net"
- "runtime"
- "sync"
- "time"
-
- "github.com/pion/logging"
- "golang.org/x/net/dns/dnsmessage"
- "golang.org/x/net/ipv4"
-)
-
-// Conn represents a mDNS Server
-type Conn struct {
- mu sync.RWMutex
- log logging.LeveledLogger
-
- socket *ipv4.PacketConn
- dstAddr *net.UDPAddr
-
- queryInterval time.Duration
- localNames []string
- queries []query
- ifaces []net.Interface
-
- closed chan interface{}
-}
-
-type query struct {
- nameWithSuffix string
- queryResultChan chan queryResult
-}
-
-type queryResult struct {
- answer dnsmessage.ResourceHeader
- addr net.Addr
-}
-
-const (
- defaultQueryInterval = time.Second
- destinationAddress = "224.0.0.251:5353"
- maxMessageRecords = 3
- responseTTL = 120
-)
-
-var errNoPositiveMTUFound = errors.New("no positive MTU found")
-
-// Server establishes a mDNS connection over an existing conn
-func Server(conn *ipv4.PacketConn, config *Config) (*Conn, error) {
- if config == nil {
- return nil, errNilConfig
- }
-
- ifaces, err := net.Interfaces()
- if err != nil {
- return nil, err
- }
-
- inboundBufferSize := 0
- joinErrCount := 0
- ifacesToUse := make([]net.Interface, 0, len(ifaces))
- for i, ifc := range ifaces {
- if err = conn.JoinGroup(&ifaces[i], &net.UDPAddr{IP: net.IPv4(224, 0, 0, 251)}); err != nil {
- joinErrCount++
- continue
- }
-
- ifcCopy := ifc
- ifacesToUse = append(ifacesToUse, ifcCopy)
- if ifaces[i].MTU > inboundBufferSize {
- inboundBufferSize = ifaces[i].MTU
- }
- }
-
- if inboundBufferSize == 0 {
- return nil, errNoPositiveMTUFound
- }
- if joinErrCount >= len(ifaces) {
- return nil, errJoiningMulticastGroup
- }
-
- dstAddr, err := net.ResolveUDPAddr("udp", destinationAddress)
- if err != nil {
- return nil, err
- }
-
- loggerFactory := config.LoggerFactory
- if loggerFactory == nil {
- loggerFactory = logging.NewDefaultLoggerFactory()
- }
-
- localNames := []string{}
- for _, l := range config.LocalNames {
- localNames = append(localNames, l+".")
- }
-
- c := &Conn{
- queryInterval: defaultQueryInterval,
- queries: []query{},
- socket: conn,
- dstAddr: dstAddr,
- localNames: localNames,
- ifaces: ifacesToUse,
- log: loggerFactory.NewLogger("mdns"),
- closed: make(chan interface{}),
- }
- if config.QueryInterval != 0 {
- c.queryInterval = config.QueryInterval
- }
-
- // https://www.rfc-editor.org/rfc/rfc6762.html#section-17
- // Multicast DNS messages carried by UDP may be up to the IP MTU of the
- // physical interface, less the space required for the IP header (20
- // bytes for IPv4; 40 bytes for IPv6) and the UDP header (8 bytes).
- go c.start(inboundBufferSize - 20 - 8)
- return c, nil
-}
-
-// Close closes the mDNS Conn
-func (c *Conn) Close() error {
- select {
- case <-c.closed:
- return nil
- default:
- }
-
- if err := c.socket.Close(); err != nil {
- return err
- }
-
- <-c.closed
- return nil
-}
-
-// Query sends mDNS Queries for the following name until
-// either the Context is canceled/expires or we get a result
-func (c *Conn) Query(ctx context.Context, name string) (dnsmessage.ResourceHeader, net.Addr, error) {
- select {
- case <-c.closed:
- return dnsmessage.ResourceHeader{}, nil, errConnectionClosed
- default:
- }
-
- nameWithSuffix := name + "."
-
- queryChan := make(chan queryResult, 1)
- c.mu.Lock()
- c.queries = append(c.queries, query{nameWithSuffix, queryChan})
- ticker := time.NewTicker(c.queryInterval)
- c.mu.Unlock()
-
- defer ticker.Stop()
-
- c.sendQuestion(nameWithSuffix)
- for {
- select {
- case <-ticker.C:
- c.sendQuestion(nameWithSuffix)
- case <-c.closed:
- return dnsmessage.ResourceHeader{}, nil, errConnectionClosed
- case res := <-queryChan:
- return res.answer, res.addr, nil
- case <-ctx.Done():
- return dnsmessage.ResourceHeader{}, nil, errContextElapsed
- }
- }
-}
-
-func ipToBytes(ip net.IP) (out [4]byte) {
- rawIP := ip.To4()
- if rawIP == nil {
- return
- }
-
- ipInt := big.NewInt(0)
- ipInt.SetBytes(rawIP)
- copy(out[:], ipInt.Bytes())
- return
-}
-
-func interfaceForRemote(remote string) (net.IP, error) {
- conn, err := net.Dial("udp", remote)
- if err != nil {
- return nil, err
- }
-
- localAddr, ok := conn.LocalAddr().(*net.UDPAddr)
- if !ok {
- return nil, errFailedCast
- }
-
- if err := conn.Close(); err != nil {
- return nil, err
- }
-
- return localAddr.IP, nil
-}
-
-func (c *Conn) sendQuestion(name string) {
- packedName, err := dnsmessage.NewName(name)
- if err != nil {
- c.log.Warnf("Failed to construct mDNS packet %v", err)
- return
- }
-
- msg := dnsmessage.Message{
- Header: dnsmessage.Header{},
- Questions: []dnsmessage.Question{
- {
- Type: dnsmessage.TypeA,
- Class: dnsmessage.ClassINET,
- Name: packedName,
- },
- },
- }
-
- rawQuery, err := msg.Pack()
- if err != nil {
- c.log.Warnf("Failed to construct mDNS packet %v", err)
- return
- }
-
- c.writeToSocket(rawQuery)
-}
-
-const isWindows = runtime.GOOS == "windows"
-
-func (c *Conn) writeToSocket(b []byte) {
- var wcm ipv4.ControlMessage
- for i := range c.ifaces {
- if isWindows {
- if err := c.socket.SetMulticastInterface(&c.ifaces[i]); err != nil {
- c.log.Warnf("Failed to set multicast interface for %d: %v", i, err)
- }
- } else {
- wcm.IfIndex = c.ifaces[i].Index
- }
- if _, err := c.socket.WriteTo(b, &wcm, c.dstAddr); err != nil {
- c.log.Warnf("Failed to send mDNS packet on interface %d: %v", i, err)
- }
- }
-}
-
-func (c *Conn) sendAnswer(name string, dst net.IP) {
- packedName, err := dnsmessage.NewName(name)
- if err != nil {
- c.log.Warnf("Failed to construct mDNS packet %v", err)
- return
- }
-
- msg := dnsmessage.Message{
- Header: dnsmessage.Header{
- Response: true,
- Authoritative: true,
- },
- Answers: []dnsmessage.Resource{
- {
- Header: dnsmessage.ResourceHeader{
- Type: dnsmessage.TypeA,
- Class: dnsmessage.ClassINET,
- Name: packedName,
- TTL: responseTTL,
- },
- Body: &dnsmessage.AResource{
- A: ipToBytes(dst),
- },
- },
- },
- }
-
- rawAnswer, err := msg.Pack()
- if err != nil {
- c.log.Warnf("Failed to construct mDNS packet %v", err)
- return
- }
-
- c.writeToSocket(rawAnswer)
-}
-
-func (c *Conn) start(inboundBufferSize int) { //nolint gocognit
- defer func() {
- c.mu.Lock()
- defer c.mu.Unlock()
- close(c.closed)
- }()
-
- b := make([]byte, inboundBufferSize)
- p := dnsmessage.Parser{}
-
- for {
- n, _, src, err := c.socket.ReadFrom(b)
- if err != nil {
- if errors.Is(err, net.ErrClosed) {
- return
- }
- c.log.Warnf("Failed to ReadFrom %q %v", src, err)
- continue
- }
-
- func() {
- c.mu.RLock()
- defer c.mu.RUnlock()
-
- if _, err := p.Start(b[:n]); err != nil {
- c.log.Warnf("Failed to parse mDNS packet %v", err)
- return
- }
-
- for i := 0; i <= maxMessageRecords; i++ {
- q, err := p.Question()
- if errors.Is(err, dnsmessage.ErrSectionDone) {
- break
- } else if err != nil {
- c.log.Warnf("Failed to parse mDNS packet %v", err)
- return
- }
-
- for _, localName := range c.localNames {
- if localName == q.Name.String() {
- localAddress, err := interfaceForRemote(src.String())
- if err != nil {
- c.log.Warnf("Failed to get local interface to communicate with %s: %v", src.String(), err)
- continue
- }
-
- c.sendAnswer(q.Name.String(), localAddress)
- }
- }
- }
-
- for i := 0; i <= maxMessageRecords; i++ {
- a, err := p.AnswerHeader()
- if errors.Is(err, dnsmessage.ErrSectionDone) {
- return
- }
- if err != nil {
- c.log.Warnf("Failed to parse mDNS packet %v", err)
- return
- }
-
- if a.Type != dnsmessage.TypeA && a.Type != dnsmessage.TypeAAAA {
- continue
- }
-
- for i := len(c.queries) - 1; i >= 0; i-- {
- if c.queries[i].nameWithSuffix == a.Name.String() {
- ip, err := ipFromAnswerHeader(a, p)
- if err != nil {
- c.log.Warnf("Failed to parse mDNS answer %v", err)
- return
- }
-
- c.queries[i].queryResultChan <- queryResult{a, &net.IPAddr{
- IP: ip,
- }}
- c.queries = append(c.queries[:i], c.queries[i+1:]...)
- }
- }
- }
- }()
- }
-}
-
-func ipFromAnswerHeader(a dnsmessage.ResourceHeader, p dnsmessage.Parser) (ip []byte, err error) {
- if a.Type == dnsmessage.TypeA {
- resource, err := p.AResource()
- if err != nil {
- return nil, err
- }
- ip = net.IP(resource.A[:])
- } else {
- resource, err := p.AAAAResource()
- if err != nil {
- return nil, err
- }
- ip = resource.AAAA[:]
- }
-
- return
-}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/mdns.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/mdns.go
deleted file mode 100644
index 853ae833e..000000000
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/mdns.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// Package mdns implements mDNS (multicast DNS)
-package mdns
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/stun/.gitignore b/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/.gitignore
similarity index 100%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/stun/.gitignore
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/.gitignore
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/.golangci.yml b/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/.golangci.yml
similarity index 96%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/.golangci.yml
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/.golangci.yml
index 4e3eddf42..6dd80c805 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/ice/v2/.golangci.yml
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/.golangci.yml
@@ -29,7 +29,6 @@ linters:
- bodyclose # checks whether HTTP response body is closed successfully
- contextcheck # check the function whether use a non-inherited context
- decorder # check declaration order and count of types, constants, variables and functions
- - depguard # Go linter that checks if package imports are in a list of acceptable packages
- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
- dupl # Tool for code clone detection
- durationcheck # check for two durations multiplied together
@@ -63,7 +62,6 @@ linters:
- importas # Enforces consistent import aliases
- ineffassign # Detects when assignments to existing variables are not used
- misspell # Finds commonly misspelled English words in comments
- - nakedret # Finds naked returns in functions greater than a specified function length
- nilerr # Finds the code that returns nil even if it checks that the error is not nil.
- nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value.
- noctx # noctx finds sending http request without context.Context
@@ -81,6 +79,7 @@ linters:
- wastedassign # wastedassign finds wasted assignment statements
- whitespace # Tool for detection of leading and trailing whitespace
disable:
+ - depguard # Go linter that checks if package imports are in a list of acceptable packages
- containedctx # containedctx is a linter that detects struct contained context.Context field
- cyclop # checks function and package cyclomatic complexity
- exhaustivestruct # Checks if all struct's fields are initialized
@@ -94,6 +93,7 @@ linters:
- maintidx # maintidx measures the maintainability index of each function.
- makezero # Finds slice declarations with non-zero initial length
- maligned # Tool to detect Go structs that would take less memory if their fields were sorted
+ - nakedret # Finds naked returns in functions greater than a specified function length
- nestif # Reports deeply nested if statements
- nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
- nolintlint # Reports ill-formed or insufficient nolint directives
@@ -111,22 +111,11 @@ linters:
issues:
exclude-use-default: false
exclude-rules:
- # Allow complex tests, better to be self contained
- - path: _test\.go
- linters:
- - gocognit
- - forbidigo
-
- # Allow complex main function in examples
- - path: examples
- text: "of func `main` is high"
- linters:
- - gocognit
-
- # Allow forbidden identifiers in examples
- - path: examples
+ # Allow complex tests and examples, better to be self contained
+ - path: (examples|main\.go|_test\.go)
linters:
- forbidigo
+ - gocognit
# Allow forbidden identifiers in CLI commands
- path: cmd
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/transport/v2/.goreleaser.yml b/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/.goreleaser.yml
similarity index 100%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/transport/v2/.goreleaser.yml
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/.goreleaser.yml
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/LICENSE b/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/LICENSE
similarity index 100%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/LICENSE
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/LICENSE
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/README.md b/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/README.md
similarity index 56%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/README.md
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/README.md
index b2d7d9c73..70b488188 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/README.md
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/README.md
@@ -8,8 +8,8 @@
-
-
+
+
@@ -24,6 +24,12 @@ For a mDNS server that responds to queries for `pion-test.local`
go run examples/server/main.go
```
+For a mDNS server that responds to queries for `pion-test.local` with a given address
+```sh
+go run examples/server/publish_ip/main.go -ip=[IP]
+```
+If you don't set the `ip` parameter, "1.2.3.4" will be used instead.
+
### Running Client
To query using Pion you can run the `query` example
@@ -41,19 +47,26 @@ Or the avahi client
avahi-resolve -a pion-test.local
```
-### References
-https://tools.ietf.org/html/rfc6762
-https://tools.ietf.org/id/draft-ietf-rtcweb-mdns-ice-candidates-02.html
+### RFCs
+#### Implemented
+- **RFC 6762** [Multicast DNS][rfc6762]
+- **draft-ietf-rtcweb-mdns-ice-candidates-02** [Using Multicast DNS to protect privacy when exposing ICE candidates](https://datatracker.ietf.org/doc/html/draft-ietf-rtcweb-mdns-ice-candidates-02.html)
+
+[rfc6762]: https://tools.ietf.org/html/rfc6762
+
+### Roadmap
+The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
### Community
-Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion).
+Pion has an active community on the [Slack](https://pion.ly/slack).
+
+Follow the [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news.
We are always looking to support **your projects**. Please reach out if you have something to build!
-
If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
### Contributing
-Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
+Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible
### License
-MIT License - see [LICENSE](LICENSE) for full text
+MIT License - see [LICENSE](LICENSE) for full text
\ No newline at end of file
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/stun/codecov.yml b/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/codecov.yml
similarity index 100%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/stun/codecov.yml
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/codecov.yml
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/config.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/config.go
new file mode 100644
index 000000000..4659e0664
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/config.go
@@ -0,0 +1,49 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package mdns
+
+import (
+ "net"
+ "time"
+
+ "github.com/pion/logging"
+)
+
+const (
+ // DefaultAddressIPv4 is the default used by mDNS
+ // and in most cases should be the address that the
+ // ipv4.PacketConn passed to Server is bound to
+ DefaultAddressIPv4 = "224.0.0.0:5353"
+
+ // DefaultAddressIPv6 is the default IPv6 address used
+ // by mDNS and in most cases should be the address that
+ // the ipv6.PacketConn passed to Server is bound to
+ DefaultAddressIPv6 = "[FF02::]:5353"
+)
+
+// Config is used to configure a mDNS client or server.
+type Config struct {
+ // Name is the name of the client/server used for logging purposes.
+ Name string
+
+ // QueryInterval controls how often we sends Queries until we
+ // get a response for the requested name
+ QueryInterval time.Duration
+
+ // LocalNames are the names that we will generate answers for
+ // when we get questions
+ LocalNames []string
+
+ // LocalAddress will override the published address with the given IP
+ // when set. Otherwise, the automatically determined address will be used.
+ LocalAddress net.IP
+
+ LoggerFactory logging.LoggerFactory
+
+ // IncludeLoopback will include loopback interfaces to be eligble for queries and answers.
+ IncludeLoopback bool
+
+ // Interfaces will override the interfaces used for queries and answers.
+ Interfaces []net.Interface
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/conn.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/conn.go
new file mode 100644
index 000000000..d300163ad
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/conn.go
@@ -0,0 +1,1220 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package mdns
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "net"
+ "net/netip"
+ "sync"
+ "time"
+
+ "github.com/pion/logging"
+ "golang.org/x/net/dns/dnsmessage"
+ "golang.org/x/net/ipv4"
+ "golang.org/x/net/ipv6"
+)
+
+// Conn represents a mDNS Server
+type Conn struct {
+ mu sync.RWMutex
+ name string
+ log logging.LeveledLogger
+
+ multicastPktConnV4 ipPacketConn
+ multicastPktConnV6 ipPacketConn
+ dstAddr4 *net.UDPAddr
+ dstAddr6 *net.UDPAddr
+
+ unicastPktConnV4 ipPacketConn
+ unicastPktConnV6 ipPacketConn
+
+ queryInterval time.Duration
+ localNames []string
+ queries []*query
+ ifaces map[int]netInterface
+
+ closed chan interface{}
+}
+
+type query struct {
+ nameWithSuffix string
+ queryResultChan chan queryResult
+}
+
+type queryResult struct {
+ answer dnsmessage.ResourceHeader
+ addr netip.Addr
+}
+
+const (
+ defaultQueryInterval = time.Second
+ destinationAddress4 = "224.0.0.251:5353"
+ destinationAddress6 = "[FF02::FB]:5353"
+ maxMessageRecords = 3
+ responseTTL = 120
+ // maxPacketSize is the maximum size of a mdns packet.
+ // From RFC 6762:
+ // Even when fragmentation is used, a Multicast DNS packet, including IP
+ // and UDP headers, MUST NOT exceed 9000 bytes.
+ // https://datatracker.ietf.org/doc/html/rfc6762#section-17
+ maxPacketSize = 9000
+)
+
+var (
+ errNoPositiveMTUFound = errors.New("no positive MTU found")
+ errNoPacketConn = errors.New("must supply at least a multicast IPv4 or IPv6 PacketConn")
+ errNoUsableInterfaces = errors.New("no usable interfaces found for mDNS")
+ errFailedToClose = errors.New("failed to close mDNS Conn")
+)
+
+type netInterface struct {
+ net.Interface
+ ipAddrs []netip.Addr
+ supportsV4 bool
+ supportsV6 bool
+}
+
+// Server establishes a mDNS connection over an existing conn.
+// Either one or both of the multicast packet conns should be provided.
+// The presence of each IP type of PacketConn will dictate what kinds
+// of questions are sent for queries. That is, if an ipv6.PacketConn is
+// provided, then AAAA questions will be sent. A questions will only be
+// sent if an ipv4.PacketConn is also provided. In the future, we may
+// add a QueryAddr method that allows specifying this more clearly.
+//
+//nolint:gocognit
+func Server(
+ multicastPktConnV4 *ipv4.PacketConn,
+ multicastPktConnV6 *ipv6.PacketConn,
+ config *Config,
+) (*Conn, error) {
+ if config == nil {
+ return nil, errNilConfig
+ }
+ loggerFactory := config.LoggerFactory
+ if loggerFactory == nil {
+ loggerFactory = logging.NewDefaultLoggerFactory()
+ }
+ log := loggerFactory.NewLogger("mdns")
+
+ c := &Conn{
+ queryInterval: defaultQueryInterval,
+ log: log,
+ closed: make(chan interface{}),
+ }
+ c.name = config.Name
+ if c.name == "" {
+ c.name = fmt.Sprintf("%p", &c)
+ }
+
+ if multicastPktConnV4 == nil && multicastPktConnV6 == nil {
+ return nil, errNoPacketConn
+ }
+
+ ifaces := config.Interfaces
+ if ifaces == nil {
+ var err error
+ ifaces, err = net.Interfaces()
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ var unicastPktConnV4 *ipv4.PacketConn
+ {
+ addr4, err := net.ResolveUDPAddr("udp4", "0.0.0.0:0")
+ if err != nil {
+ return nil, err
+ }
+
+ unicastConnV4, err := net.ListenUDP("udp4", addr4)
+ if err != nil {
+ log.Warnf("[%s] failed to listen on unicast IPv4 %s: %s; will not be able to receive unicast responses on IPv4", c.name, addr4, err)
+ } else {
+ unicastPktConnV4 = ipv4.NewPacketConn(unicastConnV4)
+ }
+ }
+
+ var unicastPktConnV6 *ipv6.PacketConn
+ {
+ addr6, err := net.ResolveUDPAddr("udp6", "[::]:")
+ if err != nil {
+ return nil, err
+ }
+
+ unicastConnV6, err := net.ListenUDP("udp6", addr6)
+ if err != nil {
+ log.Warnf("[%s] failed to listen on unicast IPv6 %s: %s; will not be able to receive unicast responses on IPv6", c.name, addr6, err)
+ } else {
+ unicastPktConnV6 = ipv6.NewPacketConn(unicastConnV6)
+ }
+ }
+
+ mutlicastGroup4 := net.IPv4(224, 0, 0, 251)
+ multicastGroupAddr4 := &net.UDPAddr{IP: mutlicastGroup4}
+
+ // FF02::FB
+ mutlicastGroup6 := net.IP{0xff, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfb}
+ multicastGroupAddr6 := &net.UDPAddr{IP: mutlicastGroup6}
+
+ inboundBufferSize := 0
+ joinErrCount := 0
+ ifacesToUse := make(map[int]netInterface, len(ifaces))
+ for i := range ifaces {
+ ifc := ifaces[i]
+ if !config.IncludeLoopback && ifc.Flags&net.FlagLoopback == net.FlagLoopback {
+ continue
+ }
+ if ifc.Flags&net.FlagUp == 0 {
+ continue
+ }
+
+ addrs, err := ifc.Addrs()
+ if err != nil {
+ continue
+ }
+ var supportsV4, supportsV6 bool
+ ifcIPAddrs := make([]netip.Addr, 0, len(addrs))
+ for _, addr := range addrs {
+ var ipToConv net.IP
+ switch addr := addr.(type) {
+ case *net.IPNet:
+ ipToConv = addr.IP
+ case *net.IPAddr:
+ ipToConv = addr.IP
+ default:
+ continue
+ }
+
+ ipAddr, ok := netip.AddrFromSlice(ipToConv)
+ if !ok {
+ continue
+ }
+ if multicastPktConnV4 != nil {
+ // don't want mapping since we also support IPv4/A
+ ipAddr = ipAddr.Unmap()
+ }
+ ipAddr = addrWithOptionalZone(ipAddr, ifc.Name)
+
+ if ipAddr.Is6() && !ipAddr.Is4In6() {
+ supportsV6 = true
+ } else {
+ // we'll claim we support v4 but defer if we send it or not
+ // based on IPv4-to-IPv6 mapping rules later (search for Is4In6 below)
+ supportsV4 = true
+ }
+ ifcIPAddrs = append(ifcIPAddrs, ipAddr)
+ }
+ if !(supportsV4 || supportsV6) {
+ continue
+ }
+
+ var atLeastOneJoin bool
+ if supportsV4 && multicastPktConnV4 != nil {
+ if err := multicastPktConnV4.JoinGroup(&ifc, multicastGroupAddr4); err == nil {
+ atLeastOneJoin = true
+ }
+ }
+ if supportsV6 && multicastPktConnV6 != nil {
+ if err := multicastPktConnV6.JoinGroup(&ifc, multicastGroupAddr6); err == nil {
+ atLeastOneJoin = true
+ }
+ }
+ if !atLeastOneJoin {
+ joinErrCount++
+ continue
+ }
+
+ ifacesToUse[ifc.Index] = netInterface{
+ Interface: ifc,
+ ipAddrs: ifcIPAddrs,
+ supportsV4: supportsV4,
+ supportsV6: supportsV6,
+ }
+ if ifc.MTU > inboundBufferSize {
+ inboundBufferSize = ifc.MTU
+ }
+ }
+
+ if len(ifacesToUse) == 0 {
+ return nil, errNoUsableInterfaces
+ }
+ if inboundBufferSize == 0 {
+ return nil, errNoPositiveMTUFound
+ }
+ if inboundBufferSize > maxPacketSize {
+ inboundBufferSize = maxPacketSize
+ }
+ if joinErrCount >= len(ifaces) {
+ return nil, errJoiningMulticastGroup
+ }
+
+ dstAddr4, err := net.ResolveUDPAddr("udp4", destinationAddress4)
+ if err != nil {
+ return nil, err
+ }
+
+ dstAddr6, err := net.ResolveUDPAddr("udp6", destinationAddress6)
+ if err != nil {
+ return nil, err
+ }
+
+ var localNames []string
+ for _, l := range config.LocalNames {
+ localNames = append(localNames, l+".")
+ }
+
+ c.dstAddr4 = dstAddr4
+ c.dstAddr6 = dstAddr6
+ c.localNames = localNames
+ c.ifaces = ifacesToUse
+
+ if config.QueryInterval != 0 {
+ c.queryInterval = config.QueryInterval
+ }
+
+ if multicastPktConnV4 != nil {
+ if err := multicastPktConnV4.SetControlMessage(ipv4.FlagInterface, true); err != nil {
+ c.log.Warnf("[%s] failed to SetControlMessage(ipv4.FlagInterface) on multicast IPv4 PacketConn %v", c.name, err)
+ }
+ if err := multicastPktConnV4.SetControlMessage(ipv4.FlagDst, true); err != nil {
+ c.log.Warnf("[%s] failed to SetControlMessage(ipv4.FlagDst) on multicast IPv4 PacketConn %v", c.name, err)
+ }
+ c.multicastPktConnV4 = ipPacketConn4{c.name, multicastPktConnV4, log}
+ }
+ if multicastPktConnV6 != nil {
+ if err := multicastPktConnV6.SetControlMessage(ipv6.FlagInterface, true); err != nil {
+ c.log.Warnf("[%s] failed to SetControlMessage(ipv6.FlagInterface) on multicast IPv6 PacketConn %v", c.name, err)
+ }
+ if err := multicastPktConnV6.SetControlMessage(ipv6.FlagDst, true); err != nil {
+ c.log.Warnf("[%s] failed to SetControlMessage(ipv6.FlagInterface) on multicast IPv6 PacketConn %v", c.name, err)
+ }
+ c.multicastPktConnV6 = ipPacketConn6{c.name, multicastPktConnV6, log}
+ }
+ if unicastPktConnV4 != nil {
+ if err := unicastPktConnV4.SetControlMessage(ipv4.FlagInterface, true); err != nil {
+ c.log.Warnf("[%s] failed to SetControlMessage(ipv4.FlagInterface) on unicast IPv4 PacketConn %v", c.name, err)
+ }
+ if err := unicastPktConnV4.SetControlMessage(ipv4.FlagDst, true); err != nil {
+ c.log.Warnf("[%s] failed to SetControlMessage(ipv4.FlagInterface) on unicast IPv4 PacketConn %v", c.name, err)
+ }
+ c.unicastPktConnV4 = ipPacketConn4{c.name, unicastPktConnV4, log}
+ }
+ if unicastPktConnV6 != nil {
+ if err := unicastPktConnV6.SetControlMessage(ipv6.FlagInterface, true); err != nil {
+ c.log.Warnf("[%s] failed to SetControlMessage(ipv6.FlagInterface) on unicast IPv6 PacketConn %v", c.name, err)
+ }
+ if err := unicastPktConnV6.SetControlMessage(ipv6.FlagDst, true); err != nil {
+ c.log.Warnf("[%s] failed to SetControlMessage(ipv6.FlagInterface) on unicast IPv6 PacketConn %v", c.name, err)
+ }
+ c.unicastPktConnV6 = ipPacketConn6{c.name, unicastPktConnV6, log}
+ }
+
+ if config.IncludeLoopback {
+ // this is an efficient way for us to send ourselves a message faster instead of it going
+ // further out into the network stack.
+ if multicastPktConnV4 != nil {
+ if err := multicastPktConnV4.SetMulticastLoopback(true); err != nil {
+ c.log.Warnf("[%s] failed to SetMulticastLoopback(true) on multicast IPv4 PacketConn %v; this may cause inefficient network path c.name,communications", c.name, err)
+ }
+ }
+ if multicastPktConnV6 != nil {
+ if err := multicastPktConnV6.SetMulticastLoopback(true); err != nil {
+ c.log.Warnf("[%s] failed to SetMulticastLoopback(true) on multicast IPv6 PacketConn %v; this may cause inefficient network path c.name,communications", c.name, err)
+ }
+ }
+ if unicastPktConnV4 != nil {
+ if err := unicastPktConnV4.SetMulticastLoopback(true); err != nil {
+ c.log.Warnf("[%s] failed to SetMulticastLoopback(true) on unicast IPv4 PacketConn %v; this may cause inefficient network path c.name,communications", c.name, err)
+ }
+ }
+ if unicastPktConnV6 != nil {
+ if err := unicastPktConnV6.SetMulticastLoopback(true); err != nil {
+ c.log.Warnf("[%s] failed to SetMulticastLoopback(true) on unicast IPv6 PacketConn %v; this may cause inefficient network path c.name,communications", c.name, err)
+ }
+ }
+ }
+
+ // https://www.rfc-editor.org/rfc/rfc6762.html#section-17
+ // Multicast DNS messages carried by UDP may be up to the IP MTU of the
+ // physical interface, less the space required for the IP header (20
+ // bytes for IPv4; 40 bytes for IPv6) and the UDP header (8 bytes).
+ started := make(chan struct{})
+ go c.start(started, inboundBufferSize-20-8, config)
+ <-started
+ return c, nil
+}
+
+// Close closes the mDNS Conn
+func (c *Conn) Close() error {
+ select {
+ case <-c.closed:
+ return nil
+ default:
+ }
+
+ // Once on go1.20, can use errors.Join
+ var errs []error
+ if c.multicastPktConnV4 != nil {
+ if err := c.multicastPktConnV4.Close(); err != nil {
+ errs = append(errs, err)
+ }
+ }
+
+ if c.multicastPktConnV6 != nil {
+ if err := c.multicastPktConnV6.Close(); err != nil {
+ errs = append(errs, err)
+ }
+ }
+
+ if c.unicastPktConnV4 != nil {
+ if err := c.unicastPktConnV4.Close(); err != nil {
+ errs = append(errs, err)
+ }
+ }
+
+ if c.unicastPktConnV6 != nil {
+ if err := c.unicastPktConnV6.Close(); err != nil {
+ errs = append(errs, err)
+ }
+ }
+
+ if len(errs) == 0 {
+ <-c.closed
+ return nil
+ }
+
+ rtrn := errFailedToClose
+ for _, err := range errs {
+ rtrn = fmt.Errorf("%w\n%w", err, rtrn)
+ }
+ return rtrn
+}
+
+// Query sends mDNS Queries for the following name until
+// either the Context is canceled/expires or we get a result
+//
+// Deprecated: Use QueryAddr instead as it supports the easier to use netip.Addr.
+func (c *Conn) Query(ctx context.Context, name string) (dnsmessage.ResourceHeader, net.Addr, error) {
+ header, addr, err := c.QueryAddr(ctx, name)
+ if err != nil {
+ return header, nil, err
+ }
+ return header, &net.IPAddr{
+ IP: addr.AsSlice(),
+ Zone: addr.Zone(),
+ }, nil
+}
+
+// QueryAddr sends mDNS Queries for the following name until
+// either the Context is canceled/expires or we get a result
+func (c *Conn) QueryAddr(ctx context.Context, name string) (dnsmessage.ResourceHeader, netip.Addr, error) {
+ select {
+ case <-c.closed:
+ return dnsmessage.ResourceHeader{}, netip.Addr{}, errConnectionClosed
+ default:
+ }
+
+ nameWithSuffix := name + "."
+
+ queryChan := make(chan queryResult, 1)
+ query := &query{nameWithSuffix, queryChan}
+ c.mu.Lock()
+ c.queries = append(c.queries, query)
+ c.mu.Unlock()
+
+ defer func() {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ for i := len(c.queries) - 1; i >= 0; i-- {
+ if c.queries[i] == query {
+ c.queries = append(c.queries[:i], c.queries[i+1:]...)
+ }
+ }
+ }()
+
+ ticker := time.NewTicker(c.queryInterval)
+ defer ticker.Stop()
+
+ c.sendQuestion(nameWithSuffix)
+ for {
+ select {
+ case <-ticker.C:
+ c.sendQuestion(nameWithSuffix)
+ case <-c.closed:
+ return dnsmessage.ResourceHeader{}, netip.Addr{}, errConnectionClosed
+ case res := <-queryChan:
+ // Given https://datatracker.ietf.org/doc/html/draft-ietf-mmusic-mdns-ice-candidates#section-3.2.2-2
+ // An ICE agent SHOULD ignore candidates where the hostname resolution returns more than one IP address.
+ //
+ // We will take the first we receive which could result in a race between two suitable addresses where
+ // one is better than the other (e.g. localhost vs LAN).
+ return res.answer, res.addr, nil
+ case <-ctx.Done():
+ return dnsmessage.ResourceHeader{}, netip.Addr{}, errContextElapsed
+ }
+ }
+}
+
+type ipToBytesError struct {
+ addr netip.Addr
+ expectedType string
+}
+
+func (err ipToBytesError) Error() string {
+ return fmt.Sprintf("ip (%s) is not %s", err.addr, err.expectedType)
+}
+
+// assumes ipv4-to-ipv6 mapping has been checked
+func ipv4ToBytes(ipAddr netip.Addr) ([4]byte, error) {
+ if !ipAddr.Is4() {
+ return [4]byte{}, ipToBytesError{ipAddr, "IPv4"}
+ }
+
+ md, err := ipAddr.MarshalBinary()
+ if err != nil {
+ return [4]byte{}, err
+ }
+
+ // net.IPs are stored in big endian / network byte order
+ var out [4]byte
+ copy(out[:], md)
+ return out, nil
+}
+
+// assumes ipv4-to-ipv6 mapping has been checked
+func ipv6ToBytes(ipAddr netip.Addr) ([16]byte, error) {
+ if !ipAddr.Is6() {
+ return [16]byte{}, ipToBytesError{ipAddr, "IPv6"}
+ }
+ md, err := ipAddr.MarshalBinary()
+ if err != nil {
+ return [16]byte{}, err
+ }
+
+ // net.IPs are stored in big endian / network byte order
+ var out [16]byte
+ copy(out[:], md)
+ return out, nil
+}
+
+type ipToAddrError struct {
+ ip []byte
+}
+
+func (err ipToAddrError) Error() string {
+ return fmt.Sprintf("failed to convert ip address '%s' to netip.Addr", err.ip)
+}
+
+func interfaceForRemote(remote string) (*netip.Addr, error) {
+ conn, err := net.Dial("udp", remote)
+ if err != nil {
+ return nil, err
+ }
+
+ localAddr, ok := conn.LocalAddr().(*net.UDPAddr)
+ if !ok {
+ return nil, errFailedCast
+ }
+
+ if err := conn.Close(); err != nil {
+ return nil, err
+ }
+
+ ipAddr, ok := netip.AddrFromSlice(localAddr.IP)
+ if !ok {
+ return nil, ipToAddrError{localAddr.IP}
+ }
+ ipAddr = addrWithOptionalZone(ipAddr, localAddr.Zone)
+ return &ipAddr, nil
+}
+
+type writeType byte
+
+const (
+ writeTypeQuestion writeType = iota
+ writeTypeAnswer
+)
+
+func (c *Conn) sendQuestion(name string) {
+ packedName, err := dnsmessage.NewName(name)
+ if err != nil {
+ c.log.Warnf("[%s] failed to construct mDNS packet %v", c.name, err)
+ return
+ }
+
+ // https://datatracker.ietf.org/doc/html/draft-ietf-rtcweb-mdns-ice-candidates-04#section-3.2.1
+ //
+ // 2. Otherwise, resolve the candidate using mDNS. The ICE agent
+ // SHOULD set the unicast-response bit of the corresponding mDNS
+ // query message; this minimizes multicast traffic, as the response
+ // is probably only useful to the querying node.
+ //
+ // 18.12. Repurposing of Top Bit of qclass in Question Section
+ //
+ // In the Question Section of a Multicast DNS query, the top bit of the
+ // qclass field is used to indicate that unicast responses are preferred
+ // for this particular question. (See Section 5.4.)
+ //
+ // We'll follow this up sending on our unicast based packet connections so that we can
+ // get a unicast response back.
+ msg := dnsmessage.Message{
+ Header: dnsmessage.Header{},
+ }
+
+ // limit what we ask for based on what IPv is available. In the future,
+ // this could be an option since there's no reason you cannot get an
+ // A record on an IPv6 sourced question and vice versa.
+ if c.multicastPktConnV4 != nil {
+ msg.Questions = append(msg.Questions, dnsmessage.Question{
+ Type: dnsmessage.TypeA,
+ Class: dnsmessage.ClassINET | (1 << 15),
+ Name: packedName,
+ })
+ }
+ if c.multicastPktConnV6 != nil {
+ msg.Questions = append(msg.Questions, dnsmessage.Question{
+ Type: dnsmessage.TypeAAAA,
+ Class: dnsmessage.ClassINET | (1 << 15),
+ Name: packedName,
+ })
+ }
+
+ rawQuery, err := msg.Pack()
+ if err != nil {
+ c.log.Warnf("[%s] failed to construct mDNS packet %v", c.name, err)
+ return
+ }
+
+ c.writeToSocket(-1, rawQuery, false, false, writeTypeQuestion, nil)
+}
+
+//nolint:gocognit
+func (c *Conn) writeToSocket(
+ ifIndex int,
+ b []byte,
+ hasLoopbackData bool,
+ hasIPv6Zone bool,
+ wType writeType,
+ unicastDst *net.UDPAddr,
+) {
+ var dst4, dst6 net.Addr
+ if wType == writeTypeAnswer {
+ if unicastDst == nil {
+ dst4 = c.dstAddr4
+ dst6 = c.dstAddr6
+ } else {
+ if unicastDst.IP.To4() == nil {
+ dst6 = unicastDst
+ } else {
+ dst4 = unicastDst
+ }
+ }
+ }
+
+ if ifIndex != -1 {
+ if wType == writeTypeQuestion {
+ c.log.Errorf("[%s] Unexpected question using specific interface index %d; dropping question", c.name, ifIndex)
+ return
+ }
+
+ ifc, ok := c.ifaces[ifIndex]
+ if !ok {
+ c.log.Warnf("[%s] no interface for %d", c.name, ifIndex)
+ return
+ }
+ if hasLoopbackData && ifc.Flags&net.FlagLoopback == 0 {
+ // avoid accidentally tricking the destination that itself is the same as us
+ c.log.Debugf("[%s] interface is not loopback %d", c.name, ifIndex)
+ return
+ }
+
+ c.log.Debugf("[%s] writing answer to IPv4: %v, IPv6: %v", c.name, dst4, dst6)
+
+ if ifc.supportsV4 && c.multicastPktConnV4 != nil && dst4 != nil {
+ if !hasIPv6Zone {
+ if _, err := c.multicastPktConnV4.WriteTo(b, &ifc.Interface, nil, dst4); err != nil {
+ c.log.Warnf("[%s] failed to send mDNS packet on IPv4 interface %d: %v", c.name, ifIndex, err)
+ }
+ } else {
+ c.log.Debugf("[%s] refusing to send mDNS packet with IPv6 zone over IPv4", c.name)
+ }
+ }
+ if ifc.supportsV6 && c.multicastPktConnV6 != nil && dst6 != nil {
+ if _, err := c.multicastPktConnV6.WriteTo(b, &ifc.Interface, nil, dst6); err != nil {
+ c.log.Warnf("[%s] failed to send mDNS packet on IPv6 interface %d: %v", c.name, ifIndex, err)
+ }
+ }
+
+ return
+ }
+ for ifcIdx := range c.ifaces {
+ ifc := c.ifaces[ifcIdx]
+ if hasLoopbackData {
+ c.log.Debugf("[%s] Refusing to send loopback data with non-specific interface", c.name)
+ continue
+ }
+
+ if wType == writeTypeQuestion {
+ // we'll write via unicast if we can in case the responder chooses to respond to the address the request
+ // came from (i.e. not respecting unicast-response bit). If we were to use the multicast packet
+ // conn here, we'd be writing from a specific multicast address which won't be able to receive unicast
+ // traffic (it only works when listening on 0.0.0.0/[::]).
+ if c.unicastPktConnV4 == nil && c.unicastPktConnV6 == nil {
+ c.log.Debugf("[%s] writing question to multicast IPv4/6 %s", c.name, c.dstAddr4)
+ if ifc.supportsV4 && c.multicastPktConnV4 != nil {
+ if _, err := c.multicastPktConnV4.WriteTo(b, &ifc.Interface, nil, c.dstAddr4); err != nil {
+ c.log.Warnf("[%s] failed to send mDNS packet (multicast) on IPv4 interface %d: %v", c.name, ifc.Index, err)
+ }
+ }
+ if ifc.supportsV6 && c.multicastPktConnV6 != nil {
+ if _, err := c.multicastPktConnV6.WriteTo(b, &ifc.Interface, nil, c.dstAddr6); err != nil {
+ c.log.Warnf("[%s] failed to send mDNS packet (multicast) on IPv6 interface %d: %v", c.name, ifc.Index, err)
+ }
+ }
+ }
+ if ifc.supportsV4 && c.unicastPktConnV4 != nil {
+ c.log.Debugf("[%s] writing question to unicast IPv4 %s", c.name, c.dstAddr4)
+ if _, err := c.unicastPktConnV4.WriteTo(b, &ifc.Interface, nil, c.dstAddr4); err != nil {
+ c.log.Warnf("[%s] failed to send mDNS packet (unicast) on interface %d: %v", c.name, ifc.Index, err)
+ }
+ }
+ if ifc.supportsV6 && c.unicastPktConnV6 != nil {
+ c.log.Debugf("[%s] writing question to unicast IPv6 %s", c.name, c.dstAddr6)
+ if _, err := c.unicastPktConnV6.WriteTo(b, &ifc.Interface, nil, c.dstAddr6); err != nil {
+ c.log.Warnf("[%s] failed to send mDNS packet (unicast) on interface %d: %v", c.name, ifc.Index, err)
+ }
+ }
+ } else {
+ c.log.Debugf("[%s] writing answer to IPv4: %v, IPv6: %v", c.name, dst4, dst6)
+
+ if ifc.supportsV4 && c.multicastPktConnV4 != nil && dst4 != nil {
+ if !hasIPv6Zone {
+ if _, err := c.multicastPktConnV4.WriteTo(b, &ifc.Interface, nil, dst4); err != nil {
+ c.log.Warnf("[%s] failed to send mDNS packet (multicast) on IPv4 interface %d: %v", c.name, ifIndex, err)
+ }
+ } else {
+ c.log.Debugf("[%s] refusing to send mDNS packet with IPv6 zone over IPv4", c.name)
+ }
+ }
+ if ifc.supportsV6 && c.multicastPktConnV6 != nil && dst6 != nil {
+ if _, err := c.multicastPktConnV6.WriteTo(b, &ifc.Interface, nil, dst6); err != nil {
+ c.log.Warnf("[%s] failed to send mDNS packet (multicast) on IPv6 interface %d: %v", c.name, ifIndex, err)
+ }
+ }
+ }
+ }
+}
+
+func createAnswer(id uint16, name string, addr netip.Addr) (dnsmessage.Message, error) {
+ packedName, err := dnsmessage.NewName(name)
+ if err != nil {
+ return dnsmessage.Message{}, err
+ }
+
+ msg := dnsmessage.Message{
+ Header: dnsmessage.Header{
+ ID: id,
+ Response: true,
+ Authoritative: true,
+ },
+ Answers: []dnsmessage.Resource{
+ {
+ Header: dnsmessage.ResourceHeader{
+ Class: dnsmessage.ClassINET,
+ Name: packedName,
+ TTL: responseTTL,
+ },
+ },
+ },
+ }
+
+ if addr.Is4() {
+ ipBuf, err := ipv4ToBytes(addr)
+ if err != nil {
+ return dnsmessage.Message{}, err
+ }
+ msg.Answers[0].Header.Type = dnsmessage.TypeA
+ msg.Answers[0].Body = &dnsmessage.AResource{
+ A: ipBuf,
+ }
+ } else if addr.Is6() {
+ // we will lose the zone here, but the receiver can reconstruct it
+ ipBuf, err := ipv6ToBytes(addr)
+ if err != nil {
+ return dnsmessage.Message{}, err
+ }
+ msg.Answers[0].Header.Type = dnsmessage.TypeAAAA
+ msg.Answers[0].Body = &dnsmessage.AAAAResource{
+ AAAA: ipBuf,
+ }
+ }
+
+ return msg, nil
+}
+
+func (c *Conn) sendAnswer(queryID uint16, name string, ifIndex int, result netip.Addr, dst *net.UDPAddr) {
+ answer, err := createAnswer(queryID, name, result)
+ if err != nil {
+ c.log.Warnf("[%s] failed to create mDNS answer %v", c.name, err)
+ return
+ }
+
+ rawAnswer, err := answer.Pack()
+ if err != nil {
+ c.log.Warnf("[%s] failed to construct mDNS packet %v", c.name, err)
+ return
+ }
+
+ c.writeToSocket(
+ ifIndex,
+ rawAnswer,
+ result.IsLoopback(),
+ result.Is6() && result.Zone() != "",
+ writeTypeAnswer,
+ dst,
+ )
+}
+
+type ipControlMessage struct {
+ IfIndex int
+ Dst net.IP
+}
+
+type ipPacketConn interface {
+ ReadFrom(b []byte) (n int, cm *ipControlMessage, src net.Addr, err error)
+ WriteTo(b []byte, via *net.Interface, cm *ipControlMessage, dst net.Addr) (n int, err error)
+ Close() error
+}
+
+type ipPacketConn4 struct {
+ name string
+ conn *ipv4.PacketConn
+ log logging.LeveledLogger
+}
+
+func (c ipPacketConn4) ReadFrom(b []byte) (n int, cm *ipControlMessage, src net.Addr, err error) {
+ n, cm4, src, err := c.conn.ReadFrom(b)
+ if err != nil || cm4 == nil {
+ return n, nil, src, err
+ }
+ return n, &ipControlMessage{IfIndex: cm4.IfIndex, Dst: cm4.Dst}, src, err
+}
+
+func (c ipPacketConn4) WriteTo(b []byte, via *net.Interface, cm *ipControlMessage, dst net.Addr) (n int, err error) {
+ var cm4 *ipv4.ControlMessage
+ if cm != nil {
+ cm4 = &ipv4.ControlMessage{
+ IfIndex: cm.IfIndex,
+ }
+ }
+ if err := c.conn.SetMulticastInterface(via); err != nil {
+ c.log.Warnf("[%s] failed to set multicast interface for %d: %v", c.name, via.Index, err)
+ return 0, err
+ }
+ return c.conn.WriteTo(b, cm4, dst)
+}
+
+func (c ipPacketConn4) Close() error {
+ return c.conn.Close()
+}
+
+type ipPacketConn6 struct {
+ name string
+ conn *ipv6.PacketConn
+ log logging.LeveledLogger
+}
+
+func (c ipPacketConn6) ReadFrom(b []byte) (n int, cm *ipControlMessage, src net.Addr, err error) {
+ n, cm6, src, err := c.conn.ReadFrom(b)
+ if err != nil || cm6 == nil {
+ return n, nil, src, err
+ }
+ return n, &ipControlMessage{IfIndex: cm6.IfIndex, Dst: cm6.Dst}, src, err
+}
+
+func (c ipPacketConn6) WriteTo(b []byte, via *net.Interface, cm *ipControlMessage, dst net.Addr) (n int, err error) {
+ var cm6 *ipv6.ControlMessage
+ if cm != nil {
+ cm6 = &ipv6.ControlMessage{
+ IfIndex: cm.IfIndex,
+ }
+ }
+ if err := c.conn.SetMulticastInterface(via); err != nil {
+ c.log.Warnf("[%s] failed to set multicast interface for %d: %v", c.name, via.Index, err)
+ return 0, err
+ }
+ return c.conn.WriteTo(b, cm6, dst)
+}
+
+func (c ipPacketConn6) Close() error {
+ return c.conn.Close()
+}
+
+func (c *Conn) readLoop(name string, pktConn ipPacketConn, inboundBufferSize int, config *Config) { //nolint:gocognit
+ b := make([]byte, inboundBufferSize)
+ p := dnsmessage.Parser{}
+
+ for {
+ n, cm, src, err := pktConn.ReadFrom(b)
+ if err != nil {
+ if errors.Is(err, net.ErrClosed) {
+ return
+ }
+ c.log.Warnf("[%s] failed to ReadFrom %q %v", c.name, src, err)
+ continue
+ }
+ c.log.Debugf("[%s] got read on %s from %s", c.name, name, src)
+
+ var ifIndex int
+ var pktDst net.IP
+ if cm != nil {
+ ifIndex = cm.IfIndex
+ pktDst = cm.Dst
+ } else {
+ ifIndex = -1
+ }
+ srcAddr, ok := src.(*net.UDPAddr)
+ if !ok {
+ c.log.Warnf("[%s] expected source address %s to be UDP but got %", c.name, src, src)
+ continue
+ }
+
+ func() {
+ header, err := p.Start(b[:n])
+ if err != nil {
+ c.log.Warnf("[%s] failed to parse mDNS packet %v", c.name, err)
+ return
+ }
+
+ for i := 0; i <= maxMessageRecords; i++ {
+ q, err := p.Question()
+ if errors.Is(err, dnsmessage.ErrSectionDone) {
+ break
+ } else if err != nil {
+ c.log.Warnf("[%s] failed to parse mDNS packet %v", c.name, err)
+ return
+ }
+
+ if q.Type != dnsmessage.TypeA && q.Type != dnsmessage.TypeAAAA {
+ continue
+ }
+
+ // https://datatracker.ietf.org/doc/html/rfc6762#section-6
+ // The destination UDP port in all Multicast DNS responses MUST be 5353,
+ // and the destination address MUST be the mDNS IPv4 link-local
+ // multicast address 224.0.0.251 or its IPv6 equivalent FF02::FB, except
+ // when generating a reply to a query that explicitly requested a
+ // unicast response
+ shouldUnicastResponse := (q.Class&(1<<15)) != 0 || // via the unicast-response bit
+ srcAddr.Port != 5353 || // by virtue of being a legacy query (Section 6.7), or
+ (len(pktDst) != 0 && !(pktDst.Equal(c.dstAddr4.IP) || // by virtue of being a direct unicast query
+ pktDst.Equal(c.dstAddr6.IP)))
+ var dst *net.UDPAddr
+ if shouldUnicastResponse {
+ dst = srcAddr
+ }
+
+ queryWantsV4 := q.Type == dnsmessage.TypeA
+
+ for _, localName := range c.localNames {
+ if localName == q.Name.String() {
+ var localAddress *netip.Addr
+ if config.LocalAddress != nil {
+ // this means the LocalAddress does not support link-local since
+ // we have no zone to set here.
+ ipAddr, ok := netip.AddrFromSlice(config.LocalAddress)
+ if !ok {
+ c.log.Warnf("[%s] failed to convert config.LocalAddress '%s' to netip.Addr", c.name, config.LocalAddress)
+ continue
+ }
+ if c.multicastPktConnV4 != nil {
+ // don't want mapping since we also support IPv4/A
+ ipAddr = ipAddr.Unmap()
+ }
+ localAddress = &ipAddr
+ } else {
+ // prefer the address of the interface if we know its index, but otherwise
+ // derive it from the address we read from. We do this because even if
+ // multicast loopback is in use or we send from a loopback interface,
+ // there are still cases where the IP packet will contain the wrong
+ // source IP (e.g. a LAN interface).
+ // For example, we can have a packet that has:
+ // Source: 192.168.65.3
+ // Destination: 224.0.0.251
+ // Interface Index: 1
+ // Interface Addresses @ 1: [127.0.0.1/8 ::1/128]
+ if ifIndex != -1 {
+ ifc, ok := c.ifaces[ifIndex]
+ if !ok {
+ c.log.Warnf("[%s] no interface for %d", c.name, ifIndex)
+ return
+ }
+ var selectedAddrs []netip.Addr
+ for _, addr := range ifc.ipAddrs {
+ addrCopy := addr
+
+ // match up respective IP types based on question
+ if queryWantsV4 {
+ if addrCopy.Is4In6() {
+ // we may allow 4-in-6, but the question wants an A record
+ addrCopy = addrCopy.Unmap()
+ }
+ if !addrCopy.Is4() {
+ continue
+ }
+ } else { // queryWantsV6
+ if !addrCopy.Is6() {
+ continue
+ }
+ if !isSupportedIPv6(addrCopy, c.multicastPktConnV4 == nil) {
+ c.log.Debugf("[%s] interface %d address not a supported IPv6 address %s", c.name, ifIndex, &addrCopy)
+ continue
+ }
+ }
+
+ selectedAddrs = append(selectedAddrs, addrCopy)
+ }
+ if len(selectedAddrs) == 0 {
+ c.log.Debugf("[%s] failed to find suitable IP for interface %d; deriving address from source address c.name,instead", c.name, ifIndex)
+ } else {
+ // choose the best match
+ var choice *netip.Addr
+ for _, option := range selectedAddrs {
+ optCopy := option
+ if option.Is4() {
+ // select first
+ choice = &optCopy
+ break
+ }
+ // we're okay with 4In6 for now but ideally we get a an actual IPv6.
+ // Maybe in the future we never want this but it does look like Docker
+ // can route IPv4 over IPv6.
+ if choice == nil {
+ choice = &optCopy
+ } else if !optCopy.Is4In6() {
+ choice = &optCopy
+ }
+ if !optCopy.Is4In6() {
+ break
+ }
+ // otherwise keep searching for an actual IPv6
+ }
+ localAddress = choice
+ }
+ }
+ if ifIndex == -1 || localAddress == nil {
+ localAddress, err = interfaceForRemote(src.String())
+ if err != nil {
+ c.log.Warnf("[%s] failed to get local interface to communicate with %s: %v", c.name, src.String(), err)
+ continue
+ }
+ }
+ }
+ if queryWantsV4 {
+ if !localAddress.Is4() {
+ c.log.Debugf("[%s] have IPv6 address %s to respond with but question is for A not c.name,AAAA", c.name, localAddress)
+ continue
+ }
+ } else {
+ if !localAddress.Is6() {
+ c.log.Debugf("[%s] have IPv4 address %s to respond with but question is for AAAA not c.name,A", c.name, localAddress)
+ continue
+ }
+ if !isSupportedIPv6(*localAddress, c.multicastPktConnV4 == nil) {
+ c.log.Debugf("[%s] got local interface address but not a supported IPv6 address %v", c.name, localAddress)
+ continue
+ }
+ }
+
+ if dst != nil && len(dst.IP) == net.IPv4len &&
+ localAddress.Is6() &&
+ localAddress.Zone() != "" &&
+ (localAddress.IsLinkLocalUnicast() || localAddress.IsLinkLocalMulticast()) {
+ // This case happens when multicast v4 picks up an AAAA question that has a zone
+ // in the address. Since we cannot send this zone over DNS (it's meaningless),
+ // the other side can only infer this via the response interface on the other
+ // side (some IPv6 interface).
+ c.log.Debugf("[%s] refusing to send link-local address %s to an IPv4 destination %s", c.name, localAddress, dst)
+ continue
+ }
+ c.log.Debugf("[%s] sending response for %s on ifc %d of %s to %s", c.name, q.Name, ifIndex, *localAddress, dst)
+ c.sendAnswer(header.ID, q.Name.String(), ifIndex, *localAddress, dst)
+ }
+ }
+ }
+
+ for i := 0; i <= maxMessageRecords; i++ {
+ a, err := p.AnswerHeader()
+ if errors.Is(err, dnsmessage.ErrSectionDone) {
+ return
+ }
+ if err != nil {
+ c.log.Warnf("[%s] failed to parse mDNS packet %v", c.name, err)
+ return
+ }
+
+ if a.Type != dnsmessage.TypeA && a.Type != dnsmessage.TypeAAAA {
+ continue
+ }
+
+ c.mu.Lock()
+ queries := make([]*query, len(c.queries))
+ copy(queries, c.queries)
+ c.mu.Unlock()
+
+ var answered []*query
+ for _, query := range queries {
+ queryCopy := query
+ if queryCopy.nameWithSuffix == a.Name.String() {
+ addr, err := addrFromAnswerHeader(a, p)
+ if err != nil {
+ c.log.Warnf("[%s] failed to parse mDNS answer %v", c.name, err)
+ return
+ }
+
+ resultAddr := *addr
+ // DNS records don't contain IPv6 zones.
+ // We're trusting that since we're on the same link, that we will only
+ // be sent link-local addresses from that source's interface's address.
+ // If it's not present, we're out of luck since we cannot rely on the
+ // interface zone to be the same as the source's.
+ resultAddr = addrWithOptionalZone(resultAddr, srcAddr.Zone)
+
+ select {
+ case queryCopy.queryResultChan <- queryResult{a, resultAddr}:
+ answered = append(answered, queryCopy)
+ default:
+ }
+ }
+ }
+
+ c.mu.Lock()
+ for queryIdx := len(c.queries) - 1; queryIdx >= 0; queryIdx-- {
+ for answerIdx := len(answered) - 1; answerIdx >= 0; answerIdx-- {
+ if c.queries[queryIdx] == answered[answerIdx] {
+ c.queries = append(c.queries[:queryIdx], c.queries[queryIdx+1:]...)
+ answered = append(answered[:answerIdx], answered[answerIdx+1:]...)
+ queryIdx--
+ break
+ }
+ }
+ }
+ c.mu.Unlock()
+ }
+ }()
+ }
+}
+
+func (c *Conn) start(started chan<- struct{}, inboundBufferSize int, config *Config) {
+ defer func() {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ close(c.closed)
+ }()
+
+ var numReaders int
+ readerStarted := make(chan struct{})
+ readerEnded := make(chan struct{})
+
+ if c.multicastPktConnV4 != nil {
+ numReaders++
+ go func() {
+ defer func() {
+ readerEnded <- struct{}{}
+ }()
+ readerStarted <- struct{}{}
+ c.readLoop("multi4", c.multicastPktConnV4, inboundBufferSize, config)
+ }()
+ }
+ if c.multicastPktConnV6 != nil {
+ numReaders++
+ go func() {
+ defer func() {
+ readerEnded <- struct{}{}
+ }()
+ readerStarted <- struct{}{}
+ c.readLoop("multi6", c.multicastPktConnV6, inboundBufferSize, config)
+ }()
+ }
+ if c.unicastPktConnV4 != nil {
+ numReaders++
+ go func() {
+ defer func() {
+ readerEnded <- struct{}{}
+ }()
+ readerStarted <- struct{}{}
+ c.readLoop("uni4", c.unicastPktConnV4, inboundBufferSize, config)
+ }()
+ }
+ if c.unicastPktConnV6 != nil {
+ numReaders++
+ go func() {
+ defer func() {
+ readerEnded <- struct{}{}
+ }()
+ readerStarted <- struct{}{}
+ c.readLoop("uni6", c.unicastPktConnV6, inboundBufferSize, config)
+ }()
+ }
+ for i := 0; i < numReaders; i++ {
+ <-readerStarted
+ }
+ close(started)
+ for i := 0; i < numReaders; i++ {
+ <-readerEnded
+ }
+}
+
+func addrFromAnswerHeader(a dnsmessage.ResourceHeader, p dnsmessage.Parser) (addr *netip.Addr, err error) {
+ if a.Type == dnsmessage.TypeA {
+ resource, err := p.AResource()
+ if err != nil {
+ return nil, err
+ }
+ ipAddr, ok := netip.AddrFromSlice(resource.A[:])
+ if !ok {
+ return nil, fmt.Errorf("failed to convert A record: %w", ipToAddrError{resource.A[:]})
+ }
+ ipAddr = ipAddr.Unmap() // do not want 4-in-6
+ addr = &ipAddr
+ } else {
+ resource, err := p.AAAAResource()
+ if err != nil {
+ return nil, err
+ }
+ ipAddr, ok := netip.AddrFromSlice(resource.AAAA[:])
+ if !ok {
+ return nil, fmt.Errorf("failed to convert AAAA record: %w", ipToAddrError{resource.AAAA[:]})
+ }
+ addr = &ipAddr
+ }
+
+ return
+}
+
+func isSupportedIPv6(addr netip.Addr, ipv6Only bool) bool {
+ if !addr.Is6() {
+ return false
+ }
+ // IPv4-mapped-IPv6 addresses cannot be connected to unless
+ // unmapped.
+ if !ipv6Only && addr.Is4In6() {
+ return false
+ }
+ return true
+}
+
+func addrWithOptionalZone(addr netip.Addr, zone string) netip.Addr {
+ if zone == "" {
+ return addr
+ }
+ if addr.Is6() && (addr.IsLinkLocalUnicast() || addr.IsLinkLocalMulticast()) {
+ return addr.WithZone(zone)
+ }
+ return addr
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/errors.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/errors.go
similarity index 80%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/errors.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/errors.go
index 0a1b61240..f065e165f 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/errors.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/errors.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package mdns
import "errors"
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/mdns.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/mdns.go
new file mode 100644
index 000000000..a76b16b65
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/mdns.go
@@ -0,0 +1,5 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package mdns implements mDNS (multicast DNS)
+package mdns
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v2/renovate.json b/trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/renovate.json
similarity index 100%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v2/renovate.json
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/mdns/v2/renovate.json
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/.gitignore b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/.gitignore
index f977e7485..6e2f206a9 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/.gitignore
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/.gitignore
@@ -1,3 +1,6 @@
+# SPDX-FileCopyrightText: 2023 The Pion community
+# SPDX-License-Identifier: MIT
+
### JetBrains IDE ###
#####################
.idea/
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/.golangci.yml b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/.golangci.yml
index d7a88eca3..a3235bec2 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/.golangci.yml
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/.golangci.yml
@@ -1,6 +1,13 @@
+# SPDX-FileCopyrightText: 2023 The Pion community
+# SPDX-License-Identifier: MIT
+
+run:
+ timeout: 5m
+
linters-settings:
govet:
- check-shadowing: true
+ enable:
+ - shadow
misspell:
locale: US
exhaustive:
@@ -10,7 +17,14 @@ linters-settings:
modules:
- github.com/pkg/errors:
recommendations:
- - errors
+ - errors
+ forbidigo:
+ forbid:
+ - ^fmt.Print(f|ln)?$
+ - ^log.(Panic|Fatal|Print)(f|ln)?$
+ - ^os.Exit$
+ - ^panic$
+ - ^print(ln)?$
linters:
enable:
@@ -18,9 +32,7 @@ linters:
- bidichk # Checks for dangerous unicode character sequences
- bodyclose # checks whether HTTP response body is closed successfully
- contextcheck # check the function whether use a non-inherited context
- - deadcode # Finds unused code
- decorder # check declaration order and count of types, constants, variables and functions
- - depguard # Go linter that checks if package imports are in a list of acceptable packages
- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
- dupl # Tool for code clone detection
- durationcheck # check for two durations multiplied together
@@ -30,6 +42,7 @@ linters:
- errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13.
- exhaustive # check exhaustiveness of enum switch statements
- exportloopref # checks for pointers to enclosing loop variables
+ - forbidigo # Forbids identifiers
- forcetypeassert # finds forced type assertions
- gci # Gci control golang package import order and make it always deterministic.
- gochecknoglobals # Checks that no globals are present in Go code
@@ -38,7 +51,7 @@ linters:
- goconst # Finds repeated strings that could be replaced by a constant
- gocritic # The most opinionated Go source code linter
- godox # Tool for detection of FIXME, TODO and other comment keywords
- - goerr113 # Golang linter to check the errors handling expressions
+ - err113 # Golang linter to check the errors handling expressions
- gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
- gofumpt # Gofumpt checks whether code was gofumpt-ed.
- goheader # Checks is file header matches to pattern
@@ -53,14 +66,12 @@ linters:
- importas # Enforces consistent import aliases
- ineffassign # Detects when assignments to existing variables are not used
- misspell # Finds commonly misspelled English words in comments
- - nakedret # Finds naked returns in functions greater than a specified function length
- nilerr # Finds the code that returns nil even if it checks that the error is not nil.
- nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value.
- noctx # noctx finds sending http request without context.Context
- predeclared # find code that shadows one of Go's predeclared identifiers
- revive # golint replacement, finds style mistakes
- staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
- - structcheck # Finds unused struct fields
- stylecheck # Stylecheck is a replacement for golint
- tagliatelle # Checks the struct tags.
- tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17
@@ -69,24 +80,21 @@ linters:
- unconvert # Remove unnecessary type conversions
- unparam # Reports unused function parameters
- unused # Checks Go code for unused constants, variables, functions and types
- - varcheck # Finds unused global variables and constants
- wastedassign # wastedassign finds wasted assignment statements
- whitespace # Tool for detection of leading and trailing whitespace
disable:
+ - depguard # Go linter that checks if package imports are in a list of acceptable packages
- containedctx # containedctx is a linter that detects struct contained context.Context field
- cyclop # checks function and package cyclomatic complexity
- - exhaustivestruct # Checks if all struct's fields are initialized
- - forbidigo # Forbids identifiers
- funlen # Tool for detection of long functions
- gocyclo # Computes and checks the cyclomatic complexity of functions
- godot # Check if comments end in a period
- gomnd # An analyzer to detect magic numbers.
- - ifshort # Checks that your code uses short syntax for if-statements whenever possible
- ireturn # Accept Interfaces, Return Concrete Types
- lll # Reports long lines
- maintidx # maintidx measures the maintainability index of each function.
- makezero # Finds slice declarations with non-zero initial length
- - maligned # Tool to detect Go structs that would take less memory if their fields were sorted
+ - nakedret # Finds naked returns in functions greater than a specified function length
- nestif # Reports deeply nested if statements
- nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
- nolintlint # Reports ill-formed or insufficient nolint directives
@@ -103,17 +111,15 @@ linters:
issues:
exclude-use-default: false
+ exclude-dirs-use-default: false
exclude-rules:
- # Allow complex tests, better to be self contained
- - path: _test\.go
+ # Allow complex tests and examples, better to be self contained
+ - path: (examples|main\.go|_test\.go)
linters:
+ - forbidigo
- gocognit
- # Allow complex main function in examples
- - path: examples
- text: "of func `main` is high"
+ # Allow forbidden identifiers in CLI commands
+ - path: cmd
linters:
- - gocognit
-
-run:
- skip-dirs-use-default: false
+ - forbidigo
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/.goreleaser.yml b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/.goreleaser.yml
similarity index 100%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/.goreleaser.yml
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/.goreleaser.yml
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/AUTHORS.txt b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/AUTHORS.txt
deleted file mode 100644
index 5d5a53cc2..000000000
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/AUTHORS.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-# Thank you to everyone that made Pion possible. If you are interested in contributing
-# we would love to have you https://github.com/pion/webrtc/wiki/Contributing
-#
-# This file is auto generated, using git to list all individuals contributors.
-# see `.github/generate-authors.sh` for the scripting
-Adam Roach
-adwpc
-aggresss
-Atsushi Watanabe
-cnderrauber
-Gabor Pongracz
-Hugo Arregui
-Hugo Arregui
-Juliusz Chroboczek
-Kevin Wang
-lllf
-Luke Curley
-Mathis Engelbart
-Max Hawkins
-Sean DuBois
-Sean DuBois
-Simone Gotti
-Woodrow Douglass
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/LICENSE b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/LICENSE
index ab602974d..491caf6b0 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/LICENSE
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/LICENSE
@@ -1,21 +1,9 @@
MIT License
-Copyright (c) 2018
+Copyright (c) 2023 The Pion community
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/README.md b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/README.md
index ec82c3fa6..063574aa9 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/README.md
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/README.md
@@ -9,8 +9,8 @@
-
-
+
+
@@ -23,14 +23,15 @@ See [DESIGN.md](DESIGN.md) for an overview of features and future goals.
The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
### Community
-Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion).
+Pion has an active community on the [Slack](https://pion.ly/slack).
+
+Follow the [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news.
We are always looking to support **your projects**. Please reach out if you have something to build!
-
If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
### Contributing
-Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
+Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible
### License
MIT License - see [LICENSE](LICENSE) for full text
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/application_defined.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/application_defined.go
new file mode 100644
index 000000000..77a119395
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/application_defined.go
@@ -0,0 +1,122 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package rtcp
+
+import (
+ "encoding/binary"
+)
+
+// ApplicationDefined represents an RTCP application-defined packet.
+type ApplicationDefined struct {
+ SubType uint8
+ SSRC uint32
+ Name string
+ Data []byte
+}
+
+// DestinationSSRC returns the SSRC value for this packet.
+func (a ApplicationDefined) DestinationSSRC() []uint32 {
+ return []uint32{a.SSRC}
+}
+
+// Marshal serializes the application-defined struct into a byte slice with padding.
+func (a ApplicationDefined) Marshal() ([]byte, error) {
+ dataLength := len(a.Data)
+ if dataLength > 0xFFFF-12 {
+ return nil, errAppDefinedDataTooLarge
+ }
+ if len(a.Name) != 4 {
+ return nil, errAppDefinedInvalidName
+ }
+ // Calculate the padding size to be added to make the packet length a multiple of 4 bytes.
+ paddingSize := 4 - (dataLength % 4)
+ if paddingSize == 4 {
+ paddingSize = 0
+ }
+
+ packetSize := a.MarshalSize()
+ header := Header{
+ Type: TypeApplicationDefined,
+ Length: uint16((packetSize / 4) - 1),
+ Padding: paddingSize != 0,
+ Count: a.SubType,
+ }
+
+ headerBytes, err := header.Marshal()
+ if err != nil {
+ return nil, err
+ }
+
+ rawPacket := make([]byte, packetSize)
+ copy(rawPacket, headerBytes)
+ binary.BigEndian.PutUint32(rawPacket[4:8], a.SSRC)
+ copy(rawPacket[8:12], a.Name)
+ copy(rawPacket[12:], a.Data)
+
+ // Add padding if necessary.
+ if paddingSize > 0 {
+ for i := 0; i < paddingSize; i++ {
+ rawPacket[12+dataLength+i] = byte(paddingSize)
+ }
+ }
+
+ return rawPacket, nil
+}
+
+// Unmarshal parses the given raw packet into an application-defined struct, handling padding.
+func (a *ApplicationDefined) Unmarshal(rawPacket []byte) error {
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |V=2|P| subtype | PT=APP=204 | length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | SSRC/CSRC |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | name (ASCII) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | application-dependent data ...
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ header := Header{}
+ err := header.Unmarshal(rawPacket)
+ if err != nil {
+ return err
+ }
+ if len(rawPacket) < 12 {
+ return errPacketTooShort
+ }
+
+ if int(header.Length+1)*4 != len(rawPacket) {
+ return errAppDefinedInvalidLength
+ }
+
+ a.SubType = header.Count
+ a.SSRC = binary.BigEndian.Uint32(rawPacket[4:8])
+ a.Name = string(rawPacket[8:12])
+
+ // Check for padding.
+ paddingSize := 0
+ if header.Padding {
+ paddingSize = int(rawPacket[len(rawPacket)-1])
+ if paddingSize > len(rawPacket)-12 {
+ return errWrongPadding
+ }
+ }
+
+ a.Data = rawPacket[12 : len(rawPacket)-paddingSize]
+
+ return nil
+}
+
+// MarshalSize returns the size of the packet once marshaled
+func (a *ApplicationDefined) MarshalSize() int {
+ dataLength := len(a.Data)
+ // Calculate the padding size to be added to make the packet length a multiple of 4 bytes.
+ paddingSize := 4 - (dataLength % 4)
+ if paddingSize == 4 {
+ paddingSize = 0
+ }
+ return 12 + dataLength + paddingSize
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/codecov.yml b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/codecov.yml
index 085200a48..263e4d45c 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/codecov.yml
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/codecov.yml
@@ -3,6 +3,8 @@
#
# It is automatically copied from https://github.com/pion/.goassets repository.
#
+# SPDX-FileCopyrightText: 2023 The Pion community
+# SPDX-License-Identifier: MIT
coverage:
status:
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/compound_packet.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/compound_packet.go
index 33448248d..a621c6117 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/compound_packet.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/compound_packet.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtcp
import (
@@ -107,6 +110,15 @@ func (c CompoundPacket) Marshal() ([]byte, error) {
return Marshal(p)
}
+// MarshalSize returns the size of the packet once marshaled
+func (c CompoundPacket) MarshalSize() int {
+ l := 0
+ for _, p := range c {
+ l += p.MarshalSize()
+ }
+ return l
+}
+
// Unmarshal decodes a CompoundPacket from binary.
func (c *CompoundPacket) Unmarshal(rawData []byte) error {
out := make(CompoundPacket, 0)
@@ -121,11 +133,7 @@ func (c *CompoundPacket) Unmarshal(rawData []byte) error {
}
*c = out
- if err := c.Validate(); err != nil {
- return err
- }
-
- return nil
+ return c.Validate()
}
// DestinationSSRC returns the synchronization sources associated with this
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/doc.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/doc.go
index a983c2e45..22f06cc85 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/doc.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/doc.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
/*
Package rtcp implements encoding and decoding of RTCP packets according to RFCs 3550 and 5506.
@@ -35,6 +38,5 @@ Encoding RTCP packets:
}
pliData, err := pkt.Marshal()
// ...
-
*/
package rtcp
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/errors.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/errors.go
index ed5bd0029..05e6847f1 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/errors.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/errors.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtcp
import "errors"
@@ -19,6 +22,7 @@ var (
errSDESMissingType = errors.New("rtcp: sdes item missing type")
errReasonTooLong = errors.New("rtcp: reason must be < 255 octets long")
errBadVersion = errors.New("rtcp: invalid packet version")
+ errBadLength = errors.New("rtcp: invalid packet length")
errWrongPadding = errors.New("rtcp: invalid padding value")
errWrongFeedbackType = errors.New("rtcp: wrong feedback message type")
errWrongPayloadType = errors.New("rtcp: wrong payload type")
@@ -31,4 +35,7 @@ var (
errWrongChunkType = errors.New("rtcp: wrong chunk type")
errBadStructMemberType = errors.New("rtcp: struct contains unexpected member type")
errBadReadParameter = errors.New("rtcp: cannot read into non-pointer")
+ errAppDefinedInvalidLength = errors.New("rtcp: application defined type invalid length")
+ errAppDefinedDataTooLarge = errors.New("rtcp: application defined data is too large")
+ errAppDefinedInvalidName = errors.New("rtcp: application defined name must be 4 ASCII chars")
)
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/extended_report.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/extended_report.go
index 62e576bb4..0f9ee2ccf 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/extended_report.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/extended_report.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtcp
import (
@@ -10,8 +13,9 @@ import (
// more report blocks, each of which conveys a different kind of
// information.
//
-// 0 1 2 3
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+//
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P|reserved | PT=XR=207 | length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -88,8 +92,9 @@ func (t BlockTypeType) String() string {
// Loss RLE report blocks (RFC 3611 §4.1) and Duplicate RLE
// report blocks (RFC 3611 §4.2).
//
-// 0 1 2 3
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+//
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | BT = 1 or 2 | rsvd. | T | block length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -117,27 +122,27 @@ type rleReportBlock struct {
//
// Run Length Chunk:
//
-// 0 1
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// |C|R| run length |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 0 1
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |C|R| run length |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Bit Vector Chunk:
//
-// 0 1
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// |C| bit vector |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 0 1
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |C| bit vector |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Terminating Null Chunk:
//
-// 0 1
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 0 1
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
type Chunk uint16
// LossRLEReportBlock is used to report information about packet
@@ -234,8 +239,9 @@ func (c Chunk) Value() uint {
// PacketReceiptTimesReportBlock represents a Packet Receipt Times
// report block, as described in RFC 3611 section 4.3.
//
-// 0 1 2 3
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+//
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | BT=3 | rsvd. | T | block length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -278,8 +284,9 @@ func (b *PacketReceiptTimesReportBlock) unpackBlockHeader() {
// ReceiverReferenceTimeReportBlock encodes a Receiver Reference Time
// report block as described in RFC 3611 section 4.4.
//
-// 0 1 2 3
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+//
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | BT=4 | reserved | block length = 2 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -309,8 +316,9 @@ func (b *ReceiverReferenceTimeReportBlock) unpackBlockHeader() {
// DLRRReportBlock encodes a DLRR Report Block as described in
// RFC 3611 section 4.5.
//
-// 0 1 2 3
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+//
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | BT=5 | reserved | block length |
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
@@ -357,8 +365,9 @@ func (b *DLRRReportBlock) unpackBlockHeader() {
// StatisticsSummaryReportBlock encodes a Statistics Summary Report
// Block as described in RFC 3611, section 4.6.
//
-// 0 1 2 3
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+//
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | BT=6 |L|D|J|ToH|rsvd.| block length = 9 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -455,8 +464,9 @@ func (b *StatisticsSummaryReportBlock) unpackBlockHeader() {
// VoIPMetricsReportBlock encodes a VoIP Metrics Report Block as described
// in RFC 3611, section 4.7.
//
-// 0 1 2 3
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+//
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | BT=7 | reserved | block length = 8 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -535,6 +545,11 @@ func (b *UnknownReportBlock) setupBlockHeader() {
func (b *UnknownReportBlock) unpackBlockHeader() {
}
+// MarshalSize returns the size of the packet once marshaled
+func (x ExtendedReport) MarshalSize() int {
+ return wireSize(x)
+}
+
// Marshal encodes the ExtendedReport in binary
func (x ExtendedReport) Marshal() ([]byte, error) {
for _, p := range x.Reports {
@@ -631,7 +646,8 @@ func (x *ExtendedReport) Unmarshal(b []byte) error {
// DestinationSSRC returns an array of SSRC values that this packet refers to.
func (x *ExtendedReport) DestinationSSRC() []uint32 {
- ssrc := make([]uint32, 0)
+ ssrc := make([]uint32, 0, len(x.Reports)+1)
+ ssrc = append(ssrc, x.SenderSSRC)
for _, p := range x.Reports {
ssrc = append(ssrc, p.DestinationSSRC()...)
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/full_intra_request.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/full_intra_request.go
index 74ca928d6..7c67c5043 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/full_intra_request.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/full_intra_request.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtcp
import (
@@ -64,6 +67,11 @@ func (p *FullIntraRequest) Unmarshal(rawPacket []byte) error {
return errWrongType
}
+ // The FCI field MUST contain one or more FIR entries
+ if 4*h.Length-firOffset <= 0 || (4*h.Length)%8 != 0 {
+ return errBadLength
+ }
+
p.SenderSSRC = binary.BigEndian.Uint32(rawPacket[headerLength:])
p.MediaSSRC = binary.BigEndian.Uint32(rawPacket[headerLength+ssrcLength:])
for i := headerLength + firOffset; i < (headerLength + int(h.Length*4)); i += 8 {
@@ -80,11 +88,12 @@ func (p *FullIntraRequest) Header() Header {
return Header{
Count: FormatFIR,
Type: TypePayloadSpecificFeedback,
- Length: uint16((p.len() / 4) - 1),
+ Length: uint16((p.MarshalSize() / 4) - 1),
}
}
-func (p *FullIntraRequest) len() int {
+// MarshalSize returns the size of the packet once marshaled
+func (p *FullIntraRequest) MarshalSize() int {
return headerLength + firOffset + len(p.FIR)*8
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/fuzz.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/fuzz.go
deleted file mode 100644
index b8118224a..000000000
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/fuzz.go
+++ /dev/null
@@ -1,52 +0,0 @@
-//go:build gofuzz
-// +build gofuzz
-
-package rtcp
-
-import (
- "bytes"
- "io"
-)
-
-// Fuzz implements a randomized fuzz test of the rtcp
-// parser using go-fuzz.
-//
-// To run the fuzzer, first download go-fuzz:
-// `go get github.com/dvyukov/go-fuzz/...`
-//
-// Then build the testing package:
-// `go-fuzz-build github.com/pion/webrtc`
-//
-// And run the fuzzer on the corpus:
-// ```
-// mkdir workdir
-//
-// # optionally add a starter corpus of valid rtcp packets.
-// # the corpus should be as compact and diverse as possible.
-// cp -r ~/my-rtcp-packets workdir/corpus
-//
-// go-fuzz -bin=ase-fuzz.zip -workdir=workdir
-// ````
-func Fuzz(data []byte) int {
- r := NewReader(bytes.NewReader(data))
- for {
- _, data, err := r.ReadPacket()
- if err == io.EOF {
- break
- }
- if err != nil {
- return 0
- }
-
- packet, err := Unmarshal(data)
- if err != nil {
- return 0
- }
-
- if _, err := packet.Marshal(); err != nil {
- return 0
- }
- }
-
- return 1
-}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/goodbye.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/goodbye.go
index 0d09199b1..f87731c07 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/goodbye.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/goodbye.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtcp
import (
@@ -29,7 +32,7 @@ func (g Goodbye) Marshal() ([]byte, error) {
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
- rawPacket := make([]byte, g.len())
+ rawPacket := make([]byte, g.MarshalSize())
packetBody := rawPacket[headerLength:]
if len(g.Sources) > countMax {
@@ -123,13 +126,18 @@ func (g *Goodbye) Header() Header {
Padding: false,
Count: uint8(len(g.Sources)),
Type: TypeGoodbye,
- Length: uint16((g.len() / 4) - 1),
+ Length: uint16((g.MarshalSize() / 4) - 1),
}
}
-func (g *Goodbye) len() int {
+// MarshalSize returns the size of the packet once marshaled
+func (g *Goodbye) MarshalSize() int {
srcsLength := len(g.Sources) * ssrcLength
- reasonLength := len(g.Reason) + 1
+ // reason is optional
+ reasonLength := len(g.Reason)
+ if reasonLength > 0 {
+ reasonLength++
+ }
l := headerLength + srcsLength + reasonLength
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/header.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/header.go
index 43f6abc06..534c82f2e 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/header.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/header.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtcp
import (
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/packet.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/packet.go
index 885d52cde..fc3c9a3cf 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/packet.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/packet.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtcp
// Packet represents an RTCP packet, a protocol used for out-of-band statistics and control information for an RTP session
@@ -7,6 +10,7 @@ type Packet interface {
Marshal() ([]byte, error)
Unmarshal(rawPacket []byte) error
+ MarshalSize() int
}
// Unmarshal takes an entire udp datagram (which may consist of multiple RTCP packets) and
@@ -110,6 +114,9 @@ func unmarshal(rawData []byte) (packet Packet, bytesprocessed int, err error) {
case TypeExtendedReport:
packet = new(ExtendedReport)
+ case TypeApplicationDefined:
+ packet = new(ApplicationDefined)
+
default:
packet = new(RawPacket)
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/packet_buffer.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/packet_buffer.go
index 9febe9d43..f6c7eaa96 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/packet_buffer.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/packet_buffer.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtcp
import (
@@ -43,7 +46,6 @@ const omit = "omit"
// PacketBuffer is initialized with. This function will
// modify the PacketBuffer.bytes slice to exclude those
// bytes that have been written into.
-//
func (b *packetBuffer) write(v interface{}) error { //nolint:gocognit
value := reflect.ValueOf(v)
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/packet_stringifier.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/packet_stringifier.go
index 888372e15..dd7c01f51 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/packet_stringifier.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/packet_stringifier.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtcp
import (
@@ -6,26 +9,26 @@ import (
)
/*
- Converts an RTCP Packet into a human-readable format. The Packets
- themselves can control the presentation as follows:
+Converts an RTCP Packet into a human-readable format. The Packets
+themselves can control the presentation as follows:
- - Fields of a type that have a String() method will be formatted
- with that String method (which should not emit '\n' characters)
+ - Fields of a type that have a String() method will be formatted
+ with that String method (which should not emit '\n' characters)
- - Otherwise, fields with a tag containing a "fmt" string will use that
- format when serializing the value. For example, to format an SSRC
- value as base 16 insted of base 10:
+ - Otherwise, fields with a tag containing a "fmt" string will use that
+ format when serializing the value. For example, to format an SSRC
+ value as base 16 insted of base 10:
- type ExamplePacket struct {
- LocalSSRC uint32 `fmt:"0x%X"`
- RemotsSSRCs []uint32 `fmt:"%X"`
- }
+ type ExamplePacket struct {
+ LocalSSRC uint32 `fmt:"0x%X"`
+ RemotsSSRCs []uint32 `fmt:"%X"`
+ }
- - If no fmt string is present, "%+v" is used by default
+- If no fmt string is present, "%+v" is used by default
- The intention of this stringify() function is to simplify creation
- of String() methods on new packet types, as it provides a simple
- baseline implementation that works well in the majority of cases.
+The intention of this stringify() function is to simplify creation
+of String() methods on new packet types, as it provides a simple
+baseline implementation that works well in the majority of cases.
*/
func stringify(p Packet) string {
value := reflect.Indirect(reflect.ValueOf(p))
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/picture_loss_indication.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/picture_loss_indication.go
index 86d4cf1b9..56a7de256 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/picture_loss_indication.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/picture_loss_indication.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtcp
import (
@@ -26,7 +29,7 @@ func (p PictureLossIndication) Marshal() ([]byte, error) {
*
* The semantics of this FB message is independent of the payload type.
*/
- rawPacket := make([]byte, p.len())
+ rawPacket := make([]byte, p.MarshalSize())
packetBody := rawPacket[headerLength:]
binary.BigEndian.PutUint32(packetBody, p.SenderSSRC)
@@ -75,7 +78,8 @@ func (p *PictureLossIndication) Header() Header {
}
}
-func (p *PictureLossIndication) len() int {
+// MarshalSize returns the size of the packet once marshaled
+func (p *PictureLossIndication) MarshalSize() int {
return headerLength + ssrcLength*2
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/rapid_resynchronization_request.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/rapid_resynchronization_request.go
index 00c7e8626..dc67d4928 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/rapid_resynchronization_request.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/rapid_resynchronization_request.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtcp
import (
@@ -32,7 +35,7 @@ func (p RapidResynchronizationRequest) Marshal() ([]byte, error) {
*
* The semantics of this FB message is independent of the payload type.
*/
- rawPacket := make([]byte, p.len())
+ rawPacket := make([]byte, p.MarshalSize())
packetBody := rawPacket[headerLength:]
binary.BigEndian.PutUint32(packetBody, p.SenderSSRC)
@@ -67,7 +70,8 @@ func (p *RapidResynchronizationRequest) Unmarshal(rawPacket []byte) error {
return nil
}
-func (p *RapidResynchronizationRequest) len() int {
+// MarshalSize returns the size of the packet once marshaled
+func (p *RapidResynchronizationRequest) MarshalSize() int {
return headerLength + rrrHeaderLength
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/raw_packet.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/raw_packet.go
index d0f949030..eafb034fe 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/raw_packet.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/raw_packet.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtcp
import "fmt"
@@ -40,3 +43,8 @@ func (r RawPacket) String() string {
out := fmt.Sprintf("RawPacket: %v", ([]byte)(r))
return out
}
+
+// MarshalSize returns the size of the packet once marshaled
+func (r RawPacket) MarshalSize() int {
+ return len(r)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/receiver_estimated_maximum_bitrate.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/receiver_estimated_maximum_bitrate.go
index 69d1194b4..7be57e6ab 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/receiver_estimated_maximum_bitrate.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/receiver_estimated_maximum_bitrate.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtcp
import (
@@ -39,9 +42,8 @@ func (p ReceiverEstimatedMaximumBitrate) Marshal() (buf []byte, err error) {
return buf, nil
}
-// MarshalSize returns the size of the packet when marshaled.
-// This can be used in conjunction with `MarshalTo` to avoid allocations.
-func (p ReceiverEstimatedMaximumBitrate) MarshalSize() (n int) {
+// MarshalSize returns the size of the packet once marshaled
+func (p ReceiverEstimatedMaximumBitrate) MarshalSize() int {
return 20 + 4*len(p.SSRCs)
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/receiver_report.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/receiver_report.go
index 22ff233be..e91770206 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/receiver_report.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/receiver_report.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtcp
import (
@@ -55,7 +58,7 @@ func (r ReceiverReport) Marshal() ([]byte, error) {
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
- rawPacket := make([]byte, r.len())
+ rawPacket := make([]byte, r.MarshalSize())
packetBody := rawPacket[headerLength:]
binary.BigEndian.PutUint32(packetBody, r.SSRC)
@@ -154,7 +157,8 @@ func (r *ReceiverReport) Unmarshal(rawPacket []byte) error {
return nil
}
-func (r *ReceiverReport) len() int {
+// MarshalSize returns the size of the packet once marshaled
+func (r *ReceiverReport) MarshalSize() int {
repsLength := 0
for _, rep := range r.Reports {
repsLength += rep.len()
@@ -167,7 +171,7 @@ func (r *ReceiverReport) Header() Header {
return Header{
Count: uint8(len(r.Reports)),
Type: TypeReceiverReport,
- Length: uint16((r.len()/4)-1) + uint16(getPadding(len(r.ProfileExtensions))),
+ Length: uint16((r.MarshalSize()/4)-1) + uint16(getPadding(len(r.ProfileExtensions))),
}
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/reception_report.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/reception_report.go
index 5bff8f2b5..c0b06059c 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/reception_report.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/reception_report.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtcp
import "encoding/binary"
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/renovate.json b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/renovate.json
index f1614058a..f1bb98c6a 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/renovate.json
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/renovate.json
@@ -1,27 +1,6 @@
{
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
- "config:base",
- ":disableDependencyDashboard"
- ],
- "postUpdateOptions": [
- "gomodTidy"
- ],
- "commitBody": "Generated by renovateBot",
- "packageRules": [
- {
- "matchUpdateTypes": ["minor", "patch", "pin", "digest"],
- "automerge": true
- },
- {
- "packagePatterns": ["^golang.org/x/"],
- "schedule": ["on the first day of the month"]
- }
- ],
- "ignorePaths": [
- ".github/workflows/generate-authors.yml",
- ".github/workflows/lint.yaml",
- ".github/workflows/renovate-go-mod-fix.yaml",
- ".github/workflows/test.yaml",
- ".github/workflows/tidy-check.yaml"
+ "github>pion/renovate-config"
]
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/rfc8888.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/rfc8888.go
index 8527fc8e7..544c6e380 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/rfc8888.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/rfc8888.go
@@ -1,9 +1,13 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtcp
import (
"encoding/binary"
"errors"
"fmt"
+ "math"
)
// https://www.rfc-editor.org/rfc/rfc8888.html#name-rtcp-congestion-control-fee
@@ -89,8 +93,13 @@ func (b CCFeedbackReport) DestinationSSRC() []uint32 {
}
// Len returns the length of the report in bytes
-func (b *CCFeedbackReport) Len() uint16 {
- n := uint16(0)
+func (b *CCFeedbackReport) Len() int {
+ return b.MarshalSize()
+}
+
+// MarshalSize returns the size of the packet once marshaled
+func (b *CCFeedbackReport) MarshalSize() int {
+ n := 0
for _, block := range b.ReportBlocks {
n += block.len()
}
@@ -103,7 +112,7 @@ func (b *CCFeedbackReport) Header() Header {
Padding: false,
Count: FormatCCFB,
Type: TypeTransportSpecificFeedback,
- Length: b.Len()/4 - 1,
+ Length: uint16(b.MarshalSize()/4 - 1),
}
}
@@ -118,7 +127,7 @@ func (b CCFeedbackReport) Marshal() ([]byte, error) {
buf := make([]byte, length)
copy(buf[:headerLength], headerBuf)
binary.BigEndian.PutUint32(buf[headerLength:], b.SenderSSRC)
- offset := uint16(reportBlockOffset)
+ offset := reportBlockOffset
for _, block := range b.ReportBlocks {
b, err := block.marshal()
if err != nil {
@@ -160,10 +169,10 @@ func (b *CCFeedbackReport) Unmarshal(rawPacket []byte) error {
b.SenderSSRC = binary.BigEndian.Uint32(rawPacket[headerLength:])
- reportTimestampOffset := uint16(len(rawPacket) - reportTimestampLength)
+ reportTimestampOffset := len(rawPacket) - reportTimestampLength
b.ReportTimestamp = binary.BigEndian.Uint32(rawPacket[reportTimestampOffset:])
- offset := uint16(reportBlockOffset)
+ offset := reportBlockOffset
b.ReportBlocks = []CCFeedbackReportBlock{}
for offset < reportTimestampOffset {
var block CCFeedbackReportBlock
@@ -195,12 +204,12 @@ type CCFeedbackReportBlock struct {
}
// len returns the length of the report block in bytes
-func (b *CCFeedbackReportBlock) len() uint16 {
+func (b *CCFeedbackReportBlock) len() int {
n := len(b.MetricBlocks)
if n%2 != 0 {
n++
}
- return reportsOffset + 2*uint16(n)
+ return reportsOffset + 2*n
}
func (b CCFeedbackReportBlock) String() string {
@@ -253,14 +262,20 @@ func (b *CCFeedbackReportBlock) unmarshal(rawPacket []byte) error {
if numReportsField == 0 {
return nil
}
- endSequence := b.BeginSequence + numReportsField
- numReports := endSequence - b.BeginSequence + 1
- if len(rawPacket) < int(reportsOffset+numReports*2) {
+ if int(b.BeginSequence)+int(numReportsField) > math.MaxUint16 {
return errIncorrectNumReports
}
+
+ endSequence := b.BeginSequence + numReportsField
+ numReports := int(endSequence - b.BeginSequence + 1)
+
+ if len(rawPacket) < reportsOffset+numReports*2 {
+ return errIncorrectNumReports
+ }
+
b.MetricBlocks = make([]CCFeedbackMetricBlock, numReports)
- for i := uint16(0); i < numReports; i++ {
+ for i := int(0); i < numReports; i++ {
var mb CCFeedbackMetricBlock
offset := reportsOffset + 2*i
if err := mb.unmarshal(rawPacket[offset : offset+2]); err != nil {
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/sender_report.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/sender_report.go
index 8e0beeccd..aaee0ee9d 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/sender_report.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/sender_report.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtcp
import (
@@ -93,7 +96,7 @@ func (r SenderReport) Marshal() ([]byte, error) {
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
- rawPacket := make([]byte, r.len())
+ rawPacket := make([]byte, r.MarshalSize())
packetBody := rawPacket[headerLength:]
binary.BigEndian.PutUint32(packetBody[srSSRCOffset:], r.SSRC)
@@ -225,7 +228,8 @@ func (r *SenderReport) DestinationSSRC() []uint32 {
return out
}
-func (r *SenderReport) len() int {
+// MarshalSize returns the size of the packet once marshaled
+func (r *SenderReport) MarshalSize() int {
repsLength := 0
for _, rep := range r.Reports {
repsLength += rep.len()
@@ -238,7 +242,7 @@ func (r *SenderReport) Header() Header {
return Header{
Count: uint8(len(r.Reports)),
Type: TypeSenderReport,
- Length: uint16((r.len() / 4) - 1),
+ Length: uint16((r.MarshalSize() / 4) - 1),
}
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/slice_loss_indication.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/slice_loss_indication.go
index ccd93c998..014fcb7cd 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/slice_loss_indication.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/slice_loss_indication.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtcp
import (
@@ -90,7 +93,8 @@ func (p *SliceLossIndication) Unmarshal(rawPacket []byte) error {
return nil
}
-func (p *SliceLossIndication) len() int {
+// MarshalSize returns the size of the packet once marshaled
+func (p *SliceLossIndication) MarshalSize() int {
return headerLength + sliOffset + (len(p.SLI) * 4)
}
@@ -99,7 +103,7 @@ func (p *SliceLossIndication) Header() Header {
return Header{
Count: FormatSLI,
Type: TypeTransportSpecificFeedback,
- Length: uint16((p.len() / 4) - 1),
+ Length: uint16((p.MarshalSize() / 4) - 1),
}
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/source_description.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/source_description.go
index c4483c301..fc29d8e1a 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/source_description.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/source_description.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtcp
import (
@@ -94,7 +97,7 @@ func (s SourceDescription) Marshal() ([]byte, error) {
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*/
- rawPacket := make([]byte, s.len())
+ rawPacket := make([]byte, s.MarshalSize())
packetBody := rawPacket[headerLength:]
chunkOffset := 0
@@ -166,7 +169,8 @@ func (s *SourceDescription) Unmarshal(rawPacket []byte) error {
return nil
}
-func (s *SourceDescription) len() int {
+// MarshalSize returns the size of the packet once marshaled
+func (s *SourceDescription) MarshalSize() int {
chunksLength := 0
for _, c := range s.Chunks {
chunksLength += c.len()
@@ -179,7 +183,7 @@ func (s *SourceDescription) Header() Header {
return Header{
Count: uint8(len(s.Chunks)),
Type: TypeSourceDescription,
- Length: uint16((s.len() / 4) - 1),
+ Length: uint16((s.MarshalSize() / 4) - 1),
}
}
@@ -248,7 +252,7 @@ func (s *SourceDescriptionChunk) Unmarshal(rawPacket []byte) error {
return err
}
s.Items = append(s.Items, it)
- i += it.len()
+ i += it.Len()
}
return errPacketTooShort
@@ -257,7 +261,7 @@ func (s *SourceDescriptionChunk) Unmarshal(rawPacket []byte) error {
func (s SourceDescriptionChunk) len() int {
chunkLen := sdesSourceLen
for _, it := range s.Items {
- chunkLen += it.len()
+ chunkLen += it.Len()
}
chunkLen += sdesTypeLen // for terminating null octet
@@ -277,7 +281,8 @@ type SourceDescriptionItem struct {
Text string
}
-func (s SourceDescriptionItem) len() int {
+// Len returns the length of the SourceDescriptionItem when encoded as binary.
+func (s SourceDescriptionItem) Len() int {
/*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/transport_layer_cc.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/transport_layer_cc.go
index 013902163..84ead7dd3 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/transport_layer_cc.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/transport_layer_cc.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtcp
// Author: adwpc
@@ -360,13 +363,18 @@ func (t *TransportLayerCC) packetLen() uint16 {
// Len return total bytes with padding
func (t *TransportLayerCC) Len() uint16 {
+ return uint16(t.MarshalSize())
+}
+
+// MarshalSize returns the size of the packet once marshaled
+func (t *TransportLayerCC) MarshalSize() int {
n := t.packetLen()
// has padding
if n%4 != 0 {
n = (n/4 + 1) * 4
}
- return n
+ return int(n)
}
func (t TransportLayerCC) String() string {
@@ -396,7 +404,7 @@ func (t TransportLayerCC) Marshal() ([]byte, error) {
return nil, err
}
- payload := make([]byte, t.Len()-headerLength)
+ payload := make([]byte, t.MarshalSize()-headerLength)
binary.BigEndian.PutUint32(payload, t.SenderSSRC)
binary.BigEndian.PutUint32(payload[4:], t.MediaSSRC)
binary.BigEndian.PutUint16(payload[baseSequenceNumberOffset:], t.BaseSequenceNumber)
@@ -427,7 +435,7 @@ func (t TransportLayerCC) Marshal() ([]byte, error) {
}
if t.Header.Padding {
- payload[len(payload)-1] = uint8(t.Len() - t.packetLen())
+ payload[len(payload)-1] = uint8(t.MarshalSize() - int(t.packetLen()))
}
return append(header, payload...), nil
@@ -483,7 +491,7 @@ func (t *TransportLayerCC) Unmarshal(rawPacket []byte) error { //nolint:gocognit
return err
}
- packetNumberToProcess := min(t.PacketStatusCount-processedPacketNum, packetStatus.RunLength)
+ packetNumberToProcess := localMin(t.PacketStatusCount-processedPacketNum, packetStatus.RunLength)
if packetStatus.PacketStatusSymbol == TypeTCCPacketReceivedSmallDelta ||
packetStatus.PacketStatusSymbol == TypeTCCPacketReceivedLargeDelta {
for j := uint16(0); j < packetNumberToProcess; j++ {
@@ -520,10 +528,10 @@ func (t *TransportLayerCC) Unmarshal(rawPacket []byte) error { //nolint:gocognit
recvDeltasPos := packetStatusPos
for _, delta := range t.RecvDeltas {
- if recvDeltasPos >= totalLength {
- return errPacketTooShort
- }
if delta.Type == TypeTCCPacketReceivedSmallDelta {
+ if recvDeltasPos+1 > totalLength {
+ return errPacketTooShort
+ }
err := delta.Unmarshal(rawPacket[recvDeltasPos : recvDeltasPos+1])
if err != nil {
return err
@@ -531,6 +539,9 @@ func (t *TransportLayerCC) Unmarshal(rawPacket []byte) error { //nolint:gocognit
recvDeltasPos++
}
if delta.Type == TypeTCCPacketReceivedLargeDelta {
+ if recvDeltasPos+2 > totalLength {
+ return errPacketTooShort
+ }
err := delta.Unmarshal(rawPacket[recvDeltasPos : recvDeltasPos+2])
if err != nil {
return err
@@ -547,7 +558,7 @@ func (t TransportLayerCC) DestinationSSRC() []uint32 {
return []uint32{t.MediaSSRC}
}
-func min(x, y uint16) uint16 {
+func localMin(x, y uint16) uint16 {
if x < y {
return x
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/transport_layer_nack.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/transport_layer_nack.go
index ab0c03d9d..802b915ec 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/transport_layer_nack.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/transport_layer_nack.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtcp
import (
@@ -132,6 +135,11 @@ func (p *TransportLayerNack) Unmarshal(rawPacket []byte) error {
return errWrongType
}
+ // The FCI field MUST contain at least one and MAY contain more than one Generic NACK
+ if 4*h.Length <= nackOffset {
+ return errBadLength
+ }
+
p.SenderSSRC = binary.BigEndian.Uint32(rawPacket[headerLength:])
p.MediaSSRC = binary.BigEndian.Uint32(rawPacket[headerLength+ssrcLength:])
for i := headerLength + nackOffset; i < (headerLength + int(h.Length*4)); i += 4 {
@@ -143,7 +151,8 @@ func (p *TransportLayerNack) Unmarshal(rawPacket []byte) error {
return nil
}
-func (p *TransportLayerNack) len() int {
+// MarshalSize returns the size of the packet once marshaled
+func (p *TransportLayerNack) MarshalSize() int {
return headerLength + nackOffset + (len(p.Nacks) * 4)
}
@@ -152,7 +161,7 @@ func (p *TransportLayerNack) Header() Header {
return Header{
Count: FormatTLN,
Type: TypeTransportSpecificFeedback,
- Length: uint16((p.len() / 4) - 1),
+ Length: uint16((p.MarshalSize() / 4) - 1),
}
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/util.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/util.go
index 95e8f1a83..c68cebef5 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/util.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtcp/util.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtcp
// getPadding Returns the padding required to make the length a multiple of 4
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/.gitignore b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/.gitignore
index f977e7485..6e2f206a9 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/.gitignore
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/.gitignore
@@ -1,3 +1,6 @@
+# SPDX-FileCopyrightText: 2023 The Pion community
+# SPDX-License-Identifier: MIT
+
### JetBrains IDE ###
#####################
.idea/
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/.golangci.yml b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/.golangci.yml
index d6162c970..120faf29b 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/.golangci.yml
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/.golangci.yml
@@ -1,6 +1,13 @@
+# SPDX-FileCopyrightText: 2023 The Pion community
+# SPDX-License-Identifier: MIT
+
+run:
+ timeout: 5m
+
linters-settings:
govet:
- check-shadowing: true
+ enable:
+ - shadow
misspell:
locale: US
exhaustive:
@@ -10,80 +17,129 @@ linters-settings:
modules:
- github.com/pkg/errors:
recommendations:
- - errors
+ - errors
+ forbidigo:
+ analyze-types: true
+ forbid:
+ - ^fmt.Print(f|ln)?$
+ - ^log.(Panic|Fatal|Print)(f|ln)?$
+ - ^os.Exit$
+ - ^panic$
+ - ^print(ln)?$
+ - p: ^testing.T.(Error|Errorf|Fatal|Fatalf|Fail|FailNow)$
+ pkg: ^testing$
+ msg: "use testify/assert instead"
+ varnamelen:
+ max-distance: 12
+ min-name-length: 2
+ ignore-type-assert-ok: true
+ ignore-map-index-ok: true
+ ignore-chan-recv-ok: true
+ ignore-decls:
+ - i int
+ - n int
+ - w io.Writer
+ - r io.Reader
+ - b []byte
linters:
enable:
- asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
+ - bidichk # Checks for dangerous unicode character sequences
- bodyclose # checks whether HTTP response body is closed successfully
- - deadcode # Finds unused code
- - depguard # Go linter that checks if package imports are in a list of acceptable packages
+ - containedctx # containedctx is a linter that detects struct contained context.Context field
+ - contextcheck # check the function whether use a non-inherited context
+ - cyclop # checks function and package cyclomatic complexity
+ - decorder # check declaration order and count of types, constants, variables and functions
- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
- dupl # Tool for code clone detection
+ - durationcheck # check for two durations multiplied together
+ - err113 # Golang linter to check the errors handling expressions
- errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
+ - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted.
+ - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`.
+ - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13.
- exhaustive # check exhaustiveness of enum switch statements
- exportloopref # checks for pointers to enclosing loop variables
+ - forbidigo # Forbids identifiers
+ - forcetypeassert # finds forced type assertions
- gci # Gci control golang package import order and make it always deterministic.
- gochecknoglobals # Checks that no globals are present in Go code
- - gochecknoinits # Checks that no init functions are present in Go code
- gocognit # Computes and checks the cognitive complexity of functions
- goconst # Finds repeated strings that could be replaced by a constant
- gocritic # The most opinionated Go source code linter
+ - gocyclo # Computes and checks the cyclomatic complexity of functions
+ - godot # Check if comments end in a period
- godox # Tool for detection of FIXME, TODO and other comment keywords
- - goerr113 # Golang linter to check the errors handling expressions
- gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
- gofumpt # Gofumpt checks whether code was gofumpt-ed.
- goheader # Checks is file header matches to pattern
- goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
- - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
- - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
+ - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod.
- goprintffuncname # Checks that printf-like functions are named with `f` at the end
- gosec # Inspects source code for security problems
- gosimple # Linter for Go source code that specializes in simplifying a code
- govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
+ - grouper # An analyzer to analyze expression groups.
+ - importas # Enforces consistent import aliases
- ineffassign # Detects when assignments to existing variables are not used
+ - lll # Reports long lines
+ - maintidx # maintidx measures the maintainability index of each function.
+ - makezero # Finds slice declarations with non-zero initial length
- misspell # Finds commonly misspelled English words in comments
- nakedret # Finds naked returns in functions greater than a specified function length
+ - nestif # Reports deeply nested if statements
+ - nilerr # Finds the code that returns nil even if it checks that the error is not nil.
+ - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value.
+ - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
- noctx # noctx finds sending http request without context.Context
- - scopelint # Scopelint checks for unpinned variables in go programs
+ - predeclared # find code that shadows one of Go's predeclared identifiers
+ - revive # golint replacement, finds style mistakes
- staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
- - structcheck # Finds unused struct fields
- stylecheck # Stylecheck is a replacement for golint
+ - tagliatelle # Checks the struct tags.
+ - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17
+ - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers
- typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
- unconvert # Remove unnecessary type conversions
- unparam # Reports unused function parameters
- unused # Checks Go code for unused constants, variables, functions and types
- - varcheck # Finds unused global variables and constants
+ - varnamelen # checks that the length of a variable's name matches its scope
+ - wastedassign # wastedassign finds wasted assignment statements
- whitespace # Tool for detection of leading and trailing whitespace
disable:
+ - depguard # Go linter that checks if package imports are in a list of acceptable packages
- funlen # Tool for detection of long functions
- - gocyclo # Computes and checks the cyclomatic complexity of functions
- - godot # Check if comments end in a period
- - gomnd # An analyzer to detect magic numbers.
- - lll # Reports long lines
- - maligned # Tool to detect Go structs that would take less memory if their fields were sorted
- - nestif # Reports deeply nested if statements
- - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
+ - gochecknoinits # Checks that no init functions are present in Go code
+ - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
+ - interfacebloat # A linter that checks length of interface.
+ - ireturn # Accept Interfaces, Return Concrete Types
+ - mnd # An analyzer to detect magic numbers
- nolintlint # Reports ill-formed or insufficient nolint directives
+ - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test
- prealloc # Finds slice declarations that could potentially be preallocated
+ - promlinter # Check Prometheus metrics naming via promlint
- rowserrcheck # checks whether Err of rows is checked successfully
- sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
- testpackage # linter that makes you use a separate _test package
+ - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes
+ - wrapcheck # Checks that errors returned from external packages are wrapped
- wsl # Whitespace Linter - Forces you to use empty lines!
issues:
exclude-use-default: false
+ exclude-dirs-use-default: false
exclude-rules:
- # Allow complex tests, better to be self contained
+ # Allow complex tests and examples, better to be self contained
+ - path: (examples|main\.go)
+ linters:
+ - gocognit
+ - forbidigo
- path: _test\.go
linters:
- gocognit
- # Allow complex main function in examples
- - path: examples
- text: "of func `main` is high"
+ # Allow forbidden identifiers in CLI commands
+ - path: cmd
linters:
- - gocognit
-
-run:
- skip-dirs-use-default: false
+ - forbidigo
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/.goreleaser.yml b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/.goreleaser.yml
new file mode 100644
index 000000000..30093e9d6
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/.goreleaser.yml
@@ -0,0 +1,5 @@
+# SPDX-FileCopyrightText: 2023 The Pion community
+# SPDX-License-Identifier: MIT
+
+builds:
+- skip: true
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/AUTHORS.txt b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/AUTHORS.txt
deleted file mode 100644
index a7bc7a02b..000000000
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/AUTHORS.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-# Thank you to everyone that made Pion possible. If you are interested in contributing
-# we would love to have you https://github.com/pion/webrtc/wiki/Contributing
-#
-# This file is auto generated, using git to list all individuals contributors.
-# see `.github/generate-authors.sh` for the scripting
-adwpc
-aler9 <46489434+aler9@users.noreply.github.com>
-Antoine Baché
-Antoine Baché
-Atsushi Watanabe
-baiyufei
-Bao Nguyen
-boks1971
-debiandebiandebian
-ffmiyo
-Guilherme
-Haiyang Wang
-Hugo Arregui
-John Bradley
-Juliusz Chroboczek
-Kazuyuki Honda
-Luke Curley
-lxb
-Michael MacDonald
-Michael MacDonald
-Michael Uti
-Raphael Derosso Pereira
-Rob Lofthouse
-Robin Raymond
-Sean DuBois
-Sean DuBois
-Sean DuBois
-Simone Gotti
-Tarrence van As
-wangzixiang
-Woodrow Douglass
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/LICENSE b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/LICENSE
index ab602974d..2071b23b0 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/LICENSE
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/LICENSE
@@ -1,21 +1,9 @@
MIT License
-Copyright (c) 2018
+Copyright (c)
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/README.md b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/README.md
index ce04599a8..11b720a67 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/README.md
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/README.md
@@ -7,10 +7,10 @@
-
+
-
-
+
+
@@ -21,14 +21,15 @@
The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
### Community
-Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion).
+Pion has an active community on the [Discord](https://discord.gg/PngbdqpFbt).
+
+Follow the [Pion Bluesky](https://bsky.app/profile/pion.ly) or [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news.
We are always looking to support **your projects**. Please reach out if you have something to build!
-
If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
### Contributing
-Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
+Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible
### License
MIT License - see [LICENSE](LICENSE) for full text
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/abscapturetimeextension.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/abscapturetimeextension.go
new file mode 100644
index 000000000..68b43ca22
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/abscapturetimeextension.go
@@ -0,0 +1,115 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package rtp
+
+import (
+ "encoding/binary"
+ "time"
+)
+
+const (
+ absCaptureTimeExtensionSize = 8
+ absCaptureTimeExtendedExtensionSize = 16
+)
+
+// AbsCaptureTimeExtension is a extension payload format in.
+// http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | ID | len=7 | absolute capture timestamp (bit 0-23) |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | absolute capture timestamp (bit 24-55) |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | ... (56-63) |
+// +-+-+-+-+-+-+-+-+
+// .
+type AbsCaptureTimeExtension struct {
+ Timestamp uint64
+ EstimatedCaptureClockOffset *int64
+}
+
+// Marshal serializes the members to buffer.
+func (t AbsCaptureTimeExtension) Marshal() ([]byte, error) {
+ if t.EstimatedCaptureClockOffset != nil {
+ buf := make([]byte, 16)
+ binary.BigEndian.PutUint64(buf[0:8], t.Timestamp)
+ binary.BigEndian.PutUint64(buf[8:16], uint64(*t.EstimatedCaptureClockOffset)) // nolint: gosec // G115
+
+ return buf, nil
+ }
+ buf := make([]byte, 8)
+ binary.BigEndian.PutUint64(buf[0:8], t.Timestamp)
+
+ return buf, nil
+}
+
+// Unmarshal parses the passed byte slice and stores the result in the members.
+func (t *AbsCaptureTimeExtension) Unmarshal(rawData []byte) error {
+ if len(rawData) < absCaptureTimeExtensionSize {
+ return errTooSmall
+ }
+ t.Timestamp = binary.BigEndian.Uint64(rawData[0:8])
+ if len(rawData) >= absCaptureTimeExtendedExtensionSize {
+ offset := int64(binary.BigEndian.Uint64(rawData[8:16])) // nolint: gosec // G115 false positive
+ t.EstimatedCaptureClockOffset = &offset
+ }
+
+ return nil
+}
+
+// CaptureTime produces the estimated time.Time represented by this extension.
+func (t AbsCaptureTimeExtension) CaptureTime() time.Time {
+ return toTime(t.Timestamp)
+}
+
+// EstimatedCaptureClockOffsetDuration produces the estimated time.Duration represented by this extension.
+func (t AbsCaptureTimeExtension) EstimatedCaptureClockOffsetDuration() *time.Duration {
+ if t.EstimatedCaptureClockOffset == nil {
+ return nil
+ }
+ offset := *t.EstimatedCaptureClockOffset
+ negative := false
+ if offset < 0 {
+ offset = -offset
+ negative = true
+ }
+ duration := time.Duration(offset/(1<<32))*time.Second + time.Duration((offset&0xFFFFFFFF)*1e9/(1<<32))*time.Nanosecond
+ if negative {
+ duration = -duration
+ }
+
+ return &duration
+}
+
+// NewAbsCaptureTimeExtension makes new AbsCaptureTimeExtension from time.Time.
+func NewAbsCaptureTimeExtension(captureTime time.Time) *AbsCaptureTimeExtension {
+ return &AbsCaptureTimeExtension{
+ Timestamp: toNtpTime(captureTime),
+ }
+}
+
+// NewAbsCaptureTimeExtensionWithCaptureClockOffset makes new AbsCaptureTimeExtension from time.Time and a clock offset.
+func NewAbsCaptureTimeExtensionWithCaptureClockOffset(
+ captureTime time.Time,
+ captureClockOffset time.Duration,
+) *AbsCaptureTimeExtension {
+ ns := captureClockOffset.Nanoseconds()
+ negative := false
+ if ns < 0 {
+ ns = -ns
+ negative = true
+ }
+ lsb := (ns / 1e9) & 0xFFFFFFFF
+ msb := (((ns % 1e9) * (1 << 32)) / 1e9) & 0xFFFFFFFF
+ offset := (lsb << 32) | msb
+ if negative {
+ offset = -offset
+ }
+
+ return &AbsCaptureTimeExtension{
+ Timestamp: toNtpTime(captureTime),
+ EstimatedCaptureClockOffset: &offset,
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/abssendtimeextension.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/abssendtimeextension.go
index f0c6de375..d66e47c96 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/abssendtimeextension.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/abssendtimeextension.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtp
import (
@@ -29,6 +32,7 @@ func (t *AbsSendTimeExtension) Unmarshal(rawData []byte) error {
return errTooSmall
}
t.Timestamp = uint64(rawData[0])<<16 | uint64(rawData[1])<<8 | uint64(rawData[2])
+
return nil
}
@@ -55,7 +59,7 @@ func NewAbsSendTimeExtension(sendTime time.Time) *AbsSendTimeExtension {
func toNtpTime(t time.Time) uint64 {
var s uint64
var f uint64
- u := uint64(t.UnixNano())
+ u := uint64(t.UnixNano()) // nolint: gosec // G115 false positive
s = u / 1e9
s += 0x83AA7E80 // offset in seconds between unix epoch and ntp epoch
f = u % 1e9
@@ -74,5 +78,5 @@ func toTime(t uint64) time.Time {
s -= 0x83AA7E80
u := s*1e9 + f
- return time.Unix(0, int64(u))
+ return time.Unix(0, int64(u)) // nolint: gosec // G115 false positive
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/audiolevelextension.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/audiolevelextension.go
index ca44f2870..cc2e65291 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/audiolevelextension.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/audiolevelextension.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtp
import (
@@ -5,7 +8,7 @@ import (
)
const (
- // audioLevelExtensionSize One byte header size
+ // audioLevelExtensionSize One byte header size.
audioLevelExtensionSize = 1
)
@@ -30,12 +33,14 @@ var errAudioLevelOverflow = errors.New("audio level overflow")
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ID | len=1 |V| level | 0 (pad) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+//nolint:lll
type AudioLevelExtension struct {
Level uint8
Voice bool
}
-// Marshal serializes the members to buffer
+// Marshal serializes the members to buffer.
func (a AudioLevelExtension) Marshal() ([]byte, error) {
if a.Level > 127 {
return nil, errAudioLevelOverflow
@@ -46,15 +51,17 @@ func (a AudioLevelExtension) Marshal() ([]byte, error) {
}
buf := make([]byte, audioLevelExtensionSize)
buf[0] = voice | a.Level
+
return buf, nil
}
-// Unmarshal parses the passed byte slice and stores the result in the members
+// Unmarshal parses the passed byte slice and stores the result in the members.
func (a *AudioLevelExtension) Unmarshal(rawData []byte) error {
if len(rawData) < audioLevelExtensionSize {
return errTooSmall
}
a.Level = rawData[0] & 0x7F
a.Voice = rawData[0]&0x80 != 0
+
return nil
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecov.yml b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecov.yml
index 085200a48..263e4d45c 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecov.yml
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecov.yml
@@ -3,6 +3,8 @@
#
# It is automatically copied from https://github.com/pion/.goassets repository.
#
+# SPDX-FileCopyrightText: 2023 The Pion community
+# SPDX-License-Identifier: MIT
coverage:
status:
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/av1/obu/errors.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/av1/obu/errors.go
new file mode 100644
index 000000000..eeba3a4fe
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/av1/obu/errors.go
@@ -0,0 +1,14 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package obu
+
+import "errors"
+
+var (
+ // ErrInvalidOBUHeader is returned when an OBU header has forbidden bits set.
+ ErrInvalidOBUHeader = errors.New("invalid OBU header")
+ // ErrShortHeader is returned when an OBU header is not large enough.
+ // This can happen when an extension header is expected but not present.
+ ErrShortHeader = errors.New("OBU header is not large enough")
+)
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/pkg/obu/leb128.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/av1/obu/leb128.go
similarity index 64%
rename from trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/pkg/obu/leb128.go
rename to trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/av1/obu/leb128.go
index 988a8f442..56b9ff0fd 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/pkg/obu/leb128.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/av1/obu/leb128.go
@@ -1,4 +1,7 @@
-// Package obu implements tools for working with the "Open Bitstream Unit"
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package obu implements tools for working with the Open Bitstream Unit.
package obu
import "errors"
@@ -8,10 +11,10 @@ const (
msbBitmask = uint(0b10000000)
)
-// ErrFailedToReadLEB128 indicates that a buffer ended before a LEB128 value could be successfully read
+// ErrFailedToReadLEB128 indicates that a buffer ended before a LEB128 value could be successfully read.
var ErrFailedToReadLEB128 = errors.New("payload ended before LEB128 was finished")
-// EncodeLEB128 encodes a uint as LEB128
+// EncodeLEB128 encodes a uint as LEB128.
func EncodeLEB128(in uint) (out uint) {
for {
// Copy seven bits from in and discard
@@ -47,7 +50,7 @@ func decodeLEB128(in uint) (out uint) {
// ReadLeb128 scans an buffer and decodes a Leb128 value.
// If the end of the buffer is reached and all MSB are set
-// an error is returned
+// an error is returned.
func ReadLeb128(in []byte) (uint, uint, error) {
var encodedLength uint
@@ -55,7 +58,7 @@ func ReadLeb128(in []byte) (uint, uint, error) {
encodedLength |= uint(in[i])
if in[i]&byte(msbBitmask) == 0 {
- return decodeLEB128(encodedLength), uint(i + 1), nil
+ return decodeLEB128(encodedLength), uint(i + 1), nil // nolint: gosec // G115
}
// Make more room for next read
@@ -64,3 +67,19 @@ func ReadLeb128(in []byte) (uint, uint, error) {
return 0, 0, ErrFailedToReadLEB128
}
+
+// WriteToLeb128 writes a uint to a LEB128 encoded byte slice.
+func WriteToLeb128(in uint) []byte {
+ b := make([]byte, 10)
+
+ for i := 0; i < len(b); i++ {
+ b[i] = byte(in & 0x7f)
+ in >>= 7
+ if in == 0 {
+ return b[:i+1]
+ }
+ b[i] |= 0x80
+ }
+
+ return b // unreachable
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/av1/obu/obu.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/av1/obu/obu.go
new file mode 100644
index 000000000..6bc91f177
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/av1/obu/obu.go
@@ -0,0 +1,219 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package obu
+
+import (
+ "fmt"
+)
+
+// Type represents the type of an AV1 OBU.
+type Type uint8
+
+// OBU types as defined in the AV1 specification.
+// 5.3.1: https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=39
+const (
+ // OBUSequenceHeader av1 sequence_header_obu.
+ OBUSequenceHeader = Type(1)
+ // OBUTemporalDelimiter av1 temporal_delimiter_obu.
+ OBUTemporalDelimiter = Type(2)
+ // OBUFrameHeader av1 frame_header_obu.
+ OBUFrameHeader = Type(3)
+ // OBUTileGroup av1 tile_group_obu.
+ OBUTileGroup = Type(4)
+ // OBUMetadata av1 metadata_obu.
+ OBUMetadata = Type(5)
+ // OBUFrame av1 frame_obu.
+ OBUFrame = Type(6)
+ // OBURedundantFrameHeader av1 redundant_frame_header_obu.
+ OBURedundantFrameHeader = Type(7)
+ // OBUTileList av1 tile_list_obu.
+ OBUTileList = Type(8)
+ // OBUPadding av1 padding_obu.
+ OBUPadding = Type(15)
+)
+
+//nolint:cyclop
+func (o Type) String() string {
+ switch o {
+ case OBUSequenceHeader:
+ return "OBU_SEQUENCE_HEADER"
+ case OBUTemporalDelimiter:
+ return "OBU_TEMPORAL_DELIMITER"
+ case OBUFrameHeader:
+ return "OBU_FRAME_HEADER"
+ case OBUTileGroup:
+ return "OBU_TILE_GROUP"
+ case OBUMetadata:
+ return "OBU_METADATA"
+ case OBUFrame:
+ return "OBU_FRAME"
+ case OBURedundantFrameHeader:
+ return "OBU_REDUNDANT_FRAME_HEADER"
+ case OBUTileList:
+ return "OBU_TILE_LIST"
+ case OBUPadding:
+ return "OBU_PADDING"
+ default:
+ return "OBU_RESERVED"
+ }
+}
+
+// Header represents the header of an OBU obu_header().
+// 5.3.2: https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=40
+type Header struct {
+ Type Type
+ ExtensionHeader *ExtensionHeader
+ HasSizeField bool
+ Reserved1Bit bool
+}
+
+// ParseOBUHeader parses an OBU header from the given data.
+// 5.3.2: https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=40
+/*
+ obu_header() { Type
+ obu_forbidden_bit f(1)
+ obu_type f(4)
+ obu_extension_flag f(1)
+ obu_has_size_field f(1)
+ obu_reserved_1bit f(1)
+ if ( obu_extension_flag == 1 )
+ obu_extension_header()
+ }
+ }
+*/
+func ParseOBUHeader(data []byte) (*Header, error) {
+ if len(data) < 1 {
+ return nil, fmt.Errorf("%w: data is too short", ErrShortHeader)
+ }
+
+ forbiddenBit := data[0] & 0x80
+ if forbiddenBit != 0 {
+ return nil, fmt.Errorf("%w: forbidden bit is set", ErrInvalidOBUHeader)
+ }
+
+ obuType := Type((data[0] & 0x78) >> 3)
+ obuExtensionFlag := (data[0] & 0x04) != 0
+ obuHasSizeField := (data[0] & 0x02) != 0
+ obuReserved1Bit := (data[0] & 0x01) != 0
+
+ header := &Header{
+ Type: obuType,
+ HasSizeField: obuHasSizeField,
+ Reserved1Bit: obuReserved1Bit,
+ }
+
+ if obuExtensionFlag {
+ if len(data) < 2 {
+ return nil, fmt.Errorf("%w: Unexpected end of data, expected extension header", ErrShortHeader)
+ }
+
+ extensionHeader := ParseOBUExtensionHeader(data[1])
+ header.ExtensionHeader = &extensionHeader
+ }
+
+ return header, nil
+}
+
+// Marshal serializes the OBU header to a byte slice.
+// If the OBU has an extension header, the extension header is serialized as well.
+// 5.3.2: https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=40
+/*
+ obu_header() { Type
+ obu_forbidden_bit f(1)
+ obu_type f(4)
+ obu_extension_flag f(1)
+ obu_has_size_field f(1)
+ obu_reserved_1bit f(1)
+ if ( obu_extension_flag == 1 )
+ obu_extension_header()
+ }
+ }
+*/
+func (o *Header) Marshal() []byte {
+ header := make([]byte, o.Size())
+
+ header[0] = (byte(o.Type) & 0x0f) << 3
+
+ if o.ExtensionHeader != nil {
+ header[0] |= 0x04
+ header[1] = o.ExtensionHeader.Marshal()
+ }
+
+ if o.HasSizeField {
+ header[0] |= 0x02
+ }
+
+ if o.Reserved1Bit {
+ header[0] |= 0x01
+ }
+
+ return header
+}
+
+// Size returns the size of the OBU header in bytes.
+func (o *Header) Size() int {
+ size := 1
+ if o.ExtensionHeader != nil {
+ size++
+ }
+
+ return size
+}
+
+// ExtensionHeader represents an OBU extension header obu_extension_header().
+// 5.3.3 https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=40
+type ExtensionHeader struct {
+ TemporalID uint8
+ SpatialID uint8
+ Reserved3Bits uint8
+}
+
+// ParseOBUExtensionHeader parses an OBU extension header from the given data.
+// 5.3.3 https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=40
+/*
+ obu_extension_header() { Type
+ temporal_id f(3)
+ spatial_id f(2)
+ extension_header_reserved_3bits f(3)
+ }
+*/
+func ParseOBUExtensionHeader(headerData byte) ExtensionHeader {
+ return ExtensionHeader{
+ TemporalID: headerData >> 5,
+ SpatialID: (headerData >> 3) & 0x03,
+ Reserved3Bits: headerData & 0x07,
+ }
+}
+
+// Marshal serializes the OBU extension header to a byte slice.
+// 5.3.3 https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=40
+/*
+ obu_extension_header() { Type
+ temporal_id f(3)
+ spatial_id f(2)
+ extension_header_reserved_3bits f(3)
+ }
+*/
+func (o *ExtensionHeader) Marshal() byte {
+ return (o.TemporalID << 5) | ((o.SpatialID & 0x3) << 3) | (o.Reserved3Bits & 0x07)
+}
+
+// OBU represents an AV1 OBU.
+// 5.1 https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=39
+type OBU struct {
+ Header Header
+ Payload []byte
+}
+
+// Marshal serializes the OBU to low-overhead bitstream format.
+// 5.2 https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=40
+func (o *OBU) Marshal() []byte {
+ buffer := o.Header.Marshal()
+
+ if o.Header.HasSizeField {
+ buffer = append(buffer, WriteToLeb128(uint(len(o.Payload)))...)
+ }
+
+ return append(buffer, o.Payload...)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/av1_depacketizer.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/av1_depacketizer.go
new file mode 100644
index 000000000..bc83231d3
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/av1_depacketizer.go
@@ -0,0 +1,188 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package codecs
+
+import (
+ "fmt"
+
+ "github.com/pion/rtp/codecs/av1/obu"
+)
+
+// AV1Depacketizer is a AV1 RTP Packet depacketizer.
+// Reads AV1 packets from a RTP stream and outputs AV1 low overhead bitstream.
+type AV1Depacketizer struct {
+ // holds the fragmented OBU from the previous packet.
+ buffer []byte
+
+ // Z, Y, N are flags from the AV1 Aggregation Header.
+ Z, Y, N bool
+
+ videoDepacketizer
+}
+
+// Unmarshal parses an AV1 RTP payload into its constituent OBUs stream with obu_size_field,
+// It assumes that the payload is in order (e.g. the caller is responsible for reordering RTP packets).
+// If the last OBU in the payload is fragmented, it will be stored in the buffer until the
+// it is completed.
+//
+//nolint:gocognit,cyclop
+func (d *AV1Depacketizer) Unmarshal(payload []byte) (buff []byte, err error) {
+ buff = make([]byte, 0)
+
+ if len(payload) <= 1 {
+ return nil, errShortPacket
+ }
+
+ // |Z|Y| W |N|-|-|-|
+ obuZ := (av1ZMask & payload[0]) != 0 // Z
+ obuY := (av1YMask & payload[0]) != 0 // Y
+ obuCount := (av1WMask & payload[0]) >> 4 // W
+ obuN := (av1NMask & payload[0]) != 0 // N
+ d.Z = obuZ
+ d.Y = obuY
+ d.N = obuN
+ if obuN {
+ d.buffer = nil
+ }
+
+ // Make sure we clear the buffer if Z is not 0.
+ if !obuZ && len(d.buffer) > 0 {
+ d.buffer = nil
+ }
+
+ obuOffset := 0
+ for offset := 1; offset < len(payload); obuOffset++ {
+ isFirst := obuOffset == 0
+ isLast := obuCount != 0 && obuOffset == int(obuCount)-1
+
+ // https://aomediacodec.github.io/av1-rtp-spec/#44-av1-aggregation-header
+ // W: two bit field that describes the number of OBU elements in the packet.
+ // This field MUST be set equal to 0 or equal to the number of OBU elements contained in the packet.
+ // If set to 0, each OBU element MUST be preceded by a length field. If not set to 0
+ // (i.e., W = 1, 2 or 3) the last OBU element MUST NOT be preceded by a length field.
+ var lengthField, n int
+ if obuCount == 0 || !isLast {
+ obuSizeVal, nVal, err := obu.ReadLeb128(payload[offset:])
+ lengthField = int(obuSizeVal) //nolint:gosec // G115 false positive
+ n = int(nVal) //nolint:gosec // G115 false positive
+ if err != nil {
+ return nil, err
+ }
+
+ offset += n
+ if obuCount == 0 && offset+lengthField == len(payload) {
+ isLast = true
+ }
+ } else {
+ // https://aomediacodec.github.io/av1-rtp-spec/#44-av1-aggregation-header
+ // Length of the last OBU element =
+ // length of the RTP payload
+ // - length of aggregation header
+ // - length of previous OBU elements including length fields
+ lengthField = len(payload) - offset
+ }
+
+ if offset+lengthField > len(payload) {
+ return nil, fmt.Errorf(
+ "%w: OBU size %d + %d offset exceeds payload length %d",
+ errShortPacket, lengthField, offset, len(payload),
+ )
+ }
+
+ var obuBuffer []byte
+ if isFirst && obuZ {
+ // We lost the first fragment of the OBU
+ // We drop the buffer and continue
+ if len(d.buffer) == 0 {
+ if isLast {
+ break
+ }
+ offset += lengthField
+
+ continue
+ }
+
+ obuBuffer = make([]byte, len(d.buffer)+lengthField)
+
+ copy(obuBuffer, d.buffer)
+ copy(obuBuffer[len(d.buffer):], payload[offset:offset+lengthField])
+ d.buffer = nil
+ } else {
+ obuBuffer = payload[offset : offset+lengthField]
+ }
+ offset += lengthField
+
+ if isLast && obuY {
+ d.buffer = obuBuffer
+
+ break
+ }
+
+ if len(obuBuffer) == 0 {
+ continue
+ }
+
+ obuHeader, err := obu.ParseOBUHeader(obuBuffer)
+ if err != nil {
+ return nil, err
+ }
+
+ // The temporal delimiter OBU, if present, SHOULD be removed when transmitting,
+ // and MUST be ignored by receivers. Tile list OBUs are not supported.
+ // They SHOULD be removed when transmitted, and MUST be ignored by receivers.
+ // https://aomediacodec.github.io/av1-rtp-spec/#5-packetization-rules
+ if obuHeader.Type == obu.OBUTemporalDelimiter || obuHeader.Type == obu.OBUTileList {
+ continue
+ }
+
+ // obu_has_size_field should be set to 0 for AV1 RTP packets.
+ // But we still check it to be sure, if we get obu size we just use it, instead of calculating it.
+ if obuHeader.HasSizeField {
+ obuSize, n, err := obu.ReadLeb128(obuBuffer[obuHeader.Size():])
+ if err != nil {
+ return nil, err
+ }
+
+ // We validate the obu_size_field if it is present.
+ sizeFromOBUSize := obuHeader.Size() + int(obuSize) + int(n) //nolint:gosec
+ if lengthField != sizeFromOBUSize {
+ return nil, fmt.Errorf(
+ "%w: OBU size %d does not match calculated size %d",
+ errShortPacket, obuSize, sizeFromOBUSize,
+ )
+ }
+
+ buff = append(buff, obuBuffer...)
+ } else {
+ obuHeader.HasSizeField = true
+ buff = append(buff, obuHeader.Marshal()...)
+ size := len(obuBuffer) - obuHeader.Size()
+ buff = append(buff, obu.WriteToLeb128(uint(size))...) // nolint: gosec // G104
+ buff = append(buff, obuBuffer[obuHeader.Size():]...)
+ }
+
+ if isLast {
+ break
+ }
+ }
+
+ if obuCount != 0 && obuOffset != int(obuCount-1) {
+ return nil, fmt.Errorf(
+ "%w: OBU count %d does not match number of OBUs %d",
+ errShortPacket, obuCount, obuOffset,
+ )
+ }
+
+ return buff, nil
+}
+
+// IsPartitionHead returns true if Z in the AV1 Aggregation Header
+// is set to 0.
+func (d *AV1Depacketizer) IsPartitionHead(payload []byte) bool {
+ if len(payload) == 0 {
+ return false
+ }
+
+ return (payload[0] & av1ZMask) == 0
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/av1_packet.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/av1_packet.go
index 120a904c1..7eeceae91 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/av1_packet.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/av1_packet.go
@@ -1,81 +1,295 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package codecs
import (
- "github.com/pion/rtp/pkg/obu"
+ "github.com/pion/rtp/codecs/av1/obu"
)
const (
- zMask = byte(0b10000000)
- zBitshift = 7
+ av1ZMask = byte(0b10000000)
+ av1ZBitshift = 7
- yMask = byte(0b01000000)
- yBitshift = 6
+ av1YMask = byte(0b01000000)
+ av1YBitshift = 6
- wMask = byte(0b00110000)
- wBitshift = 4
+ av1WMask = byte(0b00110000)
+ av1WBitshift = 4
- nMask = byte(0b00001000)
- nBitshift = 3
-
- av1PayloaderHeadersize = 1
+ av1NMask = byte(0b00001000)
+ av1NBitshift = 3
)
-// AV1Payloader payloads AV1 packets
+// AV1Payloader payloads AV1 packets.
type AV1Payloader struct{}
-// Payload fragments a AV1 packet across one or more byte arrays
-// See AV1Packet for description of AV1 Payload Header
+// Payload implements AV1 RTP payloader.
+// Reads from a open_bitstream_unit (OBU) framing stream as defined in
+// 5.3. https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=39
+// Returns AV1 RTP packets https://aomediacodec.github.io/av1-rtp-spec/
+// The payload is fragmented into multiple packets, each packet is a valid AV1 RTP payload.
+// nolint:cyclop
func (p *AV1Payloader) Payload(mtu uint16, payload []byte) (payloads [][]byte) {
- maxFragmentSize := int(mtu) - av1PayloaderHeadersize - 2
- payloadDataRemaining := len(payload)
- payloadDataIndex := 0
-
- // Make sure the fragment/payload size is correct
- if min(maxFragmentSize, payloadDataRemaining) <= 0 {
+ // 2 is the minimum MTU for AV1 (aggregate header + 1 byte)
+ if mtu <= 1 || len(payload) == 0 {
return payloads
}
- for payloadDataRemaining > 0 {
- currentFragmentSize := min(maxFragmentSize, payloadDataRemaining)
- leb128Size := 1
- if currentFragmentSize >= 127 {
- leb128Size = 2
+ // We maximize the use of the W field in the AV1 aggregation header
+ // to minimize the need for explicit length fields for each OBU.
+ // To achieve this, we temporarily hold the OBU payload before adding it to a packet.
+ // Since we can't determine in advance whether the next OBU should be included in the same packet
+ // or start a new one, we also can't know ahead of time if an OBU is the last in the current packet.
+ var currentOBUPayload []byte
+ var currentPacketOBUHeader *obu.ExtensionHeader
+ obusInPacket := 0
+ newSequence := false
+ startWithNewPacket := false
+
+ for offset := 0; offset < len(payload); {
+ obuHeader, err := obu.ParseOBUHeader(payload[offset:])
+ if err != nil {
+ break
}
- out := make([]byte, av1PayloaderHeadersize+leb128Size+currentFragmentSize)
- leb128Value := obu.EncodeLEB128(uint(currentFragmentSize))
- if leb128Size == 1 {
- out[1] = byte(leb128Value)
+ offset += obuHeader.Size()
+ // if ( obu_has_size_field ) {
+ // obu_size leb128()
+ // } else {
+ // obu_size = sz - 1 - obu_extension_flag
+ // }
+ var obuSize int
+ if obuHeader.HasSizeField {
+ obuSizeValue, n, err := obu.ReadLeb128(payload[offset:])
+ if err != nil {
+ break
+ }
+
+ offset += int(n) //nolint:gosec // G115, leb128 size is a signle digit
+ obuSize = int(obuSizeValue) //nolint:gosec // G115, Leb128 is capped at 4 bytes
} else {
- out[1] = byte(leb128Value >> 8)
- out[2] = byte(leb128Value)
+ obuSize = len(payload) - offset
}
- copy(out[av1PayloaderHeadersize+leb128Size:], payload[payloadDataIndex:payloadDataIndex+currentFragmentSize])
- payloads = append(payloads, out)
-
- payloadDataRemaining -= currentFragmentSize
- payloadDataIndex += currentFragmentSize
-
- if len(payloads) > 1 {
- out[0] ^= zMask
+ // Each RTP packet MUST NOT contain OBUs that belong to different temporal units.
+ // If a sequence header OBU is present in an RTP packet, then it SHOULD be the first OBU in the packet.
+ // https://aomediacodec.github.io/av1-rtp-spec/#5-packetization-rules
+ needNewPacket := obuHeader.Type == obu.OBUTemporalDelimiter || obuHeader.Type == obu.OBUSequenceHeader
+ // If more than one OBU contained in an RTP packet has an OBU extension header,
+ // then the values of the temporal_id and spatial_id MUST be the same in all such OBUs in the RTP packet.
+ if !needNewPacket && obuHeader.ExtensionHeader != nil && currentPacketOBUHeader != nil {
+ needNewPacket = obuHeader.ExtensionHeader.SpatialID != currentPacketOBUHeader.SpatialID ||
+ obuHeader.ExtensionHeader.TemporalID != currentPacketOBUHeader.TemporalID
}
- if payloadDataRemaining != 0 {
- out[0] ^= yMask
+
+ if obuHeader.ExtensionHeader != nil {
+ currentPacketOBUHeader = obuHeader.ExtensionHeader
}
+
+ if obuSize > len(payload)-offset {
+ break
+ }
+
+ if len(currentOBUPayload) > 0 {
+ payloads, obusInPacket = p.appendOBUPayload(
+ payloads,
+ currentOBUPayload,
+ newSequence,
+ needNewPacket,
+ startWithNewPacket,
+ int(mtu),
+ obusInPacket,
+ )
+ currentOBUPayload = nil
+ startWithNewPacket = needNewPacket
+
+ if needNewPacket {
+ newSequence = false
+ currentPacketOBUHeader = nil
+ }
+ }
+
+ // The temporal delimiter OBU, if present, SHOULD be removed when transmitting,
+ // and MUST be ignored by receivers. Tile list OBUs are not supported.
+ // They SHOULD be removed when transmitted, and MUST be ignored by receivers.
+ // https://aomediacodec.github.io/av1-rtp-spec/#5-packetization-rules
+ if obuHeader.Type == obu.OBUTileList || obuHeader.Type == obu.OBUTemporalDelimiter {
+ offset += obuSize
+
+ continue
+ }
+
+ currentOBUPayload = make([]byte, obuSize+obuHeader.Size())
+ // The AV1 specification allows OBUs to have an optional size field called obu_size
+ // (also leb128 encoded), signaled by the obu_has_size_field flag in the OBU header.
+ // To minimize overhead, the obu_has_size_field flag SHOULD be set to zero in all OBUs.
+ // https://aomediacodec.github.io/av1-rtp-spec/#45-payload-structure
+ obuHeader.HasSizeField = false
+ copy(currentOBUPayload, obuHeader.Marshal())
+ //nolint:gosec // G115 we validate the size of the payload
+ copy(currentOBUPayload[obuHeader.Size():], payload[offset:offset+obuSize])
+ offset += obuSize
+ newSequence = obuHeader.Type == obu.OBUSequenceHeader
+ }
+
+ if len(currentOBUPayload) > 0 {
+ payloads, _ = p.appendOBUPayload(
+ payloads,
+ currentOBUPayload,
+ newSequence,
+ true,
+ startWithNewPacket,
+ int(mtu),
+ obusInPacket,
+ )
}
return payloads
}
+//nolint:cyclop
+func (p *AV1Payloader) appendOBUPayload(
+ payloads [][]byte,
+ obuPayload []byte,
+ isNewVideoSequence, isLast, startWithNewPacket bool,
+ mtu, currentOBUCount int,
+) ([][]byte, int) {
+ currentPayload := len(payloads) - 1
+ freeSpace := 0
+ if currentPayload >= 0 {
+ freeSpace = mtu - len(payloads[currentPayload])
+ }
+
+ if currentPayload < 0 || freeSpace <= 0 || startWithNewPacket {
+ payload := make([]byte, 1, mtu)
+ if isNewVideoSequence {
+ payload[0] |= 1 << av1NBitshift
+ }
+
+ payloads = append(payloads, payload)
+ currentPayload = len(payloads) - 1
+ // MTU - aggregation header
+ freeSpace = mtu - 1
+ currentOBUCount = 0
+ }
+
+ remaining := len(obuPayload)
+ // How much to write to the current packet.
+ toWrite := remaining
+ if toWrite >= freeSpace {
+ toWrite = freeSpace
+ }
+
+ // W: two bit field that describes the number of OBU elements in the packet.
+ // This field MUST be set equal to 0 or equal to the number of OBU elements contained in the packet.
+ // If set to 0, each OBU element MUST be preceded by a length field. If not set to 0 (i.e., W = 1, 2 or 3)
+ // the last OBU element MUST NOT be preceded by a length field.
+ // https://aomediacodec.github.io/av1-rtp-spec/#44-av1-aggregation-header
+ shouldUseWField := (isLast || toWrite >= freeSpace) && currentOBUCount < 3
+ switch {
+ case shouldUseWField:
+ payloads[currentPayload][0] |= byte((currentOBUCount+1)<= 2:
+ // 2 bytes is the minimum size for OBUs with length field.
+ // [1 byte for the length field] [1 byte for the OBU]
+ //nolint:gosec // G115 false positive
+ toWrite = p.computeWriteSize(toWrite, freeSpace)
+ lengthField := obu.WriteToLeb128(uint(toWrite)) //nolint:gosec // G115 false positive
+ payloads[currentPayload] = append(payloads[currentPayload], lengthField...)
+ payloads[currentPayload] = append(payloads[currentPayload], obuPayload[:toWrite]...)
+ currentOBUCount++
+ default:
+ // If we can't fit any more OBUs in the current packet (only 1 byte left and W=0)
+ toWrite = 0
+ }
+
+ obuPayload = obuPayload[toWrite:]
+ remaining -= toWrite
+
+ // Handle fragments.
+ for remaining > 0 {
+ // New packet with empty aggregation header.
+ payload := make([]byte, 1, mtu)
+ payloads = append(payloads, payload)
+ currentPayload++
+
+ // Append the Y bit to the previous packet. And Z bit to the current packet.
+ // If we wrote some bytes to the previous packet.
+ // Handles an edge case where the previous packet has only one byte remaining,
+ // while the W field is not used. This results in insufficient space
+ // for a one-byte length field and a one-byte OBU.
+ // So we don't write anything to the initial packet.
+ if toWrite != 0 {
+ payloads[currentPayload-1][0] |= av1YMask
+ payloads[currentPayload][0] |= av1ZMask
+ }
+
+ toWrite = remaining
+ if toWrite >= mtu-1 { // MTU - aggregation header
+ toWrite = mtu - 1
+ }
+
+ // Last OBU in the current packet, Or this whole packet is a fragment.
+ if isLast || remaining >= mtu-1 {
+ payloads[currentPayload][0] |= 1 << av1WBitshift
+ } else {
+ toWrite = p.computeWriteSize(toWrite, mtu-1)
+ lengthField := obu.WriteToLeb128(uint(toWrite)) //nolint:gosec // G115 false positive
+ payloads[currentPayload] = append(payloads[currentPayload], lengthField...)
+ }
+
+ payloads[currentPayload] = append(payloads[currentPayload], obuPayload[:toWrite]...)
+ obuPayload = obuPayload[toWrite:]
+ remaining -= toWrite
+ currentOBUCount = 1
+ }
+
+ return payloads, currentOBUCount
+}
+
+// Measure the maximum write size for a payload with leb128 encoding added.
+func (p *AV1Payloader) computeWriteSize(wantToWrite, canWrite int) int {
+ leb128Size, isAtEge := p.leb128Size(wantToWrite)
+ if canWrite >= wantToWrite+leb128Size {
+ return wantToWrite
+ }
+
+ // Handle edge case where subtracting one from the leb128 size
+ // results in a smaller leb128 size that can fit in the remaining space.
+ if isAtEge && canWrite >= wantToWrite+leb128Size-1 {
+ return wantToWrite - 1
+ }
+
+ return wantToWrite - leb128Size
+}
+
+func (p *AV1Payloader) leb128Size(leb128 int) (size int, isAtEge bool) {
+ switch {
+ case leb128 >= 268435456: // 2^28
+ return 5, leb128 == 268435456
+ case leb128 >= 2097152: // 2^21
+ return 4, leb128 == 2097152
+ case leb128 >= 16384: // 2^14
+ return 3, leb128 == 16384
+ case leb128 >= 128: // 2^7
+ return 2, leb128 == 128
+ default:
+ return 1, false
+ }
+}
+
// AV1Packet represents a depacketized AV1 RTP Packet
-//
-// 0 1 2 3 4 5 6 7
-// +-+-+-+-+-+-+-+-+
-// |Z|Y| W |N|-|-|-|
-// +-+-+-+-+-+-+-+-+
-//
+/*
+* 0 1 2 3 4 5 6 7
+* +-+-+-+-+-+-+-+-+
+* |Z|Y| W |N|-|-|-|
+* +-+-+-+-+-+-+-+-+
+**/
// https://aomediacodec.github.io/av1-rtp-spec/#44-av1-aggregation-header
+// Deprecated: Use AV1Depacketizer instead.
type AV1Packet struct {
// Z: MUST be set to 1 if the first OBU element is an
// OBU fragment that is a continuation of an OBU fragment
@@ -104,9 +318,12 @@ type AV1Packet struct {
// Each AV1 RTP Packet is a collection of OBU Elements. Each OBU Element may be a full OBU, or just a fragment of one.
// AV1Frame provides the tools to construct a collection of OBUs from a collection of OBU Elements
OBUElements [][]byte
+
+ // zeroAllocation prevents populating the OBUElements field
+ zeroAllocation bool
}
-// Unmarshal parses the passed byte slice and stores the result in the AV1Packet this method is called upon
+// Unmarshal parses the passed byte slice and stores the result in the AV1Packet this method is called upon.
func (p *AV1Packet) Unmarshal(payload []byte) ([]byte, error) {
if payload == nil {
return nil, errNilPacket
@@ -114,22 +331,35 @@ func (p *AV1Packet) Unmarshal(payload []byte) ([]byte, error) {
return nil, errShortPacket
}
- p.Z = ((payload[0] & zMask) >> zBitshift) != 0
- p.Y = ((payload[0] & yMask) >> yBitshift) != 0
- p.N = ((payload[0] & nMask) >> nBitshift) != 0
- p.W = (payload[0] & wMask) >> wBitshift
+ p.Z = ((payload[0] & av1ZMask) >> av1ZBitshift) != 0
+ p.Y = ((payload[0] & av1YMask) >> av1YBitshift) != 0
+ p.N = ((payload[0] & av1NMask) >> av1NBitshift) != 0
+ p.W = (payload[0] & av1WMask) >> av1WBitshift
if p.Z && p.N {
return nil, errIsKeyframeAndFragment
}
- currentIndex := uint(1)
- p.OBUElements = [][]byte{}
+ if !p.zeroAllocation {
+ obuElements, err := p.parseBody(payload[1:])
+ if err != nil {
+ return nil, err
+ }
+ p.OBUElements = obuElements
+ }
- var (
- obuElementLength, bytesRead uint
- err error
- )
+ return payload[1:], nil
+}
+
+func (p *AV1Packet) parseBody(payload []byte) ([][]byte, error) {
+ if p.OBUElements != nil {
+ return p.OBUElements, nil
+ }
+
+ obuElements := [][]byte{}
+
+ var obuElementLength, bytesRead uint
+ currentIndex := uint(0)
for i := 1; ; i++ {
if currentIndex == uint(len(payload)) {
break
@@ -140,6 +370,7 @@ func (p *AV1Packet) Unmarshal(payload []byte) ([]byte, error) {
bytesRead = 0
obuElementLength = uint(len(payload)) - currentIndex
} else {
+ var err error
obuElementLength, bytesRead, err = obu.ReadLeb128(payload[currentIndex:])
if err != nil {
return nil, err
@@ -150,9 +381,9 @@ func (p *AV1Packet) Unmarshal(payload []byte) ([]byte, error) {
if uint(len(payload)) < currentIndex+obuElementLength {
return nil, errShortPacket
}
- p.OBUElements = append(p.OBUElements, payload[currentIndex:currentIndex+obuElementLength])
+ obuElements = append(obuElements, payload[currentIndex:currentIndex+obuElementLength])
currentIndex += obuElementLength
}
- return payload[1:], nil
+ return obuElements, nil
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/codecs.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/codecs.go
index 0e078974e..cd1c89150 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/codecs.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/codecs.go
@@ -1,2 +1,5 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
// Package codecs implements codec specific RTP payloader/depayloaders
package codecs
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/common.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/common.go
index af5632ac7..8d7531a35 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/common.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/common.go
@@ -1,26 +1,40 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package codecs
-func min(a, b int) int {
+func minInt(a, b int) int {
if a < b {
return a
}
+
return b
}
-// audioDepacketizer is a mixin for audio codec depacketizers
+// audioDepacketizer is a mixin for audio codec depacketizers.
type audioDepacketizer struct{}
-func (d *audioDepacketizer) IsPartitionTail(marker bool, payload []byte) bool {
+func (d *audioDepacketizer) IsPartitionTail(_ bool, _ []byte) bool {
return true
}
-func (d *audioDepacketizer) IsPartitionHead(payload []byte) bool {
+func (d *audioDepacketizer) IsPartitionHead(_ []byte) bool {
return true
}
-// videoDepacketizer is a mixin for video codec depacketizers
-type videoDepacketizer struct{}
+// videoDepacketizer is a mixin for video codec depacketizers.
+type videoDepacketizer struct {
+ zeroAllocation bool
+}
-func (d *videoDepacketizer) IsPartitionTail(marker bool, payload []byte) bool {
+func (d *videoDepacketizer) IsPartitionTail(marker bool, _ []byte) bool {
return marker
}
+
+// SetZeroAllocation enables Zero Allocation mode for the depacketizer
+// By default the Depacketizers will allocate as they parse. These allocations
+// are needed for Metadata and other optional values. If you don't need this information
+// enabling SetZeroAllocation gives you higher performance at a reduced feature set.
+func (d *videoDepacketizer) SetZeroAllocation(zeroAllocation bool) {
+ d.zeroAllocation = zeroAllocation
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/error.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/error.go
index 7f72e7b8e..60530e97c 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/error.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/error.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package codecs
import "errors"
@@ -9,6 +12,8 @@ var (
errTooManySpatialLayers = errors.New("too many spatial layers")
errUnhandledNALUType = errors.New("NALU Type is unhandled")
- // AV1 Errors
- errIsKeyframeAndFragment = errors.New("bits Z and N are set. Not possible to have OBU be tail fragment and be keyframe")
+ // AV1 Errors.
+ errIsKeyframeAndFragment = errors.New(
+ "bits Z and N are set. Not possible to have OBU be tail fragment and be keyframe",
+ )
)
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/g711_packet.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/g711_packet.go
index 7ab68b2c3..7ac268a10 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/g711_packet.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/g711_packet.go
@@ -1,9 +1,12 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package codecs
-// G711Payloader payloads G711 packets
+// G711Payloader payloads G711 packets.
type G711Payloader struct{}
-// Payload fragments an G711 packet across one or more byte arrays
+// Payload fragments an G711 packet across one or more byte arrays.
func (p *G711Payloader) Payload(mtu uint16, payload []byte) [][]byte {
var out [][]byte
if payload == nil || mtu == 0 {
@@ -18,5 +21,6 @@ func (p *G711Payloader) Payload(mtu uint16, payload []byte) [][]byte {
}
o := make([]byte, len(payload))
copy(o, payload)
+
return append(out, o)
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/g722_packet.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/g722_packet.go
index 13e17b674..7d586ab9f 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/g722_packet.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/g722_packet.go
@@ -1,9 +1,12 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package codecs
-// G722Payloader payloads G722 packets
+// G722Payloader payloads G722 packets.
type G722Payloader struct{}
-// Payload fragments an G722 packet across one or more byte arrays
+// Payload fragments an G722 packet across one or more byte arrays.
func (p *G722Payloader) Payload(mtu uint16, payload []byte) [][]byte {
var out [][]byte
if payload == nil || mtu == 0 {
@@ -18,5 +21,6 @@ func (p *G722Payloader) Payload(mtu uint16, payload []byte) [][]byte {
}
o := make([]byte, len(payload))
copy(o, payload)
+
return append(out, o)
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/h264_packet.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/h264_packet.go
index 11a82fe40..fdc00edc6 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/h264_packet.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/h264_packet.go
@@ -1,13 +1,18 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package codecs
import (
+ "bytes"
"encoding/binary"
"fmt"
)
-// H264Payloader payloads H264 packets
+// H264Payloader payloads H264 packets.
type H264Payloader struct {
spsNalu, ppsNalu []byte
+ DisableStapA bool
}
const (
@@ -31,45 +36,59 @@ const (
outputStapAHeader = 0x78
)
-func annexbNALUStartCode() []byte { return []byte{0x00, 0x00, 0x00, 0x01} }
+// nolint:gochecknoglobals
+var (
+ naluStartCode = []byte{0x00, 0x00, 0x01}
+ annexbNALUStartCode = []byte{0x00, 0x00, 0x00, 0x01}
+)
func emitNalus(nals []byte, emit func([]byte)) {
- nextInd := func(nalu []byte, start int) (indStart int, indLen int) {
- zeroCount := 0
+ // look for 3-byte NALU start code
+ start := bytes.Index(nals, naluStartCode)
+ offset := 3
- for i, b := range nalu[start:] {
- if b == 0 {
- zeroCount++
- continue
- } else if b == 1 {
- if zeroCount >= 2 {
- return start + i - zeroCount, zeroCount + 1
- }
- }
- zeroCount = 0
- }
- return -1, -1
+ if start == -1 {
+ // no start code, emit the whole buffer
+ emit(nals)
+
+ return
}
- nextIndStart, nextIndLen := nextInd(nals, 0)
- if nextIndStart == -1 {
- emit(nals)
- } else {
- for nextIndStart != -1 {
- prevStart := nextIndStart + nextIndLen
- nextIndStart, nextIndLen = nextInd(nals, prevStart)
- if nextIndStart != -1 {
- emit(nals[prevStart:nextIndStart])
- } else {
- // Emit until end of stream, no end indicator found
- emit(nals[prevStart:])
- }
+ length := len(nals)
+
+ for start < length {
+ // look for the next NALU start (end of this NALU)
+ end := bytes.Index(nals[start+offset:], naluStartCode)
+ if end == -1 {
+ // no more NALUs, emit the rest of the buffer
+ emit(nals[start+offset:])
+
+ break
+ }
+
+ // next NALU start
+ nextStart := start + offset + end
+
+ // check if the next NALU is actually a 4-byte start code
+ endIs4Byte := nals[nextStart-1] == 0
+ if endIs4Byte {
+ nextStart--
+ }
+
+ emit(nals[start+offset : nextStart])
+
+ start = nextStart
+
+ if endIs4Byte {
+ offset = 4
+ } else {
+ offset = 3
}
}
}
-// Payload fragments a H264 packet across one or more byte arrays
-func (p *H264Payloader) Payload(mtu uint16, payload []byte) [][]byte {
+// Payload fragments a H264 packet across one or more byte arrays.
+func (p *H264Payloader) Payload(mtu uint16, payload []byte) [][]byte { //nolint:cyclop
var payloads [][]byte
if len(payload) == 0 {
return payloads
@@ -87,18 +106,24 @@ func (p *H264Payloader) Payload(mtu uint16, payload []byte) [][]byte {
case naluType == audNALUType || naluType == fillerNALUType:
return
case naluType == spsNALUType:
- p.spsNalu = nalu
- return
+ if !p.DisableStapA {
+ p.spsNalu = nalu
+
+ return
+ }
case naluType == ppsNALUType:
- p.ppsNalu = nalu
- return
- case p.spsNalu != nil && p.ppsNalu != nil:
+ if !p.DisableStapA {
+ p.ppsNalu = nalu
+
+ return
+ }
+ case !p.DisableStapA && p.spsNalu != nil && p.ppsNalu != nil:
// Pack current NALU with SPS and PPS as STAP-A
spsLen := make([]byte, 2)
- binary.BigEndian.PutUint16(spsLen, uint16(len(p.spsNalu)))
+ binary.BigEndian.PutUint16(spsLen, uint16(len(p.spsNalu))) // nolint: gosec // G115
ppsLen := make([]byte, 2)
- binary.BigEndian.PutUint16(ppsLen, uint16(len(p.ppsNalu)))
+ binary.BigEndian.PutUint16(ppsLen, uint16(len(p.ppsNalu))) // nolint: gosec // G115
stapANalu := []byte{outputStapAHeader}
stapANalu = append(stapANalu, spsLen...)
@@ -120,6 +145,7 @@ func (p *H264Payloader) Payload(mtu uint16, payload []byte) [][]byte {
out := make([]byte, len(nalu))
copy(out, nalu)
payloads = append(payloads, out)
+
return
}
@@ -137,18 +163,17 @@ func (p *H264Payloader) Payload(mtu uint16, payload []byte) [][]byte {
// the FU header. An FU payload MAY have any number of octets and MAY
// be empty.
- naluData := nalu
// According to the RFC, the first octet is skipped due to redundant information
- naluDataIndex := 1
- naluDataLength := len(nalu) - naluDataIndex
- naluDataRemaining := naluDataLength
+ naluIndex := 1
+ naluLength := len(nalu) - naluIndex
+ naluRemaining := naluLength
- if min(maxFragmentSize, naluDataRemaining) <= 0 {
+ if minInt(maxFragmentSize, naluRemaining) <= 0 {
return
}
- for naluDataRemaining > 0 {
- currentFragmentSize := min(maxFragmentSize, naluDataRemaining)
+ for naluRemaining > 0 {
+ currentFragmentSize := minInt(maxFragmentSize, naluRemaining)
out := make([]byte, fuaHeaderSize+currentFragmentSize)
// +---------------+
@@ -166,26 +191,26 @@ func (p *H264Payloader) Payload(mtu uint16, payload []byte) [][]byte {
// +---------------+
out[1] = naluType
- if naluDataRemaining == naluDataLength {
+ if naluRemaining == naluLength {
// Set start bit
out[1] |= 1 << 7
- } else if naluDataRemaining-currentFragmentSize == 0 {
+ } else if naluRemaining-currentFragmentSize == 0 {
// Set end bit
out[1] |= 1 << 6
}
- copy(out[fuaHeaderSize:], naluData[naluDataIndex:naluDataIndex+currentFragmentSize])
+ copy(out[fuaHeaderSize:], nalu[naluIndex:naluIndex+currentFragmentSize])
payloads = append(payloads, out)
- naluDataRemaining -= currentFragmentSize
- naluDataIndex += currentFragmentSize
+ naluRemaining -= currentFragmentSize
+ naluIndex += currentFragmentSize
}
})
return payloads
}
-// H264Packet represents the H264 header that is stored in the payload of an RTP Packet
+// H264Packet represents the H264 header that is stored in the payload of an RTP Packet.
type H264Packet struct {
IsAVC bool
fuaBuffer []byte
@@ -193,28 +218,38 @@ type H264Packet struct {
videoDepacketizer
}
-func (p *H264Packet) doPackaging(nalu []byte) []byte {
+func (p *H264Packet) doPackaging(buf, nalu []byte) []byte {
if p.IsAVC {
- naluLength := make([]byte, 4)
- binary.BigEndian.PutUint32(naluLength, uint32(len(nalu)))
- return append(naluLength, nalu...)
+ buf = binary.BigEndian.AppendUint32(buf, uint32(len(nalu))) // nolint: gosec // G115 false positive
+ buf = append(buf, nalu...)
+
+ return buf
}
- return append(annexbNALUStartCode(), nalu...)
+ buf = append(buf, annexbNALUStartCode...)
+ buf = append(buf, nalu...)
+
+ return buf
}
// IsDetectedFinalPacketInSequence returns true of the packet passed in has the
-// marker bit set indicated the end of a packet sequence
+// marker bit set indicated the end of a packet sequence.
func (p *H264Packet) IsDetectedFinalPacketInSequence(rtpPacketMarketBit bool) bool {
return rtpPacketMarketBit
}
-// Unmarshal parses the passed byte slice and stores the result in the H264Packet this method is called upon
+// Unmarshal parses the passed byte slice and stores the result in the H264Packet this method is called upon.
func (p *H264Packet) Unmarshal(payload []byte) ([]byte, error) {
- if payload == nil {
- return nil, errNilPacket
- } else if len(payload) <= 2 {
- return nil, fmt.Errorf("%w: %d <= 2", errShortPacket, len(payload))
+ if p.zeroAllocation {
+ return payload, nil
+ }
+
+ return p.parseBody(payload)
+}
+
+func (p *H264Packet) parseBody(payload []byte) ([]byte, error) { //nolint:cyclop
+ if len(payload) == 0 {
+ return nil, fmt.Errorf("%w: %d <=0", errShortPacket, len(payload))
}
// NALU Types
@@ -222,22 +257,32 @@ func (p *H264Packet) Unmarshal(payload []byte) ([]byte, error) {
naluType := payload[0] & naluTypeBitmask
switch {
case naluType > 0 && naluType < 24:
- return p.doPackaging(payload), nil
+ return p.doPackaging(nil, payload), nil
case naluType == stapaNALUType:
currOffset := int(stapaHeaderSize)
result := []byte{}
for currOffset < len(payload) {
- naluSize := int(binary.BigEndian.Uint16(payload[currOffset:]))
+ naluSizeBytes := payload[currOffset:]
+ if len(naluSizeBytes) < stapaNALULengthSize {
+ break
+ }
+ naluSize := int(binary.BigEndian.Uint16(naluSizeBytes))
currOffset += stapaNALULengthSize
if len(payload) < currOffset+naluSize {
- return nil, fmt.Errorf("%w STAP-A declared size(%d) is larger than buffer(%d)", errShortPacket, naluSize, len(payload)-currOffset)
+ return nil, fmt.Errorf(
+ "%w STAP-A declared size(%d) is larger than buffer(%d)",
+ errShortPacket,
+ naluSize,
+ len(payload)-currOffset,
+ )
}
- result = append(result, p.doPackaging(payload[currOffset:currOffset+naluSize])...)
+ result = p.doPackaging(result, payload[currOffset:currOffset+naluSize])
currOffset += naluSize
}
+
return result, nil
case naluType == fuaNALUType:
@@ -258,7 +303,8 @@ func (p *H264Packet) Unmarshal(payload []byte) ([]byte, error) {
nalu := append([]byte{}, naluRefIdc|fragmentedNaluType)
nalu = append(nalu, p.fuaBuffer...)
p.fuaBuffer = nil
- return p.doPackaging(nalu), nil
+
+ return p.doPackaging(nil, nalu), nil
}
return []byte{}, nil
@@ -267,9 +313,18 @@ func (p *H264Packet) Unmarshal(payload []byte) ([]byte, error) {
return nil, fmt.Errorf("%w: %d", errUnhandledNALUType, naluType)
}
-// H264PartitionHeadChecker is obsolete
+// H264PartitionHeadChecker checks H264 partition head.
+//
+// Deprecated: replaced by H264Packet.IsPartitionHead().
type H264PartitionHeadChecker struct{}
+// IsPartitionHead checks if this is the head of a packetized nalu stream.
+//
+// Deprecated: replaced by H264Packet.IsPartitionHead().
+func (*H264PartitionHeadChecker) IsPartitionHead(packet []byte) bool {
+ return (&H264Packet{}).IsPartitionHead(packet)
+}
+
// IsPartitionHead checks if this is the head of a packetized nalu stream.
func (*H264Packet) IsPartitionHead(payload []byte) bool {
if len(payload) < 2 {
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/h265_packet.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/h265_packet.go
index 6f0490dc6..ff24f080f 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/h265_packet.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/h265_packet.go
@@ -1,9 +1,13 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package codecs
import (
"encoding/binary"
"errors"
"fmt"
+ "math"
)
//
@@ -20,7 +24,7 @@ var (
//
const (
- // sizeof(uint16)
+ // sizeof(uint16).
h265NaluHeaderSize = 2
// https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2
h265NaluAggregationPacketType = 48
@@ -30,13 +34,16 @@ const (
h265NaluPACIPacketType = 50
)
-// H265NALUHeader is a H265 NAL Unit Header
+// H265NALUHeader is a H265 NAL Unit Header.
// https://datatracker.ietf.org/doc/html/rfc7798#section-1.1.4
-// +---------------+---------------+
-// |0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// |F| Type | LayerID | TID |
-// +-------------+-----------------+
+/*
+* +---------------+---------------+
+* |0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* |F| Type | LayerID | TID |
+* +-------------+-----------------+
+**/
+// .
type H265NALUHeader uint16
func newH265NALUHeader(highByte, lowByte uint8) H265NALUHeader {
@@ -52,13 +59,15 @@ func (h H265NALUHeader) F() bool {
func (h H265NALUHeader) Type() uint8 {
// 01111110 00000000
const mask = 0b01111110 << 8
- return uint8((uint16(h) & mask) >> (8 + 1))
+
+ return uint8((uint16(h) & mask) >> (8 + 1)) // nolint: gosec // G115 false positive
}
// IsTypeVCLUnit returns whether or not the NAL Unit type is a VCL NAL unit.
func (h H265NALUHeader) IsTypeVCLUnit() bool {
// Type is coded on 6 bits
const msbMask = 0b00100000
+
return (h.Type() & msbMask) == 0
}
@@ -66,13 +75,15 @@ func (h H265NALUHeader) IsTypeVCLUnit() bool {
func (h H265NALUHeader) LayerID() uint8 {
// 00000001 11111000
const mask = (0b00000001 << 8) | 0b11111000
- return uint8((uint16(h) & mask) >> 3)
+
+ return uint8((uint16(h) & mask) >> 3) // nolint: gosec // G115 false positive
}
// TID is the temporal identifier of the NAL unit +1.
func (h H265NALUHeader) TID() uint8 {
const mask = 0b00000111
- return uint8(uint16(h) & mask)
+
+ return uint8(uint16(h) & mask) // nolint: gosec // G115 false positive
}
// IsAggregationPacket returns whether or not the packet is an Aggregation packet.
@@ -95,18 +106,19 @@ func (h H265NALUHeader) IsPACIPacket() bool {
//
// H265SingleNALUnitPacket represents a NALU packet, containing exactly one NAL unit.
-// 0 1 2 3
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | PayloadHdr | DONL (conditional) |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | |
-// | NAL unit payload data |
-// | |
-// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | :...OPTIONAL RTP padding |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-//
+/*
+* 0 1 2 3
+* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | PayloadHdr | DONL (conditional) |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | |
+* | NAL unit payload data |
+* | |
+* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | :...OPTIONAL RTP padding |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+**/
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.1
type H265SingleNALUnitPacket struct {
// payloadHeader is the header of the H265 packet.
@@ -125,7 +137,8 @@ func (p *H265SingleNALUnitPacket) WithDONL(value bool) {
p.mightNeedDONL = value
}
-// Unmarshal parses the passed byte slice and stores the result in the H265SingleNALUnitPacket this method is called upon.
+// Unmarshal parses the passed byte slice and stores the result in the H265SingleNALUnitPacket
+// this method is called upon.
func (p *H265SingleNALUnitPacket) Unmarshal(payload []byte) ([]byte, error) {
// sizeof(headers)
const totalHeaderSize = h265NaluHeaderSize
@@ -184,19 +197,19 @@ func (p *H265SingleNALUnitPacket) isH265Packet() {}
//
// H265AggregationUnitFirst represent the First Aggregation Unit in an AP.
-//
-// 0 1 2 3
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// : DONL (conditional) | NALU size |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | NALU size | |
-// +-+-+-+-+-+-+-+-+ NAL unit |
-// | |
-// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | :
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-//
+/*
+* 0 1 2 3
+* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* : DONL (conditional) | NALU size |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | NALU size | |
+* +-+-+-+-+-+-+-+-+ NAL unit |
+* | |
+* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | :
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+**/
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2
type H265AggregationUnitFirst struct {
donl *uint16
@@ -222,18 +235,18 @@ func (u H265AggregationUnitFirst) NalUnit() []byte {
}
// H265AggregationUnit represent the an Aggregation Unit in an AP, which is not the first one.
-//
-// 0 1 2 3
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// : DOND (cond) | NALU size |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | |
-// | NAL unit |
-// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | :
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-//
+/*
+* 0 1 2 3
+* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* : DOND (cond) | NALU size |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | |
+* | NAL unit |
+* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | :
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+**/
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2
type H265AggregationUnit struct {
dond *uint8
@@ -259,18 +272,19 @@ func (u H265AggregationUnit) NalUnit() []byte {
}
// H265AggregationPacket represents an Aggregation packet.
-// 0 1 2 3
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | PayloadHdr (Type=48) | |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
-// | |
-// | two or more aggregation units |
-// | |
-// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | :...OPTIONAL RTP padding |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-//
+/*
+* 0 1 2 3
+* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | PayloadHdr (Type=48) | |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+* | |
+* | two or more aggregation units |
+* | |
+* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | :...OPTIONAL RTP padding |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+**/
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2
type H265AggregationPacket struct {
firstUnit *H265AggregationUnitFirst
@@ -286,7 +300,7 @@ func (p *H265AggregationPacket) WithDONL(value bool) {
}
// Unmarshal parses the passed byte slice and stores the result in the H265AggregationPacket this method is called upon.
-func (p *H265AggregationPacket) Unmarshal(payload []byte) ([]byte, error) {
+func (p *H265AggregationPacket) Unmarshal(payload []byte) ([]byte, error) { //nolint:cyclop
// sizeof(headers)
const totalHeaderSize = h265NaluHeaderSize
if payload == nil {
@@ -390,51 +404,56 @@ func (p *H265AggregationPacket) isH265Packet() {}
//
const (
- // sizeof(uint8)
+ // sizeof(uint8).
h265FragmentationUnitHeaderSize = 1
)
-// H265FragmentationUnitHeader is a H265 FU Header
+// H265FragmentationUnitHeader is a H265 FU Header.
+//
// +---------------+
// |0|1|2|3|4|5|6|7|
// +-+-+-+-+-+-+-+-+
// |S|E| FuType |
// +---------------+
+// .
type H265FragmentationUnitHeader uint8
// S represents the start of a fragmented NAL unit.
func (h H265FragmentationUnitHeader) S() bool {
const mask = 0b10000000
+
return ((h & mask) >> 7) != 0
}
// E represents the end of a fragmented NAL unit.
func (h H265FragmentationUnitHeader) E() bool {
const mask = 0b01000000
+
return ((h & mask) >> 6) != 0
}
// FuType MUST be equal to the field Type of the fragmented NAL unit.
func (h H265FragmentationUnitHeader) FuType() uint8 {
const mask = 0b00111111
+
return uint8(h) & mask
}
// H265FragmentationUnitPacket represents a single Fragmentation Unit packet.
-//
-// 0 1 2 3
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | PayloadHdr (Type=49) | FU header | DONL (cond) |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
-// | DONL (cond) | |
-// |-+-+-+-+-+-+-+-+ |
-// | FU payload |
-// | |
-// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | :...OPTIONAL RTP padding |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-//
+/*
+* 0 1 2 3
+* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | PayloadHdr (Type=49) | FU header | DONL (cond) |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
+* | DONL (cond) | |
+* |-+-+-+-+-+-+-+-+ |
+* | FU payload |
+* | |
+* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | :...OPTIONAL RTP padding |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+**/
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.3
type H265FragmentationUnitPacket struct {
// payloadHeader is the header of the H265 packet.
@@ -455,7 +474,8 @@ func (p *H265FragmentationUnitPacket) WithDONL(value bool) {
p.mightNeedDONL = value
}
-// Unmarshal parses the passed byte slice and stores the result in the H265FragmentationUnitPacket this method is called upon.
+// Unmarshal parses the passed byte slice and stores the result in the H265FragmentationUnitPacket
+// this method is called upon.
func (p *H265FragmentationUnitPacket) Unmarshal(payload []byte) ([]byte, error) {
// sizeof(headers)
const totalHeaderSize = h265NaluHeaderSize + h265FragmentationUnitHeaderSize
@@ -521,22 +541,22 @@ func (p *H265FragmentationUnitPacket) isH265Packet() {}
//
// H265PACIPacket represents a single H265 PACI packet.
-//
-// 0 1 2 3
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | PayloadHdr (Type=50) |A| cType | PHSsize |F0..2|Y|
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | Payload Header Extension Structure (PHES) |
-// |=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=|
-// | |
-// | PACI payload: NAL unit |
-// | . . . |
-// | |
-// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | :...OPTIONAL RTP padding |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-//
+/*
+* 0 1 2 3
+* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | PayloadHdr (Type=50) |A| cType | PHSsize |F0..2|Y|
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | Payload Header Extension Structure (PHES) |
+* |=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=|
+* | |
+* | PACI payload: NAL unit |
+* | . . . |
+* | |
+* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+* | :...OPTIONAL RTP padding |
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+**/
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.4
type H265PACIPacket struct {
// payloadHeader is the header of the H265 packet.
@@ -560,42 +580,49 @@ func (p *H265PACIPacket) PayloadHeader() H265NALUHeader {
// A copies the F bit of the PACI payload NALU.
func (p *H265PACIPacket) A() bool {
const mask = 0b10000000 << 8
+
return (p.paciHeaderFields & mask) != 0
}
// CType copies the Type field of the PACI payload NALU.
func (p *H265PACIPacket) CType() uint8 {
const mask = 0b01111110 << 8
- return uint8((p.paciHeaderFields & mask) >> (8 + 1))
+
+ return uint8((p.paciHeaderFields & mask) >> (8 + 1)) // nolint: gosec // G115 false positive
}
// PHSsize indicates the size of the PHES field.
func (p *H265PACIPacket) PHSsize() uint8 {
const mask = (0b00000001 << 8) | 0b11110000
- return uint8((p.paciHeaderFields & mask) >> 4)
+
+ return uint8((p.paciHeaderFields & mask) >> 4) // nolint: gosec // G115 false positive
}
// F0 indicates the presence of a Temporal Scalability support extension in the PHES.
func (p *H265PACIPacket) F0() bool {
const mask = 0b00001000
+
return (p.paciHeaderFields & mask) != 0
}
// F1 must be zero, reserved for future extensions.
func (p *H265PACIPacket) F1() bool {
const mask = 0b00000100
+
return (p.paciHeaderFields & mask) != 0
}
// F2 must be zero, reserved for future extensions.
func (p *H265PACIPacket) F2() bool {
const mask = 0b00000010
+
return (p.paciHeaderFields & mask) != 0
}
// Y must be zero, reserved for future extensions.
func (p *H265PACIPacket) Y() bool {
const mask = 0b00000001
+
return (p.paciHeaderFields & mask) != 0
}
@@ -616,6 +643,7 @@ func (p *H265PACIPacket) TSCI() *H265TSCI {
}
tsci := H265TSCI((uint32(p.phes[0]) << 16) | (uint32(p.phes[1]) << 8) | uint32(p.phes[0]))
+
return &tsci
}
@@ -645,6 +673,7 @@ func (p *H265PACIPacket) Unmarshal(payload []byte) ([]byte, error) {
if len(payload) < int(headerExtensionSize)+1 {
p.paciHeaderFields = 0
+
return nil, errShortPacket
}
@@ -674,35 +703,40 @@ type H265TSCI uint32
func (h H265TSCI) TL0PICIDX() uint8 {
const m1 = 0xFFFF0000
const m2 = 0xFF00
- return uint8((((h & m1) >> 16) & m2) >> 8)
+
+ return uint8((((h & m1) >> 16) & m2) >> 8) // nolint: gosec // G115 false positive
}
// IrapPicID see RFC7798 for more details.
func (h H265TSCI) IrapPicID() uint8 {
const m1 = 0xFFFF0000
const m2 = 0x00FF
- return uint8(((h & m1) >> 16) & m2)
+
+ return uint8(((h & m1) >> 16) & m2) // nolint: gosec // G115 false positive
}
// S see RFC7798 for more details.
func (h H265TSCI) S() bool {
const m1 = 0xFF00
const m2 = 0b10000000
- return (uint8((h&m1)>>8) & m2) != 0
+
+ return (uint8((h&m1)>>8) & m2) != 0 // nolint: gosec // G115 false positive
}
// E see RFC7798 for more details.
func (h H265TSCI) E() bool {
const m1 = 0xFF00
const m2 = 0b01000000
- return (uint8((h&m1)>>8) & m2) != 0
+
+ return (uint8((h&m1)>>8) & m2) != 0 // nolint: gosec // G115 false positive
}
// RES see RFC7798 for more details.
func (h H265TSCI) RES() uint8 {
const m1 = 0xFF00
const m2 = 0b00111111
- return uint8((h&m1)>>8) & m2
+
+ return uint8((h&m1)>>8) & m2 // nolint: gosec // G115 false positive
}
//
@@ -738,8 +772,8 @@ func (p *H265Packet) WithDONL(value bool) {
p.mightNeedDONL = value
}
-// Unmarshal parses the passed byte slice and stores the result in the H265Packet this method is called upon
-func (p *H265Packet) Unmarshal(payload []byte) ([]byte, error) {
+// Unmarshal parses the passed byte slice and stores the result in the H265Packet this method is called upon.
+func (p *H265Packet) Unmarshal(payload []byte) ([]byte, error) { // nolint:cyclop
if payload == nil {
return nil, errNilPacket
} else if len(payload) <= h265NaluHeaderSize {
@@ -817,3 +851,206 @@ func (*H265Packet) IsPartitionHead(payload []byte) bool {
return true
}
+
+// H265Payloader payloads H265 packets.
+type H265Payloader struct {
+ AddDONL bool
+ SkipAggregation bool
+ donl uint16
+}
+
+// Payload fragments a H265 packet across one or more byte arrays.
+func (p *H265Payloader) Payload(mtu uint16, payload []byte) [][]byte { //nolint:gocognit,cyclop
+ var payloads [][]byte
+ if len(payload) == 0 || mtu == 0 {
+ return payloads
+ }
+
+ bufferedNALUs := make([][]byte, 0)
+ aggregationBufferSize := 0
+
+ flushBufferedNals := func() {
+ if len(bufferedNALUs) == 0 {
+ return
+ }
+ if len(bufferedNALUs) == 1 { //nolint:nestif
+ // emit this as a single NALU packet
+ nalu := bufferedNALUs[0]
+
+ if p.AddDONL {
+ buf := make([]byte, len(nalu)+2)
+
+ // copy the NALU header to the payload header
+ copy(buf[0:h265NaluHeaderSize], nalu[0:h265NaluHeaderSize])
+
+ // copy the DONL into the header
+ binary.BigEndian.PutUint16(buf[h265NaluHeaderSize:h265NaluHeaderSize+2], p.donl)
+
+ // write the payload
+ copy(buf[h265NaluHeaderSize+2:], nalu[h265NaluHeaderSize:])
+
+ p.donl++
+
+ payloads = append(payloads, buf)
+ } else {
+ // write the nalu directly to the payload
+ payloads = append(payloads, nalu)
+ }
+ } else {
+ // construct an aggregation packet
+ aggregationPacketSize := aggregationBufferSize
+ buf := make([]byte, aggregationPacketSize)
+
+ layerID := uint8(math.MaxUint8)
+ tid := uint8(math.MaxUint8)
+ for _, nalu := range bufferedNALUs {
+ header := newH265NALUHeader(nalu[0], nalu[1])
+ headerLayerID := header.LayerID()
+ headerTID := header.TID()
+ if headerLayerID < layerID {
+ layerID = headerLayerID
+ }
+ if headerTID < tid {
+ tid = headerTID
+ }
+ }
+
+ binary.BigEndian.PutUint16(buf[0:2], (uint16(h265NaluAggregationPacketType)<<9)|(uint16(layerID)<<3)|uint16(tid))
+
+ index := 2
+ for i, nalu := range bufferedNALUs {
+ if p.AddDONL {
+ if i == 0 {
+ binary.BigEndian.PutUint16(buf[index:index+2], p.donl)
+ index += 2
+ } else {
+ buf[index] = byte(i - 1)
+ index++
+ }
+ }
+
+ // Since the type of mtu is uint16, len(nalu) fits in as well, so it is safe.
+ // #nosec
+ binary.BigEndian.PutUint16(buf[index:index+2], uint16(len(nalu)))
+ index += 2
+ index += copy(buf[index:], nalu)
+ }
+ payloads = append(payloads, buf)
+ }
+ // clear the buffered NALUs
+ bufferedNALUs = make([][]byte, 0)
+ aggregationBufferSize = 0
+ }
+
+ calcMarginalAggregationSize := func(nalu []byte) int {
+ marginalAggregationSize := len(nalu) + 2 // +2 is NALU size Field size
+ if len(bufferedNALUs) == 1 {
+ marginalAggregationSize = len(nalu) + 4 // +4 are Aggregation header + NALU size Field size
+ }
+ if p.AddDONL {
+ if len(bufferedNALUs) == 0 {
+ marginalAggregationSize += 2
+ } else {
+ marginalAggregationSize++
+ }
+ }
+
+ return marginalAggregationSize
+ }
+
+ emitNalus(payload, func(nalu []byte) {
+ if len(nalu) < 2 {
+ // NALU header is 2 bytes
+ return
+ }
+
+ naluLen := len(nalu) + 2
+ if p.AddDONL {
+ naluLen += 2
+ }
+ if naluLen <= int(mtu) { //nolint:nestif
+ // this nalu fits into a single packet, either it can be emitted as
+ // a single nalu or appended to the previous aggregation packet
+ marginalAggregationSize := calcMarginalAggregationSize(nalu)
+
+ if aggregationBufferSize+marginalAggregationSize > int(mtu) {
+ flushBufferedNals()
+ marginalAggregationSize = calcMarginalAggregationSize(nalu)
+ }
+ bufferedNALUs = append(bufferedNALUs, nalu)
+ aggregationBufferSize += marginalAggregationSize
+ if p.SkipAggregation {
+ // emit this immediately.
+ flushBufferedNals()
+ }
+ } else {
+ // if this nalu doesn't fit in the current mtu, it needs to be fragmented
+ fuPacketHeaderSize := h265FragmentationUnitHeaderSize + 2 /* payload header size */
+ if p.AddDONL {
+ fuPacketHeaderSize += 2
+ }
+
+ // then, fragment the nalu
+ maxFUPayloadSize := int(mtu) - fuPacketHeaderSize
+
+ naluHeader := newH265NALUHeader(nalu[0], nalu[1])
+
+ // the nalu header is omitted from the fragmentation packet payload
+ nalu = nalu[h265NaluHeaderSize:]
+
+ if maxFUPayloadSize <= 0 || len(nalu) == 0 {
+ return
+ }
+
+ // flush any buffered aggregation packets.
+ flushBufferedNals()
+
+ fullNALUSize := len(nalu)
+ for len(nalu) > 0 {
+ curentFUPayloadSize := len(nalu)
+ if curentFUPayloadSize > maxFUPayloadSize {
+ curentFUPayloadSize = maxFUPayloadSize
+ }
+
+ out := make([]byte, fuPacketHeaderSize+curentFUPayloadSize)
+
+ // write the payload header
+ binary.BigEndian.PutUint16(out[0:2], uint16(naluHeader))
+ out[0] = (out[0] & 0b10000001) | h265NaluFragmentationUnitType<<1
+
+ // write the fragment header
+ out[2] = byte(H265FragmentationUnitHeader(naluHeader.Type()))
+ if len(nalu) == fullNALUSize {
+ // Set start bit
+ out[2] |= 1 << 7
+ } else if len(nalu)-curentFUPayloadSize == 0 {
+ // Set end bit
+ out[2] |= 1 << 6
+ }
+
+ if p.AddDONL {
+ // write the DONL header
+ binary.BigEndian.PutUint16(out[3:5], p.donl)
+
+ p.donl++
+
+ // copy the fragment payload
+ copy(out[5:], nalu[0:curentFUPayloadSize])
+ } else {
+ // copy the fragment payload
+ copy(out[3:], nalu[0:curentFUPayloadSize])
+ }
+
+ // append the fragment to the payload
+ payloads = append(payloads, out)
+
+ // advance the nalu data pointer
+ nalu = nalu[curentFUPayloadSize:]
+ }
+ }
+ })
+
+ flushBufferedNals()
+
+ return payloads
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/opus_packet.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/opus_packet.go
index cd5ea332d..89aaae35a 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/opus_packet.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/opus_packet.go
@@ -1,27 +1,31 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package codecs
-// OpusPayloader payloads Opus packets
+// OpusPayloader payloads Opus packets.
type OpusPayloader struct{}
-// Payload fragments an Opus packet across one or more byte arrays
-func (p *OpusPayloader) Payload(mtu uint16, payload []byte) [][]byte {
+// Payload fragments an Opus packet across one or more byte arrays.
+func (p *OpusPayloader) Payload(_ uint16, payload []byte) [][]byte {
if payload == nil {
return [][]byte{}
}
out := make([]byte, len(payload))
copy(out, payload)
+
return [][]byte{out}
}
-// OpusPacket represents the Opus header that is stored in the payload of an RTP Packet
+// OpusPacket represents the Opus header that is stored in the payload of an RTP Packet.
type OpusPacket struct {
Payload []byte
audioDepacketizer
}
-// Unmarshal parses the passed byte slice and stores the result in the OpusPacket this method is called upon
+// Unmarshal parses the passed byte slice and stores the result in the OpusPacket this method is called upon.
func (p *OpusPacket) Unmarshal(packet []byte) ([]byte, error) {
if packet == nil {
return nil, errNilPacket
@@ -30,8 +34,18 @@ func (p *OpusPacket) Unmarshal(packet []byte) ([]byte, error) {
}
p.Payload = packet
+
return packet, nil
}
-// OpusPartitionHeadChecker is obsolete
+// OpusPartitionHeadChecker checks Opus partition head.
+//
+// Deprecated: replaced by OpusPacket.IsPartitionHead().
type OpusPartitionHeadChecker struct{}
+
+// IsPartitionHead checks whether if this is a head of the Opus partition.
+//
+// Deprecated: replaced by OpusPacket.IsPartitionHead().
+func (*OpusPartitionHeadChecker) IsPartitionHead(packet []byte) bool {
+ return (&OpusPacket{}).IsPartitionHead(packet)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/vp8_packet.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/vp8_packet.go
index cd8692904..64471f8af 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/vp8_packet.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/vp8_packet.go
@@ -1,6 +1,9 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package codecs
-// VP8Payloader payloads VP8 packets
+// VP8Payloader payloads VP8 packets.
type VP8Payloader struct {
EnablePictureID bool
pictureID uint16
@@ -10,8 +13,8 @@ const (
vp8HeaderSize = 1
)
-// Payload fragments a VP8 packet across one or more byte arrays
-func (p *VP8Payloader) Payload(mtu uint16, payload []byte) [][]byte {
+// Payload fragments a VP8 packet across one or more byte arrays.
+func (p *VP8Payloader) Payload(mtu uint16, payload []byte) [][]byte { //nolint:cyclop
/*
* https://tools.ietf.org/html/rfc7741#section-4.2
*
@@ -53,12 +56,12 @@ func (p *VP8Payloader) Payload(mtu uint16, payload []byte) [][]byte {
var payloads [][]byte
// Make sure the fragment/payload size is correct
- if min(maxFragmentSize, payloadDataRemaining) <= 0 {
+ if minInt(maxFragmentSize, payloadDataRemaining) <= 0 {
return payloads
}
first := true
for payloadDataRemaining > 0 {
- currentFragmentSize := min(maxFragmentSize, payloadDataRemaining)
+ currentFragmentSize := minInt(maxFragmentSize, payloadDataRemaining)
out := make([]byte, usingHeaderSize+currentFragmentSize)
if first {
@@ -71,12 +74,12 @@ func (p *VP8Payloader) Payload(mtu uint16, payload []byte) [][]byte {
case vp8HeaderSize + 2:
out[0] |= 0x80
out[1] |= 0x80
- out[2] |= uint8(p.pictureID & 0x7F)
+ out[2] |= uint8(p.pictureID & 0x7F) // nolint: gosec // G115 false positive
case vp8HeaderSize + 3:
out[0] |= 0x80
out[1] |= 0x80
- out[2] |= 0x80 | uint8((p.pictureID>>8)&0x7F)
- out[3] |= uint8(p.pictureID & 0xFF)
+ out[2] |= 0x80 | uint8((p.pictureID>>8)&0x7F) // nolint: gosec // G115 false positive
+ out[3] |= uint8(p.pictureID & 0xFF) // nolint: gosec // G115 false positive
}
}
@@ -93,7 +96,7 @@ func (p *VP8Payloader) Payload(mtu uint16, payload []byte) [][]byte {
return payloads
}
-// VP8Packet represents the VP8 header that is stored in the payload of an RTP Packet
+// VP8Packet represents the VP8 header that is stored in the payload of an RTP Packet.
type VP8Packet struct {
// Required Header
X uint8 /* extended control bits present */
@@ -119,20 +122,19 @@ type VP8Packet struct {
videoDepacketizer
}
-// Unmarshal parses the passed byte slice and stores the result in the VP8Packet this method is called upon
-func (p *VP8Packet) Unmarshal(payload []byte) ([]byte, error) {
+// Unmarshal parses the passed byte slice and stores the result in the VP8Packet this method is called upon.
+func (p *VP8Packet) Unmarshal(payload []byte) ([]byte, error) { //nolint:gocognit,cyclop
if payload == nil {
return nil, errNilPacket
}
payloadLen := len(payload)
- if payloadLen < 4 {
- return nil, errShortPacket
- }
-
payloadIndex := 0
+ if payloadIndex >= payloadLen {
+ return nil, errShortPacket
+ }
p.X = (payload[payloadIndex] & 0x80) >> 7
p.N = (payload[payloadIndex] & 0x20) >> 5
p.S = (payload[payloadIndex] & 0x10) >> 4
@@ -141,61 +143,95 @@ func (p *VP8Packet) Unmarshal(payload []byte) ([]byte, error) {
payloadIndex++
if p.X == 1 {
+ if payloadIndex >= payloadLen {
+ return nil, errShortPacket
+ }
p.I = (payload[payloadIndex] & 0x80) >> 7
p.L = (payload[payloadIndex] & 0x40) >> 6
p.T = (payload[payloadIndex] & 0x20) >> 5
p.K = (payload[payloadIndex] & 0x10) >> 4
payloadIndex++
+ } else {
+ p.I = 0
+ p.L = 0
+ p.T = 0
+ p.K = 0
}
+ // nolint: nestif
if p.I == 1 { // PID present?
+ if payloadIndex >= payloadLen {
+ return nil, errShortPacket
+ }
if payload[payloadIndex]&0x80 > 0 { // M == 1, PID is 16bit
+ if payloadIndex+1 >= payloadLen {
+ return nil, errShortPacket
+ }
p.PictureID = (uint16(payload[payloadIndex]&0x7F) << 8) | uint16(payload[payloadIndex+1])
payloadIndex += 2
} else {
p.PictureID = uint16(payload[payloadIndex])
payloadIndex++
}
- }
-
- if payloadIndex >= payloadLen {
- return nil, errShortPacket
+ } else {
+ p.PictureID = 0
}
if p.L == 1 {
+ if payloadIndex >= payloadLen {
+ return nil, errShortPacket
+ }
p.TL0PICIDX = payload[payloadIndex]
payloadIndex++
+ } else {
+ p.TL0PICIDX = 0
}
- if payloadIndex >= payloadLen {
- return nil, errShortPacket
- }
-
- if p.T == 1 || p.K == 1 {
+ if p.T == 1 || p.K == 1 { // nolint: nestif
+ if payloadIndex >= payloadLen {
+ return nil, errShortPacket
+ }
if p.T == 1 {
p.TID = payload[payloadIndex] >> 6
p.Y = (payload[payloadIndex] >> 5) & 0x1
+ } else {
+ p.TID = 0
+ p.Y = 0
}
if p.K == 1 {
p.KEYIDX = payload[payloadIndex] & 0x1F
+ } else {
+ p.KEYIDX = 0
}
payloadIndex++
+ } else {
+ p.TID = 0
+ p.Y = 0
+ p.KEYIDX = 0
}
- if payloadIndex >= payloadLen {
- return nil, errShortPacket
- }
p.Payload = payload[payloadIndex:]
+
return p.Payload, nil
}
-// VP8PartitionHeadChecker is obsolete
+// VP8PartitionHeadChecker checks VP8 partition head
+//
+// Deprecated: replaced by VP8Packet.IsPartitionHead().
type VP8PartitionHeadChecker struct{}
-// IsPartitionHead checks whether if this is a head of the VP8 partition
+// IsPartitionHead checks whether if this is a head of the VP8 partition.
+//
+// Deprecated: replaced by VP8Packet.IsPartitionHead().
+func (*VP8PartitionHeadChecker) IsPartitionHead(packet []byte) bool {
+ return (&VP8Packet{}).IsPartitionHead(packet)
+}
+
+// IsPartitionHead checks whether if this is a head of the VP8 partition.
func (*VP8Packet) IsPartitionHead(payload []byte) bool {
if len(payload) < 1 {
return false
}
+
return (payload[0] & 0x10) != 0
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/vp9/bits.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/vp9/bits.go
new file mode 100644
index 000000000..97f7022d2
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/vp9/bits.go
@@ -0,0 +1,68 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package vp9
+
+import "errors"
+
+var errNotEnoughBits = errors.New("not enough bits")
+
+func hasSpace(buf []byte, pos int, n int) error {
+ if n > ((len(buf) * 8) - pos) {
+ return errNotEnoughBits
+ }
+
+ return nil
+}
+
+func readFlag(buf []byte, pos *int) (bool, error) {
+ err := hasSpace(buf, *pos, 1)
+ if err != nil {
+ return false, err
+ }
+
+ return readFlagUnsafe(buf, pos), nil
+}
+
+func readFlagUnsafe(buf []byte, pos *int) bool {
+ b := (buf[*pos>>0x03] >> (7 - (*pos & 0x07))) & 0x01
+ *pos++
+
+ return b == 1
+}
+
+func readBits(buf []byte, pos *int, n int) (uint64, error) {
+ err := hasSpace(buf, *pos, n)
+ if err != nil {
+ return 0, err
+ }
+
+ return readBitsUnsafe(buf, pos, n), nil
+}
+
+func readBitsUnsafe(buf []byte, pos *int, n int) uint64 {
+ res := 8 - (*pos & 0x07)
+ if n < res {
+ bits := uint64((buf[*pos>>0x03] >> (res - n)) & (1<>0x03] & (1<= 8 {
+ bits = (bits << 8) | uint64(buf[*pos>>0x03])
+ *pos += 8
+ n -= 8
+ }
+
+ if n > 0 {
+ bits = (bits << n) | uint64(buf[*pos>>0x03]>>(8-n))
+ *pos += n
+ }
+
+ return bits
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/vp9/header.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/vp9/header.go
new file mode 100644
index 000000000..263147471
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/vp9/header.go
@@ -0,0 +1,225 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package vp9 contains a VP9 header parser.
+package vp9
+
+import (
+ "errors"
+)
+
+var (
+ errInvalidFrameMarker = errors.New("invalid frame marker")
+ errWrongFrameSyncByte0 = errors.New("wrong frame_sync_byte_0")
+ errWrongFrameSyncByte1 = errors.New("wrong frame_sync_byte_1")
+ errWrongFrameSyncByte2 = errors.New("wrong frame_sync_byte_2")
+)
+
+// HeaderColorConfig is the color_config member of an header.
+type HeaderColorConfig struct {
+ TenOrTwelveBit bool
+ BitDepth uint8
+ ColorSpace uint8
+ ColorRange bool
+ SubsamplingX bool
+ SubsamplingY bool
+}
+
+func (c *HeaderColorConfig) unmarshal(profile uint8, buf []byte, pos *int) error { // nolint:cyclop
+ if profile >= 2 {
+ var err error
+ c.TenOrTwelveBit, err = readFlag(buf, pos)
+ if err != nil {
+ return err
+ }
+
+ if c.TenOrTwelveBit {
+ c.BitDepth = 12
+ } else {
+ c.BitDepth = 10
+ }
+ } else {
+ c.BitDepth = 8
+ }
+
+ tmp, err := readBits(buf, pos, 3)
+ if err != nil {
+ return err
+ }
+ c.ColorSpace = uint8(tmp) // nolint: gosec // G115, no overflow we read 3 bits
+
+ if c.ColorSpace != 7 { // nolint: nestif
+ var err error
+ c.ColorRange, err = readFlag(buf, pos)
+ if err != nil {
+ return err
+ }
+
+ if profile == 1 || profile == 3 {
+ err := hasSpace(buf, *pos, 3)
+ if err != nil {
+ return err
+ }
+
+ c.SubsamplingX = readFlagUnsafe(buf, pos)
+ c.SubsamplingY = readFlagUnsafe(buf, pos)
+ *pos++
+ } else {
+ c.SubsamplingX = true
+ c.SubsamplingY = true
+ }
+ } else {
+ c.ColorRange = true
+
+ if profile == 1 || profile == 3 {
+ c.SubsamplingX = false
+ c.SubsamplingY = false
+
+ err := hasSpace(buf, *pos, 1)
+ if err != nil {
+ return err
+ }
+ *pos++
+ }
+ }
+
+ return nil
+}
+
+// HeaderFrameSize is the frame_size member of an header.
+type HeaderFrameSize struct {
+ FrameWidthMinus1 uint16
+ FrameHeightMinus1 uint16
+}
+
+func (s *HeaderFrameSize) unmarshal(buf []byte, pos *int) error {
+ err := hasSpace(buf, *pos, 32)
+ if err != nil {
+ return err
+ }
+
+ s.FrameWidthMinus1 = uint16(readBitsUnsafe(buf, pos, 16)) // nolint: gosec // G115 no overflow, we read 16 bits
+ s.FrameHeightMinus1 = uint16(readBitsUnsafe(buf, pos, 16)) // nolint: gosec // G115
+
+ return nil
+}
+
+// Header is a VP9 Frame header.
+// Specification:
+// https://storage.googleapis.com/downloads.webmproject.org/docs/vp9/vp9-bitstream-specification-v0.6-20160331-draft.pdf
+type Header struct {
+ Profile uint8
+ ShowExistingFrame bool
+ FrameToShowMapIdx uint8
+ NonKeyFrame bool
+ ShowFrame bool
+ ErrorResilientMode bool
+ ColorConfig *HeaderColorConfig
+ FrameSize *HeaderFrameSize
+}
+
+// Unmarshal decodes a Header.
+func (h *Header) Unmarshal(buf []byte) error { //nolint:cyclop
+ pos := 0
+
+ err := hasSpace(buf, pos, 4)
+ if err != nil {
+ return err
+ }
+
+ frameMarker := readBitsUnsafe(buf, &pos, 2)
+ if frameMarker != 2 {
+ return errInvalidFrameMarker
+ }
+
+ profileLowBit := uint8(readBitsUnsafe(buf, &pos, 1)) // nolint: gosec // no overflow, we read 1 bit
+ profileHighBit := uint8(readBitsUnsafe(buf, &pos, 1)) // nolint: gosec // G115
+ h.Profile = profileHighBit<<1 + profileLowBit
+
+ if h.Profile == 3 {
+ err = hasSpace(buf, pos, 1)
+ if err != nil {
+ return err
+ }
+ pos++
+ }
+
+ h.ShowExistingFrame, err = readFlag(buf, &pos)
+ if err != nil {
+ return err
+ }
+
+ if h.ShowExistingFrame {
+ var tmp uint64
+ tmp, err = readBits(buf, &pos, 3)
+ if err != nil {
+ return err
+ }
+ h.FrameToShowMapIdx = uint8(tmp) // nolint: gosec // no overflow, we read 3 bits
+
+ return nil
+ }
+
+ err = hasSpace(buf, pos, 3)
+ if err != nil {
+ return err
+ }
+
+ h.NonKeyFrame = readFlagUnsafe(buf, &pos)
+ h.ShowFrame = readFlagUnsafe(buf, &pos)
+ h.ErrorResilientMode = readFlagUnsafe(buf, &pos)
+
+ if !h.NonKeyFrame { // nolint: nestif
+ err := hasSpace(buf, pos, 24)
+ if err != nil {
+ return err
+ }
+
+ frameSyncByte0 := uint8(readBitsUnsafe(buf, &pos, 8)) // nolint: gosec // no overflow, we read 8 bits
+ if frameSyncByte0 != 0x49 {
+ return errWrongFrameSyncByte0
+ }
+
+ frameSyncByte1 := uint8(readBitsUnsafe(buf, &pos, 8)) // nolint: gosec // no overflow, we read 8 bits
+ if frameSyncByte1 != 0x83 {
+ return errWrongFrameSyncByte1
+ }
+
+ frameSyncByte2 := uint8(readBitsUnsafe(buf, &pos, 8)) // nolint: gosec // no overflow, we read 8 bits
+ if frameSyncByte2 != 0x42 {
+ return errWrongFrameSyncByte2
+ }
+
+ h.ColorConfig = &HeaderColorConfig{}
+ err = h.ColorConfig.unmarshal(h.Profile, buf, &pos)
+ if err != nil {
+ return err
+ }
+
+ h.FrameSize = &HeaderFrameSize{}
+ err = h.FrameSize.unmarshal(buf, &pos)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// Width returns the video width.
+func (h Header) Width() uint16 {
+ if h.FrameSize == nil {
+ return 0
+ }
+
+ return h.FrameSize.FrameWidthMinus1 + 1
+}
+
+// Height returns the video height.
+func (h Header) Height() uint16 {
+ if h.FrameSize == nil {
+ return 0
+ }
+
+ return h.FrameSize.FrameHeightMinus1 + 1
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/vp9_packet.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/vp9_packet.go
index 917e630bb..bbae1ce7b 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/vp9_packet.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/vp9_packet.go
@@ -1,32 +1,62 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package codecs
import (
"github.com/pion/randutil"
+ "github.com/pion/rtp/codecs/vp9"
)
// Use global random generator to properly seed by crypto grade random.
var globalMathRandomGenerator = randutil.NewMathRandomGenerator() // nolint:gochecknoglobals
-// VP9Payloader payloads VP9 packets
+// VP9Payloader payloads VP9 packets.
type VP9Payloader struct {
- pictureID uint16
- initialized bool
+ // whether to use flexible mode or non-flexible mode.
+ FlexibleMode bool
// InitialPictureIDFn is a function that returns random initial picture ID.
InitialPictureIDFn func() uint16
+
+ pictureID uint16
+ initialized bool
}
const (
- vp9HeaderSize = 3 // Flexible mode 15 bit picture ID
maxSpatialLayers = 5
maxVP9RefPics = 3
)
-// Payload fragments an VP9 packet across one or more byte arrays
+// Payload fragments an VP9 packet across one or more byte arrays.
func (p *VP9Payloader) Payload(mtu uint16, payload []byte) [][]byte {
+ if !p.initialized {
+ if p.InitialPictureIDFn == nil {
+ p.InitialPictureIDFn = func() uint16 {
+ return uint16(globalMathRandomGenerator.Intn(0x7FFF)) // nolint: gosec
+ }
+ }
+ p.pictureID = p.InitialPictureIDFn() & 0x7FFF
+ p.initialized = true
+ }
+
+ var payloads [][]byte
+ if p.FlexibleMode {
+ payloads = p.payloadFlexible(mtu, payload)
+ } else {
+ payloads = p.payloadNonFlexible(mtu, payload)
+ }
+
+ p.pictureID++
+ if p.pictureID >= 0x8000 {
+ p.pictureID = 0
+ }
+
+ return payloads
+}
+
+func (p *VP9Payloader) payloadFlexible(mtu uint16, payload []byte) [][]byte {
/*
- * https://www.ietf.org/id/draft-ietf-payload-vp9-13.txt
- *
* Flexible mode (F=1)
* 0 1 2 3 4 5 6 7
* +-+-+-+-+-+-+-+-+
@@ -43,7 +73,45 @@ func (p *VP9Payloader) Payload(mtu uint16, payload []byte) [][]byte {
* V: | SS |
* | .. |
* +-+-+-+-+-+-+-+-+
- *
+ */
+
+ headerSize := 3
+ maxFragmentSize := int(mtu) - headerSize
+ payloadDataRemaining := len(payload)
+ payloadDataIndex := 0
+ var payloads [][]byte
+
+ if minInt(maxFragmentSize, payloadDataRemaining) <= 0 {
+ return [][]byte{}
+ }
+
+ for payloadDataRemaining > 0 {
+ currentFragmentSize := minInt(maxFragmentSize, payloadDataRemaining)
+ out := make([]byte, headerSize+currentFragmentSize)
+
+ out[0] = 0x90 // F=1, I=1
+ if payloadDataIndex == 0 {
+ out[0] |= 0x08 // B=1
+ }
+ if payloadDataRemaining == currentFragmentSize {
+ out[0] |= 0x04 // E=1
+ }
+
+ out[1] = byte(p.pictureID>>8) | 0x80
+ out[2] = byte(p.pictureID)
+
+ copy(out[headerSize:], payload[payloadDataIndex:payloadDataIndex+currentFragmentSize])
+ payloads = append(payloads, out)
+
+ payloadDataRemaining -= currentFragmentSize
+ payloadDataIndex += currentFragmentSize
+ }
+
+ return payloads
+}
+
+func (p *VP9Payloader) payloadNonFlexible(mtu uint16, payload []byte) [][]byte { //nolint:cyclop
+ /*
* Non-flexible mode (F=0)
* 0 1 2 3 4 5 6 7
* +-+-+-+-+-+-+-+-+
@@ -62,56 +130,85 @@ func (p *VP9Payloader) Payload(mtu uint16, payload []byte) [][]byte {
* +-+-+-+-+-+-+-+-+
*/
- if !p.initialized {
- if p.InitialPictureIDFn == nil {
- p.InitialPictureIDFn = func() uint16 {
- return uint16(globalMathRandomGenerator.Intn(0x7FFF))
- }
- }
- p.pictureID = p.InitialPictureIDFn() & 0x7FFF
- p.initialized = true
- }
- if payload == nil {
+ var header vp9.Header
+ err := header.Unmarshal(payload)
+ if err != nil {
return [][]byte{}
}
- maxFragmentSize := int(mtu) - vp9HeaderSize
payloadDataRemaining := len(payload)
payloadDataIndex := 0
-
- if min(maxFragmentSize, payloadDataRemaining) <= 0 {
- return [][]byte{}
- }
-
var payloads [][]byte
- for payloadDataRemaining > 0 {
- currentFragmentSize := min(maxFragmentSize, payloadDataRemaining)
- out := make([]byte, vp9HeaderSize+currentFragmentSize)
- out[0] = 0x90 // F=1 I=1
+ for payloadDataRemaining > 0 {
+ var headerSize int
+ if !header.NonKeyFrame && payloadDataIndex == 0 {
+ headerSize = 3 + 8
+ } else {
+ headerSize = 3
+ }
+
+ maxFragmentSize := int(mtu) - headerSize
+ currentFragmentSize := minInt(maxFragmentSize, payloadDataRemaining)
+ if currentFragmentSize <= 0 {
+ return [][]byte{}
+ }
+
+ out := make([]byte, headerSize+currentFragmentSize)
+
+ out[0] = 0x80 | 0x01 // I=1, Z=1
+
+ if header.NonKeyFrame {
+ out[0] |= 0x40 // P=1
+ }
if payloadDataIndex == 0 {
out[0] |= 0x08 // B=1
}
if payloadDataRemaining == currentFragmentSize {
out[0] |= 0x04 // E=1
}
+
out[1] = byte(p.pictureID>>8) | 0x80
out[2] = byte(p.pictureID)
- copy(out[vp9HeaderSize:], payload[payloadDataIndex:payloadDataIndex+currentFragmentSize])
+ off := 3
+
+ if !header.NonKeyFrame && payloadDataIndex == 0 {
+ out[0] |= 0x02 // V=1
+ out[off] = 0x10 | 0x08 // N_S=0, Y=1, G=1
+ off++
+
+ width := header.Width()
+ out[off] = byte(width >> 8)
+ off++
+ out[off] = byte(width & 0xFF)
+ off++
+
+ height := header.Height()
+ out[off] = byte(height >> 8)
+ off++
+ out[off] = byte(height & 0xFF)
+ off++
+
+ out[off] = 0x01 // N_G=1
+ off++
+
+ out[off] = 1<<4 | 1<<2 // TID=0, U=1, R=1
+ off++
+
+ out[off] = 0x01 // P_DIFF=1
+ }
+
+ copy(out[headerSize:], payload[payloadDataIndex:payloadDataIndex+currentFragmentSize])
payloads = append(payloads, out)
payloadDataRemaining -= currentFragmentSize
payloadDataIndex += currentFragmentSize
}
- p.pictureID++
- if p.pictureID >= 0x8000 {
- p.pictureID = 0
- }
return payloads
}
-// VP9Packet represents the VP9 header that is stored in the payload of an RTP Packet
+// VP9Packet represents the VP9 header that is stored in the payload of an RTP Packet.
type VP9Packet struct {
// Required header
I bool // PictureID is present
@@ -152,8 +249,8 @@ type VP9Packet struct {
videoDepacketizer
}
-// Unmarshal parses the passed byte slice and stores the result in the VP9Packet this method is called upon
-func (p *VP9Packet) Unmarshal(packet []byte) ([]byte, error) {
+// Unmarshal parses the passed byte slice and stores the result in the VP9Packet this method is called upon.
+func (p *VP9Packet) Unmarshal(packet []byte) ([]byte, error) { // nolint:cyclop
if packet == nil {
return nil, errNilPacket
}
@@ -202,17 +299,19 @@ func (p *VP9Packet) Unmarshal(packet []byte) ([]byte, error) {
}
p.Payload = packet[pos:]
+
return p.Payload, nil
}
// Picture ID:
-//
-// +-+-+-+-+-+-+-+-+
-// I: |M| PICTURE ID | M:0 => picture id is 7 bits.
-// +-+-+-+-+-+-+-+-+ M:1 => picture id is 15 bits.
-// M: | EXTENDED PID |
-// +-+-+-+-+-+-+-+-+
-//
+/*
+* +-+-+-+-+-+-+-+-+
+* I: |M| PICTURE ID | M:0 => picture id is 7 bits.
+* +-+-+-+-+-+-+-+-+ M:1 => picture id is 15 bits.
+* M: | EXTENDED PID |
+* +-+-+-+-+-+-+-+-+
+**/
+// .
func (p *VP9Packet) parsePictureID(packet []byte, pos int) (int, error) {
if len(packet) <= pos {
return pos, errShortPacket
@@ -227,6 +326,7 @@ func (p *VP9Packet) parsePictureID(packet []byte, pos int) (int, error) {
p.PictureID = p.PictureID<<8 | uint16(packet[pos])
}
pos++
+
return pos, nil
}
@@ -244,11 +344,12 @@ func (p *VP9Packet) parseLayerInfo(packet []byte, pos int) (int, error) {
}
// Layer indices (flexible mode):
-//
-// +-+-+-+-+-+-+-+-+
-// L: | T |U| S |D|
-// +-+-+-+-+-+-+-+-+
-//
+/*
+* +-+-+-+-+-+-+-+-+
+* L: | T |U| S |D|
+* +-+-+-+-+-+-+-+-+
+**/
+// .
func (p *VP9Packet) parseLayerInfoCommon(packet []byte, pos int) (int, error) {
if len(packet) <= pos {
return pos, errShortPacket
@@ -264,17 +365,19 @@ func (p *VP9Packet) parseLayerInfoCommon(packet []byte, pos int) (int, error) {
}
pos++
+
return pos, nil
}
// Layer indices (non-flexible mode):
-//
-// +-+-+-+-+-+-+-+-+
-// L: | T |U| S |D|
-// +-+-+-+-+-+-+-+-+
-// | TL0PICIDX |
-// +-+-+-+-+-+-+-+-+
-//
+/*
+* +-+-+-+-+-+-+-+-+
+* L: | T |U| S |D|
+* +-+-+-+-+-+-+-+-+
+* | TL0PICIDX |
+* +-+-+-+-+-+-+-+-+
+**/
+// .
func (p *VP9Packet) parseLayerInfoNonFlexibleMode(packet []byte, pos int) (int, error) {
if len(packet) <= pos {
return pos, errShortPacket
@@ -282,16 +385,19 @@ func (p *VP9Packet) parseLayerInfoNonFlexibleMode(packet []byte, pos int) (int,
p.TL0PICIDX = packet[pos]
pos++
+
return pos, nil
}
-// Reference indices:
-//
-// +-+-+-+-+-+-+-+-+ P=1,F=1: At least one reference index
-// P,F: | P_DIFF |N| up to 3 times has to be specified.
-// +-+-+-+-+-+-+-+-+ N=1: An additional P_DIFF follows
-// current P_DIFF.
-//
+// Reference indices: .
+/*
+* +-+-+-+-+-+-+-+-+ P=1,F=1: At least one reference index
+* P,F: | P_DIFF |N| up to 3 times has to be specified.
+* +-+-+-+-+-+-+-+-+ N=1: An additional P_DIFF follows
+* current P_DIFF.
+*
+**/
+// .
func (p *VP9Packet) parseRefIndices(packet []byte, pos int) (int, error) {
for {
if len(packet) <= pos {
@@ -312,33 +418,34 @@ func (p *VP9Packet) parseRefIndices(packet []byte, pos int) (int, error) {
}
// Scalability structure (SS):
-//
-// +-+-+-+-+-+-+-+-+
-// V: | N_S |Y|G|-|-|-|
-// +-+-+-+-+-+-+-+-+ -|
-// Y: | WIDTH | (OPTIONAL) .
-// + + .
-// | | (OPTIONAL) .
-// +-+-+-+-+-+-+-+-+ . N_S + 1 times
-// | HEIGHT | (OPTIONAL) .
-// + + .
-// | | (OPTIONAL) .
-// +-+-+-+-+-+-+-+-+ -|
-// G: | N_G | (OPTIONAL)
-// +-+-+-+-+-+-+-+-+ -|
-// N_G: | T |U| R |-|-| (OPTIONAL) .
-// +-+-+-+-+-+-+-+-+ -| . N_G times
-// | P_DIFF | (OPTIONAL) . R times .
-// +-+-+-+-+-+-+-+-+ -| -|
-//
-func (p *VP9Packet) parseSSData(packet []byte, pos int) (int, error) {
+/*
+* +-+-+-+-+-+-+-+-+
+* V: | N_S |Y|G|-|-|-|
+* +-+-+-+-+-+-+-+-+ -|
+* Y: | WIDTH | (OPTIONAL) .
+* + .
+* | | (OPTIONAL) .
+* +-+-+-+-+-+-+-+-+ . N_S + 1 times
+* | HEIGHT | (OPTIONAL) .
+* + .
+* | | (OPTIONAL) .
+* +-+-+-+-+-+-+-+-+ -|
+* G: | N_G | (OPTIONAL)
+* +-+-+-+-+-+-+-+-+ -|
+* N_G: | T |U| R |-|-| (OPTIONAL) .
+* +-+-+-+-+-+-+-+-+ -| . N_G times
+* | P_DIFF | (OPTIONAL) . R times .
+* +-+-+-+-+-+-+-+-+ -| -|
+**/
+// .
+func (p *VP9Packet) parseSSData(packet []byte, pos int) (int, error) { // nolint: cyclop
if len(packet) <= pos {
return pos, errShortPacket
}
p.NS = packet[pos] >> 5
p.Y = packet[pos]&0x10 != 0
- p.G = (packet[pos]>>1)&0x7 != 0
+ p.G = packet[pos]&0x8 != 0
pos++
NS := p.NS + 1
@@ -348,6 +455,10 @@ func (p *VP9Packet) parseSSData(packet []byte, pos int) (int, error) {
p.Width = make([]uint16, NS)
p.Height = make([]uint16, NS)
for i := 0; i < int(NS); i++ {
+ if len(packet) <= (pos + 3) {
+ return pos, errShortPacket
+ }
+
p.Width[i] = uint16(packet[pos])<<8 | uint16(packet[pos+1])
pos += 2
p.Height[i] = uint16(packet[pos])<<8 | uint16(packet[pos+1])
@@ -356,17 +467,30 @@ func (p *VP9Packet) parseSSData(packet []byte, pos int) (int, error) {
}
if p.G {
+ if len(packet) <= pos {
+ return pos, errShortPacket
+ }
+
p.NG = packet[pos]
pos++
}
for i := 0; i < int(p.NG); i++ {
+ if len(packet) <= pos {
+ return pos, errShortPacket
+ }
+
p.PGTID = append(p.PGTID, packet[pos]>>5)
p.PGU = append(p.PGU, packet[pos]&0x10 != 0)
R := (packet[pos] >> 2) & 0x3
pos++
p.PGPDiff = append(p.PGPDiff, []uint8{})
+
+ if len(packet) <= (pos + int(R) - 1) {
+ return pos, errShortPacket
+ }
+
for j := 0; j < int(R); j++ {
p.PGPDiff[i] = append(p.PGPDiff[i], packet[pos])
pos++
@@ -376,13 +500,23 @@ func (p *VP9Packet) parseSSData(packet []byte, pos int) (int, error) {
return pos, nil
}
-// VP9PartitionHeadChecker is obsolete
+// VP9PartitionHeadChecker checks VP9 partition head.
+//
+// Deprecated: replaced by VP9Packet.IsPartitionHead().
type VP9PartitionHeadChecker struct{}
-// IsPartitionHead checks whether if this is a head of the VP9 partition
+// IsPartitionHead checks whether if this is a head of the VP9 partition.
+//
+// Deprecated: replaced by VP9Packet.IsPartitionHead().
+func (*VP9PartitionHeadChecker) IsPartitionHead(packet []byte) bool {
+ return (&VP9Packet{}).IsPartitionHead(packet)
+}
+
+// IsPartitionHead checks whether if this is a head of the VP9 partition.
func (*VP9Packet) IsPartitionHead(payload []byte) bool {
if len(payload) < 1 {
return false
}
+
return (payload[0] & 0x08) != 0
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/depacketizer.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/depacketizer.go
index c66d2e30c..3027ae677 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/depacketizer.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/depacketizer.go
@@ -1,12 +1,19 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtp
-// Depacketizer depacketizes a RTP payload, removing any RTP specific data from the payload
+// Depacketizer depacketizes a RTP payload, removing any RTP specific data from the payload.
type Depacketizer interface {
+ // Unmarshal parses the RTP payload and returns media.
+ // Metadata may be stored on the Depacketizer itself
Unmarshal(packet []byte) ([]byte, error)
+
// Checks if the packet is at the beginning of a partition. This
// should return false if the result could not be determined, in
// which case the caller will detect timestamp discontinuities.
IsPartitionHead(payload []byte) bool
+
// Checks if the packet is at the end of a partition. This should
// return false if the result could not be determined.
IsPartitionTail(marker bool, payload []byte) bool
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/error.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/error.go
index 5458c6fa5..eb78874ad 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/error.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/error.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtp
import (
@@ -11,11 +14,21 @@ var (
errHeaderExtensionsNotEnabled = errors.New("h.Extension not enabled")
errHeaderExtensionNotFound = errors.New("extension not found")
- errRFC8285OneByteHeaderIDRange = errors.New("header extension id must be between 1 and 14 for RFC 5285 one byte extensions")
- errRFC8285OneByteHeaderSize = errors.New("header extension payload must be 16bytes or less for RFC 5285 one byte extensions")
+ errRFC8285OneByteHeaderIDRange = errors.New(
+ "header extension id must be between 1 and 14 for RFC 5285 one byte extensions",
+ )
+ errRFC8285OneByteHeaderSize = errors.New(
+ "header extension payload must be 16bytes or less for RFC 5285 one byte extensions",
+ )
- errRFC8285TwoByteHeaderIDRange = errors.New("header extension id must be between 1 and 255 for RFC 5285 two byte extensions")
- errRFC8285TwoByteHeaderSize = errors.New("header extension payload must be 255bytes or less for RFC 5285 two byte extensions")
+ errRFC8285TwoByteHeaderIDRange = errors.New(
+ "header extension id must be between 1 and 255 for RFC 5285 two byte extensions",
+ )
+ errRFC8285TwoByteHeaderSize = errors.New(
+ "header extension payload must be 255bytes or less for RFC 5285 two byte extensions",
+ )
errRFC3550HeaderIDRange = errors.New("header extension id must be 0 for non-RFC 5285 extensions")
+
+ errInvalidRTPPadding = errors.New("invalid RTP padding")
)
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/header_extension.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/header_extension.go
index c143ac119..b36d09af8 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/header_extension.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/header_extension.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtp
import (
@@ -42,22 +45,25 @@ func (e *OneByteHeaderExtension) Set(id uint8, buf []byte) error {
for n := 4; n < len(e.payload); {
if e.payload[n] == 0x00 { // padding
n++
+
continue
}
extid := e.payload[n] >> 4
- len := int(e.payload[n]&^0xF0 + 1)
+ payloadLen := int(e.payload[n]&^0xF0 + 1)
n++
if extid == id {
- e.payload = append(e.payload[:n+1], append(buf, e.payload[n+1+len:]...)...)
+ e.payload = append(e.payload[:n+1], append(buf, e.payload[n+1+payloadLen:]...)...)
+
return nil
}
- n += len
+ n += payloadLen
}
- e.payload = append(e.payload, (id<<4 | uint8(len(buf)-1)))
+ e.payload = append(e.payload, (id<<4 | uint8(len(buf)-1))) // nolint: gosec // G115
e.payload = append(e.payload, buf...)
binary.BigEndian.PutUint16(e.payload[2:4], binary.BigEndian.Uint16(e.payload[2:4])+1)
+
return nil
}
@@ -67,11 +73,12 @@ func (e *OneByteHeaderExtension) GetIDs() []uint8 {
for n := 4; n < len(e.payload); {
if e.payload[n] == 0x00 { // padding
n++
+
continue
}
extid := e.payload[n] >> 4
- len := int(e.payload[n]&^0xF0 + 1)
+ payloadLen := int(e.payload[n]&^0xF0 + 1)
n++
if extid == headerExtensionIDReserved {
@@ -79,8 +86,9 @@ func (e *OneByteHeaderExtension) GetIDs() []uint8 {
}
ids = append(ids, extid)
- n += len
+ n += payloadLen
}
+
return ids
}
@@ -89,18 +97,20 @@ func (e *OneByteHeaderExtension) Get(id uint8) []byte {
for n := 4; n < len(e.payload); {
if e.payload[n] == 0x00 { // padding
n++
+
continue
}
extid := e.payload[n] >> 4
- len := int(e.payload[n]&^0xF0 + 1)
+ payloadLen := int(e.payload[n]&^0xF0 + 1)
n++
if extid == id {
- return e.payload[n : n+len]
+ return e.payload[n : n+payloadLen]
}
- n += len
+ n += payloadLen
}
+
return nil
}
@@ -109,18 +119,21 @@ func (e *OneByteHeaderExtension) Del(id uint8) error {
for n := 4; n < len(e.payload); {
if e.payload[n] == 0x00 { // padding
n++
+
continue
}
extid := e.payload[n] >> 4
- len := int(e.payload[n]&^0xF0 + 1)
+ payloadLen := int(e.payload[n]&^0xF0 + 1)
if extid == id {
- e.payload = append(e.payload[:n], e.payload[n+1+len:]...)
+ e.payload = append(e.payload[:n], e.payload[n+1+payloadLen:]...)
+
return nil
}
- n += len + 1
+ n += payloadLen + 1
}
+
return errHeaderExtensionNotFound
}
@@ -131,6 +144,7 @@ func (e *OneByteHeaderExtension) Unmarshal(buf []byte) (int, error) {
return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, buf[0:2])
}
e.payload = buf
+
return len(buf), nil
}
@@ -145,6 +159,7 @@ func (e OneByteHeaderExtension) MarshalTo(buf []byte) (int, error) {
if size > len(buf) {
return 0, io.ErrShortBuffer
}
+
return copy(buf, e.payload), nil
}
@@ -160,7 +175,7 @@ type TwoByteHeaderExtension struct {
// Set sets the extension payload for the specified ID.
func (e *TwoByteHeaderExtension) Set(id uint8, buf []byte) error {
- if id < 1 || id > 255 {
+ if id < 1 {
return fmt.Errorf("%w actual(%d)", errRFC8285TwoByteHeaderIDRange, id)
}
if len(buf) > 255 {
@@ -170,24 +185,27 @@ func (e *TwoByteHeaderExtension) Set(id uint8, buf []byte) error {
for n := 4; n < len(e.payload); {
if e.payload[n] == 0x00 { // padding
n++
+
continue
}
extid := e.payload[n]
n++
- len := int(e.payload[n])
+ payloadLen := int(e.payload[n])
n++
if extid == id {
- e.payload = append(e.payload[:n+2], append(buf, e.payload[n+2+len:]...)...)
+ e.payload = append(e.payload[:n+2], append(buf, e.payload[n+2+payloadLen:]...)...)
+
return nil
}
- n += len
+ n += payloadLen
}
- e.payload = append(e.payload, id, uint8(len(buf)))
+ e.payload = append(e.payload, id, uint8(len(buf))) // nolint: gosec // G115
e.payload = append(e.payload, buf...)
binary.BigEndian.PutUint16(e.payload[2:4], binary.BigEndian.Uint16(e.payload[2:4])+1)
+
return nil
}
@@ -197,18 +215,20 @@ func (e *TwoByteHeaderExtension) GetIDs() []uint8 {
for n := 4; n < len(e.payload); {
if e.payload[n] == 0x00 { // padding
n++
+
continue
}
extid := e.payload[n]
n++
- len := int(e.payload[n])
+ payloadLen := int(e.payload[n])
n++
ids = append(ids, extid)
- n += len
+ n += payloadLen
}
+
return ids
}
@@ -217,20 +237,22 @@ func (e *TwoByteHeaderExtension) Get(id uint8) []byte {
for n := 4; n < len(e.payload); {
if e.payload[n] == 0x00 { // padding
n++
+
continue
}
extid := e.payload[n]
n++
- len := int(e.payload[n])
+ payloadLen := int(e.payload[n])
n++
if extid == id {
- return e.payload[n : n+len]
+ return e.payload[n : n+payloadLen]
}
- n += len
+ n += payloadLen
}
+
return nil
}
@@ -239,19 +261,22 @@ func (e *TwoByteHeaderExtension) Del(id uint8) error {
for n := 4; n < len(e.payload); {
if e.payload[n] == 0x00 { // padding
n++
+
continue
}
extid := e.payload[n]
- len := int(e.payload[n+1])
+ payloadLen := int(e.payload[n+1])
if extid == id {
- e.payload = append(e.payload[:n], e.payload[n+2+len:]...)
+ e.payload = append(e.payload[:n], e.payload[n+2+payloadLen:]...)
+
return nil
}
- n += len + 2
+ n += payloadLen + 2
}
+
return errHeaderExtensionNotFound
}
@@ -262,6 +287,7 @@ func (e *TwoByteHeaderExtension) Unmarshal(buf []byte) (int, error) {
return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, buf[0:2])
}
e.payload = buf
+
return len(buf), nil
}
@@ -276,6 +302,7 @@ func (e TwoByteHeaderExtension) MarshalTo(buf []byte) (int, error) {
if size > len(buf) {
return 0, io.ErrShortBuffer
}
+
return copy(buf, e.payload), nil
}
@@ -295,6 +322,7 @@ func (e *RawExtension) Set(id uint8, payload []byte) error {
return fmt.Errorf("%w actual(%d)", errRFC3550HeaderIDRange, id)
}
e.payload = payload
+
return nil
}
@@ -308,6 +336,7 @@ func (e *RawExtension) Get(id uint8) []byte {
if id == 0 {
return e.payload
}
+
return nil
}
@@ -315,8 +344,10 @@ func (e *RawExtension) Get(id uint8) []byte {
func (e *RawExtension) Del(id uint8) error {
if id == 0 {
e.payload = nil
+
return nil
}
+
return fmt.Errorf("%w actual(%d)", errRFC3550HeaderIDRange, id)
}
@@ -327,6 +358,7 @@ func (e *RawExtension) Unmarshal(buf []byte) (int, error) {
return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, buf[0:2])
}
e.payload = buf
+
return len(buf), nil
}
@@ -341,6 +373,7 @@ func (e RawExtension) MarshalTo(buf []byte) (int, error) {
if size > len(buf) {
return 0, io.ErrShortBuffer
}
+
return copy(buf, e.payload), nil
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/packet.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/packet.go
index b3ae12400..c51d8c7d8 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/packet.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/packet.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtp
import (
@@ -6,13 +9,13 @@ import (
"io"
)
-// Extension RTP Header extension
+// Extension RTP Header extension.
type Extension struct {
id uint8
payload []byte
}
-// Header represents an RTP packet header
+// Header represents an RTP packet header.
type Header struct {
Version uint8
Padding bool
@@ -25,13 +28,19 @@ type Header struct {
CSRC []uint32
ExtensionProfile uint16
Extensions []Extension
+
+ // Deprecated: will be removed in a future version.
+ PayloadOffset int
}
-// Packet represents an RTP Packet
+// Packet represents an RTP Packet.
type Packet struct {
Header
Payload []byte
PaddingSize byte
+
+ // Deprecated: will be removed in a future version.
+ Raw []byte
}
const (
@@ -59,7 +68,7 @@ const (
csrcLength = 4
)
-// String helps with debugging by printing packet information in a readable way
+// String helps with debugging by printing packet information in a readable way.
func (p Packet) String() string {
out := "RTP PACKET:\n"
@@ -76,7 +85,7 @@ func (p Packet) String() string {
// Unmarshal parses the passed byte slice and stores the result in the Header.
// It returns the number of bytes read n and any error.
-func (h *Header) Unmarshal(buf []byte) (n int, err error) { //nolint:gocognit
+func (h *Header) Unmarshal(buf []byte) (n int, err error) { //nolint:gocognit,cyclop
if len(buf) < headerLength {
return 0, fmt.Errorf("%w: %d < %d", errHeaderSizeInsufficient, len(buf), headerLength)
}
@@ -128,7 +137,7 @@ func (h *Header) Unmarshal(buf []byte) (n int, err error) { //nolint:gocognit
h.Extensions = h.Extensions[:0]
}
- if h.Extension {
+ if h.Extension { // nolint: nestif
if expected := n + 4; len(buf) < expected {
return n, fmt.Errorf("size %d < %d: %w",
len(buf), expected,
@@ -140,68 +149,61 @@ func (h *Header) Unmarshal(buf []byte) (n int, err error) { //nolint:gocognit
n += 2
extensionLength := int(binary.BigEndian.Uint16(buf[n:])) * 4
n += 2
+ extensionEnd := n + extensionLength
- if expected := n + extensionLength; len(buf) < expected {
- return n, fmt.Errorf("size %d < %d: %w",
- len(buf), expected,
- errHeaderSizeInsufficientForExtension,
- )
+ if len(buf) < extensionEnd {
+ return n, fmt.Errorf("size %d < %d: %w", len(buf), extensionEnd, errHeaderSizeInsufficientForExtension)
}
- switch h.ExtensionProfile {
- // RFC 8285 RTP One Byte Header Extension
- case extensionProfileOneByte:
- end := n + extensionLength
- for n < end {
+ if h.ExtensionProfile == extensionProfileOneByte || h.ExtensionProfile == extensionProfileTwoByte {
+ var (
+ extid uint8
+ payloadLen int
+ )
+
+ for n < extensionEnd {
if buf[n] == 0x00 { // padding
n++
+
continue
}
- extid := buf[n] >> 4
- len := int(buf[n]&^0xF0 + 1)
- n++
-
- if extid == extensionIDReserved {
- break
- }
-
- extension := Extension{id: extid, payload: buf[n : n+len]}
- h.Extensions = append(h.Extensions, extension)
- n += len
- }
-
- // RFC 8285 RTP Two Byte Header Extension
- case extensionProfileTwoByte:
- end := n + extensionLength
- for n < end {
- if buf[n] == 0x00 { // padding
+ if h.ExtensionProfile == extensionProfileOneByte {
+ extid = buf[n] >> 4
+ payloadLen = int(buf[n]&^0xF0 + 1)
+ n++
+
+ if extid == extensionIDReserved {
+ break
+ }
+ } else {
+ extid = buf[n]
+ n++
+
+ if len(buf) <= n {
+ return n, fmt.Errorf("size %d < %d: %w", len(buf), n, errHeaderSizeInsufficientForExtension)
+ }
+
+ payloadLen = int(buf[n])
n++
- continue
}
- extid := buf[n]
- n++
+ if extensionPayloadEnd := n + payloadLen; len(buf) <= extensionPayloadEnd {
+ return n, fmt.Errorf("size %d < %d: %w", len(buf), extensionPayloadEnd, errHeaderSizeInsufficientForExtension)
+ }
- len := int(buf[n])
- n++
-
- extension := Extension{id: extid, payload: buf[n : n+len]}
+ extension := Extension{id: extid, payload: buf[n : n+payloadLen]}
h.Extensions = append(h.Extensions, extension)
- n += len
+ n += payloadLen
}
-
- default: // RFC3550 Extension
- if len(buf) < n+extensionLength {
- return n, fmt.Errorf("%w: %d < %d",
- errHeaderSizeInsufficientForExtension, len(buf), n+extensionLength)
- }
-
- extension := Extension{id: 0, payload: buf[n : n+extensionLength]}
+ } else {
+ // RFC3550 Extension
+ extension := Extension{id: 0, payload: buf[n:extensionEnd]}
h.Extensions = append(h.Extensions, extension)
n += len(h.Extensions[0].payload)
}
}
+
return n, nil
}
@@ -211,15 +213,23 @@ func (p *Packet) Unmarshal(buf []byte) error {
if err != nil {
return err
}
+
end := len(buf)
if p.Header.Padding {
+ if end <= n {
+ return errTooSmall
+ }
p.PaddingSize = buf[end-1]
end -= int(p.PaddingSize)
+ } else {
+ p.PaddingSize = 0
}
if end < n {
return errTooSmall
}
+
p.Payload = buf[n:end]
+
return nil
}
@@ -231,11 +241,12 @@ func (h Header) Marshal() (buf []byte, err error) {
if err != nil {
return nil, err
}
+
return buf[:n], nil
}
// MarshalTo serializes the header and writes to the buffer.
-func (h Header) MarshalTo(buf []byte) (n int, err error) {
+func (h Header) MarshalTo(buf []byte) (n int, err error) { //nolint:cyclop
/*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
@@ -258,7 +269,7 @@ func (h Header) MarshalTo(buf []byte) (n int, err error) {
// The first byte contains the version, padding bit, extension bit,
// and csrc size.
- buf[0] = (h.Version << versionShift) | uint8(len(h.CSRC))
+ buf[0] = (h.Version << versionShift) | uint8(len(h.CSRC)) // nolint: gosec // G115
if h.Padding {
buf[0] |= 1 << paddingShift
}
@@ -293,7 +304,7 @@ func (h Header) MarshalTo(buf []byte) (n int, err error) {
// RFC 8285 RTP One Byte Header Extension
case extensionProfileOneByte:
for _, extension := range h.Extensions {
- buf[n] = extension.id<<4 | (uint8(len(extension.payload)) - 1)
+ buf[n] = extension.id<<4 | (uint8(len(extension.payload)) - 1) // nolint: gosec // G115
n++
n += copy(buf[n:], extension.payload)
}
@@ -302,7 +313,7 @@ func (h Header) MarshalTo(buf []byte) (n int, err error) {
for _, extension := range h.Extensions {
buf[n] = extension.id
n++
- buf[n] = uint8(len(extension.payload))
+ buf[n] = uint8(len(extension.payload)) // nolint: gosec // G115
n++
n += copy(buf[n:], extension.payload)
}
@@ -319,6 +330,7 @@ func (h Header) MarshalTo(buf []byte) (n int, err error) {
extSize := n - startExtensionsPos
roundedExtSize := ((extSize + 3) / 4) * 4
+ // nolint: gosec // G115 false positive
binary.BigEndian.PutUint16(buf[extHeaderPos+2:extHeaderPos+4], uint16(roundedExtSize/4))
// add padding to reach 4 bytes boundaries
@@ -361,9 +373,9 @@ func (h Header) MarshalSize() int {
return size
}
-// SetExtension sets an RTP header extension
-func (h *Header) SetExtension(id uint8, payload []byte) error { //nolint:gocognit
- if h.Extension {
+// SetExtension sets an RTP header extension.
+func (h *Header) SetExtension(id uint8, payload []byte) error { //nolint:gocognit, cyclop
+ if h.Extension { // nolint: nestif
switch h.ExtensionProfile {
// RFC 8285 RTP One Byte Header Extension
case extensionProfileOneByte:
@@ -375,7 +387,7 @@ func (h *Header) SetExtension(id uint8, payload []byte) error { //nolint:gocogni
}
// RFC 8285 RTP Two Byte Header Extension
case extensionProfileTwoByte:
- if id < 1 || id > 255 {
+ if id < 1 {
return fmt.Errorf("%w actual(%d)", errRFC8285TwoByteHeaderIDRange, id)
}
if len(payload) > 255 {
@@ -391,28 +403,32 @@ func (h *Header) SetExtension(id uint8, payload []byte) error { //nolint:gocogni
for i, extension := range h.Extensions {
if extension.id == id {
h.Extensions[i].payload = payload
+
return nil
}
}
+
h.Extensions = append(h.Extensions, Extension{id: id, payload: payload})
+
return nil
}
// No existing header extensions
h.Extension = true
- switch len := len(payload); {
- case len <= 16:
+ switch payloadLen := len(payload); {
+ case payloadLen <= 16:
h.ExtensionProfile = extensionProfileOneByte
- case len > 16 && len < 256:
+ case payloadLen > 16 && payloadLen < 256:
h.ExtensionProfile = extensionProfileTwoByte
}
h.Extensions = append(h.Extensions, Extension{id: id, payload: payload})
+
return nil
}
-// GetExtensionIDs returns an extension id array
+// GetExtensionIDs returns an extension id array.
func (h *Header) GetExtensionIDs() []uint8 {
if !h.Extension {
return nil
@@ -426,10 +442,11 @@ func (h *Header) GetExtensionIDs() []uint8 {
for _, extension := range h.Extensions {
ids = append(ids, extension.id)
}
+
return ids
}
-// GetExtension returns an RTP header extension
+// GetExtension returns an RTP header extension.
func (h *Header) GetExtension(id uint8) []byte {
if !h.Extension {
return nil
@@ -439,10 +456,11 @@ func (h *Header) GetExtension(id uint8) []byte {
return extension.payload
}
}
+
return nil
}
-// DelExtension Removes an RTP Header extension
+// DelExtension Removes an RTP Header extension.
func (h *Header) DelExtension(id uint8) error {
if !h.Extension {
return errHeaderExtensionsNotEnabled
@@ -450,9 +468,11 @@ func (h *Header) DelExtension(id uint8) error {
for i, extension := range h.Extensions {
if extension.id == id {
h.Extensions = append(h.Extensions[:i], h.Extensions[i+1:]...)
+
return nil
}
}
+
return errHeaderExtensionNotFound
}
@@ -469,8 +489,11 @@ func (p Packet) Marshal() (buf []byte, err error) {
}
// MarshalTo serializes the packet and writes to the buffer.
-func (p Packet) MarshalTo(buf []byte) (n int, err error) {
- p.Header.Padding = p.PaddingSize != 0
+func (p *Packet) MarshalTo(buf []byte) (n int, err error) {
+ if p.Header.Padding && p.PaddingSize == 0 {
+ return 0, errInvalidRTPPadding
+ }
+
n, err = p.Header.MarshalTo(buf)
if err != nil {
return 0, err
@@ -482,6 +505,7 @@ func (p Packet) MarshalTo(buf []byte) (n int, err error) {
}
m := copy(buf[n:], p.Payload)
+
if p.Header.Padding {
buf[n+m+int(p.PaddingSize-1)] = p.PaddingSize
}
@@ -503,6 +527,7 @@ func (p Packet) Clone() *Packet {
copy(clone.Payload, p.Payload)
}
clone.PaddingSize = p.PaddingSize
+
return clone
}
@@ -524,5 +549,6 @@ func (h Header) Clone() Header {
}
clone.Extensions = ext
}
+
return clone
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/packetizer.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/packetizer.go
index 19f6c756d..c0dbea49f 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/packetizer.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/packetizer.go
@@ -1,37 +1,52 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtp
import (
"time"
)
-// Payloader payloads a byte array for use as rtp.Packet payloads
+// Payloader payloads a byte array for use as rtp.Packet payloads.
type Payloader interface {
Payload(mtu uint16, payload []byte) [][]byte
}
-// Packetizer packetizes a payload
+// Packetizer packetizes a payload.
type Packetizer interface {
Packetize(payload []byte, samples uint32) []*Packet
+ GeneratePadding(samples uint32) []*Packet
EnableAbsSendTime(value int)
SkipSamples(skippedSamples uint32)
}
type packetizer struct {
- MTU uint16
- PayloadType uint8
- SSRC uint32
- Payloader Payloader
- Sequencer Sequencer
- Timestamp uint32
- ClockRate uint32
- extensionNumbers struct { // put extension numbers in here. If they're 0, the extension is disabled (0 is not a legal extension number)
+ MTU uint16
+ PayloadType uint8
+ SSRC uint32
+ Payloader Payloader
+ Sequencer Sequencer
+ Timestamp uint32
+
+ // Deprecated: will be removed in a future version.
+ ClockRate uint32
+
+ // put extension numbers in here. If they're 0, the extension is disabled (0 is not a legal extension number)
+ extensionNumbers struct {
AbsSendTime int // http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
}
timegen func() time.Time
}
-// NewPacketizer returns a new instance of a Packetizer for a specific payloader
-func NewPacketizer(mtu uint16, pt uint8, ssrc uint32, payloader Payloader, sequencer Sequencer, clockRate uint32) Packetizer {
+// NewPacketizer returns a new instance of a Packetizer for a specific payloader.
+func NewPacketizer(
+ mtu uint16,
+ pt uint8,
+ ssrc uint32,
+ payloader Payloader,
+ sequencer Sequencer,
+ clockRate uint32,
+) Packetizer {
return &packetizer{
MTU: mtu,
PayloadType: pt,
@@ -44,11 +59,59 @@ func NewPacketizer(mtu uint16, pt uint8, ssrc uint32, payloader Payloader, seque
}
}
+// WithSSRC sets the SSRC for the Packetizer.
+func WithSSRC(ssrc uint32) func(*packetizer) {
+ return func(p *packetizer) {
+ p.SSRC = ssrc
+ }
+}
+
+// WithPayloadType sets the PayloadType for the Packetizer.
+func WithPayloadType(pt uint8) func(*packetizer) {
+ return func(p *packetizer) {
+ p.PayloadType = pt
+ }
+}
+
+// WithTimestamp sets the initial Timestamp for the Packetizer.
+func WithTimestamp(timestamp uint32) func(*packetizer) {
+ return func(p *packetizer) {
+ p.Timestamp = timestamp
+ }
+}
+
+// PacketizerOption is a function that configures a RTP Packetizer.
+type PacketizerOption func(*packetizer)
+
+// NewPacketizerWithOptions returns a new instance of a Packetizer with the given options.
+func NewPacketizerWithOptions(
+ mtu uint16,
+ payloader Payloader,
+ sequencer Sequencer,
+ clockRate uint32,
+ options ...PacketizerOption,
+) Packetizer {
+ packetizerInstance := &packetizer{
+ MTU: mtu,
+ Payloader: payloader,
+ Sequencer: sequencer,
+ Timestamp: globalMathRandomGenerator.Uint32(),
+ ClockRate: clockRate,
+ timegen: time.Now,
+ }
+
+ for _, option := range options {
+ option(packetizerInstance)
+ }
+
+ return packetizerInstance
+}
+
func (p *packetizer) EnableAbsSendTime(value int) {
p.extensionNumbers.AbsSendTime = value
}
-// Packetize packetizes the payload of an RTP packet and returns one or more RTP packets
+// Packetize packetizes the payload of an RTP packet and returns one or more RTP packets.
func (p *packetizer) Packetize(payload []byte, samples uint32) []*Packet {
// Guard against an empty payload
if len(payload) == 0 {
@@ -69,6 +132,7 @@ func (p *packetizer) Packetize(payload []byte, samples uint32) []*Packet {
SequenceNumber: p.Sequencer.NextSequenceNumber(),
Timestamp: p.Timestamp, // Figure out how to do timestamps
SSRC: p.SSRC,
+ CSRC: []uint32{},
},
Payload: pp,
}
@@ -82,7 +146,7 @@ func (p *packetizer) Packetize(payload []byte, samples uint32) []*Packet {
if err != nil {
return nil // never happens
}
- err = packets[len(packets)-1].SetExtension(uint8(p.extensionNumbers.AbsSendTime), b)
+ err = packets[len(packets)-1].SetExtension(uint8(p.extensionNumbers.AbsSendTime), b) // nolint: gosec // G115
if err != nil {
return nil // never happens
}
@@ -91,8 +155,40 @@ func (p *packetizer) Packetize(payload []byte, samples uint32) []*Packet {
return packets
}
+// GeneratePadding returns required padding-only packages.
+func (p *packetizer) GeneratePadding(samples uint32) []*Packet {
+ // Guard against an empty payload
+ if samples == 0 {
+ return nil
+ }
+
+ packets := make([]*Packet, samples)
+
+ for i := 0; i < int(samples); i++ {
+ pp := make([]byte, 255)
+ pp[254] = 255
+
+ packets[i] = &Packet{
+ Header: Header{
+ Version: 2,
+ Padding: true,
+ Extension: false,
+ Marker: false,
+ PayloadType: p.PayloadType,
+ SequenceNumber: p.Sequencer.NextSequenceNumber(),
+ Timestamp: p.Timestamp, // Use latest timestamp
+ SSRC: p.SSRC,
+ CSRC: []uint32{},
+ },
+ Payload: pp,
+ }
+ }
+
+ return packets
+}
+
// SkipSamples causes a gap in sample count between Packetize requests so the
-// RTP payloads produced have a gap in timestamps
+// RTP payloads produced have a gap in timestamps.
func (p *packetizer) SkipSamples(skippedSamples uint32) {
p.Timestamp += skippedSamples
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/partitionheadchecker.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/partitionheadchecker.go
index 6ec2a7631..51f16a961 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/partitionheadchecker.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/partitionheadchecker.go
@@ -1,6 +1,9 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtp
-// PartitionHeadChecker is the interface that checks whether the packet is keyframe or not
+// PartitionHeadChecker is the interface that checks whether the packet is keyframe or not.
type PartitionHeadChecker interface {
IsPartitionHead([]byte) bool
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/payload_types.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/payload_types.go
new file mode 100644
index 000000000..a9bf2272a
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/payload_types.go
@@ -0,0 +1,68 @@
+// SPDX-FileCopyrightText: 2024 The Pion community
+// SPDX-License-Identifier: MIT
+
+package rtp
+
+// https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml
+// https://en.wikipedia.org/wiki/RTP_payload_formats
+
+// Audio Payload Types as defined in https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml
+const (
+ // PayloadTypePCMU is a payload type for ITU-T G.711 PCM μ-Law audio 64 kbit/s (RFC 3551).
+ PayloadTypePCMU = 0
+ // PayloadTypeGSM is a payload type for European GSM Full Rate audio 13 kbit/s (GSM 06.10).
+ PayloadTypeGSM = 3
+ // PayloadTypeG723 is a payload type for ITU-T G.723.1 audio (RFC 3551).
+ PayloadTypeG723 = 4
+ // PayloadTypeDVI4_8000 is a payload type for IMA ADPCM audio 32 kbit/s (RFC 3551).
+ PayloadTypeDVI4_8000 = 5
+ // PayloadTypeDVI4_16000 is a payload type for IMA ADPCM audio 64 kbit/s (RFC 3551).
+ PayloadTypeDVI4_16000 = 6
+ // PayloadTypeLPC is a payload type for Experimental Linear Predictive Coding audio 5.6 kbit/s (RFC 3551).
+ PayloadTypeLPC = 7
+ // PayloadTypePCMA is a payload type for ITU-T G.711 PCM A-Law audio 64 kbit/s (RFC 3551).
+ PayloadTypePCMA = 8
+ // PayloadTypeG722 is a payload type for ITU-T G.722 audio 64 kbit/s (RFC 3551).
+ PayloadTypeG722 = 9
+ // PayloadTypeL16Stereo is a payload type for Linear PCM 16-bit Stereo audio 1411.2 kbit/s, uncompressed (RFC 3551).
+ PayloadTypeL16Stereo = 10
+ // PayloadTypeL16Mono is a payload type for Linear PCM 16-bit audio 705.6 kbit/s, uncompressed (RFC 3551).
+ PayloadTypeL16Mono = 11
+ // PayloadTypeQCELP is a payload type for Qualcomm Code Excited Linear Prediction (RFC 2658, RFC 3551).
+ PayloadTypeQCELP = 12
+ // PayloadTypeCN is a payload type for Comfort noise (RFC 3389).
+ PayloadTypeCN = 13
+ // PayloadTypeMPA is a payload type for MPEG-1 or MPEG-2 audio only (RFC 3551, RFC 2250).
+ PayloadTypeMPA = 14
+ // PayloadTypeG728 is a payload type for ITU-T G.728 audio 16 kbit/s (RFC 3551).
+ PayloadTypeG728 = 15
+ // PayloadTypeDVI4_11025 is a payload type for IMA ADPCM audio 44.1 kbit/s (RFC 3551).
+ PayloadTypeDVI4_11025 = 16
+ // PayloadTypeDVI4_22050 is a payload type for IMA ADPCM audio 88.2 kbit/s (RFC 3551).
+ PayloadTypeDVI4_22050 = 17
+ // PayloadTypeG729 is a payload type for ITU-T G.729 and G.729a audio 8 kbit/s (RFC 3551, RFC 3555).
+ PayloadTypeG729 = 18
+)
+
+// Video Payload Types as defined in https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml
+const (
+ // PayloadTypeCELLB is a payload type for Sun CellB video (RFC 2029).
+ PayloadTypeCELLB = 25
+ // PayloadTypeJPEG is a payload type for JPEG video (RFC 2435).
+ PayloadTypeJPEG = 26
+ // PayloadTypeNV is a payload type for Xerox PARC's Network Video (nv, RFC 3551).
+ PayloadTypeNV = 28
+ // PayloadTypeH261 is a payload type for ITU-T H.261 video (RFC 4587).
+ PayloadTypeH261 = 31
+ // PayloadTypeMPV is a payload type for MPEG-1 and MPEG-2 video (RFC 2250).
+ PayloadTypeMPV = 32
+ // PayloadTypeMP2T is a payload type for MPEG-2 transport stream (RFC 2250).
+ PayloadTypeMP2T = 33
+ // PayloadTypeH263 is a payload type for H.263 video, first version (1996, RFC 3551, RFC 2190).
+ PayloadTypeH263 = 34
+)
+
+const (
+ // PayloadTypeFirstDynamic is a first non-static payload type.
+ PayloadTypeFirstDynamic = 35
+)
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/pkg/frame/av1.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/pkg/frame/av1.go
deleted file mode 100644
index 30525abc0..000000000
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/pkg/frame/av1.go
+++ /dev/null
@@ -1,44 +0,0 @@
-// Package frame provides code to construct complete media frames from packetized media
-package frame
-
-import "github.com/pion/rtp/codecs"
-
-// AV1 represents a collection of OBUs given a stream of AV1 Packets.
-// Each AV1 RTP Packet is a collection of OBU Elements. Each OBU Element may be a full OBU, or just a fragment of one.
-// AV1 provides the tools to construct a collection of OBUs from a collection of OBU Elements. This structure
-// contains an internal cache and should be used for the entire RTP Stream.
-type AV1 struct {
- // Buffer for fragmented OBU. If ReadFrames is called on a RTP Packet
- // that doesn't contain a fully formed OBU
- obuBuffer []byte
-}
-
-func (f *AV1) pushOBUElement(isFirstOBUFragment *bool, obuElement []byte, obuList [][]byte) [][]byte {
- if *isFirstOBUFragment {
- *isFirstOBUFragment = false
- // Discard pushed because we don't have a fragment to combine it with
- if f.obuBuffer == nil {
- return obuList
- }
- obuElement = append(f.obuBuffer, obuElement...)
- f.obuBuffer = nil
- }
- return append(obuList, obuElement)
-}
-
-// ReadFrames processes the codecs.AV1Packet and returns fully constructed frames
-func (f *AV1) ReadFrames(pkt *codecs.AV1Packet) ([][]byte, error) {
- OBUs := [][]byte{}
- isFirstOBUFragment := pkt.Z
-
- for i := range pkt.OBUElements {
- OBUs = f.pushOBUElement(&isFirstOBUFragment, pkt.OBUElements[i], OBUs)
- }
-
- if pkt.Y && len(OBUs) > 0 {
- // Take copy of OBUElement that is being cached
- f.obuBuffer = append(f.obuBuffer, append([]byte{}, OBUs[len(OBUs)-1]...)...)
- OBUs = OBUs[:len(OBUs)-1]
- }
- return OBUs, nil
-}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/playoutdelayextension.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/playoutdelayextension.go
new file mode 100644
index 000000000..81d2d5bf0
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/playoutdelayextension.go
@@ -0,0 +1,52 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package rtp
+
+import (
+ "encoding/binary"
+ "errors"
+)
+
+const (
+ playoutDelayExtensionSize = 3
+ playoutDelayMaxValue = (1 << 12) - 1
+)
+
+var errPlayoutDelayInvalidValue = errors.New("invalid playout delay value")
+
+// PlayoutDelayExtension is a extension payload format in
+// http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | ID | len=2 | MIN delay | MAX delay |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// .
+type PlayoutDelayExtension struct {
+ MinDelay, MaxDelay uint16
+}
+
+// Marshal serializes the members to buffer.
+func (p PlayoutDelayExtension) Marshal() ([]byte, error) {
+ if p.MinDelay > playoutDelayMaxValue || p.MaxDelay > playoutDelayMaxValue {
+ return nil, errPlayoutDelayInvalidValue
+ }
+
+ return []byte{
+ byte(p.MinDelay >> 4),
+ byte(p.MinDelay<<4) | byte(p.MaxDelay>>8),
+ byte(p.MaxDelay),
+ }, nil
+}
+
+// Unmarshal parses the passed byte slice and stores the result in the members.
+func (p *PlayoutDelayExtension) Unmarshal(rawData []byte) error {
+ if len(rawData) < playoutDelayExtensionSize {
+ return errTooSmall
+ }
+ p.MinDelay = binary.BigEndian.Uint16(rawData[0:2]) >> 4
+ p.MaxDelay = binary.BigEndian.Uint16(rawData[1:3]) & 0x0FFF
+
+ return nil
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/rand.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/rand.go
index ee8552356..3ddddd1bf 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/rand.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/rand.go
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
package rtp
import (
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/renovate.json b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/renovate.json
index f1614058a..f1bb98c6a 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/renovate.json
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/renovate.json
@@ -1,27 +1,6 @@
{
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
- "config:base",
- ":disableDependencyDashboard"
- ],
- "postUpdateOptions": [
- "gomodTidy"
- ],
- "commitBody": "Generated by renovateBot",
- "packageRules": [
- {
- "matchUpdateTypes": ["minor", "patch", "pin", "digest"],
- "automerge": true
- },
- {
- "packagePatterns": ["^golang.org/x/"],
- "schedule": ["on the first day of the month"]
- }
- ],
- "ignorePaths": [
- ".github/workflows/generate-authors.yml",
- ".github/workflows/lint.yaml",
- ".github/workflows/renovate-go-mod-fix.yaml",
- ".github/workflows/test.yaml",
- ".github/workflows/tidy-check.yaml"
+ "github>pion/renovate-config"
]
}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/rtp.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/rtp.go
index b66b2e4b8..5487232e5 100644
--- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/rtp.go
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/rtp.go
@@ -1,2 +1,5 @@
+// SPDX-FileCopyrightText: 2023 The Pion community